
#include "ABuffer.h"

//=======================================================================================
//=======================================================================================

ABuffer::ABuffer(int w,int h,int subpixels)
{
	SS_ASSERT(w > 0);
	SS_ASSERT(h > 0);
	SS_ASSERT(subpixels > 0);

	m_width				= w;
	m_height			= h;
	m_subpixels			= subpixels;
	m_oofsubpixels		= 1.0f / (float)subpixels;

	m_firstFragments = new int[w*h*subpixels];
	m_fragmentsAllocated = 4*w*h*subpixels;
	m_fragments.resize(m_fragmentsAllocated);

	m_sortBuffer.resize(100);

	clear();
}

//=======================================================================================
//=======================================================================================

ABuffer::~ABuffer(void)
{
	if( m_firstFragments )
	{
		delete[] m_firstFragments;
	}
	m_firstFragments = NULL;
	m_width = 0;
	m_height = 0;
	m_subpixels = 0;
}

//=======================================================================================
//=======================================================================================

void ABuffer::clear(void)
{
	memset(m_firstFragments,-1,sizeof(int)*m_width*m_height*m_subpixels);

	m_firstFreeFragment = 0;
	m_averageFragmentsPerPixel = 0;
}

//=======================================================================================
//=======================================================================================

void ABuffer::appendFragment(int x, int y, int subpixel, const ABuffer::Fragment &f)
{
	SS_ASSERT(x>=0&&x<m_width);
	SS_ASSERT(y>=0&&y<m_height);
	SS_ASSERT(subpixel>=0&&subpixel<m_subpixels);

	int &stub = m_firstFragments[(y*m_width+x)*m_subpixels+subpixel];

	//allocate new one
	if(m_firstFreeFragment == m_fragmentsAllocated)
	{
		svPrint("abuffer fragment resize: %d => %d\n",m_fragmentsAllocated,m_fragmentsAllocated*2);
		m_fragmentsAllocated *= 2;
		m_fragments.resize(m_fragmentsAllocated);
	}
	int newf = m_firstFreeFragment++;
	m_fragments[newf] = f;
	m_fragments[newf].nextFragment = stub;
	stub = newf;
}

//=======================================================================================
//=======================================================================================

void ABuffer::collapseToFrameBuffer	(Color* frameBuffer, const Vector4 &background, bool zrange, float zthreshold)
{
	int totalFragments = 0;
	int totalPixels = 0;

	for(int y=0;y<m_height;y++)
	for(int x=0;x<m_width;x++)
	{
		Vector4 pixelcolor(0,0,0,0);

		for(int j=0;j<m_subpixels;++j)
		{
			unsigned int coverageMask = 0;
			Vector4 subpixelcolor(0,0,0,0);

			//insert fragments into sort buffer
			int fragmentCount = 0;
			int fragmentIndex = m_firstFragments[(y*m_width+x)*m_subpixels+j];
			while(fragmentIndex != -1)
			{
				if( fragmentCount >= m_sortBuffer.size() )
				{
					svPrint("abuffer sortbuffer resize: %d => %d\n",m_sortBuffer.size(),m_sortBuffer.size()*2);
					m_sortBuffer.resize( fragmentCount * 2 );
				}
				m_sortBuffer[fragmentCount] = &m_fragments[fragmentIndex];				// store fragment pointer
				fragmentIndex				 = m_fragments[fragmentIndex].nextFragment;	// traverse linked list
				fragmentCount++;
			}
			//sort fragments (insertion sort)
			for(int i=0;i<fragmentCount-1;i++)
			{
				for(int k=i+1;k<fragmentCount;k++)
				{
					if( (zrange && (m_sortBuffer[i]->zmin > m_sortBuffer[k]->zmin)) ||
						(!zrange && (m_sortBuffer[i]->z > m_sortBuffer[k]->z)) )
					{
						swap( m_sortBuffer[i], m_sortBuffer[k]);
					}
				}
			}
			if( fragmentCount )
			{
				totalFragments += fragmentCount;
				totalPixels++;
			}

			// outer loop combines separate surfaces
			int i = 0;
			while(i < fragmentCount)
			{
				const Fragment *f1 = m_sortBuffer[i];
				i++;

				float surfaceweight = f1->weight;
				Vector4 surfacecolor;
				f1->color.toVector4( surfacecolor );
				surfacecolor *= surfaceweight;

				// inner loop collects fragments of one connected surface
				if( !zrange )
				{
					//zthreshold comparison
					float prevz = f1->z;
					while(i < fragmentCount)
					{
						const Fragment *f2 = m_sortBuffer[i];

						if( f2->dotSign != f1->dotSign ) break;	//facing the opposite directions
						if( f2->z > prevz + zthreshold ) break;	//no depth overlap

						surfacecolor += f2->color.toVector4() * f2->weight;
						surfaceweight += f2->weight;

						prevz = f2->z;
						i++;
						f1 = f2;
					}
				}
				else
				{
					//zrange comparison
					float zmax = f1->zmax;
					while(i < fragmentCount)
					{
						const Fragment *f2 = m_sortBuffer[i];

						if( f2->dotSign != f1->dotSign ) break;	//facing the opposite directions
						if( f2->zmin > zmax ) break;	//no depth overlap
						if( f2->zmax > zmax ) zmax = f2->zmax;

						surfacecolor += f2->color.toVector4() * f2->weight;
						surfaceweight += f2->weight;

						i++;
						f1 = f2;
					}
				}

				surfacecolor *= 1.0f/surfaceweight;	//normalize color

				if( surfacecolor.w >= 0.999f )
				{	//opaque
					surfacecolor.w = 1.0f;
					coverageMask = 1;	//surface occludes the rest of the surfaces
				}

				subpixelcolor += surfacecolor * (1.0f - subpixelcolor.w);

				if( coverageMask )
					break;	//no more visible surfaces, so early exit
			}

			//if no full coverage, blend with the background color
			if( !coverageMask )
				subpixelcolor += background * (1.0f - subpixelcolor.w);
			pixelcolor += subpixelcolor;
		}
		//write color to frame buffer
		const float oofsubpixels = 1.0f / (float)m_subpixels;
		pixelcolor *= oofsubpixels;
		frameBuffer[y*m_width + x] = pixelcolor;
	}

	m_averageFragmentsPerPixel = (float)totalFragments / (float)totalPixels;
}

//=======================================================================================
//=======================================================================================
