/* -------------------------------------------------------------------------------

   Copyright (C) 1999-2007 id Software, Inc. and contributors.
   For a list of contributors, see the accompanying CONTRIBUTORS file.

   This file is part of GtkRadiant.

   GtkRadiant 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.

   GtkRadiant 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 GtkRadiant; if not, write to the Free Software
   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

   ----------------------------------------------------------------------------------

   Foliage code for Wolfenstein: Enemy Territory by ydnar@splashdamage.com

   ------------------------------------------------------------------------------- */



/* dependencies */
#include "q3map2.h"



#define MAX_FOLIAGE_INSTANCES   8192

static int numFoliageInstances;
static foliageInstance_t foliageInstances[ MAX_FOLIAGE_INSTANCES ];



/*
   SubdivideFoliageTriangle_r()
   recursively subdivides a triangle until the triangle is smaller than
   the desired density, then pseudo-randomly sets a point
 */

static void SubdivideFoliageTriangle_r( const foliage_t& foliage, const TriRef& tri ){
	int max;


	/* limit test */
	if ( numFoliageInstances >= MAX_FOLIAGE_INSTANCES ) {
		return;
	}

	/* plane test */
	{
		Plane3f plane;

		/* make a plane */
		if ( !PlaneFromPoints( plane, tri[ 0 ]->xyz, tri[ 1 ]->xyz, tri[ 2 ]->xyz ) ) {
			return;
		}

		/* if normal is too far off vertical, then don't place an instance */
		if ( plane.normal().z() < 0.5f ) {
			return;
		}
	}

	/* subdivide calc */
	{
		/* get instance */
		foliageInstance_t *fi = &foliageInstances[ numFoliageInstances ];

		/* find the longest edge and split it */
		max = -1;
		float maxDist = 0;
		fi->xyz.set( 0 );
		fi->normal.set( 0 );
		for ( int i = 0; i < 3; ++i )
		{
			const float dist = vector3_length_squared( tri[ i ]->xyz - tri[ ( i + 1 ) % 3 ]->xyz );

			/* longer? */
			if ( dist > maxDist ) {
				maxDist = dist;
				max = i;
			}

			/* add to centroid */
			fi->xyz += tri[ i ]->xyz;
			fi->normal += tri[ i ]->normal;
		}

		/* is the triangle small enough? */
		if ( maxDist <= ( foliage.density * foliage.density ) ) {
			float alpha, odds, r;


			/* get average alpha */
			if ( foliage.inverseAlpha == 2 ) {
				alpha = 1;
			}
			else
			{
				alpha = ( tri[ 0 ]->color[ 0 ].alpha() + tri[ 1 ]->color[ 0 ].alpha() + tri[ 2 ]->color[ 0 ].alpha() ) / 765.0f;
				if ( foliage.inverseAlpha == 1 ) {
					alpha = 1.0f - alpha;
				}
				if ( alpha < 0.75f ) {
					return;
				}
			}

			/* roll the dice */
			odds = foliage.odds * alpha;
			r = Random();
			if ( r > odds ) {
				return;
			}

			/* scale centroid */
			fi->xyz *= 0.33333333f;
			if ( VectorNormalize( fi->normal ) == 0 ) {
				return;
			}

			/* add to count and return */
			numFoliageInstances++;
			return;
		}
	}

	/* split the longest edge and map it */
	const bspDrawVert_t mid = LerpDrawVert( *tri[ max ], *tri[ ( max + 1 ) % 3 ] );

	/* recurse to first triangle */
	TriRef tri2 = tri;
	tri2[ max ] = &mid;
	SubdivideFoliageTriangle_r( foliage, tri2 );

	/* recurse to second triangle */
	tri2 = tri;
	tri2[ ( max + 1 ) % 3 ] = &mid;
	SubdivideFoliageTriangle_r( foliage, tri2 );
}



/*
   GenFoliage()
   generates a foliage file for a bsp
 */

void Foliage( const mapDrawSurface_t& src, entity_t& entity ){
	/* do every foliage */
	for ( const auto& foliage : src.shaderInfo->foliage )
	{
		/* zero out */
		numFoliageInstances = 0;

		/* map the surface onto the lightmap origin/cluster/normal buffers */
		switch ( src.type )
		{
		case ESurfaceType::Meta:
		case ESurfaceType::ForcedMeta:
		case ESurfaceType::Triangles:
		{
			/* map the triangles */
			for ( auto i = src.indexes.cbegin(); i != src.indexes.cend(); i += 3 )
				SubdivideFoliageTriangle_r( foliage, TriRef{
					&src.verts[ *( i + 0 ) ],
					&src.verts[ *( i + 1 ) ],
					&src.verts[ *( i + 2 ) ]
				} );
			break;
		}
		case ESurfaceType::Patch:
		{
			/* make a mesh from the drawsurf */
			mesh_t subdivided = SubdivideMesh( mesh_view_t( src.patchWidth, src.patchHeight, src.verts.data() ), 8, 512 );

			/* fit it to the curve and remove colinear verts on rows/columns */
			PutMeshOnCurve( subdivided );
			const mesh_t mesh = RemoveLinearMeshColumnsRows( subdivided );

			/* map the mesh quads */
			for( MeshQuadIterator it( mesh ); it; ++it )
				for( const TriRef& tri : it.tris() )
					SubdivideFoliageTriangle_r( foliage, tri );

			break;
		}
		default:
			break;
		}

		/* any origins? */
		if ( numFoliageInstances < 1 ) {
			continue;
		}

		/* remember surface count */
		const int oldNumMapDrawSurfs = numMapDrawSurfs;

		/* add the model to the bsp */
		InsertModel( foliage.model.c_str(), nullptr, 0, matrix4_scale_for_vec3( Vector3( foliage.scale ) ), nullptr, entity, 0, clipDepthGlobal,
			EntityCompileParams {
				.castShadows   = src.castShadows,
				.recvShadows   = src.recvShadows,
				.celShader = nullptr,
				.lightmapSampleSize = 0,
				.lightmapScale = src.lightmapScale,
				.shadeAngle = 0,
				.ambientColor  = src.ambientColor
			} );

		/* walk each new surface */
		for ( int i = oldNumMapDrawSurfs; i < numMapDrawSurfs; ++i )
		{
			/* get surface */
			mapDrawSurface_t& ds = mapDrawSurfs[ i ];

			/* set up */
			ds.type = ESurfaceType::Foliage;
			ds.numFoliageInstances = numFoliageInstances;

			/* a wee hack */
			ds.patchWidth = ds.numFoliageInstances;
			ds.patchHeight = ds.verts.size();

			/* set fog to be same as source surface */
			ds.fogNum = src.fogNum;

			/* add a drawvert for every instance */
			ds.verts.reserve( ds.verts.size() + ds.numFoliageInstances );

			/* copy the verts */
			for ( const foliageInstance_t& fi : Span( foliageInstances, ds.numFoliageInstances ) )
			{
				/* get vert (foliage instance) */
				bspDrawVert_t& dv = ds.verts.emplace_back( c_bspDrawVert_t0 );

				/* copy xyz and normal */
				dv.xyz = fi.xyz;
				dv.normal = fi.normal;

				/* ydnar: set color */
				dv.color.fill( Color4b( 255 ) );
			}
		}
	}
}
