//================================================================
// E3D library
//
// - Rays
//
// AUTHOR:  Gabor Nagy
// DATE:    2026-Aug-16 16:42:02
//
// Copyright (C) 1996-2026 by Gabor Nagy. All rights reserved.
//================================================================
#include <assert.h>
#ifndef _E3D_Ray_h
#define _E3D_Ray_h


#ifndef _E3D_Math_h
#include <E3D/Math.h>
#endif

#ifndef _E3D_Polygon_h
#include <E3D/Polygon.h>
#endif

#ifndef _E3dStackRay
#define _E3dStackRay
typedef struct E3dStackRay_S  E3dStackRay;  // Forward-define E3dStackRay
#endif


#define E3dMAX_RAY_DEPTH  42

typedef struct
{
	E3dCoord3  dP_div_dx, dP_div_dy;  // Ray position  partial derivatives dP/dx and dP/dy
	E3dCoord3  dD_div_dx, dD_div_dy;  // Ray direction partial derivatives dD/dx and dD/dy
} E3dRayDifferentials;

typedef struct
{
	E3dCoord3  dN_div_dx, dN_div_dy;
	E3dCoord2  dD_dot_N_div_d;
} E3dRayDifferentialsTmp;



#define E3dRayCore\
	union { E3dCoord3  Origin;E3dCoordinate     Origin_4[4]; };\
	union { E3dCoord3  Direction;E3dCoordinate  Direction_4[4]; }

typedef struct
{
	E3dRayCore;
} E3dRay;


#define E3dRayHitCore\
	E3dCoordinate  T

typedef struct
{
	E3dRayHitCore;
} E3dRayHitBase;

#define E3dRayHitUVCore\
	E3dRayHitCore;\
	E3dCoord2  UV;

typedef struct
{
	E3dRayHitUVCore;
} E3dRayHitUV;

typedef struct
{
	E3dRayHitUVCore;
	const E3dRTriangle*   RTriangle;
	const E3dRPolyGroup*  RPolyGroup;       // If it's a custom shape, a hit will have an RPolyGroup, but no RTriangle
	E3dCoord3             Position;         // Hit point on a custom shape
	E3dCoord3             LocalPosition;    // Hit point on a custom shape in local coordinates
	uint32_t              SurfaceHit : 1, VolumeHit : 1;
	E3dRay                LocalRay;
	E3dCoordinate         LocalEntryT, LocalExitT;    // E.g. for a volume BBox
} E3dRayHit;


//
// Ray with ray differentials
//
#define E3dDRayCore\
	E3dRayCore;\
\
	union { E3dCoord3    PixelCornerDirections[4];E3dCoordinate  PixelCornerDirections_4[4][4]; };  /* Sub-ray directions for MIP-mapping */\
\
/* Sub-ray directions for anisotropic texture filtering  */\
	union { E3dCoord3    Directions[E3dMAX_TEXTURE_ANISOTROPY];E3dCoordinate  Directions_4[E3dMAX_TEXTURE_ANISOTROPY][4]; };\
\
	E3dCoord2            UVs[E3dMAX_TEXTURE_ANISOTROPY];                                            /* Sub-ray stratified 2D samples */\
\
	E3dRayDifferentials  Differentials;\
\
	EBool                HasSubRays, HasDifferentials

typedef struct
{
	E3dDRayCore;
} E3dDRay;



enum
{
/*
	MMM,
	MMP,
	MPM,
	MPP,
	PMM,
	PMP,
	PPM,
	PPP
*/
	MMM,
	MMP,
	MMO,
	MPM,
	MOM,
	MOO,	
	MPO,
	MOP,
	MPP,
	PMM,
	OMM,
	OMO,
	PMO,
	OMP,
	PMP,
	OOM,
	OPM,
	POM,
	PPM,
	OOP,
	OPO,
	OPP,
	POO,
	POP,
	PPO,
	PPP
};


//
// Ray with accelerator data
//
#define E3dGridRayCore\
	E3dDRayCore;\
\
	E3dCoord3      HitPosition;\
	uint16_t       FrontFaceHit : 1,     /* The last intersection was with a front-facing primitive  */\
	               DirClassification : 5,\
	               HasExtinction : 1;\
\
/* Pluecker ray-AABB intersection test  */\
	union { E3dCoord3	DirectionInv;E3dCoordinate	DirectionInv4[4]; };\
	E3dCoordinate  R0, R1, R3


typedef struct
{
	E3dGridRayCore;
} E3dGridRay;


#define E3dIRayCore\
	E3dGridRayCore;\
	int    SpectralIndex, NSpectralSamples;\
	float  Intensity[EcMAX_NUM_OF_SPECTRAL_SAMPLES];\
	float  Extinction[EcMAX_NUM_OF_SPECTRAL_SAMPLES]         /* Absorption coefficient + Scattering albedo */

//
// Ray with spectral intensity
//
typedef struct
{
	E3dIRayCore;
} E3dIRay;


struct E3dStackRay_S
{
	E3dIRayCore;

// Transparency stuff
//
	const E3dMaterial*  MaterialStack[E3dMAX_RAY_DEPTH];  // Materials 
	int16_t             MaterialStackPointer;             // Index to current Material in MaterialStack.
	int16_t             IterationsLeft;
// When a ray hits a front-face (entering a Material), the Material of that face is stored in MaterialStack and MaterialIndex is incremented.
// When a ray hits a back-face (exiting a Material), the Material MaterialIndex is decremented.

// For ray-stacking
//
	void*               ExcludeFace;

// For photon tracing
//
	int                 DiffuseBounces, SpecularBounces, VolumeBounces;
	char                PrevHit;
};



//----------------------------------------------------------------
// Function prototypes
//----------------------------------------------------------------
#if defined (__cplusplus) && (!(defined (__CPLUSLIBS)))
extern "C" {
#endif


//================================================================================
// Intersect ray with triangle
//
// This version:
// -assumes that the triangle is known to be hit
// -uses the precomputed V0->V1 and V2->V2 edges of the triangle
//================================================================================
static inline E3dCoordinate E3dRay_IntersectTriangle_KnownnHit_UV(const E3dGridRay* PRay, const E3dRTriangle* PRTriangle, E3dCoord2* PUV)
{
	if(E3dRAY_TRIANGLE_INTERSECTOR == E3dRayTriMOLLER)
	{
		const E3dCoord3  LRayOrigin = PRay->Origin, LRayDirection = PRay->Direction;
		const E3dCoord3  LVertex0 = PRTriangle->Vertices[0].Position;
		const E3dCoord3  LEdge1 = PRTriangle->Edge1, LEdge2 = PRTriangle->Edge2;

// Begin calculating determinant - also used to calculate U parameter
//
// Cross-product of ray direction and LEdge2
//
		E3dCoord3            LPVector;E_Cross3_vvv(LRayDirection, LEdge2, LPVector);
		const E3dCoordinate  LDet = E_Dot3_vv(LEdge1, LPVector);

// Calculate distance from LVertex0 to ray origin
//
		const E3dCoord3  E_Sub3i_vv(LTVector,  LRayOrigin, LVertex0);

// Calculate U parameter and test bounds
//
		{
			const E3dCoordinate  LDetInv = 1.0 / LDet;
			E3dCoordinate        LU = E_Dot3_vv(LTVector, LPVector) * LDetInv;

			if(LU < 0.0) LU = 0.0;else if(LU > 1.0) LU = 1.0;

// Prepare to test V parameter
//
			{
				E3dCoord3      LQVector;E_Cross3_vvv(LTVector, LEdge1, LQVector);
				E3dCoordinate  LV = E_Dot3_vv(PRay->Direction, LQVector) * LDetInv;

				if(LV < 0.0) LV = 0.0;else if(LV > 1.0) LV = 1.0;

// Ray intersects triangle. Calculate t,
//
				{
					const E3dCoordinate  LT = E_Dot3_vv(LEdge2, LQVector) * LDetInv;

					PUV->X = LU;PUV->Y = LV;
					return(LT);
				}
			}
		}
	}


#if E3dRAY_TRIANGLE_INTERSECTOR == E3dRayTriORI_DET
	{
		const E3dCoord3      LRayDirection = PRay->Direction;
		const E3dCoord4      LTrianglePlane = PRTriangle->Plane;
		const E3dCoordinate  LDet = -E_Dot3_vv(LRayDirection, LTrianglePlane);
		{
			if(LDet == 0.0) return(FALSE);  // Ray is parallel to triangle

			const E3dCoord3      LVertex0 = PRTriangle->Vertices[0].Position, LVertex1 = PRTriangle->Vertices[1].Position, LVertex2 = PRTriangle->Vertices[2].Position;
			const E3dCoord3      LRayOrigin = PRay->Origin;
			const E3dCoord3      E_Sub3i_vv(LV0minusOrigin,  LVertex0, LRayOrigin);
			E3dCoord3            LCross;E_Cross3_vvv(LRayDirection, LV0minusOrigin, LCross);

			const E3dCoord3      E_Sub3i_vv(LV2minusOrigin,  LVertex2, LRayOrigin);
			const E3dCoordinate  LDetInv = 1.0 / LDet;
			E3dCoordinate        LU = E_Dot3_vv(LCross, LV2minusOrigin) * LDetInv;

			if(LU < 0.0) LU = 0.0;else if(LU > 1.0) LU = 1.0;

			const E3dCoord3      E_Sub3i_vv(LV1minusOrigin,  LVertex1, LRayOrigin);
			E3dCoordinate	       LV = -E_Dot3_vv(LCross, LV1minusOrigin) * LDetInv;

			if(LV < 0.0) LV = 0.0;else if(LV > 1.0) LV = 1.0;

			PUV->X = LU;PUV->Y = LV;

			const E3dCoordinate  LT = -E_Dot3_vv(LV0minusOrigin, LTrianglePlane) * LDetInv;

			return(LT);
		}
	}
#endif
}


//================================================================================
// Intersect ray with triangle
//
// This version:
// -assumes that the triangle is known to be hit
// -uses the precomputed V0->V1 and V2->V2 edges of the triangle
//================================================================================
static inline EBool E3dRay_IntersectTriangleFront_KnownnHit_UV(const E3dGridRay* PRay, const E3dRTriangle* PRTriangle, E3dCoord2* PUV)
{
	if(E3dRAY_TRIANGLE_INTERSECTOR == E3dRayTriMOLLER)
	{
		const E3dCoord3  LRayOrigin = PRay->Origin, LRayDirection = PRay->Direction;
		const E3dCoord3  LVertex0 = PRTriangle->Vertices[0].Position;
		const E3dCoord3  LEdge1 = PRTriangle->Edge1, LEdge2 = PRTriangle->Edge2;

// Begin calculating determinant - also used to calculate U parameter
//
// Cross-product of ray direction and LEdge2
//
		E3dCoord3            LPVector;E_Cross3_vvv(LRayDirection, LEdge2, LPVector);
		const E3dCoordinate  LDet = E_Dot3_vv(LEdge1, LPVector);if(LDet <= 0.0) return(FALSE);

// Calculate distance from LVertex0 to ray origin
//
		const E3dCoord3  E_Sub3i_vv(LTVector,  LRayOrigin, LVertex0);

// Calculate U parameter and test bounds
//
		{
			const E3dCoordinate  LDetInv = 1.0 / LDet;
			E3dCoordinate        LU = E_Dot3_vv(LTVector, LPVector) * LDetInv;

			if(LU < 0.0) LU = 0.0;else if(LU > 1.0) LU = 1.0;

// Prepare to test V parameter
//
			{
				E3dCoord3      LQVector;E_Cross3_vvv(LTVector, LEdge1, LQVector);
				E3dCoordinate  LV = E_Dot3_vv(PRay->Direction, LQVector) * LDetInv;

				if(LV < 0.0) LV = 0.0;else if(LV > 1.0) LV = 1.0;

				PUV->X = LU;PUV->Y = LV;
				return(TRUE);
			}
		}
	}


#if E3dRAY_TRIANGLE_INTERSECTOR == E3dRayTriORI_DET
	{
		const E3dCoord3      LRayDirection = PRay->Direction;
		const E3dCoord4      LTrianglePlane = PRTriangle->Plane;
		const E3dCoordinate  LDet = -E_Dot3_vv(LRayDirection, LTrianglePlane);if(LDet <= 0.0) return(FALSE);
		{
			const E3dCoord3      LVertex0 = PRTriangle->Vertices[0].Position, LVertex1 = PRTriangle->Vertices[1].Position, LVertex2 = PRTriangle->Vertices[2].Position;
			const E3dCoord3      LRayOrigin = PRay->Origin;
			const E3dCoord3      E_Sub3i_vv(LV0minusOrigin,  LVertex0, LRayOrigin);
			E3dCoord3            LCross;E_Cross3_vvv(LRayDirection, LV0minusOrigin, LCross);

			const E3dCoord3      E_Sub3i_vv(LV2minusOrigin,  LVertex2, LRayOrigin);
			const E3dCoordinate  LDetInv = 1.0 / LDet;
			E3dCoordinate        LU = E_Dot3_vv(LCross, LV2minusOrigin) * LDetInv;

			if(LU < 0.0) LU = 0.0;else if(LU > 1.0) LU = 1.0;

			const E3dCoord3      E_Sub3i_vv(LV1minusOrigin,  LVertex1, LRayOrigin);
			E3dCoordinate        LV = -E_Dot3_vv(LCross, LV1minusOrigin) * LDetInv;

			if(LV < 0.0) LV = 0.0;else if(LV > 1.0) LV = 1.0;

			PUV->X = LU;PUV->Y = LV;

			return(TRUE);
		}
	}
#endif
}


static inline E3dCoordinate E3d_PlaneDistance(const E3dCoord4* PPlane, const E3dCoord3* PPoint)
{
	const E3dCoordinate  LDot = E_Dot3_pp(PPlane, PPoint);

/*
#if E3dRTUVDEBUG
assert(E3d_Abs(LDot - PPlane->W) > 0.000001);
#endif
*/

	return(LDot - PPlane->W);
}


//========================================================================
// Precompute fiends of an RTriangle for ray-triangle intersections
//========================================================================
static inline int E3dRTriangle_PrepForRayTracing(E3dRTriangle* PRTriangle)
{
	const E3dCoord3*  LVertex0 = &PRTriangle->Vertices[0].Position;
	const E3dCoord3*  LVertex1 = &PRTriangle->Vertices[1].Position;
	const E3dCoord3*  LVertex2 = &PRTriangle->Vertices[2].Position;
	const E3dCoord3   E_Sub3i_pp(LEdge1,  LVertex1, LVertex0);
	const E3dCoord3   E_Sub3i_pp(LEdge2,  LVertex2, LVertex0);

// Precompute vectors: Vertex0->Vertex1 and Vertex0->Vertex2 to speed up the ray/triangle intersection tests
//
	PRTriangle->Edge1 = LEdge1;
	PRTriangle->Edge2 = LEdge2;

#if E3dRTriangleHasPlane
// Unnormalized normal for orientation determinants-based and barycentric plane-based ray-triangle intersection tests
//
		E_Cross3_vvv(LEdge1, LEdge2, PRTriangle->Plane);
#endif

	const E3dCoord3  LNormal = PRTriangle->Normal;

#if E3dRAY_TRIANGLE_INTERSECTOR == E3dRayTriBARYPLANES
//
// Based on the description: http://www.cbloom.com/3d/techdocs/fast_ray_tri.txt
//
	{
		E3dCoord3  LEdgePerp1, LEdgePerp2;

		PRTriangle->Plane.W = -E_Dot3_vp(PRTriangle->Plane, LVertex0);

// Determine principal axis of normal for fast shadowray-triangle intersection test
//
/*
		const E3dCoordinate  LAbsX = E3d_Abs(LNormal.X);
		const E3dCoordinate  LAbsY = E3d_Abs(LNormal.Y);
		const E3dCoordinate  LAbsZ = E3d_Abs(LNormal.Z);

		if(LAbsX > LAbsY)
		{
			if(LAbsX > LAbsZ) PRTriangle->NormalPrincipalAxis = E3dX;
			else PRTriangle->NormalPrincipalAxis = E3dZ;
		}
		else
		{
			if(LAbsY > LAbsZ) PRTriangle->NormalPrincipalAxis = E3dY;
			else PRTriangle->NormalPrincipalAxis = E3dZ;
		}
*/

		E_Cross3_vvv(LNormal, LEdge1, LEdgePerp1);
		E_Cross3_vvv(LNormal, LEdge2, LEdgePerp2);

		const E3dCoord4  LEdgePlane1 = E3dCoord4_s(LEdgePerp1.X, LEdgePerp1.Y, LEdgePerp1.Z, E_Dot3_vp(LEdgePerp1, LVertex0));
		const E3dCoord4  LEdgePlane2 = E3dCoord4_s(LEdgePerp2.X, LEdgePerp2.Y, LEdgePerp2.Z, E_Dot3_vp(LEdgePerp2, LVertex0));

// Scale plane normal to change the distances we want.
//

//		const E3dCoordinate  LMinDistAbs = 0.0;

// BaryPlane1 = LEdgePlane2 / (LEdgePlane2.Distance(LVertex1))
//
		{
			const E3dCoordinate  LDist = E3d_PlaneDistance(&LEdgePlane2, LVertex1);
			{
//			const E3dCoordinate  LDistSign = LDist < 0.0 ? -1.0 : 1.0;
//			const E3dCoordinate  LDistAbs = E3d_Abs(LDist);
//			const E3dCoordinate  LD = LDistAbs > LMinDistAbs ? 1.0 / LDist : LDistSign / LMinDistAbs;
				const E3dCoordinate  LD = 1.0 / LDist;
#if E3dRTUVDEBUG
assert(E3d_Finite(LD));
#endif
				PRTriangle->BaryPlane1.X = LEdgePlane2.X * LD;
				PRTriangle->BaryPlane1.Y = LEdgePlane2.Y * LD;
				PRTriangle->BaryPlane1.Z = LEdgePlane2.Z * LD;
				PRTriangle->BaryPlane1.W = LEdgePlane2.W * LD;

#if E3dRTUVDEBUG
/*
assert(E3d_Finite(PRTriangle->BaryPlane1.X));
assert(E3d_Finite(PRTriangle->BaryPlane1.Y));
assert(E3d_Finite(PRTriangle->BaryPlane1.Z));
assert(E3d_Finite(PRTriangle->BaryPlane1.W));
*/
#endif
			}
		}

		{
// BaryPlane1 = LEdgePlane1 / (LEdgePlane1.Distance(LVertex2))
//
			const E3dCoordinate  LDist = E3d_PlaneDistance(&LEdgePlane1, LVertex2);
			{
//				const E3dCoordinate  LDistSign = LDist < 0.0 ? -1.0 : 1.0;
//				const E3dCoordinate  LDistAbs = E3d_Abs(LDist);
//				const E3dCoordinate  LD = LDistAbs > LMinDistAbs ? 1.0 / LDist : LDistSign / LMinDistAbs;
				const E3dCoordinate  LD = 1.0 / LDist;
#if E3dRTUVDEBUG
assert(E3d_Finite(LD));
#endif
				PRTriangle->BaryPlane2.X = LEdgePlane1.X * LD;
				PRTriangle->BaryPlane2.Y = LEdgePlane1.Y * LD;
				PRTriangle->BaryPlane2.Z = LEdgePlane1.Z * LD;
				PRTriangle->BaryPlane2.W = LEdgePlane1.W * LD;

#if E3dRTUVDEBUG
/*
assert(E3d_Finite(PRTriangle->BaryPlane2.X));
assert(E3d_Finite(PRTriangle->BaryPlane2.Y));
assert(E3d_Finite(PRTriangle->BaryPlane2.Z));
assert(E3d_Finite(PRTriangle->BaryPlane2.W));
*/
#endif
			}
		}
	}

#endif


#if E3dRAY_DIFFERENTIALS
	const E3dCoord3  E_Sub3i_pp(LEdge12,  LVertex2, LVertex1);
	E_Cross3_vvv(LEdge12, LNormal, PRTriangle->L0);PRTriangle->L0.W = -E_Dot3_vp(PRTriangle->L0, LVertex1);
	E_Cross3_vvv(LNormal, LEdge2,  PRTriangle->L1);PRTriangle->L1.W = -E_Dot3_vp(PRTriangle->L1, LVertex2);
	E_Cross3_vvv(LEdge1,  LNormal, PRTriangle->L2);PRTriangle->L2.W = -E_Dot3_vp(PRTriangle->L2, LVertex0);

// Adjust plane coefficients, so V0 . L0 = 1.0 etc.
//
 #define _PN(m)\
	{\
		const E3dCoordinate  lL = E_Dot3_pv(LVertex##m, PRTriangle->L##m) + PRTriangle->L##m.W;	/* 4-component dot-product with LVertex##m = 1.0	*/\
/*printf("   %f %f %f   %d %f\n",  LVertex##m->X, LVertex##m->Y, LVertex##m->Z,  m, lL);fflush(stdout);*/\
		if(lL != 0.0)\
		{\
			PRTriangle->L##m.X /= lL;\
			PRTriangle->L##m.Y /= lL;\
			PRTriangle->L##m.Z /= lL;\
			PRTriangle->L##m.W /= lL;\
/*printf("%s() %s:%d  L%s % 8.2f % 8.2f % 8.2f % 8.2f\n", __FUNCTION__, __FILE__, __LINE__, #m, XYZW_s(PRTriangle->L##m));fflush(stdout);*/\
		}\
		else\
		{\
/*printf("lL = 0!\n");fflush(stdout);*/\
		}\
/*printf(" L%s % 8.2f % 8.2f % 8.2f % 8.2f\n", #m, XYZW_s(PRTriangle->L##m));fflush(stdout);*/\
	}

	_PN(0);
	_PN(1);
	_PN(2);

 #undef _PN
#endif	// E3dRAY_DIFFERENTIALS

	return(E_SUCCESS);
}




//================================================================================
// Intersect ray with triangle
//================================================================================
static inline int E3dRay_IntersectTriangleFrontBack(const E3dGridRay* PRay, const E3dRTriangleICore* PRTriangleICore, E3dCoordinate* PT)
{
	const E3dCoord3  LRayOrigin = PRay->Origin, LRayDirection = PRay->Direction;


#if E3dRAY_TRIANGLE_INTERSECTOR == E3dRayTriMOLLER
//---------------------
// Moller-Trumbore
//---------------------
	const E3dCoord3  LVertex0 = PRTriangleICore->Vertices[0].Position;
	const E3dCoord3  LEdge1 = PRTriangleICore->Edge1, LEdge2 = PRTriangleICore->Edge2;

// Begin calculating determinant - also used to calculate U parameter
//
	E3dCoord3            LPVector;E_Cross3_vvv(LRayDirection, LEdge2, LPVector);
	const E3dCoordinate  LDet = E_Dot3_vv(PRTriangleICore->Edge1, LPVector);

	if(LDet == 0.0) return(FALSE);	// If determinant is near zero, ray lies in plane of triangle

	{
// Calculate distance from LVertex0 to ray origin
//
		const E3dCoord3  E_Sub3i_vv(LTVector,  LRayOrigin, LVertex0);

// Calculate U parameter and test bounds
//
		{
			const E3dCoordinate  LDetInv = 1.0 / LDet, LU = E_Dot3_vv(LTVector, LPVector) * LDetInv;

			if(LU < 0.0 || LU > 1.0) return(FALSE);

// Prepare to test V parameter
//
			{
				E3dCoord3            LQVector;E_Cross3_vvv(LTVector, LEdge1, LQVector);
				const E3dCoordinate  LV = E_Dot3_vv(PRay->Direction, LQVector) * LDetInv;

// Calculate V parameter and test bounds
//
				if(LV < 0.0 || LU + LV > 1.0) return(FALSE);

// Ray intersects triangle. Calculate t,
//
				{
					const E3dCoordinate  LT = E_Dot3_vv(LEdge2, LQVector) * LDetInv;

// Make sure the intersection isn't behind the ray origin
//
					if(LT <= 0.00001) return(FALSE);
					*PT = LT;
				}
			}
		}
	}

	return(TRUE);
#endif


#if E3dRAY_TRIANGLE_INTERSECTOR == E3dRayTriORI_DET
//------------------------------------------------------------------------
// Orientation determinants (slightly faster than Moller-Trumbore)
// Based on http://jgt.akpeters.com/papers/GuigueDevillers03/addendum.html
// (minor optimization + errors in the article fixed by Gabor Nagy).
//------------------------------------------------------------------------
	{
		const E3dCoord4      LTrianglePlane = PRTriangleICore->Plane;
		const E3dCoordinate  LDet = -E_Dot3_vv(LRayDirection, LTrianglePlane);

		if(LDet == 0.0) return(FALSE);  // Ray is parallel to triangle

		const E3dCoordinate  LDetInv = 1.0 / LDet;
		const E3dRTriangle*  LRTriangle = (const E3dRTriangle*)PRTriangleICore;
		const E3dCoord3      LVertex0 = LRTriangle->Vertices[0].Position, LVertex1 = LRTriangle->Vertices[1].Position, LVertex2 = LRTriangle->Vertices[2].Position;
		const E3dCoord3      E_Sub3i_vv(LV0minusOrigin,  LVertex0, LRayOrigin);
		E3dCoord3            LCross;E_Cross3_vvv(LRayDirection, LV0minusOrigin, LCross);

		const E3dCoord3      E_Sub3i_vv(LV2minusOrigin,  LVertex2, LRayOrigin);
		const E3dCoordinate  LU = E_Dot3_vv(LCross, LV2minusOrigin) * LDetInv;

		if(LU < 0 || LU > 1.0) return(FALSE);

		const E3dCoord3      E_Sub3i_vv(LV1minusOrigin,  LVertex1, LRayOrigin);
		const E3dCoordinate  LV = -E_Dot3_vv(LCross, LV1minusOrigin) * LDetInv;

		if(LV < 0.0 || LU + LV > 1.0) return(FALSE);

// (LRayOrigin - LVertex0) . ((LVertex1 - LVertex0) x (LVertex2 - LVertex0)) = (LRayOrigin - LVertex0) . Plane[XYZ]
//
		const E3dCoordinate  LT = -E_Dot3_vv(LV0minusOrigin, LTrianglePlane) * LDetInv;

// Make sure the intersection isn't behind the ray origin
//
		if(LT <= 0.0) return(FALSE);
		*PT = LT;

		return(TRUE);
	}
#endif


#if E3dRAY_TRIANGLE_INTERSECTOR == E3dRayTriBARYPLANES
//------------------------------------------------------------------------
// Barycentric-plane algorithm, based on Charles Bloom's description
// (rewritten to use Ray Origin + Direction and bugs fixed by Gabor Nagy)
//------------------------------------------------------------------------
	{
		const E3dCoord4      LTrianglePlane = PRTriangleICore->Plane;
		const E3dCoordinate  LDet = -E_Dot3_vv(LRayDirection, LTrianglePlane);

		if(LDet == 0.0) return(FALSE);	// Ray is parallel to triangle

		const E3dCoordinate  LDetInv = 1.0 / LDet;
		const E3dCoord3      LVertex0 = PRTriangleICore->Vertices[0].Position;
		const E3dCoord3      E_Sub3i_vv(LV0minusOrigin,  LVertex0, LRayOrigin);
		const E3dCoord4      LBaryPlane1 = PRTriangleICore->BaryPlane1, LBaryPlane2 = PRTriangleICore->BaryPlane2;

		const E3dCoordinate  LTDet = -E_Dot3_vv(LV0minusOrigin, LTrianglePlane);
		const E3dCoord3      E_AddScaledToScaled3i_vv(LHitPointDet,  LRayOrigin, LDet, LRayDirection, LTDet);

		const E3dCoordinate  LU = (E_Dot3_vv(LBaryPlane1, LHitPointDet) - LBaryPlane1.W * LDet) * LDetInv;

		if(LU < 0.0 || LU > 1.0) return(FALSE);

		const E3dCoordinate  LV = (E_Dot3_vv(LBaryPlane2, LHitPointDet) - LBaryPlane2.W * LDet) * LDetInv;

		if(LV < 0.0 || LU + LV > 1.0) return(FALSE);

		const E3dCoordinate  LT = LTDet * LDetInv;

// Make sure the intersection isn't behind the ray origin
//
		if(LT <= 0.00001) return(FALSE);
		*PT = LT;

		return(TRUE);
	}
//--------------------------------
#endif


printf("%s() %s:%d  %sIMPLEMENT ME%s\n", __FUNCTION__, __FILE__, __LINE__, ECodeYellow, ECodeNormal);fflush(stdout);
	return(FALSE);



//--------------------------------
// http://www.softsurfer.com/Archive/algorithm_0105/algorithm_0105.htm#intersect_RayTriangle()
// This is the slowest one...
//--------------------------------
/*
	const E3dRTriangle*  LRTriangle = (const E3dRTriangle*)PRTriangleICore;
	const E3dCoord3      LVertex0 = PRTriangleICore->Vertices[0].Position, LNormal = LRTriangle->Normal;
	const E3dCoord3      E_Sub3i_vv(w0,  LRayOrigin, LVertex0);
	const E3dCoordinate  a = -E_Dot3_vv(LNormal, w0);
	const E3dCoordinate  b = E_Dot3_vv(LNormal, LRayDirection);

#define SMALL_NUM  0.00000001  // Anything that avoids division overflow
	if(E3d_Abs(b) < SMALL_NUM)     // Ray is parallel to triangle plane
	{
		if(a == 0.0) return(0);      // Ray lies in triangle plane
		else return(0);              // Ray disjoint from plane
	}


	const E3dCoord3  u = PRTriangleICore->Edge1, v = PRTriangleICore->Edge2;

// Get intersection point of ray with triangle plane
//
	const E3dCoordinate  LT = a / b;
	if(LT < 0.0)                   // Ray goes away from triangle
			return(0);                 // => no intersection

// For a segment, also test if(LT > 1.0) => no intersection
//
	const E3dCoordinate  uu = E_Dot3_vv(u, u);
	const E3dCoordinate  uv = E_Dot3_vv(u, v);
	const E3dCoordinate  vv = E_Dot3_vv(v, v);

	const E3dCoord3  LHitPosition = { LRayOrigin.X + LRayDirection.X * LT, LRayOrigin.Y + LRayDirection.Y * LT, LRayOrigin.Z + LRayDirection.Z * LT };

// is LHitPosition inside T?
//
	const E3dCoord3      E_Sub3i_vv(w,  LHitPosition, LVertex0);
	const E3dCoordinate  wu = E_Dot3_vv(w, u);
	const E3dCoordinate  wv = E_Dot3_vv(w, v);
	const E3dCoordinate  D = uv * uv - uu * vv;

// Get and test parametric coords
//
	const E3dCoordinate  LInvD = 1.0 / D;

	const E3dCoordinate  LU = (uv * wv - vv * wu) * LInvD;
	if(LU < 0.0 || LU > 1.0)        // I is outside T
			return(0);

	const E3dCoordinate  LV = (uv * wu - uu * wv) * LInvD;
	if(LV < 0.0 || (LU + LV) > 1.0)  // I is outside T
			return(0);

	*PT = LT;

	return(TRUE);                      // I is in T
*/
}


//================================================================================
// Intersect ray with triangle
//
// This version:
// -uses the precomputed V0->V1 and V2->V2 edges of the triangle
// -tells whether the ray hit the front or the back face of the triangle
//================================================================================
static inline int E3dRay_IntersectTriangleFrontBack_UV(E3dGridRay* PRay, const E3dRTriangleICore* PRTriangleICore, E3dRayHitUV* PHit)
{
	const E3dCoord3  LRayOrigin = PRay->Origin, LRayDirection = PRay->Direction;

//---------------------
// Moller-Trumbore
//---------------------
	if(E3dRAY_TRIANGLE_INTERSECTOR == E3dRayTriMOLLER)
	{
		const E3dCoord3  LVertex0 = PRTriangleICore->Vertices[0].Position;
		const E3dCoord3  LEdge1 = PRTriangleICore->Edge1, LEdge2 = PRTriangleICore->Edge2;

// Begin calculating determinant - also used to calculate U parameter
//
		E3dCoord3            LPVector;E_Cross3_vvv(LRayDirection, LEdge2, LPVector);
		const E3dCoordinate  LDet = E_Dot3_vv(PRTriangleICore->Edge1, LPVector);

		if(LDet == 0.0) return(FALSE);	// If determinant is near zero, ray lies in plane of triangle

		{
// Calculate distance from LVertex0 to ray origin
//
			const E3dCoord3  E_Sub3i_vv(LTVector,  LRayOrigin, LVertex0);

// Calculate U parameter and test bounds
//
			{
				const E3dCoordinate  LDetInv = 1.0 / LDet, LU = E_Dot3_vv(LTVector, LPVector) * LDetInv;

				if(LU < 0.0 || LU > 1.0) return(FALSE);

// Prepare to test V parameter
//
				{
					E3dCoord3            LQVector;E_Cross3_vvv(LTVector, LEdge1, LQVector);
					const E3dCoordinate  LV = E_Dot3_vv(PRay->Direction, LQVector) * LDetInv;

// Calculate V parameter and test bounds
//
					if(LV < 0.0 || LU + LV > 1.0) return(FALSE);

// Ray intersects triangle. Calculate t,
//
					{
						const E3dCoordinate  LT = E_Dot3_vv(LEdge2, LQVector) * LDetInv;

// Make sure the intersection isn't behind the ray origin
//
						if(LT <= 0.00001) return(FALSE);

						if(LDet > 0) PRay->FrontFaceHit = TRUE;
						else PRay->FrontFaceHit = FALSE;

						PHit->T = LT;
						PHit->UV.X = LU;PHit->UV.Y = LV;
					}
				}
			}
		}

		return(TRUE);
	}

//------------------------------------------------------------------------
// Orientation determinants (slightly faster than Moller-Trumbore)
// Based on http://jgt.akpeters.com/papers/GuigueDevillers03/addendum.html
// (minor optimization + errors in the article fixed by Gabor Nagy).
//------------------------------------------------------------------------
#if E3dRAY_TRIANGLE_INTERSECTOR == E3dRayTriORI_DET
	{
		const E3dCoord4      LTrianglePlane = PRTriangleICore->Plane;
		const E3dCoordinate  LDet = -E_Dot3_vv(LRayDirection, LTrianglePlane);

		if(LDet == 0.0) return(FALSE);  // Ray is parallel to triangle

		const E3dRTriangle*  LRTriangle = (E3dRTriangle*)PRTriangleICore;
		const E3dCoord3      LVertex0 = LRTriangle->Vertices[0].Position, LVertex1 = LRTriangle->Vertices[1].Position, LVertex2 = LRTriangle->Vertices[2].Position;
		const E3dCoord3      E_Sub3i_vv(LV0minusOrigin,  LVertex0, LRayOrigin);
		E3dCoord3            LCross;E_Cross3_vvv(LRayDirection, LV0minusOrigin, LCross);

		const E3dCoord3      E_Sub3i_vv(LV2minusOrigin,  LVertex2, LRayOrigin);
		const E3dCoordinate  LDetInv = 1.0 / LDet;
		const E3dCoordinate  LU = E_Dot3_vv(LCross, LV2minusOrigin) * LDetInv;

		if(LU < 0 || LU > 1.0) return(FALSE);

		const E3dCoord3      E_Sub3i_vv(LV1minusOrigin,  LVertex1, LRayOrigin);
		const E3dCoordinate	 LV = -E_Dot3_vv(LCross, LV1minusOrigin) * LDetInv;

		if(LV < 0.0 || LU + LV > 1.0) return(FALSE);

// (LRayOrigin - LVertex0) . ((LVertex1 - LVertex0) x (LVertex2 - LVertex0)) = (LRayOrigin - LVertex0) . Plane[XYZ]
//
		const E3dCoordinate  LT = -E_Dot3_vv(LV0minusOrigin, LTrianglePlane) * LDetInv;

// Make sure the intersection isn't behind the ray origin
//
		if(LT <= 0.00001) return(FALSE);

		PHit->T = LT;
		PHit->UV.X = LU;PHit->UV.Y = LV;

		if(LDet > 0.0) PRay->FrontFaceHit = TRUE;
		else PRay->FrontFaceHit = FALSE;
		return(TRUE);
	}
#endif

//------------------------------------------------------------------------
// Barycentric-plane algorithm, based on Charles Bloom's description
// (rewritten to use Ray Origin + Direction and bugs fixed by Gabor Nagy)
//------------------------------------------------------------------------
#if E3dRAY_TRIANGLE_INTERSECTOR == E3dRayTriBARYPLANES
	{
		const E3dCoord4      LTrianglePlane = PRTriangleICore->Plane;
		const E3dCoordinate  LDet = -E_Dot3_vv(LRayDirection, LTrianglePlane);

		if(LDet == 0.0) return(FALSE);  // Ray is parallel to triangle

		const E3dCoordinate  LDetInv = 1.0 / LDet;
		const E3dCoord3      LVertex0 = PRTriangleICore->Vertices[0].Position;
		const E3dCoord3      E_Sub3i_vv(LV0minusOrigin,  LVertex0, LRayOrigin);

		const E3dCoordinate  LTDet = -E_Dot3_vv(LV0minusOrigin, LTrianglePlane);
		const E3dCoord3      E_AddScaledToScaled3i_vv(LHitPointDet,  LRayOrigin, LDet, LRayDirection, LTDet);

		const E3dCoord4      LBaryPlane1 = PRTriangleICore->BaryPlane1;
		const E3dCoordinate  LU = (E_Dot3_vv(LBaryPlane1, LHitPointDet) - LBaryPlane1.W * LDet) * LDetInv;

		if(LU < 0.0 || LU > 1.0) return(FALSE);

		const E3dCoord4      LBaryPlane2 = PRTriangleICore->BaryPlane2;
		const E3dCoordinate  LV = (E_Dot3_vv(LBaryPlane2, LHitPointDet) - LBaryPlane2.W * LDet) * LDetInv;

		if(LV < 0.0 || LU + LV > 1.0) return(FALSE);

		const E3dCoordinate  LT = LTDet * LDetInv;

// Make sure the intersection isn't behind the ray origin
//
		if(LT <= 0.00001) return(FALSE);

		PHit->T = LT;
		PHit->UV.X = LU;PHit->UV.Y = LV;

		if(LDet > 0.0) PRay->FrontFaceHit = TRUE;
		else PRay->FrontFaceHit = FALSE;


//if(!PRay->FrontFaceHit) { printf("%s() %s:%d LDet %f\n", __FUNCTION__, __FILE__, __LINE__, LDet);fflush(stdout); }

/*
		LHitPoint->X = LHitPointDet.X * LInvDet;
		LHitPoint->Y = LHitPointDet.Y * LInvDet;
		LHitPoint->Z = LHitPointDet.Z * LInvDet;
*/

//printf("  BARY %f %f\n", LU, LV);fflush(stdout);

		return(TRUE);
	}
#endif
}


//================================================================================
// Intersect ray with triangle and do back-face culling
//================================================================================
static inline int E3dRay_IntersectTriangleFront_UV(E3dGridRay* PRay, const E3dRTriangleICore* PRTriangleICore, E3dRayHitUV* PHit)
{
	const E3dCoord3		LRayOrigin = PRay->Origin, LRayDirection = PRay->Direction;

#if E3dRAY_TRIANGLE_INTERSECTOR == E3dRayTriMOLLER
//---------------------
// Moller-Trumbore
//---------------------
	const E3dCoord3  LVertex0 = PRTriangleICore->Vertices[0].Position;
	const E3dCoord3  LEdge1 = PRTriangleICore->Edge1, LEdge2 = PRTriangleICore->Edge2;

// Begin calculating determinant - also used to calculate U parameter
//
	E3dCoord3            LPVector;E_Cross3_vvv(LRayDirection, LEdge2, LPVector);
	const E3dCoordinate  LDet = E_Dot3_vv(PRTriangleICore->Edge1, LPVector);

	if(LDet == 0.0) return(FALSE);	// If determinant is near zero, ray lies in plane of triangle

	{
// Calculate distance from LVertex0 to ray origin
//
		const E3dCoord3  E_Sub3i_vv(LTVector,  LRayOrigin, LVertex0);

// Calculate U parameter and test bounds
//
		{
			const E3dCoordinate  LDetInv = 1.0 / LDet, LU = E_Dot3_vv(LTVector, LPVector) * LDetInv;

			if(LU < 0.0 || LU > 1.0) return(FALSE);

// Prepare to test V parameter
//
			{
				E3dCoord3            LQVector;E_Cross3_vvv(LTVector, LEdge1, LQVector);
				const E3dCoordinate  LV = E_Dot3_vv(PRay->Direction, LQVector) * LDetInv;

// Calculate V parameter and test bounds
//
				if(LV < 0.0 || LU + LV > 1.0) return(FALSE);

// Ray intersects triangle. Calculate t,
//
				{
					const E3dCoordinate  LT = E_Dot3_vv(LEdge2, LQVector) * LDetInv;

// Make sure the intersection isn't behind the ray origin
//
					if(LT <= 0.00001) return(FALSE);

					if(LDet > 0)
					{
						PRay->FrontFaceHit = TRUE;
						PHit->T = LT;
						PHit->UV.X = LU;PHit->UV.Y = LV;
					}
					else return(FALSE);
				}
			}
		}
	}

	return(TRUE);
#endif


#if E3dRAY_TRIANGLE_INTERSECTOR == E3dRayTriORI_DET
//------------------------------------------------------------------------
// Using orientation determinants (slightly faster than Moller-Trumbore)
// Based on (errors in the article fixed by Gabor Nagy)
// http://jgt.akpeters.com/papers/GuigueDevillers03/addendum.html
//------------------------------------------------------------------------
	{
		const E3dCoordinate  LDet = -E_Dot3_vv(LRayDirection, PRTriangleICore->Plane);

		if(LDet <= 0.0) return(FALSE);
//		if(LDet == 0.0) return(FALSE);  // Ray is parallel to triangle

		const E3dRTriangle*  LRTriangle = (const E3dRTriangle*)PRTriangleICore;
		const E3dCoord3      LVertex0 = LRTriangle->Vertices[0].Position, LVertex1 = LRTriangle->Vertices[1].Position, LVertex2 = LRTriangle->Vertices[2].Position;
		const E3dCoord3      E_Sub3i_vv(LV0minusOrigin,  LVertex0, LRayOrigin);
		E3dCoord3            LCross;E_Cross3_vvv(LRayDirection, LV0minusOrigin, LCross);

		const E3dCoord3      E_Sub3i_vv(LV2minusOrigin,  LVertex2, LRayOrigin);
		const E3dCoordinate  LU = E_Dot3_vv(LCross, LV2minusOrigin);

		if(LU < 0 || LU > LDet) return(FALSE);

		const E3dCoord3      E_Sub3i_vv(LV1minusOrigin,  LVertex1, LRayOrigin);
		const E3dCoordinate  LV = -E_Dot3_vv(LCross, LV1minusOrigin);

		if(LV < 0.0 || LU + LV > LDet) return(FALSE);

		const E3dCoordinate	LDetInv = 1.0 / LDet;

// (LRayOrigin - LVertex0) . ((LVertex1 - LVertex0) x (LVertex2 - LVertex0)) = (LRayOrigin - LVertex0) . Plane[XYZ]
//
		const E3dCoordinate  LT = -E_Dot3_vv(LV0minusOrigin, LTrianglePlane) * LDetInv;

// Make sure the intersection isn't behind the ray origin
//
		if(LT <= 0.00001) return(FALSE);

		PHit->T = LT;
		PHit->UV.X = LU * LDetInv;PHit->UV.Y = LV * LDetInv;

		return(TRUE);
	}
#endif

#if E3dRAY_TRIANGLE_INTERSECTOR == E3dRayTriBARYPLANES
//------------------------------------------------------------------------
// Barycentric-plane algorithm, based on Charles Bloom's description
// (rewritten to use Ray Origin + Direction and bugs fixed by Gabor Nagy)
//------------------------------------------------------------------------
	{
		const E3dCoord4      LTrianglePlane = PRTriangleICore->Plane;
		const E3dCoordinate  LDet = -E_Dot3_vv(LRayDirection, LTrianglePlane);

		if(LDet <= 0.0) return(FALSE);	// Ray is parallel to triangle or had hit the Triangle's back side

		const E3dCoord3  LVertex0 = PRTriangleICore->Vertices[0].Position;
		const E3dCoord3  E_Sub3i_vv(LV0minusOrigin,  LVertex0, LRayOrigin);
		const E3dCoord4  LBaryPlane1 = PRTriangleICore->BaryPlane1, LBaryPlane2 = PRTriangleICore->BaryPlane2;

		const E3dCoordinate  LTDet = -E_Dot3_vv(LV0minusOrigin, LTrianglePlane);
		const E3dCoord3      E_AddScaledToScaled3i_vv(LHitPointDet,  LRayOrigin, LDet, LRayDirection, LTDet);

		const E3dCoordinate  LU = (E_Dot3_vv(LBaryPlane1, LHitPointDet) - LBaryPlane1.W * LDet);

		if(LU < 0.0 || LU > LDet) return(FALSE);

		const E3dCoordinate	LV = (E_Dot3_vv(LBaryPlane2, LHitPointDet) - LBaryPlane2.W * LDet);

		if(LV < 0.0 || LU + LV > LDet) return(FALSE);

		const E3dCoordinate  LDetInv = 1.0 / LDet;
		const E3dCoordinate  LT = LTDet * LDetInv;

// Make sure the intersection isn't behind the ray origin
//
		if(LT <= 0.00001) return(FALSE);

		PHit->T = LT;
		PHit->UV.X = LU * LDetInv;PHit->UV.Y = LV * LDetInv;

		return(TRUE);
	}
#endif

printf("%s() %s:%d  %sIMPLEMENT ME%s\n", __FUNCTION__, __FILE__, __LINE__, ECodeYellow, ECodeNormal);fflush(stdout);
	return(FALSE);
}


static inline int E3dShadowRay_IntersectTriangleFrontBack(E3dGridRay* PRay, const E3dRTriangleICore* PRTriangleICore, const E3dCoordinate PDistance)
{
	const E3dCoord3  LRayOrigin = PRay->Origin, LRayDirection = PRay->Direction;

#if E3dRAY_TRIANGLE_INTERSECTOR == E3dRayTriMOLLER
//---------------------
// Moller-Trumbore
//---------------------
	const E3dCoord3  LVertex0 = PRTriangleICore->Vertices[0].Position;
	const E3dCoord3  LEdge1 = PRTriangleICore->Edge1, LEdge2 = PRTriangleICore->Edge2;

// Begin calculating determinant - also used to calculate U parameter
//
	E3dCoord3            LPVector;E_Cross3_vvv(LRayDirection, LEdge2, LPVector);
	const E3dCoordinate  LDet = E_Dot3_vv(PRTriangleICore->Edge1, LPVector);

	if(LDet == 0.0) return(FALSE);	// If determinant is near zero, ray lies in plane of triangle

	{
// Calculate distance from LVertex0 to ray origin
//
		const E3dCoord3  E_Sub3i_vv(LTVector,  LRayOrigin, LVertex0);

// Calculate U parameter and test bounds
//
		{
			const E3dCoordinate  LDetInv = 1.0 / LDet, LU = E_Dot3_vv(LTVector, LPVector) * LDetInv;

			if(LU < 0.0 || LU > 1.0) return(FALSE);

// Prepare to test V parameter
//
			{
				E3dCoord3            LQVector;E_Cross3_vvv(LTVector, LEdge1, LQVector);
				const E3dCoordinate  LV = E_Dot3_vv(PRay->Direction, LQVector) * LDetInv;

// Calculate V parameter and test bounds
//
				if(LV < 0.0 || LU + LV > 1.0) return(FALSE);

// Ray intersects triangle. Calculate t,
//
				{
					const E3dCoordinate  LT = E_Dot3_vv(LEdge2, LQVector) * LDetInv;

// Make sure the intersection isn't behind the ray origin
//
					if(LT <= 0.00001) return(FALSE);
					if(LT > PDistance) return(FALSE);

					if(LDet > 0) PRay->FrontFaceHit = TRUE;
					else PRay->FrontFaceHit = FALSE;
				}
			}
		}
	}

	return(TRUE);
#endif

#if E3dRAY_TRIANGLE_INTERSECTOR == E3dRayTriORI_DET
//------------------------------------------------------------------------
// Orientation determinants (slightly faster than Moller-Trumbore)
// Based on http://jgt.akpeters.com/papers/GuigueDevillers03/addendum.html
// (minor optimization + errors in the article fixed by Gabor Nagy).
//------------------------------------------------------------------------
	{
		const E3dCoord4      LTrianglePlane = PRTriangleICore->Plane;
		const E3dCoordinate  LDet = -E_Dot3_vv(LRayDirection, LTrianglePlane);

		if(LDet == 0.0) return(FALSE);  // Ray is parallel to triangle

		const E3dCoordinate  LDetInv = 1.0 / LDet;
		const E3dRTriangle*  LRTriangle = (const E3dRTriangle*)PRTriangleICore;
		const E3dCoord3      LVertex0 = LRTriangle->Vertices[0].Position, LVertex1 = LRTriangle->Vertices[1].Position, LVertex2 = LRTriangle->Vertices[2].Position;
		const E3dCoord3      E_Sub3i_vv(LV0minusOrigin,  LVertex0, LRayOrigin);
		E3dCoord3            LCross;E_Cross3_vvv(LRayDirection, LV0minusOrigin, LCross);

		const E3dCoord3      E_Sub3i_vv(LV2minusOrigin,  LVertex2, LRayOrigin);
		const E3dCoordinate  LU = E_Dot3_vv(LCross, LV2minusOrigin) * LDetInv;

		if(LU < 0 || LU > 1.0) return(FALSE);

		const E3dCoord3      E_Sub3i_vv(LV1minusOrigin,  LVertex1, LRayOrigin);
		const E3dCoordinate  LV = -E_Dot3_vv(LCross, LV1minusOrigin) * LDetInv;

		if(LV < 0.0 || LU + LV > 1.0) return(FALSE);

// (LRayOrigin - LVertex0) . ((LVertex1 - LVertex0) x (LVertex2 - LVertex0)) = (LRayOrigin - LVertex0) . Plane[XYZ]
//
		const E3dCoordinate  LT = -E_Dot3_vv(LV0minusOrigin, LTrianglePlane) * LDetInv;

// Make sure the intersection isn't behind the ray origin
//
		if(LT <= 0.0) return(FALSE);

		return(TRUE);
	}
#endif

#if E3dRAY_TRIANGLE_INTERSECTOR == E3dRayTriBARYPLANES
//------------------------------------------------------------------------
// Barycentric-plane algorithm, based on Charles Bloom's description
// (rewritten to use Ray Origin + Direction and bugs fixed by Gabor Nagy)
//------------------------------------------------------------------------
	{
		const E3dCoord4      LTrianglePlane = PRTriangleICore->Plane;
		const E3dCoordinate  LDet = -E_Dot3_vv(LRayDirection, LTrianglePlane);

		if(LDet == 0.0) return(FALSE);  // The ray is parallel with the triangle

		const E3dCoordinate  LDetInv = 1.0 / LDet;
		const E3dCoord3      LVertex0 = PRTriangleICore->Vertices[0].Position;
		const E3dCoord3      E_Sub3i_vv(LV0minusOrigin,  LVertex0, LRayOrigin);
		const E3dCoord4      LBaryPlane1 = PRTriangleICore->BaryPlane1, LBaryPlane2 = PRTriangleICore->BaryPlane2;

		const E3dCoordinate  LTDet = -E_Dot3_vv(LV0minusOrigin, LTrianglePlane);

		const E3dCoordinate  LT = LTDet * LDetInv;

// Make sure the intersection isn't behind the ray origin
//
		if(LT <= 0.00001 || LT > PDistance) return(FALSE);

		const E3dCoord3  E_AddScaledToScaled3i_vv(LHitPointDet,  LRayOrigin, LDet, LRayDirection, LTDet);

		const E3dCoordinate  LU = (E_Dot3_vv(LBaryPlane1, LHitPointDet) - LBaryPlane1.W * LDet) * LDetInv;

		if(LU < 0.0 || LU > 1.0) return(FALSE);

		const E3dCoordinate  LV = (E_Dot3_vv(LBaryPlane2, LHitPointDet) - LBaryPlane2.W * LDet) * LDetInv;

		if(LV < 0.0 || LU + LV > 1.0) return(FALSE);

		return(TRUE);
	}
#endif

printf("%s() %s:%d  %sIMPLEMENT ME%s\n", __FUNCTION__, __FILE__, __LINE__, ECodeYellow, ECodeNormal);fflush(stdout);
return(FALSE);
}


//========================================================================
// Return the t parameter at the intersection
//========================================================================
static inline E3dCoordinate E3dRay_IntersectPlane(const E3dRay* PRay, const E3dPlane* PPlane)
{
	const E3dCoordinate  LDiv = (PPlane->A * PRay->Direction.X + PPlane->B * PRay->Direction.Y + PPlane->C * PRay->Direction.Z);
	return((PPlane->D - PPlane->A * PRay->Origin.X - PPlane->B * PRay->Origin.Y - PPlane->C * PRay->Origin.Z) / LDiv);
}


//========================================================================
// Reflect a Ray's partial derivatives and sub-rays
//========================================================================
static inline void E3dRay_Reflect_d(const E3dRTriangle* PRTriangle, const E3dCoord3* PNormal, const E3dStackRay* PRay, const E3dRayDifferentialsTmp* PRayDiffTmp,
 E3dStackRay* PNewRay)
{
	const E3dCoord3  LNormal = *PNormal;
	const E3dCoord3  LRayDirection = PRay->Direction;

	{
// Ray-differential : REFLECT operation
//
		const E3dCoordinate  LD_dot_N = E_Dot3_vp(LRayDirection, PNormal);

		const E3dCoord3      LdN_div_dx = PRayDiffTmp->dN_div_dx;
		const E3dCoord3      LdN_div_dy = PRayDiffTmp->dN_div_dy;

		const E3dCoordinate  LdD_dot_N_div_dx = PRayDiffTmp->dD_dot_N_div_d.X;
		const E3dCoordinate  LdD_dot_N_div_dy = PRayDiffTmp->dD_dot_N_div_d.Y;

// Store new dD/dx and dD/dy
// - dP/dx and dP/dy don't change with REFLECT
//
		PNewRay->Differentials.dP_div_dx = PRay->Differentials.dP_div_dx;
		PNewRay->Differentials.dP_div_dy = PRay->Differentials.dP_div_dy;

		{
			E3dCoord3  E_Mul3i_vs(L0,  LdN_div_dx, LD_dot_N);
			E3dCoord3  E_Mul3i_vs(L1,  LNormal, LdD_dot_N_div_dx);
			E3dCoord3  E_Add3i_vv(L2,  L0, L1);
			E_AddScaled3_vvv(PRay->Differentials.dD_div_dx, L2, -2.0, PNewRay->Differentials.dD_div_dx);
		}
		{
			E3dCoord3  E_Mul3i_vs(L0,  LdN_div_dy, LD_dot_N);
			E3dCoord3  E_Mul3i_vs(L1,  LNormal, LdD_dot_N_div_dy);
			E3dCoord3  E_Add3i_vv(L2,  L0, L1);
			E_AddScaled3_vvv(PRay->Differentials.dD_div_dy, L2, -2.0, PNewRay->Differentials.dD_div_dy);
		}
	}
}


//========================================================
// Refract a ray crossing an interface
// Notes: esrc/libsrc/E3D/Notes/Refract.sxi
//========================================================
static inline EBool E3dRay_Refract(const E3dRay* PRay, E3dRay* PNewRay, const E3dCoord3* PNormal, const E3dCoordinate PRefrIndex12)
{
	const E3dCoordinate  LIX = PRay->Direction.X, LIY = PRay->Direction.Y, LIZ = PRay->Direction.Z;

// ci = N . -I
//
	const E3dCoordinate  Lci = -PNormal->X * LIX - PNormal->Y * LIY - PNormal->Z * LIZ;

// Si = -I - Ci = -I - ci * N
//
	const E3dCoordinate  LSiX = -LIX - Lci * PNormal->X,
	                     LSiY = -LIY - Lci * PNormal->Y,
	                     LSiZ = -LIZ - Lci * PNormal->Z;

	const E3dCoordinate  LaSqr = 1.0 - PRefrIndex12 * PRefrIndex12 * (LSiX * LSiX + LSiY * LSiY + LSiZ * LSiZ);

	if(LaSqr < 0.0) return(FALSE);

// T= sqrt(a)*-PNormal - PRefrIndex12 * Si
//
	const E3dCoordinate  La = E3d_Sqrt(LaSqr);

	PNewRay->Direction.X = -La * PNormal->X - PRefrIndex12 * LSiX;
	PNewRay->Direction.Y = -La * PNormal->Y - PRefrIndex12 * LSiY;
	PNewRay->Direction.Z = -La * PNormal->Z - PRefrIndex12 * LSiZ;


	return(TRUE);
}


//========================================================================
// Refract a Ray's partial derivatives and sub-rays
//========================================================================
static inline void E3dRay_Refract_d(const E3dRTriangle* PRTriangle, const E3dCoord3* PNormal,  const E3dCoordinate PRefrIndex12,
 const E3dDRay* PRay, const E3dRayDifferentialsTmp* PRayDiffTmp,  E3dStackRay* PNewRay)
{
	const E3dCoord3  LNormal = *PNormal;

	{
		const E3dCoord3  LRayDirection    = PRay->Direction;
		const E3dCoord3  LNewRayDirection = PNewRay->Direction;

// Ray-differential : REFRACT operation
//
		const E3dCoord3      LdN_div_dx = PRayDiffTmp->dN_div_dx;
		const E3dCoord3      LdN_div_dy = PRayDiffTmp->dN_div_dy;

		const E3dCoordinate  LdD_dot_N_div_dx = PRayDiffTmp->dD_dot_N_div_d.X;
		const E3dCoordinate  LdD_dot_N_div_dy = PRayDiffTmp->dD_dot_N_div_d.Y;

// Store new dD/dx and dD/dy
// - dP/dx and dP/dy don't change with REFLECT
//
		PNewRay->Differentials.dP_div_dx = PRay->Differentials.dP_div_dx;
		PNewRay->Differentials.dP_div_dy = PRay->Differentials.dP_div_dy;

		const E3dCoordinate  LD_dot_N    = E_Dot3_vv(LRayDirection,    LNormal);
		const E3dCoordinate  LDNew_dot_N = E_Dot3_vv(LNewRayDirection, LNormal);
		const E3dCoordinate  Lmu = PRefrIndex12 * LD_dot_N - LDNew_dot_N;

		const E3dCoordinate  Ldmu_div_dx = (PRefrIndex12 - PRefrIndex12 * PRefrIndex12 * LD_dot_N / LDNew_dot_N) * LdD_dot_N_div_dx;
		const E3dCoordinate  Ldmu_div_dy = (PRefrIndex12 - PRefrIndex12 * PRefrIndex12 * LD_dot_N / LDNew_dot_N) * LdD_dot_N_div_dy;

		{
			E3dCoord3  E_Mul3i_vs(L0,  LdN_div_dx, Lmu);
			E3dCoord3  E_Mul3i_vs(L1,  LNormal, Ldmu_div_dx);
			E3dCoord3  E_Add3i_vv(L2,  L0, L1);
			E_SubFromScaled3_vvv(PRay->Differentials.dD_div_dx, PRefrIndex12,  L2,  PNewRay->Differentials.dD_div_dx);
		}
		{
			E3dCoord3  E_Mul3i_vs(L0,  LdN_div_dy, Lmu);
			E3dCoord3  E_Mul3i_vs(L1,  LNormal, Ldmu_div_dy);
			E3dCoord3  E_Add3i_vv(L2,  L0, L1);
			E_SubFromScaled3_vvv(PRay->Differentials.dD_div_dy, PRefrIndex12,  L2,  PNewRay->Differentials.dD_div_dy);
		}

	}
}


//================================================================================================
// Check if a ray should be reflected from an interface.
// If so, add new Ray to the stack.
//================================================================================================
static inline int _E3dRay_CheckReflection(const E3dRTriangle* PRTriangle, const E3dCoord3* PSPosition, const E3dCoord3* PNormal,
 const int PSpectralIndex, const int PNSpectralSamples,
 const int PNumOfAnisotropicTextureSamples,
 const float PRayLumaMinTimesNumOfSpectralSamples,
 const float* PReflective,
 const E3dStackRay* PRay, const EBool PRayHasDifferentials, const E3dRayDifferentialsTmp* PRayDiffTmp, E3dStackRay* PNewRay)

{
	float  LImportance = 0.0;
	int    LSI = PSpectralIndex;const int  LSIEnd = PSpectralIndex + PNSpectralSamples;for(;LSI < LSIEnd;LSI++)
	{
		PNewRay->Intensity[LSI] = PRay->Intensity[LSI] * PReflective[LSI];LImportance += PNewRay->Intensity[LSI];
	}

// To avoid endless bounces, only reflect this ray if its luma >= LRenderer->RayLumaMin
//
	if(LImportance >= PRayLumaMinTimesNumOfSpectralSamples)
	{
		PNewRay->MaterialStackPointer = PRay->MaterialStackPointer;
		memcpy(PNewRay->MaterialStack, PRay->MaterialStack, sizeof(E3dMaterial*) * (PRay->MaterialStackPointer + 1));
		PNewRay->ExcludeFace = PRTriangle->Face;

		PNewRay->Origin = *PSPosition;E3d_Reflect3_vpv(PRay->Direction, PNormal, PNewRay->Direction);

		PNewRay->HasSubRays = FALSE;	// We don't reflect sub-rays yet (used for anisotropic texture filtering)

		PNewRay->HasDifferentials = PRayHasDifferentials;
		if(PRayHasDifferentials)
		{
			E3dRay_Reflect_d(PRTriangle, PNormal,  PRay, PRayDiffTmp,  PNewRay);
			memcpy(PNewRay->UVs, PRay->UVs, sizeof(E3dCoord2) * PNumOfAnisotropicTextureSamples);
		}

		PNewRay->SpectralIndex = PSpectralIndex;PNewRay->NSpectralSamples = PNSpectralSamples;

		return(1);
	}
	else return(0);
}


//================================================================================================
// Check if a ray should be transmitted through an interface (in our out of a Material).
// If so, add new Ray to the stack.
//================================================================================================
static inline int _E3dRay_CheckTransmission(const E3dRTriangle* PRTriangle,  const E3dCoord3* PSPosition, const E3dCoord3* PNormal,
 const int PSpectralIndex, const int PNSpectralSamples,
 const int PNumOfAnisotropicTextureSamples,
 const float PRayLumaMinTimesNumOfSpectralSamples,
 const float* PTransparent,
 const EBool PFrontFaceHit,
 const E3dShader* PFromShader, const E3dShader* PToShader, const E3dMaterial* PToMaterial,
 const E3dStackRay* PRay, const EBool PRayHasDifferentials, const E3dRayDifferentialsTmp* PRayDiffTmp,  E3dStackRay* PNewRay)
{
	const E3dCoordinate  LRefrIndex1 = PFromShader ? PFromShader->RefractiveIndex.Samples[PSpectralIndex] : 1.0;
	const E3dCoordinate  LRefrIndex2 = PToShader ? PToShader->RefractiveIndex.Samples[PSpectralIndex] : 1.0;

// RayRefract() returns FALSE if there is total internal reflection (ray hits the surface below critical angle).
// If that occurs, we won't transmit a ray.
//
	E3dRay               LTRay;
	const E3dCoordinate  LRefrIndex12 = LRefrIndex1 / LRefrIndex2;
	const E3dCoordinate  LSolidAngleRatio = LRefrIndex12 * LRefrIndex12;
//const E3dCoordinate  LSolidAngleRatio = 1.0;

	float  LImportance = 0.0;
	{
		int  LSI = PSpectralIndex;const int  LSIEnd = PSpectralIndex + PNSpectralSamples;for(;LSI < LSIEnd;LSI++)
		{
			const float  LSIn = PRay->Intensity[LSI] * PTransparent[LSI] * LSolidAngleRatio;
			PNewRay->Intensity[LSI] = LSIn;
			LImportance += LSIn;
		}
	}

	if(LImportance < PRayLumaMinTimesNumOfSpectralSamples) return(0);

	if(E3dRay_Refract((E3dRay*)PRay, &LTRay, PNormal, LRefrIndex12))
	{
// Add a new ray that enters or exits the object
//
		PNewRay->Origin = *PSPosition;
		PNewRay->Direction = LTRay.Direction;

		PNewRay->HasSubRays = FALSE;	// We don't refract sub-rays yet (used for anisotropic texture filtering)

		PNewRay->HasDifferentials = PRayHasDifferentials;
		if(PRayHasDifferentials)
		{
			E3dRay_Refract_d(PRTriangle, PNormal,  LRefrIndex12, (E3dDRay*)PRay, PRayDiffTmp,  PNewRay);
			memcpy(PNewRay->UVs, PRay->UVs, sizeof(E3dCoord2) * PNumOfAnisotropicTextureSamples);
		}

		memcpy(PNewRay->MaterialStack, PRay->MaterialStack, sizeof(E3dMaterial*) * (PRay->MaterialStackPointer + 1));
		if(PFrontFaceHit)
		{
// If ray hit a front-face (entering a Material), increment MaterialIndex and store the Material of that face in MaterialStack.
//
			PNewRay->MaterialStackPointer = PRay->MaterialStackPointer + 1;
			PNewRay->MaterialStack[PNewRay->MaterialStackPointer] = PToMaterial;

if(0)
{
//	printf("%s() %s:%d Frntface [%s]->[%s]\n", __FUNCTION__, __FILE__, __LINE__,  PFromMaterial ? LFromMaterial->Name : "vacuum", PToMaterial->Name);
//	fflush(stdout);
}
if(0)
{
//	printf("%s() %s:%d  PNewRay %p  REFRACT-IN  PNewRay->MaterialStackPointer %d  %p\n", __FUNCTION__, __FILE__, __LINE__,  PNewRay,
//	 PNewRay->MaterialStackPointer, PToMaterial);
//	fflush(stdout);
}
		}
		else
		{
// If the ray hit a back-face (exiting a Material), decrement MaterialIndex
//
			if(PRay->MaterialStackPointer > 0) PNewRay->MaterialStackPointer = PRay->MaterialStackPointer - 1;
			else PNewRay->MaterialStackPointer = 0;

if(0)
{
//	printf("%s() %s:%d Backface [%s]->[%s]\n", __FUNCTION__, __FILE__, __LINE__, LFromMaterial ? LFromMaterial->Name : "vacuum",
//	 LToMaterial ? LToMaterial->Name : "vacuum");
//	fflush(stdout);
}
if(0)
{
//	printf("%s() LRenderer %p  PNewRay %p  REFRACT-OUT  PNewRay->MaterialStackPointer %d\n", __FUNCTION__, LRenderer, PNewRay, PNewRay->MaterialStackPointer);
//	fflush(stdout);
}
		}

		PNewRay->ExcludeFace = PRTriangle->Face;
		PNewRay->SpectralIndex = PSpectralIndex;PNewRay->NSpectralSamples = PNSpectralSamples;

		return(1);
	}
	else
	{
// Total Internal Reflection...
//
		return(0);
	}
}


//================================================================================================
// Check if a ray should be transmitted through an interface (in our out of a Material).
// If so, add new Ray to the stack.
//================================================================================================
static inline int _E3dRay_CheckTransmissionDoubleSided(const E3dRTriangle* PRTriangle,  const E3dCoord3* PSPosition, const E3dCoord3* PNormal,
 const int PSpectralIndex, const int PNSpectralSamples,
 const int PNumOfAnisotropicTextureSamples,
 const float PRayLumaMinTimesNumOfSpectralSamples,
 const float* PTransparent,
 const EBool PFrontFaceHit,
 const E3dShader* PFromShader, const E3dShader* PToShader, const E3dMaterial* PToMaterial,
 const E3dStackRay* PRay, const EBool PRayHasDifferentials, const E3dRayDifferentialsTmp* PRayDiffTmp,  E3dStackRay* PNewRay)
{
	float  LImportance = 0.0;
	{
		int  LSI = PSpectralIndex;const int  LSIEnd = PSpectralIndex + PNSpectralSamples;for(;LSI < LSIEnd;LSI++)
		{
			const float  LSIn = PRay->Intensity[LSI] * PTransparent[LSI];
			PNewRay->Intensity[LSI] = LSIn;
			LImportance += LSIn;
		}
	}

	if(LImportance < PRayLumaMinTimesNumOfSpectralSamples) return(0);

	{
// Add a new ray that enters or exits the object
//
		PNewRay->Origin = *PSPosition;
		PNewRay->Direction = PRay->Direction;

		PNewRay->HasSubRays = PRay->HasSubRays;

		PNewRay->HasDifferentials = PRayHasDifferentials;
		PNewRay->Differentials = PRay->Differentials;

		memcpy(PNewRay->UVs, PRay->UVs, sizeof(E3dCoord2) * PNumOfAnisotropicTextureSamples);

		PNewRay->MaterialStackPointer = PRay->MaterialStackPointer;
		memcpy(PNewRay->MaterialStack, PRay->MaterialStack, sizeof(E3dMaterial*) * (PRay->MaterialStackPointer + 1));

		PNewRay->ExcludeFace = PRTriangle->Face;
		PNewRay->SpectralIndex = PSpectralIndex;PNewRay->NSpectralSamples = PNSpectralSamples;

		return(1);
	}
}

extern E3dCoordinate  E3dRay_IntersectSphereNear(const E3dRay* PRay, E3dCoordinate PDistance2, E3dCoordinate PRadius2);

extern int            E3dRay_IntersectTriangle3FrontBack(const E3dGridRay* PRay, const E3dCoord3* LVertex0, const E3dCoord3* PVertex1, const E3dCoord3* PVertex2,
                       E3dCoordinate* PT);

extern E3dCoordinate  E3dRay_ClosestPointOnSegment(E3dRay* PRay,
                       E3dCoordinate LX0, E3dCoordinate LY0, E3dCoordinate LZ0, E3dCoordinate LX1, E3dCoordinate LY1, E3dCoordinate LZ1,
                       E3dCoord3* LResult, E3dCoordinate* LUPtr);


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

#endif	// _E3D_Ray_h
