//================================================================
// E3D
//
// Rendering-related functions
//
// AUTHOR:  Gabor Nagy
// DATE:    2025-Feb-11 14:50:48
//
// Copyright (C) 1996-2026 by Gabor Nagy. All rights reserved.
//================================================================
#ifndef _E3D_RenderingAdv_h
#define _E3D_RenderingAdv_h


#ifndef _E3D_Rendering_h
#include <E3D/Rendering.h>
#endif


#if defined (__cplusplus) && (!(defined (__CPLUSLIBS)))
extern "C" {
#endif


static inline void E3dRTriangle_ComputeTextureSpaceTangent(const E3dRTriangle* PRTriangle, const E3dTextureMapperIndex PSTIndex,  E3dST* PST)
{
	const E3dRPolyGroup*     LRPolyGroup = PRTriangle->RPolyGroup;
	const E3dVertexNodeSize  LRVertexNodeSize = LRPolyGroup->RVertexNodeSize;
	const uint8_t*           LRVertexNodes8 = (const uint8_t*)PRTriangle->RVertexNodes;
	const E3dST*             LST0 = (const E3dST*)(LRVertexNodes8 + LRVertexNodeSize * 0 + LRPolyGroup->ST0OffsetNew) + PSTIndex;

/*
	const E3dST*    LST1 = (const E3dST*)(LRVertexNodes8 + LRVertexNodeSize * 1 + LRPolyGroup->ST0Offset) + PTxMapperIndex;
	const E3dST*    LST2 = (const E3dST*)(LRVertexNodes8 + LRVertexNodeSize * 2 + LRPolyGroup->ST0Offset) + PTxMapperIndex;

	E3dCoordinate  LS0 = LST0->S, LT0 = LST0->T,
	               LS1 = LST1->S, LT1 = LST1->T,
	               LS2 = LST2->S, LT2 = LST2->T,
	               LTgX, LTgY, LTgZ,
	               LDeltaS1, LDeltaT1, LDeltaS2, LDeltaT2,
	               LA, LB;


	LDeltaS1 = LS1 - LS0;
	LDeltaT1 = LT1 - LT0;
	LDeltaS2 = LS2 - LS0;
	LDeltaT2 = LT2 - LT0;

	LA = (LDeltaS1 - LDeltaT1 * LDeltaS2 / LDeltaT2);
	if(LA != 0.0f)
	{
		LA = 1.0f / LA;
	}
	LB = (LDeltaS2 - LDeltaT2 * LDeltaS1 / LDeltaT1);
	if(LB != 0.0f)
	{
		LB = 1.0f / LB;
	}

	LTgX = LA * PRTriangle->Edge1.X + LB * PRTriangle->Edge2.X;
	LTgY = LA * PRTriangle->Edge1.Y + LB * PRTriangle->Edge2.Y;
	LTgZ = LA * PRTriangle->Edge1.Z + LB * PRTriangle->Edge2.Z;

	E3d_Normalize3_v(LTgX, LTgY, LTgZ);

	PST->Tangent.X = LTgX;
	PST->Tangent.Y = LTgY;
	PST->Tangent.Z = LTgZ;
*/

	PST->Tangent = LST0->Tangent;
//printf("%s() %f,%f,%f\n", __FUNCTION__, PST->Tangent.X, PST->Tangent.Y, PST->Tangent.Z);fflush(stdout);
}


//
// Do not normalize texcoords here (clamp for repeat etc.) because differential rays may be used for mip-mapping etc. and they need the un-clamped texcoords.
// Clamping must be done just before sampling the texture.
//
static inline void E3dRTriangle_MapTextures(const E3dRTriangle* PRTriangle, const E3dCoord3* LPosition, const E3dCoord3* PNormal, const E3dCoord2* PUV, const E3dMaterial* PMaterial, E3dSTWithDiff* PST)
{
	const E3dRPolyGroup*     LRPolyGroup = PRTriangle->RPolyGroup;
	const E3dVertexNodeSize  LRVertexNodeSize = LRPolyGroup->RVertexNodeSize;
	const E3dCoordinate      LU = PUV->X, LV = PUV->Y, LNUV = 1.0 - LU - LV;
	E3dSTWithDiff*           LST = PST;

	const E3dTextureMapperIndex  LTxMN = LRPolyGroup->TextureMappers.Count;
#ifdef E3dRenderDEBUG
	if(LTxMN > 0)  // We should not have to do this check. The render prep should ensure that PolyGroups with Textures have the corresponding TextureMappers
#endif  // E3dRenderDEBUG
	{
		EBool  LNeedTxTangents[LTxMN];EClr_s(LNeedTxTangents);

		{
			EIndex  LTxI = 0;const EIndex  LTxN = PMaterial->Textures.Count;for(;LTxI < LTxN;LTxI++)
			{
				const E3d2DTexture*  LTexture = PMaterial->Textures.Items[LTxI];
				if(LTexture->DoNormalMap && LTexture->NormalMapType == E3dTANGENT_SPACE) LNeedTxTangents[LRPolyGroup->STIndices.Items[LTxI]] = TRUE;
			}
		}

		E3dTextureMapperIndex  LTxMI = 0;for(;LTxMI < LTxMN;LTxMI++, LST++)
		{
			const E3dTextureMapper*       LTextureMapper = LRPolyGroup->TextureMappers.Items[LTxMI];
			const E3dTextureMapperClass*  LTextureMapperClass = LTextureMapper->Class;

			if(LTextureMapperClass->MapPoint)
			{
//printf("%s() %s:%d  %s %p\n", __FUNCTION__, __FILE__, __LINE__, LTextureMapperClass->Name, LTextureMapperClass->MapPoint);fflush(stdout);
				LTextureMapperClass->MapPoint(LTextureMapper,  LPosition, PNormal,  (E3dST*)LST);
_E3dCheckST_p(LST);
			}
			else
			{
				const uint8_t*           LRVertexNodes8 = (uint8_t*)PRTriangle->RVertexNodes;
				const E3dVertexNodeSize  LST0Offset = LRPolyGroup->ST0OffsetNew;
				const E3dST*             LST0 = (E3dST*)(LRVertexNodes8 + LRVertexNodeSize * 0 + LST0Offset) + LTxMI;
				const E3dST*             LST1 = (E3dST*)(LRVertexNodes8 + LRVertexNodeSize * 1 + LST0Offset) + LTxMI;
				const E3dST*             LST2 = (E3dST*)(LRVertexNodes8 + LRVertexNodeSize * 2 + LST0Offset) + LTxMI;

_E3dCheckST_p(LST0);
_E3dCheckST_p(LST1);
_E3dCheckST_p(LST2);

				LST->S = LNUV * LST0->S + LST1->S * LU + LST2->S * LV;
				LST->T = LNUV * LST0->T + LST1->T * LU + LST2->T * LV;

				if(LNeedTxTangents[LTxMI]) E3dRTriangle_ComputeTextureSpaceTangent(PRTriangle, LTxMI,  (E3dST*)LST);
			}

// Indicate that no MIP-mapping should be used
//
			LST->dS = 0.0;
			LST->dT = 0.0;
		}
	}
}


//========================================================
// Sample Textures at a SurfacePoint
//========================================================
static inline void _E3d_SampleTextures(const E3dMaterial* PMaterial, const E3dRTriangle* PRTriangle, E3dSurfacePointBase* PSurfacePoint)
{
	const E3dRPolyGroup*          LRPolyGroup = PRTriangle->RPolyGroup;
	const E3dTextureMapperIndex*  LTextureMapperIndices = LRPolyGroup->STIndices.Items;
	E3dTextureMapper* const*      LTextureMappers = LRPolyGroup->TextureMappers.Items;

	EIndex  LTxI = 0;const EIndex  LTxN = PMaterial->Textures.Count;EcRGBAf32Color*  LColor = PSurfacePoint->TextureColors;for(;LTxI < LTxN;LTxI++, LColor++)
	{
		const E3d2DTexture*          LTexture = PMaterial->Textures.Items[LTxI];
		const E3dTextureMapperIndex  LMapperIndex = LTextureMapperIndices[LTxI];
		const E3dTextureMapper*      LMapper = LTextureMappers[LMapperIndex];

/*printf("%s() %s:%d  %p %p\n", __FUNCTION__, __FILE__, __LINE__, LTexture, LTexture->Sample);fflush(stdout);*/
		LTexture->Sample(LTexture, PSurfacePoint->STs + LMapperIndex,  LMapper->TilingS, LMapper->TilingT,  PSurfacePoint, (float*)LColor);
	}
}


//========================================================
// Simplified version of _E3d_SampleTextures
// It doesn't do normal mapping etc.
// E3dSPECTRALFIXME
//========================================================
static inline void _E3d_LightSampleTextures(const E3dMaterial* PMaterial, const E3dTextureMapperPtrList* PTextureMappers, const E3dTextureMapperIndex* PTextureMapperIndices, E3dSurfacePoint* PPoint)
{
	EcRGBAf32Color*  LColor = PPoint->TextureColors;

	EIndex  LTxI = 0;const EIndex  LTxN = PMaterial->Textures.Count;for(;LTxI < LTxN;LTxI++, LColor++)
	{
		float*                       LResult = (float*)LColor;
		const E3dTextureMapperIndex  LMapperIndex = PTextureMapperIndices[LTxI];

		const E3dCoordinate  LFS = PPoint->STs[LMapperIndex].S;

		if(LFS != E3dST_OFF_TEXTURE)
		{
			const E3d2DTexture*      LTexture = PMaterial->Textures.Items[LTxI];
			const E3dTextureMapper*  LTextureMapper = PTextureMappers->Items[LMapperIndex];
			const E3dImage*          LImage = LTexture->Image;
			const EIndex             LSizeX = LImage->Size.X, LSizeY = LImage->Size.Y;
			const E3dCoordinate      LFX = LFS * (E3dCoordinate)LSizeX;
			const E3dCoordinate      LFY = PPoint->STs[LMapperIndex].T * (E3dCoordinate)LSizeY;

// Bi-linear interpolation
//
			if(LTexture->MagFilter == E3dTextureFilterLINEAR)
			{
//printf("%s() %s:%d  %d %s %dx%d\n", __FUNCTION__, __FILE__, __LINE__, LTxI, LTexture->FileName,  LSizeX, LSizeY);fflush(stdout);
				EIndex2  LT, LNeighbor;E3dCoord2  LFraction, LFractionInv;_E3d_Bilinear0(LFX, LFY,  LTextureMapper->TilingS, LTextureMapper->TilingT);
				_E3d_ImageColorBilinear_AUTO_RGBAf32(LImage,  LT, LFraction, LFractionInv, LNeighbor,  LResult);
			}
			else
			{
				_E3d_ImageColorNearest_AUTO_RGBAf32(LImage,  LFX, LFY,  LTextureMapper->TilingS, LTextureMapper->TilingT,  LResult);
			}
		}
		else
		{
			_E3dM_NoTextureCoverage(LResult);
		}
	}
}


static inline void E3d_ClosestPointOnTriangle(const E3dRTriangle* PRTriangle, const E3dCoord3* PSrcPosition, E3dCoord3* PDstPosition, E3dCoord2* PUV)
{
	const E3dCoord3  LEdge0 = PRTriangle->Edge1;
	const E3dCoord3  LEdge1 = PRTriangle->Edge2;
	const E3dCoord3  LVertex0 = PRTriangle->Vertices[0].Position;
	const E3dCoord3  E_Sub3i_vp(v0, LVertex0, PSrcPosition);

	const E3dCoordinate  a = E_Dot3_vv(LEdge0, LEdge0);
	const E3dCoordinate  b = E_Dot3_vv(LEdge0, LEdge1);
	const E3dCoordinate  c = E_Dot3_vv(LEdge1, LEdge1);
	const E3dCoordinate  d = E_Dot3_vv(LEdge0, v0);
	const E3dCoordinate  e = E_Dot3_vv(LEdge1, v0);


/*
 vector3 edge0 = triangle[1] - triangle[0];
 vector3 edge1 = triangle[2] - triangle[0];
 vector3 v0 = triangle[0] - sourcePosition;

 E3dCoordinate a = edge0.dot(edge0);
 E3dCoordinate b = edge0.dot(edge1);
 E3dCoordinate c = edge1.dot(edge1);
 E3dCoordinate d = edge0.dot(v0);
 E3dCoordinate e = edge1.dot(v0);
*/

	const E3dCoordinate  det = a * c - b * b;
	E3dCoordinate        s = b * e - c * d;
	E3dCoordinate        t = b * d - a * e;

	if(s + t < det)
	{
		if(s < 0.0)
		{
			if(t < 0.0)
			{
				if(d < 0.0)
				{
					s = E3d_ClampMinMax_Coordinate(-d / a, 0.0, 1.0);
					t = 0.0;
				}
				else
				{
					s = 0.0;
					t = E3d_ClampMinMax_Coordinate(-e / c, 0.0, 1.0);
				}
			}
			else
			{
				s = 0.0;
				t = E3d_ClampMinMax_Coordinate(-e / c,  0.0, 1.0);
			}
		}
		else if (t < 0.0)
		{
			s = E3d_ClampMinMax_Coordinate(-d / a, 0.0, 1.0);
			t = 0.0;
		}
		else
		{
			const E3dCoordinate invDet = 1.0 / det;
			s *= invDet;
			t *= invDet;
		}
	}
	else
	{
		if(s < 0.0)
		{
		 const E3dCoordinate  tmp0 = b + d;
		 const E3dCoordinate  tmp1 = c + e;
		 if(tmp1 > tmp0)
		 {
			 const E3dCoordinate  numer = tmp1 - tmp0;
			 const E3dCoordinate  denom = a - 2.0 * b + c;
			 s = E3d_ClampMinMax_Coordinate(numer / denom,  0.0, 1.0);
			 t = 1.0 - s;
		 }
		 else
		 {
			 t = E3d_ClampMinMax_Coordinate(-e/c, 0.0, 1.0);
			 s = 0.0;
		 }
		}
		else if(t < 0.0)
		{
			if(a + d > b + e)
			{
				const E3dCoordinate  numer = c + e - b - d;
				const E3dCoordinate  denom = a - 2 * b + c;
				s = E3d_ClampMinMax_Coordinate(numer / denom, 0.0, 1.0);
				t = 1 - s;
			}
			else
			{
				s = E3d_ClampMinMax_Coordinate(-e / c, 0.0, 1.0);
				t = 0.0;
			}
		}
		else
		{
			const E3dCoordinate  numer = c + e - b - d;
			const E3dCoordinate  denom = a - 2 * b + c;
			s = E3d_ClampMinMax_Coordinate(numer / denom, 0.0, 1.0);
			t = 1.0 - s;
		}
	}

// return triangle[0] + s * edge0 + t * edge1;

// Return barycentric coordinates
//
	PUV->X = s;
	PUV->Y = t;
	PDstPosition->X = LVertex0.X + s * LEdge0.X + t * LEdge1.X;
	PDstPosition->Y = LVertex0.Y + s * LEdge0.Y + t * LEdge1.Y;
	PDstPosition->Z = LVertex0.Z + s * LEdge0.Z + t * LEdge1.Z;
}


static inline void E3d_MapTextureNoNormalize(const E3dRTriangle* PRTriangle,  const E3dMaterial* PMaterial, const EIndex PTextureMapperIndex, const EBool PNeedsTxTangent,  const E3dCoord3* LPosition, const E3dCoord3* PNormal, const E3dCoord2* PUV,  E3dST* PST)
{
	const E3dRPolyGroup*     LRPolyGroup = PRTriangle->RPolyGroup;
	const E3dVertexNodeSize  LRVertexNodeSize = LRPolyGroup->RVertexNodeSize;
	const E3dCoordinate      LU = PUV->X, LV = PUV->Y, LNUV = 1.0 - LU - LV;

	{
		const E3dTextureMapper*       LTextureMapper = LRPolyGroup->TextureMappers.Items[PTextureMapperIndex];
		const E3dTextureMapperClass*  LTextureMapperClass = LTextureMapper->Class;

		if(LTextureMapperClass->MapPoint)
		{
			LTextureMapperClass->MapPoint(LTextureMapper, LPosition, PNormal, (E3dST*)PST);
		}
		else
		{
			const uint8_t*  LRVertexNodes8 = (const uint8_t*)PRTriangle->RVertexNodes;
			const E3dVertexNodeSize  LST0Offset = LRPolyGroup->ST0OffsetNew;
			const E3dST*    LST0 = (const E3dST*)(LRVertexNodes8 + LRVertexNodeSize * 0 + LST0Offset) + PTextureMapperIndex;
			const E3dST*    LST1 = (const E3dST*)(LRVertexNodes8 + LRVertexNodeSize * 1 + LST0Offset) + PTextureMapperIndex;
			const E3dST*    LST2 = (const E3dST*)(LRVertexNodes8 + LRVertexNodeSize * 2 + LST0Offset) + PTextureMapperIndex;

			PST->S = LNUV * LST0->S + LST1->S * LU + LST2->S * LV;
			PST->T = LNUV * LST0->T + LST1->T * LU + LST2->T * LV;

			if(PNeedsTxTangent)
			{
				E3dRTriangle_ComputeTextureSpaceTangent(PRTriangle, PTextureMapperIndex,  PST);
			}
		}
	}
}


//
// Do not normalize texcoords here (clamp for repeat etc.) because differential rays may be used for mip-mapping etc. and they need the un-clamped texcoords.
// Clamping must be done just before sampling the texture.
//
static inline void E3dRTriangle_MapTexture(const E3dRTriangle* PRTriangle, const E3dCoord3* LPosition, const E3dCoord3* PNormal, const E3dCoord2* PUV, const EIndex PTextureIndex, E3dSTWithDiff* PST)
{
	const E3dRPolyGroup*     LRPolyGroup = PRTriangle->RPolyGroup;
	const E3dVertexNodeSize  LRVertexNodeSize = LRPolyGroup->RVertexNodeSize;
	const E3dCoordinate      LU = PUV->X, LV = PUV->Y, LNUV = 1.0 - LU - LV;
	E3dSTWithDiff*           LST = PST;

	const E3dTextureMapperIndex  LTxMN = LRPolyGroup->TextureMappers.Count;
#ifdef E3dRenderDEBUG
	if(LTxMN > 0)  // We should not have to do this check. The render prep should ensure that PolyGroups with Textures have the corresponding TextureMappers
#endif  // E3dRenderDEBUG
	{
		const EIndex                  LTextureMapperIndex = LRPolyGroup->STIndices.Items[PTextureIndex];
		const E3dTextureMapper*       LTextureMapper = LRPolyGroup->TextureMappers.Items[LTextureMapperIndex];
		const E3dTextureMapperClass*  LTextureMapperClass = LTextureMapper->Class;

		if(LTextureMapperClass->MapPoint)
		{
//printf("%s() %s:%d  %s %p\n", __FUNCTION__, __FILE__, __LINE__, LTextureMapperClass->Name, LTextureMapperClass->MapPoint);fflush(stdout);
			LTextureMapperClass->MapPoint(LTextureMapper,  LPosition, PNormal,  (E3dST*)LST);
_E3dCheckST_p(LST);
		}
		else
		{
			const uint8_t*           LRVertexNodes8 = (uint8_t*)PRTriangle->RVertexNodes;
			const E3dVertexNodeSize  LST0Offset = LRPolyGroup->ST0OffsetNew;
			const E3dST*             LST0 = (const E3dST*)(LRVertexNodes8 + LRVertexNodeSize * 0 + LST0Offset) + LTextureMapperIndex;
			const E3dST*             LST1 = (const E3dST*)(LRVertexNodes8 + LRVertexNodeSize * 1 + LST0Offset) + LTextureMapperIndex;
			const E3dST*             LST2 = (const E3dST*)(LRVertexNodes8 + LRVertexNodeSize * 2 + LST0Offset) + LTextureMapperIndex;

_E3dCheckST_p(LST0);
_E3dCheckST_p(LST1);
_E3dCheckST_p(LST2);

			LST->S = LNUV * LST0->S + LST1->S * LU + LST2->S * LV;
			LST->T = LNUV * LST0->T + LST1->T * LU + LST2->T * LV;

//		if(LTexture->DoNormalMap && LTexture->NormalMapType == E3dTANGENT_SPACE) E3dRTriangle_ComputeTextureSpaceTangent(PRTriangle, LTxMI,  (E3dST*)LST);
		}

// Indicate that no MIP-mapping should be used
//
		LST->dS = 0.0;
		LST->dT = 0.0;
	}
}


//========================================================
//========================================================
static inline const E3dShader* _E3d_LightShaderGetAtPoint(E3dSurfacePoint* PPoint, const E3dRTriangle* PRTriangle, const E3dCoord2* PUV,  const int PSpectralIndex, const int PNSpectralSamples,  const E3dMaterial* PMaterial, const E3dShader* PSrcShader,  E3dShader* PDstShader)
{
	switch(PMaterial->Textures.Count)
	{
		case 0:
		return(PSrcShader);

		case 1:
			{
				E3dSTWithDiff*        LST = PPoint->STs;
				const E3dRPolyGroup*  LRPolyGroup = PRTriangle->RPolyGroup;

				E3dRTriangle_MapTextures(PRTriangle,  &PPoint->Position, &PPoint->Normal,  PUV, PMaterial, PPoint->STs);
				_E3d_LightSampleTextures(PMaterial,  &LRPolyGroup->TextureMappers, LRPolyGroup->STIndices.Items,  PPoint);

// With 1 texture, we can avoid calling _E3dShader_ModulateWithTextures if the texture is not mapped at the given point
//
				if(LST->S != E3dST_OFF_TEXTURE)
				{
					E3dShader_ModulateWithTextures(PMaterial, PSrcShader, PDstShader,  PPoint->TextureColors,  PSpectralIndex, PNSpectralSamples);
					return(PDstShader);
				}
				else return(PSrcShader);
			}
		break;

		default:
			{
				const E3dRPolyGroup*  LRPolyGroup = PRTriangle->RPolyGroup;
				E3dRTriangle_MapTextures(PRTriangle,  &PPoint->Position, &PPoint->Normal,  PUV, PMaterial, PPoint->STs);
				_E3d_LightSampleTextures(PMaterial,  &LRPolyGroup->TextureMappers, LRPolyGroup->STIndices.Items,  PPoint);
				E3dShader_ModulateWithTextures(PMaterial, PSrcShader, PDstShader,  PPoint->TextureColors,  PSpectralIndex, PNSpectralSamples);
			}
		return(PDstShader);
	}
}


//================================================================================================
// Intersect Ray with a bounding box and return the t parameter at the entry and exit points
// PEntryT < 0.0 means we're inside the BBox (the entry point is behind us)
//================================================================================================
static inline EBool E3dRay_IntersectBBoxInOut(const E3dRay* PRay, const E3dBBox* PBBox, const E3dCoordinate* PRayDirInv, E3dCoordinate* PEntryT, E3dCoordinate* PExitT)
{
	const E3dCoord3  LBBoxMin = PBBox->Min, LBBoxMax = PBBox->Max;
	const E3dCoord3  E_Sub3i_vv  (LOriginToBoxMin,  PRay->Origin, LBBoxMin);
	E3dCoordinate    LLMin = -1.0, LLMax = -1.0;

	if(PRay->Direction.X != 0.0)
	{
		E3dCoordinate  LTmp1 = -LOriginToBoxMin.X  * PRayDirInv[0], LTmp2 = ((LBBoxMax.X - LBBoxMin.X) - LOriginToBoxMin.X) * PRayDirInv[0];
		if(LTmp1 > LTmp2) E_Swap(LTmp1, LTmp2);
		LLMin = LTmp1;
		LLMax = LTmp2;
		if(LLMax < 0.0) return(FALSE);
	}

	if(PRay->Direction.Y != 0.0)
	{
		E3dCoordinate  LTmp1 = -LOriginToBoxMin.Y * PRayDirInv[1], LTmp2 = ((LBBoxMax.Y - LBBoxMin.Y) - LOriginToBoxMin.Y) * PRayDirInv[1];
		if(LTmp1 > LTmp2) E_Swap(LTmp1, LTmp2);
		if(LTmp1 > LLMin) LLMin = LTmp1;
		if(LTmp2 < LLMax || LLMax < 0.0) LLMax = LTmp2;
		if(LLMax < 0.0) return(FALSE);
	}

	if(PRay->Direction.Z != 0.0)
	{
		E3dCoordinate  LTmp1 = -LOriginToBoxMin.Z * PRayDirInv[2], LTmp2 = ((LBBoxMax.Z - LBBoxMin.Z) - LOriginToBoxMin.Z) * PRayDirInv[2];
		if(LTmp1 > LTmp2) E_Swap(LTmp1, LTmp2);
		if(LTmp1 > LLMin) LLMin = LTmp1;
		if(LTmp2 < LLMax || LLMax < 0.0) LLMax = LTmp2;	
	} 

	if(LLMin <= LLMax && LLMax >= 0.0)
	{
		*PEntryT = LLMin;
		*PExitT = LLMax;
		return(TRUE);
	}
	else return(FALSE);
}


//================================================================================================
// Intersect Ray with a bounding box and return the t parameter at the entry and exit points
// PEntryT < 0.0 means we're inside the BBox (the entry point is behind us)
//================================================================================================
static inline EBool E3dRay_IntersectBBoxMaxDist(const E3dGridRay* PRay, const E3dBBox* PBBox, const E3dCoordinate* PRayDirInv, E3dCoordinate* PEntryT, E3dCoordinate* PExitT, const E3dCoordinate LMaxDistance)
{
	const E3dCoord3  LBBoxMin = PBBox->Min, LBBoxMax = PBBox->Max;
	const E3dCoord3  E_Sub3i_vv(LOriginToBoxMin,  PRay->Origin, LBBoxMin);
	E3dCoordinate    LLMin = -1.0, LLMax = -1.0;

	if(PRay->Direction.X != 0.0)
	{
		E3dCoordinate  LTmp1 =                             -LOriginToBoxMin.X  * PRayDirInv[0];
		E3dCoordinate  LTmp2 = ((LBBoxMax.X - LBBoxMin.X) - LOriginToBoxMin.X) * PRayDirInv[0];
		if(LTmp1 > LTmp2) E_Swap(LTmp1, LTmp2);
		LLMin = LTmp1;
		LLMax = LTmp2;
		if(LLMax < 0.0 || LLMin > LMaxDistance) return(FALSE);
	}

	if(PRay->Direction.Y != 0.0)
	{
		E3dCoordinate  LTmp1 =                              -LOriginToBoxMin.Y * PRayDirInv[1];
		E3dCoordinate  LTmp2 = ((LBBoxMax.Y - LBBoxMin.Y) - LOriginToBoxMin.Y) * PRayDirInv[1];
		if(LTmp1 > LTmp2) E_Swap(LTmp1, LTmp2);
		if(LTmp1 > LLMin) LLMin = LTmp1;
		if(LTmp2 < LLMax || LLMax < 0.0) LLMax = LTmp2;
		if(LLMax < 0.0 || LLMin > LMaxDistance) return(FALSE);
	}

	if(PRay->Direction.Z != 0.0)
	{
		E3dCoordinate  LTmp1 =                              -LOriginToBoxMin.Z * PRayDirInv[2];
		E3dCoordinate  LTmp2 = ((LBBoxMax.Z - LBBoxMin.Z) - LOriginToBoxMin.Z) * PRayDirInv[2];
		if(LTmp1 > LTmp2) E_Swap(LTmp1, LTmp2);
		if(LTmp1 > LLMin) LLMin = LTmp1;
		if(LTmp2 < LLMax || LLMax < 0.0) LLMax = LTmp2;	
	} 

	if(LLMin <= LLMax && LLMax >= 0.0 && LLMin <= LMaxDistance)
	{
		*PEntryT = LLMin;
		*PExitT = LLMax;
		return(TRUE);
	}
	else return(FALSE);
}


#if defined (__cplusplus) && (!(defined (__CPLUSLIBS)))
}	// Close scope of 'extern "C"' declaration
#endif

#endif	// _E3D_RenderingAdv_h
