forked from dolphin-emu/dolphin
		
	
		
			
				
	
	
		
			459 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			459 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
// Copyright 2009 Dolphin Emulator Project
 | 
						|
// Licensed under GPLv2+
 | 
						|
// Refer to the license.txt file included.
 | 
						|
 | 
						|
/*
 | 
						|
Portions of this file are based off work by Markus Trenkwalder.
 | 
						|
Copyright (c) 2007, 2008 Markus Trenkwalder
 | 
						|
 | 
						|
All rights reserved.
 | 
						|
 | 
						|
Redistribution and use in source and binary forms, with or without
 | 
						|
modification, are permitted provided that the following conditions are met:
 | 
						|
 | 
						|
* Redistributions of source code must retain the above copyright notice,
 | 
						|
  this list of conditions and the following disclaimer.
 | 
						|
 | 
						|
* Redistributions in binary form must reproduce the above copyright notice,
 | 
						|
  this list of conditions and the following disclaimer in the documentation
 | 
						|
  and/or other materials provided with the distribution.
 | 
						|
 | 
						|
* Neither the name of the library's copyright owner nor the names of its
 | 
						|
  contributors may be used to endorse or promote products derived from this
 | 
						|
  software without specific prior written permission.
 | 
						|
 | 
						|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | 
						|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | 
						|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | 
						|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 | 
						|
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 | 
						|
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 | 
						|
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 | 
						|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 | 
						|
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 | 
						|
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 | 
						|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
						|
*/
 | 
						|
 | 
						|
#include "Common/ChunkFile.h"
 | 
						|
#include "VideoBackends/Software/BPMemLoader.h"
 | 
						|
#include "VideoBackends/Software/Clipper.h"
 | 
						|
#include "VideoBackends/Software/NativeVertexFormat.h"
 | 
						|
#include "VideoBackends/Software/Rasterizer.h"
 | 
						|
#include "VideoBackends/Software/SWStatistics.h"
 | 
						|
#include "VideoBackends/Software/XFMemLoader.h"
 | 
						|
 | 
						|
namespace Clipper
 | 
						|
{
 | 
						|
	enum
 | 
						|
	{
 | 
						|
		NUM_CLIPPED_VERTICES = 33,
 | 
						|
		NUM_INDICES = NUM_CLIPPED_VERTICES + 3
 | 
						|
	};
 | 
						|
 | 
						|
	static float m_ViewOffset[2];
 | 
						|
 | 
						|
	static OutputVertexData ClippedVertices[NUM_CLIPPED_VERTICES];
 | 
						|
	static OutputVertexData *Vertices[NUM_INDICES];
 | 
						|
 | 
						|
	void DoState(PointerWrap &p)
 | 
						|
	{
 | 
						|
		p.DoArray(m_ViewOffset,2);
 | 
						|
		for (auto& ClippedVertice : ClippedVertices)
 | 
						|
			ClippedVertice.DoState(p);
 | 
						|
	}
 | 
						|
 | 
						|
	void Init()
 | 
						|
	{
 | 
						|
		for (int i = 0; i < NUM_CLIPPED_VERTICES; ++i)
 | 
						|
			Vertices[i+3] = &ClippedVertices[i];
 | 
						|
	}
 | 
						|
 | 
						|
	void SetViewOffset()
 | 
						|
	{
 | 
						|
		m_ViewOffset[0] = xfmem.viewport.xOrig - 342;
 | 
						|
		m_ViewOffset[1] = xfmem.viewport.yOrig - 342;
 | 
						|
	}
 | 
						|
 | 
						|
 | 
						|
	enum
 | 
						|
	{
 | 
						|
		SKIP_FLAG = -1,
 | 
						|
		CLIP_POS_X_BIT = 0x01,
 | 
						|
		CLIP_NEG_X_BIT = 0x02,
 | 
						|
		CLIP_POS_Y_BIT = 0x04,
 | 
						|
		CLIP_NEG_Y_BIT = 0x08,
 | 
						|
		CLIP_POS_Z_BIT = 0x10,
 | 
						|
		CLIP_NEG_Z_BIT = 0x20
 | 
						|
	};
 | 
						|
 | 
						|
	static inline int CalcClipMask(OutputVertexData *v)
 | 
						|
	{
 | 
						|
		int cmask = 0;
 | 
						|
		Vec4 pos = v->projectedPosition;
 | 
						|
 | 
						|
		if (pos.w - pos.x < 0)
 | 
						|
			cmask |= CLIP_POS_X_BIT;
 | 
						|
 | 
						|
		if (pos.x + pos.w < 0)
 | 
						|
			cmask |= CLIP_NEG_X_BIT;
 | 
						|
 | 
						|
		if (pos.w - pos.y < 0)
 | 
						|
			cmask |= CLIP_POS_Y_BIT;
 | 
						|
 | 
						|
		if (pos.y + pos.w < 0)
 | 
						|
			cmask |= CLIP_NEG_Y_BIT;
 | 
						|
 | 
						|
		if (pos.w * pos.z > 0)
 | 
						|
			cmask |= CLIP_POS_Z_BIT;
 | 
						|
 | 
						|
		if (pos.z + pos.w < 0)
 | 
						|
			cmask |= CLIP_NEG_Z_BIT;
 | 
						|
 | 
						|
		return cmask;
 | 
						|
	}
 | 
						|
 | 
						|
	static inline void AddInterpolatedVertex(float t, int out, int in, int* numVertices)
 | 
						|
	{
 | 
						|
		Vertices[(*numVertices)++]->Lerp(t, Vertices[out], Vertices[in]);
 | 
						|
	}
 | 
						|
 | 
						|
	#define DIFFERENT_SIGNS(x,y) ((x <= 0 && y > 0) || (x > 0 && y <= 0))
 | 
						|
 | 
						|
	#define CLIP_DOTPROD(I, A, B, C, D) \
 | 
						|
		(Vertices[I]->projectedPosition.x * A + Vertices[I]->projectedPosition.y * B + Vertices[I]->projectedPosition.z * C + Vertices[I]->projectedPosition.w * D)
 | 
						|
 | 
						|
	#define POLY_CLIP( PLANE_BIT, A, B, C, D )							\
 | 
						|
	{																	\
 | 
						|
		if (mask & PLANE_BIT) {											\
 | 
						|
			int idxPrev = inlist[0];									\
 | 
						|
			float dpPrev = CLIP_DOTPROD(idxPrev, A, B, C, D );			\
 | 
						|
			int outcount = 0;											\
 | 
						|
																		\
 | 
						|
			inlist[n] = inlist[0];										\
 | 
						|
			for (int j = 1; j <= n; j++) { 								\
 | 
						|
				int idx = inlist[j];									\
 | 
						|
				float dp = CLIP_DOTPROD(idx, A, B, C, D );				\
 | 
						|
				if (dpPrev >= 0) {										\
 | 
						|
					outlist[outcount++] = idxPrev;						\
 | 
						|
				}														\
 | 
						|
																		\
 | 
						|
				if (DIFFERENT_SIGNS(dp, dpPrev)) {						\
 | 
						|
					if (dp < 0) {										\
 | 
						|
						float t = dp / (dp - dpPrev);					\
 | 
						|
						AddInterpolatedVertex(t, idx, idxPrev, &numVertices);		\
 | 
						|
					} else {											\
 | 
						|
						float t = dpPrev / (dpPrev - dp);				\
 | 
						|
						AddInterpolatedVertex(t, idxPrev, idx, &numVertices);		\
 | 
						|
					}													\
 | 
						|
					outlist[outcount++] = numVertices - 1;				\
 | 
						|
				}														\
 | 
						|
																		\
 | 
						|
				idxPrev = idx;											\
 | 
						|
				dpPrev = dp;											\
 | 
						|
			}															\
 | 
						|
																		\
 | 
						|
			if (outcount < 3)											\
 | 
						|
				continue;												\
 | 
						|
																		\
 | 
						|
	 		{															\
 | 
						|
				int *tmp = inlist;										\
 | 
						|
				inlist = outlist;										\
 | 
						|
				outlist = tmp;											\
 | 
						|
				n = outcount;											\
 | 
						|
			}															\
 | 
						|
		}																\
 | 
						|
	}
 | 
						|
 | 
						|
	#define LINE_CLIP(PLANE_BIT, A, B, C, D )					\
 | 
						|
	{															\
 | 
						|
		if (mask & PLANE_BIT) {									\
 | 
						|
			const float dp0 = CLIP_DOTPROD( 0, A, B, C, D );	\
 | 
						|
			const float dp1 = CLIP_DOTPROD( 1, A, B, C, D );	\
 | 
						|
			const bool neg_dp0 = dp0 < 0;						\
 | 
						|
			const bool neg_dp1 = dp1 < 0;						\
 | 
						|
																\
 | 
						|
			if (neg_dp0 && neg_dp1)								\
 | 
						|
				return;											\
 | 
						|
																\
 | 
						|
			if (neg_dp1) {										\
 | 
						|
				float t = dp1 / (dp1 - dp0);						\
 | 
						|
				if (t > t1) t1 = t;								\
 | 
						|
			} else if (neg_dp0) {								\
 | 
						|
				float t = dp0 / (dp0 - dp1);						\
 | 
						|
				if (t > t0) t0 = t;								\
 | 
						|
			}													\
 | 
						|
		}														\
 | 
						|
	}
 | 
						|
 | 
						|
	static void ClipTriangle(int *indices, int* numIndices)
 | 
						|
	{
 | 
						|
		int mask = 0;
 | 
						|
 | 
						|
		mask |= CalcClipMask(Vertices[0]);
 | 
						|
		mask |= CalcClipMask(Vertices[1]);
 | 
						|
		mask |= CalcClipMask(Vertices[2]);
 | 
						|
 | 
						|
		if (mask != 0)
 | 
						|
		{
 | 
						|
			for (int i = 0; i < 3; i += 3)
 | 
						|
			{
 | 
						|
				int vlist[2][2*6+1];
 | 
						|
				int *inlist = vlist[0], *outlist = vlist[1];
 | 
						|
				int n = 3;
 | 
						|
				int numVertices = 3;
 | 
						|
 | 
						|
				inlist[0] = 0;
 | 
						|
				inlist[1] = 1;
 | 
						|
				inlist[2] = 2;
 | 
						|
 | 
						|
				// mark this triangle as unused in case it should be completely
 | 
						|
				// clipped
 | 
						|
				indices[0] = SKIP_FLAG;
 | 
						|
				indices[1] = SKIP_FLAG;
 | 
						|
				indices[2] = SKIP_FLAG;
 | 
						|
 | 
						|
				POLY_CLIP(CLIP_POS_X_BIT, -1,  0,  0, 1);
 | 
						|
				POLY_CLIP(CLIP_NEG_X_BIT,  1,  0,  0, 1);
 | 
						|
				POLY_CLIP(CLIP_POS_Y_BIT,  0, -1,  0, 1);
 | 
						|
				POLY_CLIP(CLIP_NEG_Y_BIT,  0,  1,  0, 1);
 | 
						|
				POLY_CLIP(CLIP_POS_Z_BIT,  0,  0,  0, 1);
 | 
						|
				POLY_CLIP(CLIP_NEG_Z_BIT,  0,  0,  1, 1);
 | 
						|
 | 
						|
				INCSTAT(swstats.thisFrame.numTrianglesClipped);
 | 
						|
 | 
						|
				// transform the poly in inlist into triangles
 | 
						|
				indices[0] = inlist[0];
 | 
						|
				indices[1] = inlist[1];
 | 
						|
				indices[2] = inlist[2];
 | 
						|
				for (int j = 3; j < n; ++j)
 | 
						|
				{
 | 
						|
					indices[(*numIndices)++] = inlist[0];
 | 
						|
					indices[(*numIndices)++] = inlist[j - 1];
 | 
						|
					indices[(*numIndices)++] = inlist[j];
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	static void ClipLine(int *indices)
 | 
						|
	{
 | 
						|
		int mask = 0;
 | 
						|
		int clip_mask[2] = { 0, 0 };
 | 
						|
 | 
						|
		for (int i = 0; i < 2; ++i)
 | 
						|
		{
 | 
						|
			clip_mask[i] = CalcClipMask(Vertices[i]);
 | 
						|
			mask |= clip_mask[i];
 | 
						|
		}
 | 
						|
 | 
						|
		if (mask == 0)
 | 
						|
			return;
 | 
						|
 | 
						|
		float t0 = 0;
 | 
						|
		float t1 = 0;
 | 
						|
 | 
						|
		// Mark unused in case of early termination
 | 
						|
		// of the macros below. (When fully clipped)
 | 
						|
		indices[0] = SKIP_FLAG;
 | 
						|
		indices[1] = SKIP_FLAG;
 | 
						|
 | 
						|
		LINE_CLIP(CLIP_POS_X_BIT, -1,  0,  0, 1);
 | 
						|
		LINE_CLIP(CLIP_NEG_X_BIT,  1,  0,  0, 1);
 | 
						|
		LINE_CLIP(CLIP_POS_Y_BIT,  0, -1,  0, 1);
 | 
						|
		LINE_CLIP(CLIP_NEG_Y_BIT,  0,  1,  0, 1);
 | 
						|
		LINE_CLIP(CLIP_POS_Z_BIT,  0,  0, -1, 1);
 | 
						|
		LINE_CLIP(CLIP_NEG_Z_BIT,  0,  0,  1, 1);
 | 
						|
 | 
						|
		// Restore the old values as this line
 | 
						|
		// was not fully clipped.
 | 
						|
		indices[0] = 0;
 | 
						|
		indices[1] = 1;
 | 
						|
 | 
						|
		int numVertices = 2;
 | 
						|
 | 
						|
		if (clip_mask[0])
 | 
						|
		{
 | 
						|
			indices[0] = numVertices;
 | 
						|
			AddInterpolatedVertex(t0, 0, 1, &numVertices);
 | 
						|
		}
 | 
						|
 | 
						|
		if (clip_mask[1])
 | 
						|
		{
 | 
						|
			indices[1] = numVertices;
 | 
						|
			AddInterpolatedVertex(t1, 1, 0, &numVertices);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	void ProcessTriangle(OutputVertexData *v0, OutputVertexData *v1, OutputVertexData *v2)
 | 
						|
	{
 | 
						|
		INCSTAT(swstats.thisFrame.numTrianglesIn)
 | 
						|
 | 
						|
		bool backface;
 | 
						|
 | 
						|
		if (!CullTest(v0, v1, v2, backface))
 | 
						|
			return;
 | 
						|
 | 
						|
		int indices[NUM_INDICES] = {
 | 
						|
			0, 1, 2, SKIP_FLAG, SKIP_FLAG, SKIP_FLAG, SKIP_FLAG, SKIP_FLAG, SKIP_FLAG,
 | 
						|
			SKIP_FLAG, SKIP_FLAG, SKIP_FLAG, SKIP_FLAG, SKIP_FLAG, SKIP_FLAG,
 | 
						|
			SKIP_FLAG, SKIP_FLAG, SKIP_FLAG, SKIP_FLAG, SKIP_FLAG, SKIP_FLAG
 | 
						|
		};
 | 
						|
		int numIndices = 3;
 | 
						|
 | 
						|
		if (backface)
 | 
						|
		{
 | 
						|
			Vertices[0] = v0;
 | 
						|
			Vertices[1] = v2;
 | 
						|
			Vertices[2] = v1;
 | 
						|
		}
 | 
						|
		else
 | 
						|
		{
 | 
						|
			Vertices[0] = v0;
 | 
						|
			Vertices[1] = v1;
 | 
						|
			Vertices[2] = v2;
 | 
						|
		}
 | 
						|
 | 
						|
		ClipTriangle(indices, &numIndices);
 | 
						|
 | 
						|
		for (int i = 0; i+3 <= numIndices; i+=3)
 | 
						|
		{
 | 
						|
			_assert_(i < NUM_INDICES);
 | 
						|
			if (indices[i] != SKIP_FLAG)
 | 
						|
			{
 | 
						|
				PerspectiveDivide(Vertices[indices[i]]);
 | 
						|
				PerspectiveDivide(Vertices[indices[i+1]]);
 | 
						|
				PerspectiveDivide(Vertices[indices[i+2]]);
 | 
						|
 | 
						|
				Rasterizer::DrawTriangleFrontFace(Vertices[indices[i]], Vertices[indices[i+1]], Vertices[indices[i+2]]);
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	static void CopyVertex(OutputVertexData *dst, OutputVertexData *src, float dx, float dy, unsigned int sOffset)
 | 
						|
	{
 | 
						|
		dst->screenPosition.x = src->screenPosition.x + dx;
 | 
						|
		dst->screenPosition.y = src->screenPosition.y + dy;
 | 
						|
		dst->screenPosition.z = src->screenPosition.z;
 | 
						|
 | 
						|
		for (int i = 0; i < 3; ++i)
 | 
						|
			dst->normal[i] = src->normal[i];
 | 
						|
 | 
						|
		for (int i = 0; i < 4; ++i)
 | 
						|
			dst->color[0][i] = src->color[0][i];
 | 
						|
 | 
						|
		// todo - s offset
 | 
						|
		for (int i = 0; i < 8; ++i)
 | 
						|
			dst->texCoords[i] = src->texCoords[i];
 | 
						|
	}
 | 
						|
 | 
						|
	void ProcessLine(OutputVertexData *lineV0, OutputVertexData *lineV1)
 | 
						|
	{
 | 
						|
		int indices[4] = { 0, 1, SKIP_FLAG, SKIP_FLAG };
 | 
						|
 | 
						|
		Vertices[0] = lineV0;
 | 
						|
		Vertices[1] = lineV1;
 | 
						|
 | 
						|
		// point to a valid vertex to store to when clipping
 | 
						|
		Vertices[2] = &ClippedVertices[17];
 | 
						|
 | 
						|
		ClipLine(indices);
 | 
						|
 | 
						|
		if (indices[0] != SKIP_FLAG)
 | 
						|
		{
 | 
						|
			OutputVertexData *v0 = Vertices[indices[0]];
 | 
						|
			OutputVertexData *v1 = Vertices[indices[1]];
 | 
						|
 | 
						|
			PerspectiveDivide(v0);
 | 
						|
			PerspectiveDivide(v1);
 | 
						|
 | 
						|
			float dx = v1->screenPosition.x - v0->screenPosition.x;
 | 
						|
			float dy = v1->screenPosition.y - v0->screenPosition.y;
 | 
						|
 | 
						|
			float screenDx = 0;
 | 
						|
			float screenDy = 0;
 | 
						|
 | 
						|
			if (fabsf(dx) > fabsf(dy))
 | 
						|
			{
 | 
						|
				if (dx > 0)
 | 
						|
					screenDy = bpmem.lineptwidth.linesize / -12.0f;
 | 
						|
				else
 | 
						|
					screenDy = bpmem.lineptwidth.linesize / 12.0f;
 | 
						|
			}
 | 
						|
			else
 | 
						|
			{
 | 
						|
				if (dy > 0)
 | 
						|
					screenDx = bpmem.lineptwidth.linesize / 12.0f;
 | 
						|
				else
 | 
						|
					screenDx = bpmem.lineptwidth.linesize / -12.0f;
 | 
						|
			}
 | 
						|
 | 
						|
			OutputVertexData triangle[3];
 | 
						|
 | 
						|
			CopyVertex(&triangle[0], v0, screenDx, screenDy, 0);
 | 
						|
			CopyVertex(&triangle[1], v1, screenDx, screenDy, 0);
 | 
						|
			CopyVertex(&triangle[2], v1, -screenDx, -screenDy, bpmem.lineptwidth.lineoff);
 | 
						|
 | 
						|
			// ccw winding
 | 
						|
			Rasterizer::DrawTriangleFrontFace(&triangle[2], &triangle[1], &triangle[0]);
 | 
						|
 | 
						|
			CopyVertex(&triangle[1], v0, -screenDx, -screenDy, bpmem.lineptwidth.lineoff);
 | 
						|
 | 
						|
			Rasterizer::DrawTriangleFrontFace(&triangle[0], &triangle[1], &triangle[2]);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	bool CullTest(OutputVertexData *v0, OutputVertexData *v1, OutputVertexData *v2, bool &backface)
 | 
						|
	{
 | 
						|
		int mask = CalcClipMask(v0);
 | 
						|
		mask &= CalcClipMask(v1);
 | 
						|
		mask &= CalcClipMask(v2);
 | 
						|
 | 
						|
		if (mask)
 | 
						|
		{
 | 
						|
			INCSTAT(swstats.thisFrame.numTrianglesRejected)
 | 
						|
			return false;
 | 
						|
		}
 | 
						|
 | 
						|
		float x0 = v0->projectedPosition.x;
 | 
						|
		float x1 = v1->projectedPosition.x;
 | 
						|
		float x2 = v2->projectedPosition.x;
 | 
						|
		float y1 = v1->projectedPosition.y;
 | 
						|
		float y0 = v0->projectedPosition.y;
 | 
						|
		float y2 = v2->projectedPosition.y;
 | 
						|
		float w0 = v0->projectedPosition.w;
 | 
						|
		float w1 = v1->projectedPosition.w;
 | 
						|
		float w2 = v2->projectedPosition.w;
 | 
						|
 | 
						|
		float normalZDir = (x0*w2 - x2*w0)*y1 + (x2*y0 - x0*y2)*w1 + (y2*w0 - y0*w2)*x1;
 | 
						|
 | 
						|
		backface = normalZDir <= 0.0f;
 | 
						|
 | 
						|
		if ((bpmem.genMode.cullmode & 1) && !backface) // cull frontfacing
 | 
						|
		{
 | 
						|
			INCSTAT(swstats.thisFrame.numTrianglesCulled)
 | 
						|
			return false;
 | 
						|
		}
 | 
						|
 | 
						|
		if ((bpmem.genMode.cullmode & 2) && backface) // cull backfacing
 | 
						|
		{
 | 
						|
			INCSTAT(swstats.thisFrame.numTrianglesCulled)
 | 
						|
			return false;
 | 
						|
		}
 | 
						|
 | 
						|
		return true;
 | 
						|
	}
 | 
						|
 | 
						|
	void PerspectiveDivide(OutputVertexData *vertex)
 | 
						|
	{
 | 
						|
		Vec4 &projected = vertex->projectedPosition;
 | 
						|
		Vec3 &screen = vertex->screenPosition;
 | 
						|
 | 
						|
		float wInverse = 1.0f/projected.w;
 | 
						|
		screen.x = projected.x * wInverse * xfmem.viewport.wd + m_ViewOffset[0];
 | 
						|
		screen.y = projected.y * wInverse * xfmem.viewport.ht + m_ViewOffset[1];
 | 
						|
		screen.z = projected.z * wInverse * xfmem.viewport.zRange + xfmem.viewport.farZ;
 | 
						|
	}
 | 
						|
 | 
						|
}
 |