//================================================================
// E3D
//
// Rendering-related header file
//
// AUTHOR:  Gabor Nagy
// DATE:    2025-Jan-26 19:35:56
//
// Copyright (C) 1996-2026 by Gabor Nagy. All rights reserved.
//================================================================
#ifndef _E3D_Raytracing_h
#define _E3D_Raytracing_h

#include <E3D/Rendering.h>

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


#ifndef _E3dRayTracer
#define _E3dRayTracer
typedef struct E3dRayTracer_S         E3dRayTracer;
typedef struct E3dRayTracer_thread_S  E3dRayTracer_thread;
#endif


typedef struct
{
	E3dCoordinate        CDFPosition;

	const E3dRTriangle*  RTriangle;
	E3dCoordinate        Alpha, SinAlpha, CosAlpha;
	E3dCoord3            TA, TB, TC, TU;
	E3dCoordinate        Product, TriangleApparentSolidAngle;  // Apparent solid angle from sampled point
	EBool                Set;
} E3dLightTriangleCDFEntry;



typedef struct
{
	union
	{
		E3dCoordinate  SplitValue;  // Interior: split plane position
		const void**   Primitives;  // Leaf: list of primitives
		const void*    Primitive;   // Leaf: direct index of one primitive
	};
	unsigned int    Flags;        // 2 bits: Split-axis / IsLeaf (0:X, 1:Y, 2:Z, 3:IsLeaf); 30bits: NumOfPrimitives (leaf) or index of right child (left child comes immediately after the current KDNode)
} E3dKDTreeNode;


#define E3dAcceleratorTreeCore\
	EObjectCore(EObject);\
\
	E3dBBox         BBox;\
	E3dNode*        VisNode     // For visualizing the BVH/KDTree etc.

typedef struct
{
	E3dAcceleratorTreeCore;

	float           CostRatio;     // Node traversal cost divided by primitive intersection cost
	float           EmptyBonus;    // Empty bonus
	EIndex	        NextFreeNodeIndex, NumOfAllocatedNodes;
	EIndex          TotalNumOfPrimitives;
	int             MaxDepth, MaxLeafSize;
	E3dKDTreeNode*  Nodes;

// These hold temporary data, to keep the function parameter-count reasonable.
//
	const void**    Primitives;
	const E3dBBox*  AllBounds;

// Statistics
//
	int             DepthLimitReached, NumOfBadSplits, NumOfClips, NumOfNullClips, NumOfBadClips;

	ETime           BuildStartMilliseconds, BuildEndMilliseconds;
} E3dKDTree;


typedef struct
{
	E3dBBox                BBox;

	union
	{
		const E3dRTriangle**  Primitives;  // Leaf: list of primitives
		const E3dRTriangle*   Primitive;   // Leaf: direct index of one primitive
	};
	uint32_t               Flags : 2,              // 2 bits: Split-axis / IsLeaf (0:X, 1:Y, 2:Z, 3:IsLeaf)
	                       SiblingOverlaps : 1,    // If TRUE, siblings overlap along split axis, so we can't skip the far child if an intersection is found in the near child
	                       Index : 29;             // NumOfPrimitives (leaf) or index of right child (left child comes immediately after the current BVHNode). Right child index = 0 means suspended recursion for multi-threading split
} E3dBVHNode;


typedef struct
{
	E3dBBox  BBox;
	EIndex   NPrimitives;
} E3dBVHTmpNode;
EList_t(E3dBVHTmpNode);

typedef struct
{
	EIndex       NextFreeNodeIndex, NumOfAllocatedNodes;
	E3dBVHNode*  Nodes;

// For suspended tree building (used for multi-threading after the first few splits)
} E3dBVHDynData;

typedef struct
{
	E3dAcceleratorTreeCore;

	E3dBVHDynData          DynData;

	EIndex                 MaxLeafSize;
	int                    MaxDepth;

	const E3dRTriangle**   Primitives;
	E3dBBoxCenterExtents*  AllBounds;

	ETime                  BuildStartMilliseconds, BuildEndMilliseconds;

	EList_s(, E3dBVHTmpNode, TmpNodes);  // Used for multi-threaded build preprocessing
} E3dBVH;


#define E3dMAX_KDSTACK 64


typedef struct
{
	E3dStackRay      PrimaryRay;E3dRayHit  FirstHit;EIndex2  PixelPosition;
	E3dShaderResult  Pixel;
} E3dDebugRay;
EListA_t(E3dDebugRay);

typedef struct
{
	EIndex  TriQueries,
          TriQueriesAfterExcludeFace,
          TriQueriesAfterMailbox,
          RedundantTriQueries;

	EListA_s(, E3dDebugRay, DebugRays);  // When ray debugging is enabled, rays that produced NaN pixels etc.
} E3dRayStats;

typedef struct
{
	E3dBBox    BBox;
	EInt32_3   GridCount;
	E3dVoxel*  Voxels;
	E3dCoord3  VoxelSize, VoxelSizeInv;
} E3dUniformGrid;


// Ray-traversal accelerator with:
// - Uniform-grid
// - KDTree
// - Bounding Volume Hierarchy (BVH)
//
typedef struct
{
	EObjectCore(EObject);

	EList(, E3dRTriangle*, Triangles);  // Used temporarily, to build the KD-Tree, BVH etc., then freed

// This is distinct from Triangles_Count. The latter will be 0, after the Triangles array is freed.
// This one is needed to let the per-thread Accelerator know how many triangle mail-boxes to allocate.
//
	EIndex           TotalNumOfTriangles;

	E3dBBox          BBox;

	E3dUniformGrid   UniformGrid;
	E3dKDTree*       KDTree;
	E3dBVH*          BVH;
	EListA_s(, E3dRPolyGroupPtr, CustomShapes);    // Custom shapes (implicit surfaces, SDFs etc.) are also represented as RPolyGroups with potentially no triangles

	E3dRayStats      RayStats;  // For debugging / profiling
} E3dAccelerator;


// Per-thread (thread-writeable) version of E3dAccelerator
//
typedef struct
{
	E3dUniformGrid        UniformGrid;
	E3dKDTree*            KDTree;
	E3dBVH*               BVH;
	EListA_s(, E3dRPolyGroupPtr, CustomShapes);    // Custom shapes (implicit surfaces, SDFs etc.) are also represented as RPolyGroups with potentially no triangles

// Thread-writeable members
//
	E3dCoordinate         EntryT, ExitT;    // Temporary data: ray T at entering the scene and at exiting
	E3dRTriangleCore*     TestRayLastHit;
	EList(, E3dRTriangleMailbox, TriangleMailboxes);
	EIndex                RayCounter;

	E3dRayStats           RayStats;         // Debugging / profiling
} E3dRWAccelerator;


//
// Data writable by individual rendering threads
//
#define E3dRayTracer_thread_core\
	E3dRenderer_thread_core;\
\
	E3dStackRay*               RayStack;\
	E3dStackRay                Ray;\
	E3dLightTriangleCDFEntry*  LightTriangleCDF;\
\
	E3dRWAccelerator           Accelerator, ShadowAccelerator;\
\
	E3dRayHit                  FirstHit

struct E3dRayTracer_thread_S
{
	E3dRayTracer_thread_core;
};


#ifdef USEOpenGL
#define E3dRayTracerGLFields\
	EpGPUFrameBuffer*  GPUFloatFrameBuffer;      /* Used for HDR tonemapping on the GPU, SSAA etc. */\
	EpGPUFrameBuffer*  GPUFrameBuffer            /* Used for HDR tonemapping on the GPU, SSAA etc. */
#else
#define E3dRayTracerGLFields
#endif


typedef EBool (*E3dRayIntersectSceneFunc)(E3dRWAccelerator* PAccelerator, E3dGridRay* PRay, const void* PExcludeFace,  E3dRayHit* PHit);
typedef EBool (*E3dShadowRayIntersectSceneFunc)(E3dRWAccelerator* PAccelerator, E3dGridRay* PRay, const void* PExcludeFace, const E3dCoordinate PMaxDistance);


enum
{
	E3dRayDebugPRIMARY = 0x00000001,
	E3dRayDebugSHADOW = 0x00000002,
	E3dRayDebugBRDF = 0x00000004,
	E3dRayDebugSSS = 0x00000008,
};

enum
{
	E3dMIS_BALANCE_HEURISTIC = 1
};

//
// Ray-tracer
//
#define E3dRayTracerCore\
	E3dRendererCore;                        /* It's a subclass of E3dRenderer  */\
\
	int                NSubPixels;          /* Subpixel samples for renderer-native anti-aliasing    */\
	uint32_t           RayDebugging;        /* E3dRayDebugPRIMARY etc. Store ray hits with issues (e.g. NaN pixels in DebugRays. */\
\
/* For anti-aliasing, path-tracing etc. */\
\
	E3dCoord2*         SubPixelSamples;EIndex  NumOfSubPixelSamplesPerTile;\
	int                NumOfSubPixelSampleTiles;\
\
	E3dShadePointFunc  ShadePoint;\
	E3dShadePointFunc  ShadePointNoShadows;\
\
	int                MaxRayDepth;\
	float              RayLumaMin;\
	E3dCoordinate      LightmapRayOffset;\
\
	EInt32_3           GridCount;\
	int                VoxelLookAhead;\
	EBool              ShowVoxelGrid, ShowKDTree,  ShowBVHInternal, ShowBVHLeaf;\
\
	EBool              VolumeRendering;\
	int                RayMarchSteps;      /* Starting (minimum) steps (adaptive)                    */\
	int                RayMarchSubdivMax;  /* Max. adaptive subdivision depth for ray-march steps    */\
	float              RayMarchThreshold;  /* Min. difference between intensities of line-integral steps that causes a split  */\
\
	E3dAccelerator*    Accelerator;\
	E3dAccelerator*    ShadowAccelerator;\
\
	E3dGPUAA           OverlayAA;          /* Anti-aliasing for 3D overlays (wireframe, Vertices, selected Polygons etc.) */\
\
	E3dRayIntersectSceneFunc        RayIntersectScene;\
	E3dRayIntersectSceneFunc        RayIntersectSceneFront;\
	E3dShadowRayIntersectSceneFunc  ShadowRayIntersectScene;\
\
	EIndex             MaxBxDFSamplesSqrt;\
	uint32_t           MultipleImportanceSamplingMode;   /* 0: no MIS, or E3dMIS_BALANCE_HEURISTIC etc. */\
\
	E3dRayTracerGLFields

struct E3dRayTracer_S
{
	E3dRayTracerCore;
};


#define E3dAccKDTREE    0x01
#define E3dAccBVH       0x02
#define E3dUNIFORMGRID  0x04

//#define E3dRAYTRACER_ACCELERATOR  E3dAccKDTREE
#define E3dRAYTRACER_ACCELERATOR  E3dAccBVH

typedef struct
{
	E3dObjectComponentCore;

	E3dRPolyGroupPtrListA   RPolyGroups;
	E3dAccelerator*  Accelerator;

	E3dModel*        Model;         // If not NULL, the transforms of this Model are applied (to RPolyGroups), otherwise everything is in Object coordinates
	uint16_t         ContentFlags;  // The flags used when E3dObject_GetRenderShapes() was called (E3dGrtINVERTED etc.)
} E3dAcceleratorObjectComponent;


#define E3dSnapToSurface_ObjectGetRenderTrianglesContentFlags  (E3dGrtDO_INVISIBLE|E3dGrtDO_WIREFRAME|E3dGrtPREP_FOR_RAYTRACING)
extern E3dObjectComponentClass*                                E3dAcceleratorObjectComponent_class;


extern void  E3dAccelerators_Init(void);



//------------------------------------------------
// Uniform grids
//------------------------------------------------

//
// Voxel-walking 3D-DDA state.
//
typedef struct
{
	EInt32_3   GridPosition;
	EInt32_3   GridStep,
	           GridOut;
	E3dCoord3  GridNext,
	           GridDelta;
} E3dRayGridState;



static inline EBool E3dAccelerator_Valid(E3dAccelerator* PAccelerator)
{
	if(PAccelerator == NULL || PAccelerator->UniformGrid.Voxels || PAccelerator->BVH || PAccelerator->KDTree) return(TRUE);
	else return(FALSE);
}

//----------------------------------------
// Rendering/Raytracing.c
//----------------------------------------
extern E3dMaterial*  E3dRenderer_MaterialByName(E3dRenderer* PRenderer, const char* PName);


//----------------------------------------
// Panel/Renderer.c
//----------------------------------------
extern void          E3dRayTracer_InitThreads(E3dWindow* P3DWindow, E3dRayTracer* PRenderer, E3dScene* PScene, const EIndex PNumOfThreads);
extern void          E3dRayTracer_UpdateAASamples(E3dScene* PScene, E3dRayTracer* PRenderer);


//----------------------------------------
// Accelerators/Accelerator.c
//----------------------------------------
extern int   E3dAccelerator_ResetForRendering(const E3dAccelerator* PAccelerator, E3dRWAccelerator* PRWAccelerator);
extern void  E3dAccelerator_CopyToThread(const E3dAccelerator* PSrcAccelerator, E3dRWAccelerator* PDstAccelerator, const EBool PNew, pthread_mutex_t* PRef_mutex);

extern void  E3dRWAccelerator_FreeContents(E3dRWAccelerator* PAccelerator, pthread_mutex_t* PRef_mutex, const EBool PClearStats);
extern void  E3dRayStats_Clear(E3dRayStats* PStats);


extern void  E3dAccelerator_CollectPrimitives(E3dAccelerator* PAccelerator,  const E3dRPolyGroupPtrListA* PRPolyGroups,  const EBool PIncludeNonShadowCasters);
extern void  E3dAccelerator_Build(E3dAccelerator* PAccelerator,  const E3dRPolyGroupPtrListA* PRPolyGroups,  const uint16_t PAcceleratorTypes);

//----------------------------------------
// Accelerators/Voxel.c
//----------------------------------------
extern void  E3d_RayDDAInit(const E3dRWAccelerator* PAccelerator, const E3dRay* PRay, E3dRayGridState* PState);
extern void  E3d_RayDDAInitStartVoxel(const E3dRWAccelerator* PAccelerator, const E3dRay* PRay, E3dRayGridState* PState);
extern void  E3d_RayDDAInitStartSet(const E3dRWAccelerator* PAccelerator, const E3dRay* PRay, E3dRayGridState* PState);

extern void  E3dAccelerator_BuildUniformGrid(E3dRayTracer* PRenderer, E3dAccelerator* PAccelerator);

extern EBool  E3dRay_IntersectVoxels(E3dRWAccelerator* PAccelerator, E3dGridRay* PRay, const void* PExcludeFace,  E3dRayHit* PHit);
extern EBool  E3dRay_IntersectVoxelsBackFaceCull(E3dRWAccelerator* PAccelerator, E3dGridRay* PRay, const void* PExcludeFace,  E3dRayHit* PHit);

extern EBool  E3d_ShadowRayIntersectVoxels(E3dRWAccelerator* PAccelerator, E3dGridRay* PRay, const void* PExcludeFace, const E3dCoordinate PLightDistance);
extern EBool  E3d_ShadowRayIntersectVoxelsStartSet(E3dRWAccelerator* PAccelerator, E3dGridRay* PRay, E3dRayGridState* PState, const void* PExcludeFace, const E3dCoordinate PDistance);

//----------------------------------------
// Accelerators/BVH.c
//----------------------------------------
extern EObjectClass  E3dBVH_class[];
extern void        E3dAccelerator_BuildBVH(E3dAccelerator* PAccelerator);
extern void        E3dBVH_Visualize(E3dScene* PScene, E3dRayTracer* PRenderer);

extern EBool       E3dRay_IntersectBVH(E3dRWAccelerator* PAccelerator, E3dGridRay* PRay, const void* PExcludeFace,  E3dRayHit* PHit);
extern EBool       E3dRay_IntersectBVHAndCustomShapes(E3dRWAccelerator* PAccelerator, E3dGridRay* PRay, const void* PExcludeFace,  E3dRayHit* PHit);
extern EBool       E3dRay_IntersectBVHAndCustomShapesBackFaceCull(E3dRWAccelerator* PAccelerator, E3dGridRay* PRay, const void* PExcludeFace,  E3dRayHit* PHit);

extern EBool       E3dRay_IntersectBVHBackFaceCull(E3dRWAccelerator* PAccelerator, E3dGridRay* PRay, const void* PExcludeFace,  E3dRayHit* PHit);
extern EBool       E3dShadowRay_IntersectBVH(E3dRWAccelerator* PAccelerator, E3dGridRay* PRay, const void* PExcludeFace, const E3dCoordinate PMaxDistance);
extern EBool       E3dShadowRay_IntersectBVHAndCustomShapes(E3dRWAccelerator* PAccelerator, E3dGridRay* PRay, const void* PExcludeFace, const E3dCoordinate PMaxDistance);

//----------------------------------------
// Accelerators/KDTree.c
//----------------------------------------
extern EObjectClass  E3dKDTree_class[];
extern E3dKDTree*  E3dKDTree_Build(const void** PPrimitives, const EIndex PPrimitives_Count, const E3dBBox* PAllBounds, const E3dBBox PTreeBBox);
extern void        E3dAccelerator_BuildKDTree(E3dAccelerator* PAccelerator);

extern void        E3dKDTree_Visualize(E3dScene* PScene, E3dKDTree* PTree);
extern void        E3dKDTree_SaveASCII(E3dKDTree* PKDTree, const char* PFileName);

extern EBool       E3dRay_IntersectKDTree(E3dRWAccelerator* PAccelerator, E3dGridRay* PRay, const void* PExcludeFace,  E3dRayHit* PHit);
extern EBool       E3dRay_IntersectKDTreeAndCustomShapes(E3dRWAccelerator* PAccelerator, E3dGridRay* PRay, const void* PExcludeFace,  E3dRayHit* PHit);
extern EBool       E3dRay_IntersectKDTreeAndCustomShapesBackFaceCull(E3dRWAccelerator* PAccelerator, E3dGridRay* PRay, const void* PExcludeFace,  E3dRayHit* PHit);

extern EBool       E3dRay_IntersectCustomShapes(E3dRWAccelerator* PAccelerator, E3dGridRay* PRay, const void* PExcludeFace,  E3dRayHit* PHit);

extern EBool       E3dRay_IntersectKDTreeBackFaceCull(E3dRWAccelerator* PAccelerator, E3dGridRay* PRay, const void* PExcludeFace,  E3dRayHit* PHit);
extern EBool       E3dShadowRay_IntersectKDTree(E3dRWAccelerator* PAccelerator, E3dGridRay* PRay, const void* PExcludeFace, const E3dCoordinate PDistance);
extern EBool       E3dShadowRay_IntersectKDTreeT(E3dRWAccelerator* PAccelerator, E3dGridRay* PRay, const void* PExcludeFace, const E3dCoordinate PDistance, E3dRayHitBase* PHit);
extern EBool       E3dShadowRay_IntersectKDTreeAndCustomShapes(E3dRWAccelerator* PAccelerator, E3dGridRay* PRay, const void* PExcludeFace, const E3dCoordinate PMaxDistance);


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


#endif  // _E3D_Raytracing_h
