/* * Animation plugin for compiz/beryl * * animation.c * * Copyright : (C) 2006 Erkin Bahceci * E-mail : erkinbah@gmail.com * * Based on Wobbly and Minimize plugins by * : David Reveman * E-mail : davidr@novell.com> * * Particle system added by : (C) 2006 Dennis Kasprzyk * E-mail : onestone@beryl-project.org * * Beam-Up added by : Florencio Guimaraes * E-mail : florencio@nexcorp.com.br * * Hexagon tessellator added by : Mike Slegeir * E-mail : mikeslegeir@mail.utexas.edu> * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include "animation-internal.h" static Bool ensureLargerClipCapacity(PolygonSet * pset) { if (pset->clipCapacity == pset->nClips) // if list full { Clip4Polygons *newList = realloc (pset->clips, sizeof(Clip4Polygons) * (pset->clipCapacity + ANIM_CLIP_LIST_INCREMENT)); if (!newList) return FALSE; // reset newly allocated part of this memory to 0 memset(newList + pset->clipCapacity, 0, sizeof(Clip4Polygons) * ANIM_CLIP_LIST_INCREMENT); int *newList2 = realloc (pset->lastClipInGroup, sizeof(int) * (pset->clipCapacity + ANIM_CLIP_LIST_INCREMENT)); if (!newList2) { free(newList); pset->clips = 0; pset->lastClipInGroup = 0; return FALSE; } // reset newly allocated part of this memory to 0 memset(newList2 + pset->clipCapacity, 0, sizeof(int) * ANIM_CLIP_LIST_INCREMENT); pset->clips = newList; pset->clipCapacity += ANIM_CLIP_LIST_INCREMENT; pset->lastClipInGroup = newList2; } return TRUE; } // Frees up polygon objects in pset void freePolygonObjects(PolygonSet * pset) { PolygonObject *p = pset->polygons; if (!p) { pset->nPolygons = 0; return; } int i; for (i = 0; i < pset->nPolygons; i++, p++) { if (p->nVertices > 0) { if (p->vertices) free(p->vertices); if (p->sideIndices) free(p->sideIndices); if (p->normals) free(p->normals); } if (p->effectParameters) free(p->effectParameters); } free(pset->polygons); pset->polygons = 0; pset->nPolygons = 0; } // Frees up intersecting polygon info of PolygonSet clips static void freeClipsPolygons(PolygonSet * pset) { int k; for (k = 0; k < pset->clipCapacity; k++) { if (pset->clips[k].intersectingPolygons) { free(pset->clips[k].intersectingPolygons); pset->clips[k].intersectingPolygons = 0; } if (pset->clips[k].polygonVertexTexCoords) { free(pset->clips[k].polygonVertexTexCoords); pset->clips[k].polygonVertexTexCoords = 0; } pset->clips[k].nIntersectingPolygons = 0; } } // Frees up the whole polygon set void freePolygonSet(AnimWindow * aw) { PolygonSet *pset = aw->polygonSet; freePolygonObjects(pset); if (pset->clipCapacity > 0) { freeClipsPolygons(pset); free(pset->clips); } free(pset); aw->polygonSet = 0; } // Tessellates window into extruded rectangular objects Bool tessellateIntoRectangles(CompWindow * w, int gridSizeX, int gridSizeY, float thickness) { ANIM_WINDOW(w); PolygonSet *pset = aw->polygonSet; if (!pset) return FALSE; int winLimitsX; // boundaries of polygon tessellation int winLimitsY; int winLimitsW; int winLimitsH; if (pset->includeShadows) { winLimitsX = WIN_X(w); winLimitsY = WIN_Y(w); winLimitsW = WIN_W(w) - 1; // avoid artifact on right edge winLimitsH = WIN_H(w); } else { winLimitsX = BORDER_X(w); winLimitsY = BORDER_Y(w); winLimitsW = BORDER_W(w); winLimitsH = BORDER_H(w); } float minRectSize = MIN_WINDOW_GRID_SIZE; float rectW = winLimitsW / (float)gridSizeX; float rectH = winLimitsH / (float)gridSizeY; if (rectW < minRectSize) gridSizeX = winLimitsW / minRectSize; // int div. if (rectH < minRectSize) gridSizeY = winLimitsH / minRectSize; // int div. if (pset->nPolygons != gridSizeX * gridSizeY) { if (pset->nPolygons > 0) freePolygonObjects(pset); pset->nPolygons = gridSizeX * gridSizeY; pset->polygons = calloc(pset->nPolygons, sizeof(PolygonObject)); if (!pset->polygons) { compLogMessage (w->screen->display, "animation", CompLogLevelError, "Not enough memory"); pset->nPolygons = 0; return FALSE; } } thickness /= w->screen->width; pset->thickness = thickness; pset->nTotalFrontVertices = 0; float cellW = (float)winLimitsW / gridSizeX; float cellH = (float)winLimitsH / gridSizeY; float halfW = cellW / 2; float halfH = cellH / 2; float halfThick = pset->thickness / 2; PolygonObject *p = pset->polygons; int x, y; for (y = 0; y < gridSizeY; y++) { float posY = winLimitsY + cellH * (y + 0.5); for (x = 0; x < gridSizeX; x++, p++) { p->centerPos.x = p->centerPosStart.x = winLimitsX + cellW * (x + 0.5); p->centerPos.y = p->centerPosStart.y = posY; p->centerPos.z = p->centerPosStart.z = -halfThick; p->rotAngle = p->rotAngleStart = 0; p->centerRelPos.x = (x + 0.5) / gridSizeX; p->centerRelPos.y = (y + 0.5) / gridSizeY; p->nSides = 4; p->nVertices = 2 * 4; pset->nTotalFrontVertices += 4; // 4 front, 4 back vertices if (!p->vertices) { p->vertices = calloc(8 * 3, sizeof(GLfloat)); } if (!p->vertices) { compLogMessage (w->screen->display, "animation", CompLogLevelError, "Not enough memory"); freePolygonObjects(pset); return FALSE; } GLfloat *pv = p->vertices; // Determine 4 front vertices in ccw direction pv[0] = -halfW; pv[1] = -halfH; pv[2] = halfThick; pv[3] = -halfW; pv[4] = halfH; pv[5] = halfThick; pv[6] = halfW; pv[7] = halfH; pv[8] = halfThick; pv[9] = halfW; pv[10] = -halfH; pv[11] = halfThick; // Determine 4 back vertices in cw direction pv[12] = halfW; pv[13] = -halfH; pv[14] = -halfThick; pv[15] = halfW; pv[16] = halfH; pv[17] = -halfThick; pv[18] = -halfW; pv[19] = halfH; pv[20] = -halfThick; pv[21] = -halfW; pv[22] = -halfH; pv[23] = -halfThick; // 16 indices for 4 sides (for quad strip) if (!p->sideIndices) { p->sideIndices = calloc(4 * 4, sizeof(GLushort)); } if (!p->sideIndices) { compLogMessage (w->screen->display, "animation", CompLogLevelError, "Not enough memory"); freePolygonObjects(pset); return FALSE; } GLushort *ind = p->sideIndices; int id = 0; ind[id++] = 0; ind[id++] = 7; ind[id++] = 6; ind[id++] = 1; ind[id++] = 1; ind[id++] = 6; ind[id++] = 5; ind[id++] = 2; ind[id++] = 2; ind[id++] = 5; ind[id++] = 4; ind[id++] = 3; ind[id++] = 3; ind[id++] = 4; ind[id++] = 7; ind[id++] = 0; // Surface normals if (!p->normals) { p->normals = calloc((2 + 4) * 3, sizeof(GLfloat)); } if (!p->normals) { compLogMessage (w->screen->display, "animation", CompLogLevelError, "Not enough memory"); freePolygonObjects(pset); return FALSE; } GLfloat *nor = p->normals; // Front nor[0] = 0; nor[1] = 0; nor[2] = -1; // Back nor[3] = 0; nor[4] = 0; nor[5] = 1; // Sides nor[6] = -1; nor[7] = 0; nor[8] = 0; nor[9] = 0; nor[10] = 1; nor[11] = 0; nor[12] = 1; nor[13] = 0; nor[14] = 0; nor[15] = 0; nor[16] = -1; nor[17] = 0; // Determine bounding box (to test intersection with clips) p->boundingBox.x1 = -halfW + p->centerPos.x; p->boundingBox.y1 = -halfH + p->centerPos.y; p->boundingBox.x2 = ceil(halfW + p->centerPos.x); p->boundingBox.y2 = ceil(halfH + p->centerPos.y); p->boundSphereRadius = sqrt (halfW * halfW + halfH * halfH + halfThick * halfThick); } } return TRUE; } // Tessellates window into extruded hexagon objects Bool tessellateIntoHexagons(CompWindow * w, int gridSizeX, int gridSizeY, float thickness) { ANIM_WINDOW(w); PolygonSet *pset = aw->polygonSet; if (!pset) return FALSE; int winLimitsX; // boundaries of polygon tessellation int winLimitsY; int winLimitsW; int winLimitsH; if (pset->includeShadows) { winLimitsX = WIN_X(w); winLimitsY = WIN_Y(w); winLimitsW = WIN_W(w) - 1; // avoid artifact on right edge winLimitsH = WIN_H(w); } else { winLimitsX = BORDER_X(w); winLimitsY = BORDER_Y(w); winLimitsW = BORDER_W(w); winLimitsH = BORDER_H(w); } float minSize = 20; float hexW = winLimitsW / (float)gridSizeX; float hexH = winLimitsH / (float)gridSizeY; if (hexW < minSize) gridSizeX = winLimitsW / minSize; // int div. if (hexH < minSize) gridSizeY = winLimitsH / minSize; // int div. int nPolygons = (gridSizeY + 1) * gridSizeX + (gridSizeY + 1) / 2; if (pset->nPolygons != nPolygons) { if (pset->nPolygons > 0) freePolygonObjects(pset); pset->nPolygons = nPolygons; pset->polygons = calloc(pset->nPolygons, sizeof(PolygonObject)); if (!pset->polygons) { compLogMessage (w->screen->display, "animation", CompLogLevelError, "Not enough memory"); pset->nPolygons = 0; return FALSE; } } thickness /= w->screen->width; pset->thickness = thickness; pset->nTotalFrontVertices = 0; float cellW = (float)winLimitsW / gridSizeX; float cellH = (float)winLimitsH / gridSizeY; float halfW = cellW / 2; float twoThirdsH = 2*cellH / 3; float thirdH = cellH / 3; float halfThick = pset->thickness / 2; PolygonObject *p = pset->polygons; int x, y; for (y = 0; y < gridSizeY+1; y++) { float posY = winLimitsY + cellH * (y); int numPolysinRow = (y%2==0) ? gridSizeX : (gridSizeX + 1); // Clip polygons to the window dimensions float topY, topRightY, topLeftY, bottomY, bottomLeftY, bottomRightY; if(y == 0){ topY = topRightY = topLeftY = 0; bottomY = twoThirdsH; bottomLeftY = bottomRightY = thirdH; } else if(y == gridSizeY){ bottomY = bottomLeftY = bottomRightY = 0; topY = -twoThirdsH; topLeftY = topRightY = -thirdH; } else { topY = -twoThirdsH; topLeftY = topRightY = -thirdH; bottomLeftY = bottomRightY = thirdH; bottomY = twoThirdsH; } for (x = 0; x < numPolysinRow; x++, p++) { // Clip odd rows when necessary float topLeftX, topRightX, bottomLeftX, bottomRightX; if(y%2 == 1){ if(x == 0){ topLeftX = bottomLeftX = 0; topRightX = halfW; bottomRightX = halfW; } else if(x == numPolysinRow-1){ topRightX = bottomRightX = 0; topLeftX = -halfW; bottomLeftX = -halfW; } else { topLeftX = bottomLeftX = -halfW; topRightX = bottomRightX = halfW; } } else { topLeftX = bottomLeftX = -halfW; topRightX = bottomRightX = halfW; } p->centerPos.x = p->centerPosStart.x = winLimitsX + cellW * (x + (y%2 ? 0.0 : 0.5)); p->centerPos.y = p->centerPosStart.y = posY; p->centerPos.z = p->centerPosStart.z = -halfThick; p->rotAngle = p->rotAngleStart = 0; p->centerRelPos.x = (x + 0.5) / gridSizeX; p->centerRelPos.y = (y + 0.5) / gridSizeY; p->nSides = 6; p->nVertices = 2 * 6; pset->nTotalFrontVertices += 6; // 6 front, 6 back vertices if (!p->vertices) { p->vertices = calloc(6 * 2 * 3, sizeof(GLfloat)); if (!p->vertices) { compLogMessage (w->screen->display, "animation", CompLogLevelError, "Not enough memory"); freePolygonObjects(pset); return FALSE; } } GLfloat *pv = p->vertices; // Determine 6 front vertices in ccw direction // Starting at top pv[0] = 0; pv[1] = topY; pv[2] = halfThick; pv[3] = topLeftX; pv[4] = topLeftY; pv[5] = halfThick; pv[6] = bottomLeftX; pv[7] = bottomLeftY; pv[8] = halfThick; pv[9] = 0; pv[10] = bottomY; pv[11] = halfThick; pv[12] = bottomRightX; pv[13] = bottomRightY; pv[14] = halfThick; pv[15] = topRightX; pv[16] = topRightY; pv[17] = halfThick; // Determine 6 back vertices in cw direction pv[18] = topRightX; pv[19] = topRightY; pv[20] = -halfThick; pv[21] = bottomRightX; pv[22] = bottomRightY; pv[23] = -halfThick; pv[24] = 0; pv[25] = bottomY; pv[26] = -halfThick; pv[27] = bottomLeftX; pv[28] = bottomLeftY; pv[29] = -halfThick; pv[30] = topLeftX; pv[31] = topLeftY; pv[32] = -halfThick; pv[33] = 0; pv[34] = topY; pv[35] = -halfThick; // 24 indices per 6 sides (for quad strip) if (!p->sideIndices) { p->sideIndices = calloc(4 * 6, sizeof(GLushort)); } if (!p->sideIndices) { compLogMessage (w->screen->display, "animation", CompLogLevelError, "Not enough memory"); freePolygonObjects(pset); return FALSE; } GLushort *ind = p->sideIndices; int id = 0; // upper left side face ind[id++] = 0; ind[id++] = 11; ind[id++] = 10; ind[id++] = 1; // left side face ind[id++] = 1; ind[id++] = 10; ind[id++] = 9; ind[id++] = 2; // lower left side face ind[id++] = 2; ind[id++] = 9; ind[id++] = 8; ind[id++] = 3; // lower right side face ind[id++] = 3; ind[id++] = 8; ind[id++] = 7; ind[id++] = 4; // right side face ind[id++] = 4; ind[id++] = 7; ind[id++] = 6; ind[id++] = 5; // upper right side face ind[id++] = 5; ind[id++] = 6; ind[id++] = 11; ind[id++] = 0; // Surface normals if (!p->normals) { p->normals = calloc((2 + 6) * 3, sizeof(GLfloat)); } if (!p->normals) { compLogMessage (w->screen->display, "animation", CompLogLevelError, "Not enough memory"); freePolygonObjects(pset); return FALSE; } GLfloat *nor = p->normals; // Front nor[0] = 0; nor[1] = 0; nor[2] = -1; // Back nor[3] = 0; nor[4] = 0; nor[5] = 1; // Sides nor[6] = -1; nor[7] = 1; nor[8] = 0; nor[9] = -1; nor[10] = 0; nor[11] = 0; nor[12] = -1; nor[13] = -1; nor[14] = 0; nor[15] = 1; nor[16] = -1; nor[17] = 0; nor[18] = 1; nor[19] = 0; nor[20] = 0; nor[21] = 1; nor[22] = 1; nor[23] = 0; // Determine bounding box (to test intersection with clips) p->boundingBox.x1 = topLeftX + p->centerPos.x; p->boundingBox.y1 = topY + p->centerPos.y; p->boundingBox.x2 = ceil(bottomRightX + p->centerPos.x); p->boundingBox.y2 = ceil(bottomY + p->centerPos.y); p->boundSphereRadius = sqrt((topRightX - topLeftX) * (topRightX - topLeftX) / 4 + (bottomY - topY) * (bottomY - topY) / 4 + halfThick * halfThick); } } if (pset->nPolygons != p - pset->polygons) compLogMessage (w->screen->display, "animation", CompLogLevelError, "%s: Error in tessellateIntoHexagons at line %d!", __FILE__, __LINE__); return TRUE; } void polygonsStoreClips(CompScreen * s, CompWindow * w, int nClip, BoxPtr pClip, int nMatrix, CompMatrix * matrix) { ANIM_WINDOW(w); PolygonSet *pset = aw->polygonSet; // if polygon set is not valid or effect is not 3D (glide w/thickness=0) if (!pset) return; // only draw windows on current viewport if (w->attrib.x > s->width || w->attrib.x + w->width < 0 || w->attrib.y > s->height || w->attrib.y + w->height < 0 || (aw->lastKnownCoords.x != NOT_INITIALIZED && (aw->lastKnownCoords.x != w->attrib.x || aw->lastKnownCoords.y != w->attrib.y))) { return; // since this is not the viewport the window was drawn // just before animation started } Bool dontStoreClips = TRUE; // If this clip doesn't match the corresponding stored clip, // clear the stored clips from this point (aw->nClipsPassed) // to the end and store the new ones instead. if (aw->nClipsPassed < pset->nClips) // if we have clips stored earlier { Clip4Polygons *c = pset->clips + aw->nClipsPassed; // the stored clip at position aw->nClipsPassed // if either clip coordinates or texture matrix is different if (memcmp(pClip, &c->box, sizeof(Box)) || memcmp(matrix, &c->texMatrix, sizeof(CompMatrix))) { // get rid of the clips from here (aw->nClipsPassed) to the end pset->nClips = aw->nClipsPassed; dontStoreClips = FALSE; } } else dontStoreClips = FALSE; if (dontStoreClips) { aw->nClipsPassed += nClip; return; } // For each clip passed to this function for (; nClip--; pClip++, aw->nClipsPassed++) { // New clip if (!ensureLargerClipCapacity(pset)) { compLogMessage (s->display, "animation", CompLogLevelError, "Not enough memory"); return; } Clip4Polygons *newClip = &pset->clips[pset->nClips]; newClip->id = aw->nClipsPassed; memcpy(&newClip->box, pClip, sizeof(Box)); memcpy(&newClip->texMatrix, matrix, sizeof(CompMatrix)); // nMatrix is not used for now // (i.e. only first texture matrix is considered) // avoid clipping along window edge // for the "window contents" clip if (pClip->x1 == BORDER_X(w) && pClip->y1 == BORDER_Y(w) && pClip->x2 == BORDER_X(w) + BORDER_W(w) && pClip->y2 == BORDER_Y(w) + BORDER_H(w)) { newClip->boxf.x1 = pClip->x1 - 0.1f; newClip->boxf.y1 = pClip->y1 - 0.1f; newClip->boxf.x2 = pClip->x2 + 0.1f; newClip->boxf.y2 = pClip->y2 + 0.1f; } else { newClip->boxf.x1 = pClip->x1; newClip->boxf.y1 = pClip->y1; newClip->boxf.x2 = pClip->x2; newClip->boxf.y2 = pClip->y2; } pset->nClips++; aw->clipsUpdated = TRUE; } } // For each rectangular clip, this function finds polygons which // have a bounding box that intersects the clip. For intersecting // polygons, it computes the texture coordinates for the vertices // of that polygon (to draw the clip texture). static Bool processIntersectingPolygons(CompScreen * s, PolygonSet * pset) { int j; for (j = pset->firstNondrawnClip; j < pset->nClips; j++) { Clip4Polygons *c = pset->clips + j; Box *cb = &c->box; int nFrontVerticesTilThisPoly = 0; c->nIntersectingPolygons = 0; // TODO: If it doesn't affect speed much, for each clip, // consider doing 2 passes, counting the intersecting polygons // in the 1st pass and allocating just enough space for those // polygons instead of all polygons in the 2nd pass. int i; for (i = 0; i < pset->nPolygons; i++) { PolygonObject *p = pset->polygons + i; Box *bb = &p->boundingBox; if (bb->x2 <= cb->x1) continue; // no intersection if (bb->y2 <= cb->y1) continue; // no intersection if (bb->x1 >= cb->x2) continue; // no intersection if (bb->y1 >= cb->y2) continue; // no intersection // There is intersection, add clip info if (!c->intersectingPolygons) { c->intersectingPolygons = calloc(pset->nPolygons, sizeof(int)); } // allocate tex coords // 2 {x, y} * 2 {front, back} * if (!c->polygonVertexTexCoords) { c->polygonVertexTexCoords = calloc(2 * 2 * pset->nTotalFrontVertices, sizeof(GLfloat)); } if (!c->intersectingPolygons || !c->polygonVertexTexCoords) { compLogMessage (s->display, "animation", CompLogLevelError, "Not enough memory"); freeClipsPolygons(pset); return FALSE; } c->intersectingPolygons[c->nIntersectingPolygons] = i; int k; for (k = 0; k < p->nSides; k++) { float x = p->vertices[3 * k] + p->centerPosStart.x; float y = p->vertices[3 * k + 1] + p->centerPosStart.y; GLfloat tx; GLfloat ty; if (c->texMatrix.xy != 0.0f || c->texMatrix.yx != 0.0f) { // "non-rect" coordinates tx = COMP_TEX_COORD_XY(&c->texMatrix, x, y); ty = COMP_TEX_COORD_YX(&c->texMatrix, x, y); } else { tx = COMP_TEX_COORD_X(&c->texMatrix, x); ty = COMP_TEX_COORD_Y(&c->texMatrix, y); } // for front vertices int ti = 2 * (2 * nFrontVerticesTilThisPoly + k); c->polygonVertexTexCoords[ti] = tx; c->polygonVertexTexCoords[ti + 1] = ty; // for back vertices ti = 2 * (2 * nFrontVerticesTilThisPoly + (2 * p->nSides - 1 - k)); c->polygonVertexTexCoords[ti] = tx; c->polygonVertexTexCoords[ti + 1] = ty; } c->nIntersectingPolygons++; nFrontVerticesTilThisPoly += p->nSides; } } return TRUE; } // Correct perspective appearance by skewing static void getPerspectiveCorrectionMat (CompWindow *w, PolygonObject *p, GLfloat *mat, float *matf) { CompScreen *s = w->screen; ANIM_SCREEN (s); Point center; if (p) // for CorrectPerspectivePolygon { // use polygon's center center.x = p->centerPos.x; center.y = p->centerPos.y; } else // for CorrectPerspectiveWindow { // use window's center center.x = WIN_X(w) + WIN_W(w) / 2; center.y = WIN_Y(w) + WIN_H(w) / 2; } GLfloat skewx = -(((center.x - as->output->region.extents.x1) - as->output->width / 2) * 1.15); GLfloat skewy = -(((center.y - as->output->region.extents.y1) - as->output->height / 2) * 1.15); if (mat) { // column-major order skew matrix GLfloat skewMat[16] = {1,0,0,0, 0,1,0,0, skewx,skewy,1,0, 0,0,0,1}; memcpy (mat, skewMat, 16 * sizeof (GLfloat)); } else if (matf) { // column-major order skew matrix float skewMat[16] = {1,0,0,0, 0,1,0,0, skewx,skewy,1,0, 0,0,0,1}; memcpy (matf, skewMat, 16 * sizeof (float)); } } void polygonsDrawCustomGeometry(CompScreen * s, CompWindow * w) { ANIM_WINDOW(w); if (// only draw windows on current viewport w->attrib.x > s->width || w->attrib.x + w->width < 0 || w->attrib.y > s->height || w->attrib.y + w->height < 0 || (aw->lastKnownCoords.x != NOT_INITIALIZED && (aw->lastKnownCoords.x != w->attrib.x || aw->lastKnownCoords.y != w->attrib.y))) { return; // since this is not the viewport the window was drawn // just before animation started } PolygonSet *pset = aw->polygonSet; // if polygon set is not valid or effect is not 3D (glide w/thickness=0) if (!pset) return; // TODO: Fix the source of the crash problem // (uninitialized lastClipInGroup) // instead of doing this uninitialized value check if (pset->firstNondrawnClip < 0 || pset->firstNondrawnClip > pset->nClips || (!aw->clipsUpdated && (pset->lastClipInGroup[aw->nDrawGeometryCalls - 1] < 0 || pset->lastClipInGroup[aw->nDrawGeometryCalls - 1] >= pset->nClips))) { return; } if (aw->clipsUpdated && aw->nDrawGeometryCalls > 0) { if (!processIntersectingPolygons(s, pset)) { return; } } int lastClip; // last clip to draw if (aw->clipsUpdated) { lastClip = pset->nClips - 1; } else { lastClip = pset->lastClipInGroup[aw->nDrawGeometryCalls - 1]; } float forwardProgress = defaultAnimProgress(aw); // OpenGL stuff starts here if (pset->doLighting) { glPushAttrib(GL_LIGHT0); glPushAttrib(GL_COLOR_MATERIAL); glPushAttrib(GL_LIGHTING); glEnable(GL_COLOR_MATERIAL); glEnable(GL_LIGHTING); GLfloat ambientLight[] = { 0.3f, 0.3f, 0.3f, 0.3f }; GLfloat diffuseLight[] = { 0.9f, 0.9f, 0.9f, 0.9f }; GLfloat light0Position[] = { -0.5f, 0.5f, -9.0f, 0.0f }; glLightfv(GL_LIGHT0, GL_AMBIENT, ambientLight); glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuseLight); glLightfv(GL_LIGHT0, GL_POSITION, light0Position); } glPushMatrix(); // Store old blend values GLint blendSrcWas, blendDstWas; glGetIntegerv(GL_BLEND_SRC, &blendSrcWas); glGetIntegerv(GL_BLEND_DST, &blendDstWas); GLboolean blendWas = glIsEnabled(GL_BLEND); glPushAttrib(GL_STENCIL_BUFFER_BIT); glDisable(GL_STENCIL_TEST); //GLboolean normalArrayWas = glIsEnabled(GL_NORMAL_ARRAY); //glShadeModel(GL_FLAT); if (pset->doDepthTest) { // Depth test glPushAttrib(GL_DEPTH_FUNC); glPushAttrib(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); glEnable(GL_DEPTH_TEST); } // Clip planes GLdouble clipPlane0[] = { 1, 0, 0, 0 }; GLdouble clipPlane1[] = { 0, 1, 0, 0 }; GLdouble clipPlane2[] = { -1, 0, 0, 0 }; GLdouble clipPlane3[] = { 0, -1, 0, 0 }; // Save old color values GLfloat oldColor[4]; glGetFloatv(GL_CURRENT_COLOR, oldColor); // Determine where we are called from in paint.c's drawWindowTexture // to find out how we should change the opacity GLint prevActiveTexture = GL_TEXTURE0_ARB; Bool saturationFull = TRUE; if (w->screen->canDoSaturated && aw->curPaintAttrib.saturation != COLOR) { saturationFull = FALSE; if (w->screen->canDoSlightlySaturated && aw->curPaintAttrib.saturation > 0) { if (aw->curPaintAttrib.opacity < OPAQUE || aw->curPaintAttrib.brightness != BRIGHT) prevActiveTexture = GL_TEXTURE3_ARB; else prevActiveTexture = GL_TEXTURE2_ARB; } else prevActiveTexture = GL_TEXTURE1_ARB; } float brightness = aw->curPaintAttrib.brightness / 65535.0; float opacity = aw->curPaintAttrib.opacity / 65535.0; float newOpacity = opacity; float fadePassedBy; if (!blendWas) // if translucency is not already turned on in paint.c { glEnable(GL_BLEND); } if (saturationFull) { screenTexEnvMode(w->screen, GL_MODULATE); } else if (prevActiveTexture == GL_TEXTURE2_ARB) { w->screen->activeTexture(prevActiveTexture + 1); enableTexture(w->screen, aw->curTexture, aw->curTextureFilter); } glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // if fade-out duration is not specified per polygon if (pset->allFadeDuration > -1.0f) { fadePassedBy = forwardProgress - (1 - pset->allFadeDuration); // if "fade out starting point" is passed if (fadePassedBy > 1e-5) // if true, allFadeDuration should be > 0 { float opacityFac; if (aw->deceleratingMotion) opacityFac = 1 - decelerateProgress (fadePassedBy / pset->allFadeDuration); else opacityFac = 1 - fadePassedBy / pset->allFadeDuration; if (opacityFac < 0) opacityFac = 0; if (opacityFac > 1) opacityFac = 1; newOpacity = opacity * opacityFac; } } GLfloat skewMat[16]; if (pset->correctPerspective == CorrectPerspectiveWindow) getPerspectiveCorrectionMat (w, NULL, skewMat, NULL); int pass; // 0: draw opaque ones // 2: draw transparent ones for (pass = 0; pass < 2; pass++) { int j; for (j = pset->firstNondrawnClip; j <= lastClip; j++) { Clip4Polygons *c = pset->clips + j; int nFrontVerticesTilThisPoly = 0; int nNewSides = 0; int i; for (i = 0; i < c->nIntersectingPolygons; i++, nFrontVerticesTilThisPoly += nNewSides) { PolygonObject *p = pset->polygons + c->intersectingPolygons[i]; nNewSides = p->nSides; float newOpacityPolygon = newOpacity; // if fade-out duration is specified per polygon if (pset->allFadeDuration == -1.0f) { fadePassedBy = forwardProgress - p->fadeStartTime; // if "fade out starting point" is passed if (fadePassedBy > 1e-5) // if true, then allFadeDuration > 0 { float opacityFac; if (aw->deceleratingMotion) opacityFac = 1 - decelerateProgress (fadePassedBy / p->fadeDuration); else opacityFac = 1 - fadePassedBy / p->fadeDuration; if (opacityFac < 0) opacityFac = 0; if (opacityFac > 1) opacityFac = 1; newOpacityPolygon = newOpacity * opacityFac; } } if (newOpacityPolygon < 1e-5) // if polygon object is invisible continue; if (pass == 0) { if (newOpacityPolygon < 0.9999) // if not fully opaque continue; // draw only opaque ones in pass 0 } else if (newOpacityPolygon > 0.9999) // if fully opaque continue; // draw only non-opaque ones in pass 1 glPushMatrix(); if (pset->correctPerspective == CorrectPerspectivePolygon) getPerspectiveCorrectionMat (w, p, skewMat, NULL); if (pset->correctPerspective != CorrectPerspectiveNone) glMultMatrixf (skewMat); // Center glTranslatef(p->centerPos.x, p->centerPos.y, p->centerPos.z); // Scale z first glScalef(1.0f, 1.0f, 1.0f / s->width); if (pset->extraPolygonTransformFunc) pset->extraPolygonTransformFunc (p); // Move by "rotation axis offset" glTranslatef(p->rotAxisOffset.x, p->rotAxisOffset.y, p->rotAxisOffset.z); // Rotate by desired angle glRotatef(p->rotAngle, p->rotAxis.x, p->rotAxis.y, p->rotAxis.z); // Move back to center glTranslatef(-p->rotAxisOffset.x, -p->rotAxisOffset.y, -p->rotAxisOffset.z); // Scale back glScalef(1.0f, 1.0f, s->width); clipPlane0[3] = -(c->boxf.x1 - p->centerPosStart.x); clipPlane1[3] = -(c->boxf.y1 - p->centerPosStart.y); clipPlane2[3] = (c->boxf.x2 - p->centerPosStart.x); clipPlane3[3] = (c->boxf.y2 - p->centerPosStart.y); glClipPlane(GL_CLIP_PLANE0, clipPlane0); glClipPlane(GL_CLIP_PLANE1, clipPlane1); glClipPlane(GL_CLIP_PLANE2, clipPlane2); glClipPlane(GL_CLIP_PLANE3, clipPlane3); int k; for (k = 0; k < 4; k++) glEnable(GL_CLIP_PLANE0 + k); Bool fadeBackAndSides = pset->backAndSidesFadeDur > 0 && forwardProgress <= pset->backAndSidesFadeDur; float newOpacityPolygon2 = newOpacityPolygon; if (fadeBackAndSides) { // Fade-in opacity for back face and sides newOpacityPolygon2 *= (forwardProgress / pset->backAndSidesFadeDur); } if (saturationFull) glColor4f(brightness, brightness, brightness, newOpacityPolygon2); else if (prevActiveTexture == GL_TEXTURE1_ARB) { // From paint.c GLfloat constant2[4] = { 0.5f + 0.5f * RED_SATURATION_WEIGHT * brightness, 0.5f + 0.5f * GREEN_SATURATION_WEIGHT * brightness, 0.5f + 0.5f * BLUE_SATURATION_WEIGHT * brightness, newOpacityPolygon2 }; glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, constant2); } else //if (prevActiveTexture >= GL_TEXTURE2_ARB) { GLfloat constant2[4] = { brightness, brightness, brightness, newOpacityPolygon2 }; // From paint.c glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, constant2); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE); glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS); glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_CONSTANT); glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR); glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE); glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS); glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_CONSTANT); glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA); } //glEnableClientState(GL_NORMAL_ARRAY); //glEnable(GL_NORMALIZE); // Draw back face glVertexPointer(3, GL_FLOAT, 0, p->vertices + 3 * p->nSides); //glNormalPointer(GL_FLOAT, 0, // p->normals + 3 * p->nSides); glTexCoordPointer(2, GL_FLOAT, 0, c->polygonVertexTexCoords + 2 * (2 * nFrontVerticesTilThisPoly + p->nSides)); glNormal3f(p->normals[3], p->normals[4], p->normals[5]); glDrawArrays(GL_POLYGON, 0, p->nSides); // Front vertex coords glVertexPointer(3, GL_FLOAT, 0, p->vertices); //glNormalPointer(GL_FLOAT, 0, p->normals); glTexCoordPointer(2, GL_FLOAT, 0, c->polygonVertexTexCoords + 2 * 2 * nFrontVerticesTilThisPoly); // TODO: Surface normals for sides // Draw quad strip for sides if (TRUE) { // Do each quad separately to be able to specify // different normals for (k = 0; k < p->nSides; k++) { glNormal3f(p->normals[0], // front face normal for now p->normals[1], p->normals[2]); glDrawElements(GL_QUADS, 4, GL_UNSIGNED_SHORT, p->sideIndices + k * 4); } } else // no need for separate quad rendering glDrawElements(GL_QUAD_STRIP, 2 * (p->nSides + 1), GL_UNSIGNED_SHORT, p->sideIndices); if (fadeBackAndSides) // if opacity was changed just above { // Go back to normal opacity for front face if (saturationFull) glColor4f(brightness, brightness, brightness, newOpacityPolygon); else if (prevActiveTexture == GL_TEXTURE1_ARB) { GLfloat constant[4] = { 0.5f + 0.5f * RED_SATURATION_WEIGHT * brightness, 0.5f + 0.5f * GREEN_SATURATION_WEIGHT * brightness, 0.5f + 0.5f * BLUE_SATURATION_WEIGHT * brightness, newOpacityPolygon }; glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, constant); } else { GLfloat constant[4] = { brightness, brightness, brightness, newOpacityPolygon }; glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, constant); } } // Draw front face glNormal3f(p->normals[0], p->normals[1], p->normals[2]); glDrawArrays(GL_POLYGON, 0, p->nSides); for (k = 0; k < 4; k++) glDisable(GL_CLIP_PLANE0 + k); glPopMatrix(); } } } // Restore // ----------------------------------------- // Restore old color values glColor4f(oldColor[0], oldColor[1], oldColor[2], oldColor[3]); glPopAttrib(); if (pset->doDepthTest) { glPopAttrib(); glPopAttrib(); } // Restore texture stuff if (saturationFull) screenTexEnvMode(w->screen, GL_REPLACE); else if (prevActiveTexture == GL_TEXTURE2_ARB) { disableTexture(w->screen, aw->curTexture); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); w->screen->activeTexture(prevActiveTexture); } // Restore blend values if (!blendWas) glDisable(GL_BLEND); glBlendFunc(blendSrcWas, blendDstWas); glPopMatrix(); if (pset->doLighting) // && !s->lighting) { glPopAttrib(); glPopAttrib(); glPopAttrib(); } if (aw->clipsUpdated) // set end mark for this group of clips pset->lastClipInGroup[aw->nDrawGeometryCalls - 1] = lastClip; // Next time, start drawing from next group of clips pset->firstNondrawnClip = pset->lastClipInGroup[aw->nDrawGeometryCalls - 1] + 1; } void polygonsPrePaintWindow(CompScreen * s, CompWindow * w) { ANIM_WINDOW(w); if (aw->polygonSet) aw->polygonSet->firstNondrawnClip = 0; } void polygonsPostPaintWindow(CompScreen * s, CompWindow * w) { ANIM_WINDOW(w); if (aw->clipsUpdated && // clips should be dropped only in the 1st step aw->polygonSet && aw->nDrawGeometryCalls == 0) // if clips not drawn { // drop these unneeded clips (e.g. ones passed by blurfx) aw->polygonSet->nClips = aw->polygonSet->firstNondrawnClip; } } // Computes polygon's new position and orientation // with linear movement void polygonsLinearAnimStepPolygon(CompWindow * w, PolygonObject * p, float forwardProgress) { float moveProgress = forwardProgress - p->moveStartTime; if (p->moveDuration > 0) moveProgress /= p->moveDuration; if (moveProgress < 0) moveProgress = 0; else if (moveProgress > 1) moveProgress = 1; p->centerPos.x = moveProgress * p->finalRelPos.x + p->centerPosStart.x; p->centerPos.y = moveProgress * p->finalRelPos.y + p->centerPosStart.y; p->centerPos.z = 1.0f / w->screen->width * moveProgress * p->finalRelPos.z + p->centerPosStart.z; p->rotAngle = moveProgress * p->finalRotAng + p->rotAngleStart; } // Similar to polygonsLinearAnimStepPolygon, // but slightly ac/decelerates movement void polygonsDeceleratingAnimStepPolygon(CompWindow * w, PolygonObject * p, float forwardProgress) { float moveProgress = forwardProgress - p->moveStartTime; if (p->moveDuration > 0) moveProgress /= p->moveDuration; if (moveProgress < 0) moveProgress = 0; else if (moveProgress > 1) moveProgress = 1; moveProgress = decelerateProgress(moveProgress); p->centerPos.x = moveProgress * p->finalRelPos.x + p->centerPosStart.x; p->centerPos.y = moveProgress * p->finalRelPos.y + p->centerPosStart.y; p->centerPos.z = 1.0f / w->screen->width * moveProgress * p->finalRelPos.z + p->centerPosStart.z; p->rotAngle = moveProgress * p->finalRotAng + p->rotAngleStart; } Bool polygonsAnimStep(CompScreen * s, CompWindow * w, float time) { if (!defaultAnimStep(s, w, time)) return FALSE; ANIM_WINDOW(w); float forwardProgress = defaultAnimProgress(aw); if (aw->polygonSet) { if (animEffectPropertiesTmp[aw->curAnimEffect]. animStepPolygonFunc) { int i; for (i = 0; i < aw->polygonSet->nPolygons; i++) animEffectPropertiesTmp[aw->curAnimEffect]. animStepPolygonFunc (w, &aw->polygonSet->polygons[i], forwardProgress); } } else compLogMessage (s->display, "animation", CompLogLevelDebug, "%s: pset null at line %d\n",__FILE__, __LINE__); return TRUE; } void polygonsUpdateBB (CompOutput *output, CompWindow * w) { CompScreen *s = w->screen; ANIM_WINDOW (w); PolygonSet *pset = aw->polygonSet; if (!pset) return; CompTransform wTransform; CompTransform wTransform2; resetToIdentity (&wTransform2); prepareTransform (s, output, &wTransform, &wTransform2); GLdouble dModel[16]; GLdouble dProjection[16]; int i; for (i = 0; i < 16; i++) { dProjection[i] = s->projection[i]; } GLint viewport[4] = {output->region.extents.x1, output->region.extents.y1, output->width, output->height}; GLdouble px, py, pz; PolygonObject *p = aw->polygonSet->polygons; CompTransform *modelViewTransform = &wTransform; float skewMat[16]; if (pset->correctPerspective == CorrectPerspectiveWindow) { getPerspectiveCorrectionMat (w, NULL, NULL, skewMat); matmul4 (wTransform2.m, wTransform.m, skewMat); } if (pset->correctPerspective == CorrectPerspectiveWindow || pset->correctPerspective == CorrectPerspectivePolygon) modelViewTransform = &wTransform2; for (i = 0; i < aw->polygonSet->nPolygons; i++, p++) { if (pset->correctPerspective == CorrectPerspectivePolygon) { getPerspectiveCorrectionMat (w, p, NULL, skewMat); matmul4 (wTransform2.m, wTransform.m, skewMat); } // if modelViewTransform == wTransform2, then // it changes for each polygon int j; for (j = 0; j < 16; j++) dModel[j] = modelViewTransform->m[j]; Point3d center = p->centerPos; float radius = p->boundSphereRadius + 2; float zradius = radius / s->width; #define N_POINTS 8 // Corners of almost-bounding rect. prism Point3d cubeCorners[N_POINTS] = {{center.x - radius, center.y - radius, center.z + zradius}, {center.x - radius, center.y + radius, center.z + zradius}, {center.x + radius, center.y - radius, center.z + zradius}, {center.x + radius, center.y + radius, center.z + zradius}, {center.x - radius, center.y - radius, center.z - zradius}, {center.x - radius, center.y + radius, center.z - zradius}, {center.x + radius, center.y - radius, center.z - zradius}, {center.x + radius, center.y + radius, center.z - zradius}}; Point3d *pnt = cubeCorners; for (j = 0; j < N_POINTS; j++, pnt++) { if (!gluProject (pnt->x, pnt->y, pnt->z, dModel, dProjection, viewport, &px, &py, &pz)) return; py = s->height - py; expandBoxWithPoint (&aw->BB, px + 0.5, py + 0.5); } #undef N_POINTS } }