//===========================================================================================================================================
// EQUINOX-3D plugin
//
// This sample code demonstrates how to write your own ray-tracing Renderer for EQUINOX-3D
//
// In a nutshell:
// - You register a Renderer Class
//
// - Equinox3D creates an instance of this class for every 3D window, so the user may use whis renderer in each window
//
// - When this plugin is unloaded, you deactivate the Renderer Class (as opposed to deleting it).
//   This is because you can change, recompile and reload the plugin while Equinox3D is running and the renderer instances will be preserved.
//
// - You should lock your plugin to prevent the user from unloading it during a critical operation, while Equinox3D is running.
//   Upon exit, Equinox3D will force-unlock locked plugins and calls a callback you can register to properly clean up memory.
//
// - To keep the code simple, this is a single-threaded renderer and it doesn't support anti-aliasing.
//   It supports direct light from both point lights and area lights, as well as shaodws, shaders and specular (mirror) reflection.
//   If you are interested in writing a more advanced Equinox3D renderer, feel free contact me for help.
//
// To avoid cluttering the UI, example plugins are not loaded by default, when Equinox3D is started.
// You can manually load them from the Plugin manager, or you can edit your Plugins/Plugins.list file,
// and comment out (e.g. with a trailing "#") or remove the "Load = FALSE;" line for a plugin.
//
// AUTHOR:  Gabor Nagy
// DATE:    2025-Mar-12 22:37:06
//
// This source code may be freely modified and redistributed.
// No warranties of any kind.
//
// EQUINOX-3D(TM) is Copyright (C) 1996-2026 By Gabor Nagy. All rights reserved.
//===========================================================================================================================================
#include <math.h>
#include <stdio.h>

#include <EMalloc.h>
#include <EPlugins.h>

#include <E3D/Matrix.h>
#include <E3D/RenderingAdv.h>
#include <E3D/Raytracing.h>
#include <E3D/Raytracing.ci>

#include <E3D/UI/Panel.h>

#include <Xe/PushButton.h>

static EPlugin*  _Plugin = NULL;
static XeDWidget  _MenuButton = NULL;


// This note will be visible from the plugin-manager under plugin information
//
char*  E_PluginInfo = "Sample code for writing a renderer plugin for EQUINOX-3D";


typedef struct
{
	E3dRayTracerCore;  // It's a subclass of E3dRayTracer

// Members, specific to this renderer come here:
//
	int  SomeVariable;
} E3dMyRenderer;


static EArg  EmptyUI[] = { { NULL, 0 } };


// These resources will be saved and restored for each Renderer instance, when the user saves a Scene in an appropriate file format (e.g. COLLADA)
//
#define ROffset(field) (void*)(EPtrOffset(E3dMyRenderer, field))
static EResource _Resources[]=
{
// Inherited from E3dRayTracer.
//
	{ "Shadows",       EResBOOLEAN,  ROffset(ShadowsOn),     .UI= EmptyUI },
	{ "Reflections",   EResBOOLEAN,  ROffset(ReflectionsOn), .UI= EmptyUI },

// Our own variables
//
	{ "SomeVariable",  EResINT32,    ROffset(SomeVariable),  .UI= EmptyUI },
};
#undef ROffset



static E3dRendererClass*  _RendererClass = NULL;



//========================================
// The Renderer's constructor
//========================================
static void _MyRendererInit(E3dRenderer* PRenderer)
{
	E3dMyRenderer*  LRenderer = (E3dMyRenderer*)PRenderer;

// Indicate that we have these features
//
	LRenderer->Features.ZBuffer = TRUE;
	LRenderer->Features.SolidSurfaces = TRUE;
	LRenderer->Features.Textures = TRUE;
	LRenderer->Features.Lighting = TRUE;
	LRenderer->Features.HDRImage = TRUE;
	LRenderer->Features.ProgressiveRefinement = TRUE;

	LRenderer->TexturesOn = TRUE;
	LRenderer->ReflectionsOn = TRUE;
	LRenderer->ShadowsOn = TRUE;

// This flag is inherited from E3dRayTracer
//
	LRenderer->ShadowsOn = TRUE;

// This is our own
//
	LRenderer->SomeVariable = 42;
}


//========================================
// The Renderer's destructor
//========================================
static int _Free(E3dRenderer* PRenderer)
{
// This is where you would free up tables etc., that you've allocated for this Renderer instance.
//
	return(E_SUCCESS);
}


//========================================================================================
// This function is called when the user changes the Renderer's parameters.
// You get a copy of the old values and a pointer to the actual Renderer, so you
// can determine what changed and update any acceleration structures etc. accordingly.
// Do not call E3dWindow_Redraw, but rather:
// - Set LRenderer->SceneChangedFlags and LRenderer->RendererChangedFlags
// - Return TRUE if you determine that the window needs to be redrawn.
//========================================================================================
static EBool _Update(E3dRenderer* POldRenderer, E3dRenderer* PRenderer)
{
	E3dRayTracer*  LOldRenderer = (E3dRayTracer*)POldRenderer;
	E3dRayTracer*  LRenderer = (E3dRayTracer*)PRenderer;
	EBool          LRedrawWindow = FALSE;

	if(LRenderer->ReflectionsOn != LOldRenderer->ReflectionsOn)
	{
// Just an example.
		LRenderer->RequestedChanges.SceneChangeFlags |= E3dChgLIGHT;
		LRedrawWindow = TRUE;
	}

	return(LRedrawWindow);
}


//========================================
// Non-HDR tone-mapper
//========================================
static void _SetPixel(const E3dRenderer* PRenderer, E3dShaderResult* PColor, EpRGBA8Pixel* PRGBA8Pixel, EpRGBAf32Pixel* PRGBAf32Pixel)
{
	const float  LAF = PColor->Opacity[0] + PColor->Opacity[1] + PColor->Opacity[2] * 0.333333;

// E3dSPECTRALFIXME
//
	int  LR = (int)(PColor->Color[0] * E256N);if(LR < 0) LR = 0;else if(LR > 255) LR = 255;
	int  LG = (int)(PColor->Color[1] * E256N);if(LG < 0) LG = 0;else if(LG > 255) LG = 255;
	int  LB = (int)(PColor->Color[2] * E256N);if(LB < 0) LB = 0;else if(LB > 255) LB = 255;
	int  LA = (int)(LAF * E256N);if(LA < 0) LA = 0;else if(LA > 255) LA = 255;

	PRGBA8Pixel->R = LR;
	PRGBA8Pixel->G = LG;
	PRGBA8Pixel->B = LB;
	PRGBA8Pixel->A = LA;
}


//========================================
// HDR tone-mapper
//========================================
static void _SetPixelHDR(const E3dRenderer* PRenderer, E3dShaderResult* PColor, EpRGBA8Pixel* PRGBA8Pixel, EpRGBAf32Pixel* PRGBAf32Pixel)
{
	const EpExposure*  LExposure = &PRenderer->Exposure;
//	EThreadSet*        LThreadSet = &LRenderer->ThreadSet;


// Exposure may be changed during rendering
//
	float  LRF = PColor->Color[0], LGF = PColor->Color[1], LBF = PColor->Color[2];

// Alpha is the average of the band opacities
//
	float  LAF = 0.0;
	const int	LNumOfSpectralSamples = PRenderer->NumOfSpectralSamples;
	{ int  LSC = 0;for(;LSC < LNumOfSpectralSamples;LSC++) LAF += PColor->Opacity[LSC]; }
	LAF /= (float)LNumOfSpectralSamples;

	PRGBAf32Pixel->R = LRF;
	PRGBAf32Pixel->G = LGF;
	PRGBAf32Pixel->B = LBF;
	PRGBAf32Pixel->A = LAF;

//E3dTHREAD_LOCK
	LRF = LExposure->ToneMapR(0, LRF, LExposure);
	LGF = LExposure->ToneMapG(1, LGF, LExposure);
	LBF = LExposure->ToneMapB(2, LBF, LExposure);
//E3dTHREAD_UNLOCK

	int  LR = (int)(LRF * E256N);if(LR < 0) LR = 0;else if(LR > 255) LR = 255;
	int  LG = (int)(LGF * E256N);if(LG < 0) LG = 0;else if(LG > 255) LG = 255;
	int  LB = (int)(LBF * E256N);if(LB < 0) LB = 0;else if(LB > 255) LB = 255;
	int  LA = (int)(LAF * E256N);if(LA < 0) LA = 0;else if(LA > 255) LA = 255;

	PRGBA8Pixel->R = LR;
	PRGBA8Pixel->G = LG;
	PRGBA8Pixel->B = LB;
	PRGBA8Pixel->A = LA;
}


//========================================================================================
// Trace a ray and get its illumination contribution.
// This is a simple version that only supports direct illumination and reflections.
//========================================================================================
static int _TraceRay(E3dScene* PScene,  E3dRayTracer* PRenderer, E3dRayTracer_thread* PThread,  E3dStackRay* PRay, int PRayIter, E3dShaderResult* PResult)
{
	const E3dRayHit*     LFirstHit = &PThread->FirstHit;
	E3dCoordinate        LT = LFirstHit->T;
	E3dCoord2            LUV = LFirstHit->UV;
	const E3dRTriangle*  LRTriangle = LFirstHit->RTriangle;

	E3dMyRenderer*       LRenderer = (E3dMyRenderer*)PRenderer;
	ERandomState*        LRandomState = PThread->RandomState;
	const int            LRendererNSpectralSamples = LRenderer->NumOfSpectralSamples;
	int                  LHits = 0;

	while((--PRayIter) > 0)
	{
		E3dSurfacePoint       LSurfacePoint;EClr_s(LSurfacePoint);
		const E3dRPolyGroup*  LRPolyGroup = LRTriangle->RPolyGroup;
		E3dMaterial*          LSurfaceMaterial = LRPolyGroup->Material;
		E3dShader*            LSurfaceShader = LSurfaceMaterial->Shader;

		LSurfacePoint.Position = PRay->HitPosition;

		LSurfacePoint.PointToViewer.X = -PRay->Direction.X;
		LSurfacePoint.PointToViewer.Y = -PRay->Direction.Y;
		LSurfacePoint.PointToViewer.Z = -PRay->Direction.Z;
		LSurfacePoint.PointToViewer.W = LT;
		LSurfacePoint.ViewerPosition = PRay->Origin;
		LSurfacePoint.FaceSelected = LRTriangle->FaceSelected;
		memcpy(LSurfacePoint.AmbientLightColor, LRenderer->AmbientLightColor, sizeof(float) * LRendererNSpectralSamples);

// Sample / interpolate vertex attributes (texcoords, normal, tangent etc.) at the hit point
		_E3dRTriangle_Sample(PThread,  LRPolyGroup, LRTriangle, &LUV,  (E3dGridRay*)PRay, &LSurfacePoint,  LSurfaceMaterial, LSurfaceShader->HasMIPMapping, LSurfaceShader->HasAnisotropicFiltering ? PRenderer->NAnisotropicTextureSamples : 0);

		void*  LFace = LRTriangle->Face;
		E3dShaderResult  LShaderResult;E3dShaderResult_Init(&LShaderResult, LSurfaceMaterial, 0, LRenderer->NumOfSpectralSamples,  PRenderer, PThread, LRandomState,  LFace, TRUE, FALSE);

// Modulate the Shader with its Textures, so shader implementers don't have to do it in every ShadeSurface method.
// This simple example doesn't support hyperspectral rendering, so we just pass 0 as the starting spectral index and pass the number of spectral sampes from the Renderer settings.
		uint8_t  LTmpShader[LSurfaceShader->Class->StructSize];const E3dShader*  LTextureModulatedSurfaceShader = E3dShader_ModulateWithTextures(LSurfaceMaterial, LSurfaceShader,  (E3dShader*)&LTmpShader,  LSurfacePoint.TextureColors,  0, LRendererNSpectralSamples);
		LSurfacePoint.SurfaceShader = LTextureModulatedSurfaceShader;

// This function samples all visible lights and calls the appropriate shader. It may also change the normal for LSurfacePoint (e.g. if hit surface is bump-mapped), so specular reflections will account for that. See E3d_Reflect() below.
//
		if(LRPolyGroup->PolyGroup->ReceivesShadows) PRenderer->ShadePoint(PRenderer, PThread, LRTriangle, &LSurfacePoint, PRay,  LSurfaceMaterial, NULL, LSurfaceMaterial->Shader,  &LShaderResult, PResult);
		else PRenderer->ShadePointNoShadows(PRenderer, PThread, LRTriangle, &LSurfacePoint, PRay,  LSurfaceMaterial, NULL, LSurfaceMaterial->Shader,  &LShaderResult, PResult);

		LHits++;

		{ int  LSI = 0;for(;LSI < LRendererNSpectralSamples;LSI++) if(LShaderResult.Reflective[LSI] > 0.0) goto GoOn; }
		break;

GoOn:
		{ int  LSI = 0;for(;LSI < LRendererNSpectralSamples;LSI++) PRay->Intensity[LSI] *= LShaderResult.Reflective[LSI]; }

		E3d_Reflect(&PRay->Direction, LSurfacePoint.Normal);

		PRay->Origin = LSurfacePoint.Position;

		E3dRayHit  LHit;EClr_s(LHit);if(!E3dRay_IntersectKDTreeBackFaceCull(&PThread->Accelerator, (E3dGridRay*)PRay, LFace,  &LHit)) break;
		LT = LHit.T;
		LUV = LHit.UV;
		LRTriangle = LHit.RTriangle;
		PRay->HasSubRays = FALSE;  // In this simple example, we don't reflect sub-rays (used for MIP-mapping)
	}

	return(LHits);
}


// Don't use a do/while(0) here! Otherwise the "continue" statement will just exit the do/while(0)
#define _M_NoFirstHit()\
{\
/* A = 0, no hit  */\
	*LRGBA8Pixel = LBackgroundRGBA8Pixel;\
	if(LHaveHDR) *LRGBAf32Pixel = LBackgroundRGBAf32Pixel;\
	LPixelsDoneSinceLastSync++;\
	continue;\
}


//========================================================
// Simple ray-tracer main loop.
// - Supports progressive refinement
// - No anti-aliasing
// - No multi-threading
// - No non-blocking GUI updates
//========================================================
static int _Raytrace(E3dWindow* P3DWindow, E3dMyRenderer* PRenderer, const E3dOpticsClass* POpticsClass, E3dScene* PScene)
{
	E3dCamera*       LCamera = PRenderer->Camera;E3dModel*  LCameraModel = PRenderer->CameraModel;
	EpImage*         LImage = PRenderer->Image0;
	const int        LNumOfSpectralSamples = PRenderer->NumOfSpectralSamples;

	E3dRayTracer_InitThreads(P3DWindow, (E3dRayTracer*)PRenderer, PScene, 1);

	EThreadSet*           LThreadSet = &PRenderer->ThreadSet;
	E3dRayTracer_thread*  LThread = (E3dRayTracer_thread*)EStruct_ByIndex(LThreadSet->Threads.Items, LThreadSet->ThreadStructSize, 0);  // This example only uses a single rendering thread, so we just use thread 0

	E3dAccelerator_CopyToThread(PRenderer->Accelerator, &LThread->Accelerator, TRUE, NULL);
	E3dAccelerator_CopyToThread(PRenderer->ShadowAccelerator, &LThread->ShadowAccelerator, TRUE, NULL);

	E3dAccelerator_ResetForRendering(PRenderer->Accelerator, &LThread->Accelerator);
	E3dAccelerator_ResetForRendering(PRenderer->ShadowAccelerator, &LThread->ShadowAccelerator);

	E3dRWAccelerator*    LRWAccelerator = &LThread->Accelerator;
#if EIndex_BITS == 64
	E3dRWAccelerator*    LShadowRWAccelerator = &LThread->ShadowAccelerator;
#endif

	const E3dBBox        LSceneBBox = PRenderer->Accelerator->BBox;
	int                  LRayIter,  LPixelsDoneSinceLastSync = 0;

	void                 (*LSetPixelFunc)(const E3dRenderer* PRenderer, E3dShaderResult* PColor, EpRGBA8Pixel* PRGBA8Pixel, EpRGBAf32Pixel* PRGBAf32Pixel);
	EBool                (*LRayIntersectScene)(E3dRWAccelerator* PRWAccelerator, E3dGridRay* PRay, const void* PExcludeFace,  E3dRayHit* PHit);

	const EIndex         LImageSizeX = PRenderer->ImageSpecs.Size.X, LImageSizeY = PRenderer->ImageSpecs.Size.Y;

	const E3dOpticsFunc  LOpticsFunc = POpticsClass->Compute;
	int                  LPixelsPerSync = 16;
	EBool                LHaveHDR;
	EpRGBAf32Pixel       LBackgroundRGBAf32Pixel = PRenderer->BackgroundRGBAfColor;


	int  LXStart = 0, LXEnd = LImageSizeX, LYEnd = LImageSizeY;

	PRenderer->CurrentPixelSizeR = PRenderer->User.StartPixelSize;
	uint32_t            LCurrentPixelSizeMask = (PRenderer->CurrentPixelSizeR << 1) - 1;
	E3dMatrix           LViewerToWorldMatrix;E3dMatrix_Copy(LCameraModel->LocalToWorldMatrix, LViewerToWorldMatrix);
	const EpRGBA8Pixel  LBackgroundRGBA8Pixel = Ec_RGBAf32ToRGBA8(&PRenderer->BackgroundRGBAfColor);

	{
		const int  LResult = POpticsClass->Init(PScene, (const E3dRenderer*)PRenderer, (E3dRenderer_thread*)LThread, LCamera, LCameraModel);
		if(LResult != E_SUCCESS) return(LResult);
	}

	E3dStackRay*  LRay;ENew(LRay);

	if(PRenderer->TransparencyOn) LRayIntersectScene = E3dRay_IntersectKDTree;
	else LRayIntersectScene = E3dRay_IntersectKDTreeBackFaceCull;
	PRenderer->ShadowRayIntersectScene = E3dShadowRay_IntersectKDTree;  // We need to set the shadow-ray intersector function in the Renderre, so E3d_ShadePointShadows() can use it

	if(PRenderer->ReflectionsOn || PRenderer->TransparencyOn) LRayIter = PRenderer->MaxRayDepth;
	else LRayIter = 2;
//	ENewN(LRayIter + 1, LRay.MaterialStack);

	if(EpImage_IsHDR(LImage))
	{
		LHaveHDR = TRUE;
		LSetPixelFunc = _SetPixelHDR;

		EpExposure_Update(&PRenderer->Exposure, NULL, NULL);
	}
	else
	{
		LHaveHDR = FALSE;LSetPixelFunc = _SetPixel;
	}

	int  LCurrentPixelSize = PRenderer->CurrentPixelSizeR;

	do  // Progressive refinement loop
	{
		const EpPixelBuffer*  LRGBAf32PixelBuffer = EpImage_PixelBufferByFormat(LImage,  EpRGBA, 0, EResFLOAT32,  FALSE,  NULL);
		const EpPixelBuffer*  LRGBA8PixelBuffer = EpImage_PixelBufferByFormat(LImage,  EpRGBA, 0, EResUINT8,  FALSE,  NULL);

		EpRGBA8Pixel*    LRGBA8Line   = (EpRGBA8Pixel*)LRGBA8PixelBuffer->u8;
		EpRGBAf32Pixel*  LRGBAf32Line = (EpRGBAf32Pixel*)LRGBAf32PixelBuffer->u8;

		EIndex  LY = 0;for(;LY < LYEnd;LY += LCurrentPixelSize, LRGBA8Line += (LImageSizeX * LCurrentPixelSize), LRGBAf32Line += (LImageSizeX * LCurrentPixelSize))
		{
			EpRGBA8Pixel*    LRGBA8Pixel   = LRGBA8Line;
			EpRGBAf32Pixel*  LRGBAf32Pixel = LRGBAf32Line;

			EIndex  LX = LXStart;for(;LX < LXEnd;LX += LCurrentPixelSize, LRGBA8Pixel += LCurrentPixelSize, LRGBAf32Pixel += LCurrentPixelSize)
			{
// The top-left pixel of the previous macro-pixel is already computed in the previous iteration...
//
				if(!PRenderer->FirstIteration && ((LX & LCurrentPixelSizeMask) == 0) && ((LY & LCurrentPixelSizeMask) == 0)) continue;

				if(!LOpticsFunc((const E3dRenderer*)PRenderer, (E3dRenderer_thread*)LThread, LCamera, LViewerToWorldMatrix,  LX, LY,  LImageSizeX, LImageSizeY,  0.0, 0.0,  (E3dDRay*)LRay))
				{
					_M_NoFirstHit();
				}

				{
// If the Camera is outside the grid, compute the intersection of the ray with the Scene BBox and re-originate the ray at the intersection
//
					const E3dCoordinate  LRayDirectionInv[3] = { 1.0 / LRay->Direction.X, 1.0 / LRay->Direction.Y, 1.0 / LRay->Direction.Z };
					E3dCoordinate        LEntryT, LExitT;

					if(!E3dRay_IntersectBBoxMaxDist((E3dGridRay*)LRay, &LSceneBBox, LRayDirectionInv, &LEntryT, &LExitT, E3dHUGE))
					{
						_M_NoFirstHit();
					}
					LRWAccelerator->EntryT = LEntryT;
					LRWAccelerator->ExitT = LExitT;

// If the ray started outside the grid and was re-originated, the correct T at a hit triangle will be the sum of the T to the scene bounding box and the distance from there to the hit
// If the intersector function (LRayIntersectScene) returns an RTriangle or an RPolyGroup, it means that either a triangle or a custom shape was hit.
//
					EClr_s(LThread->FirstHit);if(LRayIntersectScene(LRWAccelerator, (E3dGridRay*)LRay, NULL,  &LThread->FirstHit))
					{
					}
					else
					{
						_M_NoFirstHit();
					}
				}

// For now, start from vacuum
//
				LRay->MaterialStack[0] = NULL;
				LRay->MaterialStackPointer = 0;

				{ int  LSC = 0;for(LSC = 0;LSC < LNumOfSpectralSamples;LSC++) LRay->Intensity[LSC] = 1.0; }

				E3dShaderResult  LPixel;
				memset(LPixel.Color,   0, sizeof(float) * LNumOfSpectralSamples);
				memset(LPixel.Opacity, 0, sizeof(float) * LNumOfSpectralSamples);

				const EBool  LHit = _TraceRay(PScene, (E3dRayTracer*)PRenderer, LThread, LRay, LRayIter, &LPixel);
//	    const int    LHit = E3d_TraceRayOpaque(PRenderer, &LRay, LRayIter, &LPixel);

				if(LHit)
				{
//printf("%s() %s:%d  %lld %lld  %p %p\n", __FUNCTION__, __FILE__, __LINE__, (long long)LX, (long long)LY,  LThread, LThread->FirstHitRTriangle);fflush(stdout);
					LSetPixelFunc((const E3dRenderer*)PRenderer, &LPixel, LRGBA8Pixel, LRGBAf32Pixel);
				}
				else
				{
// No first hit: use the background color
//
					if(LHaveHDR) *LRGBAf32Pixel = LBackgroundRGBAf32Pixel;
				}

				LPixelsDoneSinceLastSync++;

// This flag is set to FALSE, if the user presses the "Abort" button
//
				if(!PRenderer->GoRender) goto RenderExit;

// Improve GUI responsiveness during long renders
//
				if(LPixelsDoneSinceLastSync >= LPixelsPerSync)
				{
// 32-bit ray-counters can now roll over on everyday scenes...
//
#if EIndex_BITS == 64
					if(LRWAccelerator->RayCounter > 0x3FFFFFFF) E3dAccelerator_ResetForRendering(PRenderer->Accelerator, &LThread->Accelerator);
					if(LShadowRWAccelerator != LRWAccelerator && LShadowRWAccelerator->RayCounter > 0x3FFFFFFF) E3dAccelerator_ResetForRendering(PRenderer->ShadowAccelerator, &LThread->ShadowAccelerator);
#endif

					PRenderer->NumOfPixelsComputed += LPixelsDoneSinceLastSync;

					LPixelsDoneSinceLastSync = 0;
				}
			}
		}

		PRenderer->NumOfPixelsComputed += LPixelsDoneSinceLastSync;LPixelsDoneSinceLastSync = 0;

		if(PRenderer->FirstIteration) PRenderer->FirstIteration = FALSE;

// Advance to the next refinement step
//
		PRenderer->CurrentPixelSizeR >>= 1;
		LCurrentPixelSize >>= 1;
		LCurrentPixelSizeMask >>= 1;

		E3dGL_RenderWindowRedraw(P3DWindow, (E3dRenderer*)PRenderer, PScene,  TRUE);
	} while(LCurrentPixelSize);

//	EpImage_SaveToFile(LImage, NULL,  "zz.exr", NULL, MAXPATHLEN, EpFileFormatFROM_EXTENSION);

RenderExit:
//EFreeCrl(LRay->MaterialStack);

// Indicate that the renderer has finished
//
	PRenderer->State = E3dIDLE;

	if(POpticsClass->Clear) POpticsClass->Clear(LCamera);

	EFree(LRay);

	return(E_SUCCESS);
}


//================================================================
// Update Renderer's internal data before starting a render
//================================================================
static int _RayTracerStart(E3dWindow* P3DWindow,  E3dScene* PScene, const EChangeFlags PSceneEffectiveChanges,  E3dRenderer* PRenderer, const EChangeFlags PRendererEffectiveChanges)
{
//const int    LSizeX = PRenderer->ImageSpecs.Size.X, LSizeY = PRenderer->ImageSpecs.Size.Y;
//const EBool  LSizeChanged = PRenderer->Image == NULL ? TRUE : (PRenderer->Image->Size.X != LSizeX || PRenderer->Image->Size.Y != LSizeY);

// Update the image buffer
//
	E3dRenderer_CheckImageBuffers(PRenderer);

	EpImage*  LImage = PRenderer->Image0;if(LImage == NULL) return(E_NO_TARGET_IMAGE);

// PRenderer->SceneChangeFlags contains flags for the current Render request. PRenderer->InactiveQueue contains flags for any accumulated requests while this Renderer was busy.
// Merging those flags allow us to get all changes we need to respond to.
//
  const EChangeFlags  LSceneChangeFlags = PRenderer->CurrentChanges.SceneChangeFlags, LRendererChangeFlags = PRenderer->CurrentChanges.RendererChangeFlags;

	if((LSceneChangeFlags & PSceneEffectiveChanges) == 0 && (LRendererChangeFlags & PRendererEffectiveChanges) == 0)
	{
		E3dGL_RenderWindowRedraw(P3DWindow, PRenderer, PScene,  TRUE);
		return(E_SUCCESS);
	}

// Only an HDR exposure change? Don't run the Renderer. Only do a tonemap pass.
//
	if(LSceneChangeFlags == 0 && LRendererChangeFlags == E3dRendererChgEXPOSURE && LImage)
	{
		if(EpImage_IsHDR(LImage))
		{
			EpExposure*  LExposure = &PRenderer->Exposure;

			EpExposure_Update(LExposure, &PRenderer->HDRToneMapper, PRenderer->Image0);

			EpImage_RGBAf32ToRGBA8(LImage, LImage,  &PRenderer->HDRToneMapper, TRUE);

			E3d_MultiThreadedRendererRedraw(P3DWindow, (E3dRayTracer*)PRenderer, PScene);
			PRenderer->CurrentChanges.RendererChangeFlags &= ~E3dRendererChgEXPOSURE;
			return(E_DONE);
		}
		else EpImage_PixelBufferAdd(LImage,  EpRGBA, 0, EResFLOAT32,  EpCLEAR,  NULL);
	}

	E3dRayTracer*  LRayTracer = (E3dRayTracer*)PRenderer;

	{
		EBool  LRebuildAccelerator = FALSE;

		if((LSceneChangeFlags & (E3dChgHIERARCHY | E3dChgMeshSHAPE | E3dChgMeshTRANSFORM | E3dChgFACE)) || LRayTracer->RPolyGroups.Count == 0)
		{
			E3dScene_GetRenderShapes(PScene, (E3dRenderer*)LRayTracer, E3dGrtPREP_FOR_RAYTRACING, TRUE, FALSE);LRebuildAccelerator = TRUE;
		}
		else if(LSceneChangeFlags & (E3dChgTextureMAPPING | E3dChgVERTEX_NORMAL | E3dChgVERTEX_ATTRIBUTE))
		{
// If only texture-mapping, normal, tangents etc. changed, just update RTriangles in place
//
			E3dScene_GetRenderShapes(PScene, (E3dRenderer*)LRayTracer, 0, FALSE, FALSE);
		}

// Update Material references
//
		if(LSceneChangeFlags & (E3dChgMaterialTYPE|E3dChgMaterialPARAM))
		{
			EIndex  LPGI = 0;const EIndex  LPGN = LRayTracer->RPolyGroups.Count;for(;LPGI < LPGN;LPGI++)
			{
				E3dRPolyGroup*  LRPolyGroup = LRayTracer->RPolyGroups.Items[LPGI];

				if(LRPolyGroup->PolyGroup)
				{
					E3dPolyGroup*  LPolyGroup = LRPolyGroup->PolyGroup;
					E3dMaterial*   LMaterial = LPolyGroup->Material ? LPolyGroup->Material : E3dObject_GetDrawingMaterial(NULL, (E3dObject*)LPolyGroup->VertexGeometry, TRUE);

					E_AssignToMember(LMaterial, LRPolyGroup,Material);
				}
			}
		}

// We'll work in world space, so we can move the Camera without having to rebuild the Accelerator
//
		if(LRebuildAccelerator || (!E3dAccelerator_Valid(LRayTracer->Accelerator))) E3dRenderer_BuildAccelerator(LRayTracer);
	}

// No intelligent update checks, to keep this example simple.
// Just pass "force=TRUE".
//
	if(LSceneChangeFlags & E3dChgLIGHT)
	{
		E3dScene_RefreshLights(PScene, PRenderer);
		E3dScene_SampleLights(PScene, (E3dRenderer*)LRayTracer, E3dAreaLightsSAMPLE, 0, TRUE);
	}

	return(E_SUCCESS);
}


//========================================
// Render image
//========================================
static int _Render(E3dWindow* P3DWindow, E3dRenderer* PRenderer,  E3dScene* PScene,  E3dCamera* PCamera, E3dModel* PCameraModel,  E3dRendererStatsUpdateFunc PStatsUpdateFunc)
{
	E3dMyRenderer*  LRenderer = (E3dMyRenderer*)PRenderer;

	E_AssignToMember(PCamera, LRenderer,Camera);
	E_AssignToMember(PCameraModel, LRenderer,CameraModel);

	E3dRenderer_DrawingStagesReset(PRenderer);

	_RayTracerStart(P3DWindow, PScene,
									E3dChgCAMERA | E3dChgLIGHT | E3dChgMaterialTYPE|E3dChgMaterialPARAM | E3dChgTextureMAPPING | E3dChgTextureFILTERING |  // Scene changes we care about
									E3dChgHIERARCHY | E3dChgMeshSHAPE | E3dChgMeshTRANSFORM | E3dChgFACE,
									PRenderer, E3dRendererChgACCELERATOR_SETTINGS | E3dRendererChgRAY_SETTINGS);

	if(LRenderer->RPolyGroups.Count == 0)  // Nothing to render
	{
		EpImage*  LImage = LRenderer->Image0;  // No double-buffering (no multi-threading in this example), so just always use Image0

		EcRGBAiColor  LRGBAiColor;

		Ec_RGBAf32ToRGBAi(&LRenderer->BackgroundRGBAfColor, &LRGBAiColor);

		EpImage_Clear(LImage, LImage, &LRGBAiColor, EpChRGBA);
		LRenderer->NumOfPixelsComputed = LRenderer->ImageSpecs.Size.X * LRenderer->ImageSpecs.Size.Y;

		E3dGL_RenderWindowRedraw(P3DWindow, (E3dRenderer*)LRenderer, PScene,  TRUE);

		return(E_NOTHING_TO_DO);
	}

// If we get here, there is valid data to render
//
	{
		LRenderer->FirstIteration = TRUE;

		LRenderer->NumOfPixelsComputed = LRenderer->NumOfPixelsDrawn = 0;
		LRenderer->State = E3dRENDERING;

		LRenderer->GoRender = TRUE;

		LRenderer->ShadePoint = E3d_ShadePointShadows;
		LRenderer->ShadePointNoShadows = E3d_ShadePoint;

// We only have one renderer thread, but we need to create a per-tread accelerator.
// This code shows you how to create and start multiple rendering threads.
//
		EThreadSet*  LThreadSet = &PRenderer->ThreadSet;
		EThreads_WaitForFinish(LThreadSet);
		EIndex     LNumOfThreads = 1;  // We leave this 1, to keep this example simple
//		double  LCPUClock;E_GetCPUInfo(NULL, &LCPUClock, &LNumOfThreads);  // This is how you would query the number of available hardware threads

// Fill out Thread info
//
		E3dRayTracer_InitThreads(P3DWindow, (E3dRayTracer*)PRenderer, PScene, LNumOfThreads);

/*
		int  LThreadC = 0;for(;LThreadC < LNumOfThreads;LThreadC++)
		{
			E3dRayTracer_thread*  LRayTracer_thread = ((E3dRayTracer_thread*)(LThreadSet->Threads)) + LThreadC;

//printf("%s() %s:%d  LRayTracer %p KDTree %p  Thread %p KDTree %p\n", __FUNCTION__, __FILE__, __LINE__, LRenderer, LRenderer->Accelerator->KDTree,  LRayTracer_thread, LRayTracer_thread->Accelerator.KDTree);fflush(stdout);
		}
*/

		return(_Raytrace(P3DWindow, LRenderer, LRenderer->Camera->OpticsClass, PScene));
	}
}


//========================================
// Debug callback
//========================================
static void _MCB(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
printf("%s() %s:%d  MyRenderer dump\n", __FUNCTION__, __FILE__, __LINE__);fflush(stdout);
}


//========================================
// Entry point of the plugin
//========================================
int E_PluginInit(EPlugin* PPlugin)
{
	_Plugin = PPlugin;

// Register our renderer class
//
	{
		E3dRendererClass  LClass =
		{
			.SuperClass=  E3dRayTracer_class,
			.Name=        "My-renderer",
			.StructSize=  sizeof(E3dMyRenderer),
			.Resources=   EListS_s(_Resources),

			.Init=        _MyRendererInit,         // Constructor
			.Free=        _Free,         // Destructor
			.Update=      _Update,       // Called when the scene changes, so we can do some pre-computations
			.Render=      _Render,       // Render method
			.GUI=         E3dRenderer_GUIFromResources,    // Default GUI: generate automatically from resources (we could implement a pretty one ourselves as well)
		};

		_RendererClass = E3dRenderer_ClassRegister(&LClass);
	}

// Add button to debug menu, so we can print some stats for debugging, while EQX is running.
// This, of course, is optional.
//
	_MenuButton = XePushButton_Add(Xe_App, "Menu->Debug", "My-renderer state", '\0', NULL, NULL, FALSE, NULL, _MCB, (EPointer)0);


	return(E_SUCCESS);
}


//========================================
// Exit method of the plugin
//========================================
int E_PluginExit(const EBool PExiting)
{
	if(!PExiting || Xe_GUIFreeOnExit) XeItemFree(_MenuButton);

	if(_RendererClass) E3dRenderer_ClassDeactivate(_RendererClass);

	return(E_SUCCESS);
}
