/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.

This file is part of Quake III Arena source code.

Quake III Arena source code 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.

Quake III Arena source code 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 Quake III Arena source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
===========================================================================
*/
// tr_shade.c

#include "tr_local.h"


/*
=============================================================

SURFACE SHADERS

=============================================================
*/

shaderCommands_t tess;


// we must set some things up before beginning any tesselation
// because a surface may be forced to perform a RB_End due to overflow

void RB_BeginSurface( const shader_t* shader, int fogNum )
{
	tess.numIndexes = 0;
	tess.numVertexes = 0;
	tess.shader = shader;
	tess.fogNum = fogNum;
	tess.xstages = (const shaderStage_t**)shader->stages;
	tess.depthFade = DFT_NONE;
	tess.deformsPreApplied = qfalse;

	tess.shaderTime = backEnd.refdef.floatTime - tess.shader->timeOffset;
	if (tess.shader->clampTime && tess.shaderTime >= tess.shader->clampTime) {
		tess.shaderTime = tess.shader->clampTime;
	}
}


static void RB_DrawDynamicLight()
{
	backEnd.pc[RB_LIT_VERTICES_LATECULLTEST] += tess.numVertexes;

	static byte clipBits[SHADER_MAX_VERTEXES];
	const dlight_t* dl = tess.light;
	const cullType_t cullType = tess.shader->cullType;

	for (int i = 0; i < tess.numVertexes; ++i) {
		vec3_t dist;
		VectorSubtract(dl->transformed, tess.xyz[i], dist);

		const float dp = DotProduct(dist, tess.normal[i]);
		if (cullType == CT_FRONT_SIDED && dp <= 0.0f ||
			cullType == CT_BACK_SIDED  && dp >= 0.0f) {
			clipBits[i] = byte(-1);
			continue;
		}

		int clip = 0;
		if (dist[0] > dl->radius)
			clip |= 1;
		else if (dist[0] < -dl->radius)
			clip |= 2;
		if (dist[1] > dl->radius)
			clip |= 4;
		else if (dist[1] < -dl->radius)
			clip |= 8;
		if (dist[2] > dl->radius)
			clip |= 16;
		else if (dist[2] < -dl->radius)
			clip |= 32;

		clipBits[i] = clip;
	}

	// build a list of triangles that need light
	int numIndexes = 0;
	for (int i = 0; i < tess.numIndexes; i += 3) {
		const int a = tess.indexes[i + 0];
		const int b = tess.indexes[i + 1];
		const int c = tess.indexes[i + 2];
		if (!(clipBits[a] & clipBits[b] & clipBits[c])) {
			tess.dlIndexes[numIndexes + 0] = a;
			tess.dlIndexes[numIndexes + 1] = b;
			tess.dlIndexes[numIndexes + 2] = c;
			numIndexes += 3;
		}
	}
	tess.dlNumIndexes = numIndexes;

	backEnd.pc[RB_LIT_INDICES_LATECULL_IN] += numIndexes;
	backEnd.pc[RB_LIT_INDICES_LATECULL_OUT] += tess.numIndexes - numIndexes;

	if (numIndexes <= 0)
		return;

	backEnd.pc[RB_LIT_BATCHES]++;
	backEnd.pc[RB_LIT_VERTICES] += tess.numVertexes;
	backEnd.pc[RB_LIT_INDICES] += tess.numIndexes;
	gal.Draw(DT_DYNAMIC_LIGHT);
}


static void RB_DrawGeneric()
{
	if (tess.depthFade == DFT_NONE && tess.fogNum && tess.shader->fogPass) {
		tess.drawFog = qtrue;

		unsigned int fogStateBits = GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA;
		if (tess.shader->fogPass == FP_EQUAL)
			fogStateBits |= GLS_DEPTHFUNC_EQUAL;
		tess.fogStateBits = fogStateBits;

		const fog_t* fog = tr.world->fogs + tess.fogNum;
		for (int i = 0; i < tess.numVertexes; ++i) {
			*(int*)&tess.svarsFog.colors[i] = fog->colorInt;
		}
		RB_CalcFogTexCoords((float*)tess.svarsFog.texcoords, 0, tess.numVertexes);
		tess.svarsFog.texcoordsptr = tess.svarsFog.texcoords;
	} else {
		tess.drawFog = qfalse;
	}

	backEnd.pc[RB_BATCHES]++;
	backEnd.pc[RB_VERTICES] += tess.numVertexes;
	backEnd.pc[RB_INDICES] += tess.numIndexes;
	gal.Draw(tess.depthFade != DFT_NONE ? DT_SOFT_SPRITE : DT_GENERIC);
}


void RB_EndSurface()
{
	shaderCommands_t* input = &tess;

	if (!input->numIndexes || !input->numVertexes)
		return;

	if (input->indexes[SHADER_MAX_INDEXES-1] != 0) {
		ri.Error( ERR_DROP, "RB_EndSurface() - SHADER_MAX_INDEXES hit" );
	}
	if (input->xyz[SHADER_MAX_VERTEXES-1][0] != 0) {
		ri.Error( ERR_DROP, "RB_EndSurface() - SHADER_MAX_VERTEXES hit" );
	}

	// for debugging of sort order issues, stop rendering after a given sort value
	if ( r_debugSort->value > 0.0f && r_debugSort->value < tess.shader->sort ) {
		return;
	}

	const shader_t* shader = input->shader;
	if (shader->sort == SS_ENVIRONMENT) {
		RB_DrawSky();
	} else {
		if (!tess.deformsPreApplied) {
			RB_DeformTessGeometry(0, tess.numVertexes, 0, tess.numIndexes);
			for (int i = 0; i < shader->numStages; ++i) {
				R_ComputeColors(shader->stages[i], tess.svars[i], 0, tess.numVertexes);
				R_ComputeTexCoords(shader->stages[i], tess.svars[i], 0, tess.numVertexes, qtrue);
			}
		}

		if (input->pass == shaderCommands_t::TP_LIGHT)
			RB_DrawDynamicLight();
		else
			RB_DrawGeneric();
	}

	// draw debugging stuff
	if (!backEnd.projection2D &&
		(tess.pass == shaderCommands_t::TP_BASE) &&
		tess.numIndexes > 0 &&
		tess.numVertexes > 0) {
		if (r_showtris->integer) {
			RB_PushSingleStageShader(GLS_POLYMODE_LINE | GLS_DEPTHMASK_TRUE, CT_FRONT_SIDED);
			R_ComputeColors(tess.shader->stages[0], tess.svars[0], 0, tess.numVertexes);
			gal.SetDepthRange(0, 0);
			gal.Draw(DT_GENERIC);
			gal.SetDepthRange(0, 1);
			RB_PopShader();
		}
		if (r_shownormals->integer) {
			// we only draw the normals for the first (SHADER_MAX_VERTEXES / 2 - 1) vertices
			int nv = tess.numVertexes;
			if (nv >= SHADER_MAX_VERTEXES / 2)
				nv = SHADER_MAX_VERTEXES / 2 - 1;
			for (int i = 0, j = nv; i < nv; ++i, ++j) {
				VectorMA(input->xyz[i], 2, input->normal[i], tess.xyz[j]);
			}
			for (int i = 0, j = 0; i < nv; ++i, j += 3) {
				tess.indexes[j + 0] = i;
				tess.indexes[j + 1] = i;
				tess.indexes[j + 2] = i + nv;
			}
			tess.numVertexes = nv * 2;
			tess.numIndexes = nv * 3;
			RB_PushSingleStageShader(GLS_POLYMODE_LINE | GLS_DEPTHMASK_TRUE, CT_FRONT_SIDED);
			shaderStage_t* const stage = tess.shader->stages[0];
			stage->rgbGen = CGEN_CONST;
			stage->constantColor[0] = 0;
			stage->constantColor[1] = 0;
			stage->constantColor[2] = 255;
			stage->constantColor[3] = 255;
			R_ComputeColors(tess.shader->stages[0], tess.svars[0], 0, tess.numVertexes);
			gal.SetDepthRange(0, 0);
			gal.Draw(DT_GENERIC);
			gal.SetDepthRange(0, 1);
			RB_PopShader();
		}
	}

	// clear shader so we can tell we don't have any unclosed surfaces
	tess.numIndexes = 0;
}