static Bool waterToggleWiper (CompDisplay *d, CompAction *action, CompActionState state, CompOption *option, int nOption) { CompScreen *s; s = findScreenAtDisplay (d, getIntOptionNamed (option, nOption, "root", 0)); if (s) { WATER_SCREEN (s); if (!ws->wiperHandle) { ws->wiperHandle = compAddTimeout (2000, 2400, waterWiperTimeout, s); } else { compRemoveTimeout (ws->wiperHandle); ws->wiperHandle = 0; } } return FALSE; }
static bool waterLine (CompAction *action, CompAction::State state, CompOption::Vector &options) { XPoint p[2]; float amp; WATER_SCREEN (screen); p[0].x = CompOption::getIntOptionNamed (options, "x0", screen->width () / 4); p[0].y = CompOption::getIntOptionNamed (options, "y0", screen->height () / 2); p[1].x = CompOption::getIntOptionNamed (options, "x1", screen->width () - screen->width () / 4); p[1].y = CompOption::getIntOptionNamed (options, "y1", screen->height () / 2); amp = CompOption::getFloatOptionNamed (options, "amplitude", 0.25f); ws->waterVertices (GL_LINES, p, 2, amp); ws->cScreen->damageScreen (); return false; }
static void waterHandleMotionEvent (CompDisplay *d, Window root) { CompScreen *s; s = findScreenAtDisplay (d, root); if (s) { WATER_SCREEN (s); if (ws->grabIndex) { XPoint p[2]; p[0].x = waterLastPointerX; p[0].y = waterLastPointerY; p[1].x = waterLastPointerX = pointerX; p[1].y = waterLastPointerY = pointerY; waterVertices (s, GL_LINES, p, 2, 0.2f); damageScreen (s); } } }
static bool waterTitleWave (CompAction *action, CompAction::State state, CompOption::Vector &options) { CompWindow *w; int xid; WATER_SCREEN (screen); xid = CompOption::getIntOptionNamed (options, "window", screen->activeWindow ()); w = screen->findWindow (xid); if (w) { const CompWindow::Geometry &g = w->geometry (); XPoint p[2]; p[0].x = g.x () - w->border ().left; p[0].y = g.y () - w->border ().top / 2; p[1].x = g.x () + g.width () + w->border ().right; p[1].y = p[0].y; ws->waterVertices (GL_LINES, p, 2, 0.15f); ws->cScreen->damageScreen (); } return false; }
static void softwarePoints (CompScreen *s, XPoint *p, int n, float add) { WATER_SCREEN (s); while (n--) { SET (p->x - 1, p->y - 1, add); SET (p->x, p->y - 1, add); SET (p->x + 1, p->y - 1, add); SET (p->x - 1, p->y, add); SET (p->x, p->y, add); SET (p->x + 1, p->y, add); SET (p->x - 1, p->y + 1, add); SET (p->x, p->y + 1, add); SET (p->x + 1, p->y + 1, add); p++; } }
static Bool waterToggleRain(CompDisplay * d, CompAction * action, CompActionState state, CompOption * option, int nOption) { CompScreen *s; WATER_DISPLAY(d); s = findScreenAtDisplay(d, getIntOptionNamed(option, nOption, "root", 0)); if (s) { WATER_SCREEN(s); if (!ws->rainHandle) { int delay; delay = wd->opt[WATER_DISPLAY_OPTION_RAIN_DELAY].value.i; ws->rainHandle = compAddTimeout(delay, (float)delay * 1.2, waterRainTimeout, s); } else { compRemoveTimeout(ws->rainHandle); ws->rainHandle = 0; } } return FALSE; }
static void allocTexture (CompScreen *s, int index) { WATER_SCREEN (s); glGenTextures (1, &ws->texture[index]); glBindTexture (ws->target, ws->texture[index]); glTexParameteri (ws->target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri (ws->target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri (ws->target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri (ws->target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexImage2D (ws->target, 0, GL_RGBA, ws->width, ws->height, 0, GL_BGRA, #if IMAGE_BYTE_ORDER == MSBFirst GL_UNSIGNED_INT_8_8_8_8_REV, #else GL_UNSIGNED_BYTE, #endif ws->t0); glBindTexture (ws->target, 0); }
static bool waterToggleRain (CompAction *action, CompAction::State state, CompOption::Vector &options) { /* Remember StateCancel and StateCommit will be broadcast to all actions so we need to verify that we are actually being toggled... */ if (!(state & CompAction::StateTermKey)) return false; /* And only respond to key taps */ if (!(state & CompAction::StateTermTapped)) return false; WATER_SCREEN (screen); if (!ws->rainTimer.active ()) { int delay; delay = ws->optionGetRainDelay (); ws->rainTimer.start (delay, (float) delay * 1.2); } else { ws->rainTimer.stop (); } return false; }
static int fboVertices(CompScreen * s, GLenum type, XPoint * p, int n, float v) { WATER_SCREEN(s); if (!fboPrologue(s, TINDEX(ws, 0))) return 0; glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE); glColor4f(0.0f, 0.0f, 0.0f, v); glPointSize(3.0f); glLineWidth(1.0f); glScalef(1.0f / ws->width, 1.0f / ws->height, 1.0); glTranslatef(0.5f, 0.5f, 0.0f); glBegin(type); while (n--) { glVertex2i(p->x, p->y); p++; } glEnd(); glColor4usv(defaultColor); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); fboEpilogue(s); return 1; }
static void scaleVertices(CompScreen * s, XPoint * p, int n) { WATER_SCREEN(s); while (n--) { p[n].x = (ws->width * p[n].x) / s->width; p[n].y = (ws->height * p[n].y) / s->height; } }
static void waterReset(CompScreen * s) { int size, i, j; WATER_SCREEN(s); ws->height = TEXTURE_SIZE; ws->width = (ws->height * s->width) / s->height; if (s->textureNonPowerOfTwo || (POWER_OF_TWO(ws->width) && POWER_OF_TWO(ws->height))) { ws->target = GL_TEXTURE_2D; ws->tx = ws->ty = 1.0f; } else { ws->target = GL_TEXTURE_RECTANGLE_NV; ws->tx = ws->width; ws->ty = ws->height; } if (!s->fragmentProgram) return; if (s->fbo) { loadWaterProgram(s); if (!ws->fbo) (*s->genFramebuffers) (1, &ws->fbo); } ws->fboStatus = 0; for (i = 0; i < TEXTURE_NUM; i++) { if (ws->texture[i]) { glDeleteTextures(1, &ws->texture[i]); ws->texture[i] = 0; } } if (ws->data) free(ws->data); size = (ws->width + 2) * (ws->height + 2); ws->data = calloc(1, (sizeof(float) * size * 2) + (sizeof(GLubyte) * ws->width * ws->height * 4)); if (!ws->data) return; ws->d0 = ws->data; ws->d1 = (ws->d0 + (size)); ws->t0 = (unsigned char *)(ws->d1 + (size)); for (i = 0; i < ws->height; i++) { for (j = 0; j < ws->width; j++) { (ws->t0 + (ws->width * 4 * i + j * 4))[0] = 0xff; } } }
static void waterDonePaintScreen(CompScreen * s) { WATER_SCREEN(s); if (ws->count) damageScreen(s); UNWRAP(ws, s, donePaintScreen); (*s->donePaintScreen) (s); WRAP(ws, s, donePaintScreen, waterDonePaintScreen); }
static int fboPrologue (CompScreen *s, int tIndex) { WATER_SCREEN (s); if (!ws->fbo) return 0; if (!ws->texture[tIndex]) allocTexture (s, tIndex); (*s->bindFramebuffer) (GL_FRAMEBUFFER_EXT, ws->fbo); (*s->framebufferTexture2D) (GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, ws->target, ws->texture[tIndex], 0); glDrawBuffer (GL_COLOR_ATTACHMENT0_EXT); glReadBuffer (GL_COLOR_ATTACHMENT0_EXT); /* check status the first time */ if (!ws->fboStatus) { ws->fboStatus = (*s->checkFramebufferStatus) (GL_FRAMEBUFFER_EXT); if (ws->fboStatus != GL_FRAMEBUFFER_COMPLETE_EXT) { compLogMessage ("water", CompLogLevelError, "framebuffer incomplete"); (*s->bindFramebuffer) (GL_FRAMEBUFFER_EXT, 0); (*s->deleteFramebuffers) (1, &ws->fbo); glDrawBuffer (GL_BACK); glReadBuffer (GL_BACK); ws->fbo = 0; return 0; } } glViewport (0, 0, ws->width, ws->height); glMatrixMode (GL_PROJECTION); glPushMatrix (); glLoadIdentity (); glOrtho (0.0, 1.0, 0.0, 1.0, -1.0, 1.0); glMatrixMode (GL_MODELVIEW); glPushMatrix (); glLoadIdentity (); return 1; }
static Bool waterSetDisplayOption (CompPlugin *plugin, CompDisplay *display, const char *name, CompOptionValue *value) { CompOption *o; int index; WATER_DISPLAY (display); o = compFindOption (wd->opt, NUM_OPTIONS (wd), name, &index); if (!o) return FALSE; switch (index) { case WATER_DISPLAY_OPTION_OFFSET_SCALE: if (compSetFloatOption (o, value)) { wd->offsetScale = o->value.f * 50.0f; return TRUE; } break; case WATER_DISPLAY_OPTION_RAIN_DELAY: if (compSetIntOption (o, value)) { CompScreen *s; for (s = display->screens; s; s = s->next) { WATER_SCREEN (s); if (!ws->rainHandle) continue; compRemoveTimeout (ws->rainHandle); ws->rainHandle = compAddTimeout (value->i, (float)value->i * 1.2, waterRainTimeout, s); } return TRUE; } break; default: return compSetDisplayOption (display, o, value); } return FALSE; }
static Bool waterWiperTimeout(void *closure) { CompScreen *s = closure; WATER_SCREEN(s); if (ws->count) { if (ws->wiperAngle == 0.0f) ws->wiperSpeed = 2.5f; else if (ws->wiperAngle == 180.0f) ws->wiperSpeed = -2.5f; } return TRUE; }
static bool waterTerminate (CompAction *action, CompAction::State state, CompOption::Vector &options) { WATER_SCREEN (screen); if (ws->grabIndex) { screen->removeGrab (ws->grabIndex, 0); ws->grabIndex = 0; screen->handleEventSetEnabled (ws, false); } return false; }
static void waterUpdate(CompScreen * s, float dt) { GLfloat fade = 1.0f; WATER_SCREEN(s); if (ws->count < 1000) { if (ws->count > 1) fade = 0.90f + ws->count / 10000.0f; else fade = 0.0f; } if (!fboUpdate(s, dt, fade)) softwareUpdate(s, dt, fade); }
static void waterVertices(CompScreen * s, GLenum type, XPoint * p, int n, float v) { WATER_SCREEN(s); if (!s->fragmentProgram) return; scaleVertices(s, p, n); if (!fboVertices(s, type, p, n, v)) softwareVertices(s, type, p, n, v); if (ws->count < 3000) ws->count = 3000; }
static void waterFiniScreen (CompPlugin *p, CompScreen *s) { WaterFunction *function, *next; int i; WATER_SCREEN (s); if (ws->rainHandle) compRemoveTimeout (ws->rainHandle); if (ws->wiperHandle) compRemoveTimeout (ws->wiperHandle); if (ws->fbo) (*s->deleteFramebuffers) (1, &ws->fbo); for (i = 0; i < TEXTURE_NUM; i++) { if (ws->texture[i]) glDeleteTextures (1, &ws->texture[i]); } if (ws->program) (*s->deletePrograms) (1, &ws->program); if (ws->data) free (ws->data); function = ws->bumpMapFunctions; while (function) { destroyFragmentFunction (s, function->handle); next = function->next; free (function); function = next; } UNWRAP (ws, s, preparePaintScreen); UNWRAP (ws, s, donePaintScreen); UNWRAP (ws, s, drawWindowTexture); free (ws); }
static bool waterToggleWiper (CompAction *action, CompAction::State state, CompOption::Vector &options) { WATER_SCREEN (screen); if (!ws->wiperTimer.active ()) { ws->wiperTimer.start (2000, 2400); } else { ws->wiperTimer.stop (); } return false; }
static Bool waterTerminate(CompDisplay * d, CompAction * action, CompActionState state, CompOption * option, int nOption) { CompScreen *s; for (s = d->screens; s; s = s->next) { WATER_SCREEN(s); if (ws->grabIndex) { removeScreenGrab(s, ws->grabIndex, 0); ws->grabIndex = 0; } } return FALSE; }
static Bool waterInitiate (CompDisplay *d, CompAction *action, CompActionState state, CompOption *option, int nOption) { CompScreen *s; unsigned int ui; Window root, child; int xRoot, yRoot, i; for (s = d->screens; s; s = s->next) { WATER_SCREEN (s); if (otherScreenGrabExist (s, "water", NULL)) continue; if (!ws->grabIndex) ws->grabIndex = pushScreenGrab (s, None, "water"); if (XQueryPointer (d->display, s->root, &root, &child, &xRoot, &yRoot, &i, &i, &ui)) { XPoint p; p.x = waterLastPointerX = xRoot; p.y = waterLastPointerY = yRoot; waterVertices (s, GL_POINTS, &p, 1, 0.8f); damageScreen (s); } } if (state & CompActionStateInitButton) action->state |= CompActionStateTermButton; if (state & CompActionStateInitKey) action->state |= CompActionStateTermKey; return FALSE; }
static int loadWaterProgram(CompScreen * s) { char buffer[1024]; WATER_SCREEN(s); if (ws->target == GL_TEXTURE_2D) sprintf(buffer, waterFpString, "2D", "2D", 1.0f / ws->width, 1.0f / ws->width, 1.0f / ws->height, 1.0f / ws->height, "2D", "2D", "2D", "2D"); else sprintf(buffer, waterFpString, "RECT", "RECT", 1.0f, 1.0f, 1.0f, 1.0f, "RECT", "RECT", "RECT", "RECT"); return loadFragmentProgram(s, &ws->program, buffer); }
static bool waterInitiate (CompAction *action, CompAction::State state, CompOption::Vector &options) { unsigned int ui; Window root, child; int xRoot, yRoot, i; WATER_SCREEN (screen); if (!screen->otherGrabExist ("water", NULL)) { if (!ws->grabIndex) { ws->grabIndex = screen->pushGrab (None, "water"); screen->handleEventSetEnabled (ws, true); } if (XQueryPointer (screen->dpy (), screen->root (), &root, &child, &xRoot, &yRoot, &i, &i, &ui)) { XPoint p; p.x = waterLastPointerX = xRoot; p.y = waterLastPointerY = yRoot; ws->waterVertices (GL_POINTS, &p, 1, 1.0f); ws->cScreen->damageScreen (); } } if (state & CompAction::StateInitButton) action->setState (action->state () | CompAction::StateTermButton); if (state & CompAction::StateInitKey) action->setState (action->state () | CompAction::StateTermKey); return false; }
static void waterHandleEvent (CompDisplay *d, XEvent *event) { CompScreen *s; WATER_DISPLAY (d); switch (event->type) { case ButtonPress: s = findScreenAtDisplay (d, event->xbutton.root); if (s) { WATER_SCREEN (s); if (ws->grabIndex) { XPoint p; p.x = pointerX; p.y = pointerY; waterVertices (s, GL_POINTS, &p, 1, 0.8f); damageScreen (s); } } break; case EnterNotify: case LeaveNotify: waterHandleMotionEvent (d, event->xcrossing.root); break; case MotionNotify: waterHandleMotionEvent (d, event->xmotion.root); default: break; } UNWRAP (wd, d, handleEvent); (*d->handleEvent) (d, event); WRAP (wd, d, handleEvent, waterHandleEvent); }
static bool waterPoint (CompAction *action, CompAction::State state, CompOption::Vector &options) { XPoint p; float amp; WATER_SCREEN (screen); p.x = CompOption::getIntOptionNamed (options, "x", screen->width () / 2); p.y = CompOption::getIntOptionNamed (options, "y", screen->height () / 2); amp = CompOption::getFloatOptionNamed (options, "amplitude", 0.5f); ws->waterVertices (GL_POINTS, &p, 1, amp); ws->cScreen->damageScreen (); return false; }
/* bresenham */ static void softwareLines(CompScreen * s, XPoint * p, int n, float v) { int x1, y1, x2, y2; Bool steep; int tmp; int deltaX, deltaY; int error = 0; int yStep; int x, y; WATER_SCREEN(s); #define SWAP(v0, v1) \ tmp = v0; \ v0 = v1; \ v1 = tmp while (n > 1) { x1 = p->x; y1 = p->y; p++; n--; x2 = p->x; y2 = p->y; p++; n--; steep = abs(y2 - y1) > abs(x2 - x1); if (steep) { SWAP(x1, y1); SWAP(x2, y2); } if (x1 > x2) { SWAP(x1, x2); SWAP(y1, y2); } #undef SWAP deltaX = x2 - x1; deltaY = abs(y2 - y1); y = y1; if (y1 < y2) yStep = 1; else yStep = -1; for (x = x1; x <= x2; x++) { if (steep) { SET(y, x, v); } else { SET(x, y, v); } error += deltaY; if (2 * error >= deltaX) { y += yStep; error -= deltaX; } } } }
static void waterDrawWindowTexture(CompWindow * w, CompTexture * texture, const FragmentAttrib * attrib, unsigned int mask) { WATER_SCREEN(w->screen); if (ws->count) { FragmentAttrib fa = *attrib; Bool lighting = w->screen->lighting; int param, function, unit; GLfloat plane[4]; WATER_DISPLAY(w->screen->display); param = allocFragmentParameters(&fa, 1); unit = allocFragmentTextureUnits(&fa, 1); function = getBumpMapFragmentFunction(w->screen, texture, unit, param); if (function) { addFragmentFunction(&fa, function); screenLighting(w->screen, TRUE); (*w->screen->activeTexture) (GL_TEXTURE0_ARB + unit); glBindTexture(ws->target, ws->texture[TINDEX(ws, 0)]); plane[1] = plane[2] = 0.0f; plane[0] = ws->tx / (GLfloat) w->screen->width; plane[3] = 0.0f; glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR); glTexGenfv(GL_S, GL_EYE_PLANE, plane); glEnable(GL_TEXTURE_GEN_S); plane[0] = plane[2] = 0.0f; plane[1] = ws->ty / (GLfloat) w->screen->height; plane[3] = 0.0f; glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR); glTexGenfv(GL_T, GL_EYE_PLANE, plane); glEnable(GL_TEXTURE_GEN_T); (*w->screen->activeTexture) (GL_TEXTURE0_ARB); (*w->screen-> programEnvParameter4f) (GL_FRAGMENT_PROGRAM_ARB, param, texture->matrix.yy * wd->offsetScale, -texture->matrix.xx * wd->offsetScale, 0.0f, 0.0f); } /* to get appropriate filtering of texture */ mask |= PAINT_WINDOW_ON_TRANSFORMED_SCREEN_MASK; UNWRAP(ws, w->screen, drawWindowTexture); (*w->screen->drawWindowTexture) (w, texture, &fa, mask); WRAP(ws, w->screen, drawWindowTexture, waterDrawWindowTexture); if (function) { (*w->screen->activeTexture) (GL_TEXTURE0_ARB + unit); glDisable(GL_TEXTURE_GEN_T); glDisable(GL_TEXTURE_GEN_S); glBindTexture(ws->target, 0); (*w->screen->activeTexture) (GL_TEXTURE0_ARB); screenLighting(w->screen, lighting); } } else { UNWRAP(ws, w->screen, drawWindowTexture); (*w->screen->drawWindowTexture) (w, texture, attrib, mask); WRAP(ws, w->screen, drawWindowTexture, waterDrawWindowTexture); } }
static void softwareUpdate(CompScreen * s, float dt, float fade) { float *dTmp; int i, j; float v0, v1, inv; float accel, value; unsigned char *t0, *t; int dWidth, dHeight; float *d01, *d10, *d11, *d12; WATER_SCREEN(s); if (!ws->texture[TINDEX(ws, 0)]) allocTexture(s, TINDEX(ws, 0)); dt *= K * 2.0f; fade *= 0.99f; dWidth = ws->width + 2; dHeight = ws->height + 2; #define D(d, j) (*((d) + (j))) d01 = ws->d0 + dWidth; d10 = ws->d1; d11 = d10 + dWidth; d12 = d11 + dWidth; for (i = 1; i < dHeight - 1; i++) { for (j = 1; j < dWidth - 1; j++) { accel = dt * (D(d10, j) + D(d12, j) + D(d11, j - 1) + D(d11, j + 1) - 4.0f * D(d11, j)); value = (2.0f * D(d11, j) - D(d01, j) + accel) * fade; CLAMP(value, 0.0f, 1.0f); D(d01, j) = value; } d01 += dWidth; d10 += dWidth; d11 += dWidth; d12 += dWidth; } /* update border */ memcpy(ws->d0, ws->d0 + dWidth, dWidth * sizeof(GLfloat)); memcpy(ws->d0 + dWidth * (dHeight - 1), ws->d0 + dWidth * (dHeight - 2), dWidth * sizeof(GLfloat)); d01 = ws->d0 + dWidth; for (i = 1; i < dHeight - 1; i++) { D(d01, 0) = D(d01, 1); D(d01, dWidth - 1) = D(d01, dWidth - 2); d01 += dWidth; } d10 = ws->d1; d11 = d10 + dWidth; d12 = d11 + dWidth; t0 = ws->t0; /* update texture */ for (i = 0; i < ws->height; i++) { for (j = 0; j < ws->width; j++) { v0 = (D(d12, j) - D(d10, j)) * 1.5f; v1 = (D(d11, j - 1) - D(d11, j + 1)) * 1.5f; /* 0.5 for scale */ inv = 0.5f / sqrtf(v0 * v0 + v1 * v1 + 1.0f); /* add scale and bias to normal */ v0 = v0 * inv + 0.5f; v1 = v1 * inv + 0.5f; /* store normal map in RGB components */ t = t0 + (j * 4); t[0] = (unsigned char)((inv + 0.5f) * 255.0f); t[1] = (unsigned char)(v1 * 255.0f); t[2] = (unsigned char)(v0 * 255.0f); /* store height in A component */ t[3] = (unsigned char)(D(d11, j) * 255.0f); } d10 += dWidth; d11 += dWidth; d12 += dWidth; t0 += ws->width * 4; } #undef D /* swap height maps */ dTmp = ws->d0; ws->d0 = ws->d1; ws->d1 = dTmp; if (ws->texture[TINDEX(ws, 0)]) { glBindTexture(ws->target, ws->texture[TINDEX(ws, 0)]); glTexImage2D(ws->target, 0, GL_RGBA, ws->width, ws->height, 0, GL_BGRA, #if IMAGE_BYTE_ORDER == MSBFirst GL_UNSIGNED_INT_8_8_8_8_REV, #else GL_UNSIGNED_BYTE, #endif ws->t0); } }
/* TODO: a way to control the speed */ static void waterPreparePaintScreen(CompScreen * s, int msSinceLastPaint) { WATER_SCREEN(s); if (ws->count) { ws->count -= 10; if (ws->count < 0) ws->count = 0; if (ws->wiperHandle) { float step, angle0, angle1; Bool wipe = FALSE; XPoint p[3]; p[1].x = s->width / 2; p[1].y = s->height; step = ws->wiperSpeed * msSinceLastPaint / 20.0f; if (ws->wiperSpeed > 0.0f) { if (ws->wiperAngle < 180.0f) { angle0 = ws->wiperAngle; ws->wiperAngle += step; ws->wiperAngle = MIN(ws->wiperAngle, 180.0f); angle1 = ws->wiperAngle; wipe = TRUE; } } else { if (ws->wiperAngle > 0.0f) { angle1 = ws->wiperAngle; ws->wiperAngle += step; ws->wiperAngle = MAX(ws->wiperAngle, 0.0f); angle0 = ws->wiperAngle; wipe = TRUE; } } #define TAN(a) (tanf ((a) * (M_PI / 180.0f))) if (wipe) { if (angle0 > 0.0f) { p[2].x = s->width / 2 - s->height / TAN(angle0); p[2].y = 0; } else { p[2].x = 0; p[2].y = s->height; } if (angle1 < 180.0f) { p[0].x = s->width / 2 - s->height / TAN(angle1); p[0].y = 0; } else { p[0].x = s->width; p[0].y = s->height; } /* software rasterizer doesn't support triangles yet so wiper effect will only work with FBOs right now */ waterVertices(s, GL_TRIANGLES, p, 3, 0.0f); } #undef TAN } waterUpdate(s, 0.8f); } UNWRAP(ws, s, preparePaintScreen); (*s->preparePaintScreen) (s, msSinceLastPaint); WRAP(ws, s, preparePaintScreen, waterPreparePaintScreen); }