/*
 * 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 <GL/glu.h>
#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} * <total # of polygon front vertices>
	    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
    }
}


syntax highlighted by Code2HTML, v. 0.9.1