//================================================================
// EQUINOX-3D plugin
//
// Camera functions
//
// AUTHOR:  Gabor Nagy
// DATE:    2026-Jul-12 23:14:04
//
// Copyright (C) 1996-2026 By Gabor Nagy. All rights reserved.
//================================================================
#include <stdio.h>
#include <math.h>

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


#include <E3D/Matrix.h>
#include <E3D/Ray.h>

#include <E3D/GL/Draw.h>

#include <E3D/UI/Panel.h>
#include <E3D/UI/Panels.h>
#include <E3D/UI/StatusPanel.h>  // For E3dp_PrintMessage

#include <Xe/Label.h>
#include <Xe/Matrix.h>
#include <Xe/PushButton.h>
#include <Xe/Scale.h>
#include <Xe/Separator.h>
#include <Xe/TextField.h>
#include <Xe/ToggleButton.h>

#include <Xe/Shell.h>
#include <Xe/FSBox.h>

static EPlugin*      _Plugin = NULL;

static XeDWidget      _MenuButtons[16];static int  _MenuButtons_Count = 0;


// Operations
//
enum
{
	CR_RESET_CAMERA_ON_ALL_WINDOWS = 1,
	CR_RESET_WINDOW_CAMERA,
	CR_ATTACH_CAMERA,
	CR_DETACH_CAMERA,
	CR_FRAME_ALL_ON_CURRENT_WINDOW,
	CR_FRAME_ALL_ON_ALL_WINDOWS,
	CR_FRAME_SELECTION_ON_CURRENT_WINDOW,
	CR_FRAME_SELECTION_ON_ALL_WINDOWS,
	CR_FRAME_SELECTED_POINTS_ON_CURRENT_WINDOW,
	CR_FRAME_SELECTED_POINTS_ON_ALL_WINDOWS
};


typedef struct
{
	XeWidget  NearPlaneW, FarPlaneW;
	XeWidget  OrthoBW, PerspectiveBW, FisheyeBW, RearFisheyeBW, PanoramaBW, AngleMapBW, SphericalBW, CubeMapBW;
	XeWidget  FieldOfViewW, ZoomW;
	XeWidget  DOFBW, DOFMatrixW, FocalLengthW, ApertureW, SensorSizeYW, FocalDistanceW, FocusAtAimPointBW;
	XeWidget  XZoomW, YZoomW;
} E3dResourcesGUI;





typedef struct
{
	XeFileSelectionPanel  FSPanel;
	XeImageSwatch*        ImageSwatch;
	XeWidget              ShowW;
} E3dBackgroundImageResourcesGUI;


#define ROffset(field)  (void*)(EPtrOffset(E3dBackgroundImageInfo, field))
static EResource  _BackgroundImageResources[]=
{
	{ "File name",  EResSTRINGPTR,  ROffset(FileName) },
	{ "Show",       EResBOOLEAN,    ROffset(Show) },
};
#undef ROffset

//========================================================
// InitProc for the "BackgroundImage" NodeComponent
//========================================================
static int _BackgroundImageInit(E3dNode* PNode, E3dNodeComponent* PInfo)
{
	E3dBackgroundImageInfo*  LInfo = (E3dBackgroundImageInfo*)PInfo;

	LInfo->FileName = NULL;
	LInfo->Image    = NULL;
	LInfo->Show     = TRUE;

	return(E_SUCCESS);
}


//========================================================
// FreeProc for the "BackgroundImage" NodeComponent
//========================================================
static int _BackgroundImageFree(E3dNode* LNode, E3dNodeComponent* PInfo)
{
	E3dBackgroundImageInfo*  LInfo = (E3dBackgroundImageInfo*)PInfo;


	EFreeChkClr(LInfo->FileName);
	EFreeChkClr(LInfo->Image);

	return(E_SUCCESS);
}


const int  _ImageSwatchMaxSizeX = 256, _ImageSwatchMaxSizeY = 256;


//================================================
// Update the Camera background image
//================================================
static void _BackgroundImageUpdate(E3dBackgroundImageInfo* PInfo, E3dBackgroundImageResourcesGUI* PResourcesGUI)
{
	if(PInfo->Image == NULL)
	{
		if(PInfo->FileName)
		{
			E_New(EpImage, PInfo->Image);
			if(EpImage_LoadFromFile(PInfo->FileName, EpFileFormatAUTOMATIC, NULL,  PInfo->Image, NULL) == E_SUCCESS);
			else E_DeleteChk(PInfo->Image);
		}
	}

	EIndex  LSizeX = 0, LSizeY = 0;

	EpImage*  LImage = PInfo->Image;if(LImage)
	{
		const E3dCoordinate  LAspect = (E3dCoordinate)(LImage->Size.X) / (E3dCoordinate)(LImage->Size.Y);

		if(LImage->Size.X > LImage->Size.Y)
		{
			LSizeX = _ImageSwatchMaxSizeX;
			LSizeY = (int)((E3dCoordinate)_ImageSwatchMaxSizeX / LAspect);
		}
		else
		{
			LSizeX = (int)((E3dCoordinate)_ImageSwatchMaxSizeY * LAspect);
			LSizeY = _ImageSwatchMaxSizeY;
		}
	}

	if(LImage) XeImageSwatch_UpdateVa(PResourcesGUI->ImageSwatch,  LSizeX, LSizeY,  XeNImage, LImage,  NULL);
}


static void _ClearScaledBGImages(void)
{
	EIndex  LPI = 0;for(;LPI < E3dp_Panels.Count;LPI++)
	{
		E3dPanel*  L3DPanel = E3dp_Panels.Items[LPI];EIndex  LWI = 0;for(;LWI < L3DPanel->Windows.Count;LWI++)
		{
			E3dWindow*  L3DWindow = L3DPanel->Windows.Items[LWI];E3dpWindow*  L3DpWindow = E_DynamicCast(L3DWindow, E3dpWindow);if(L3DpWindow)
			{
				E_DeleteChk(L3DpWindow->RendererSet->ScaledBackgroundImage);
			}
		}
	}
}



//========================================
// FileSelectionPanel callback
//========================================
static void _CB_BackgroundImageFileSelectionPanel(XeFileSelectionPanel* PFSPanel, EPointer PClientData, XeFileSelectionBoxCallbackStruct* PCallData)
{
	char*                            LText = XeTextField_GetStringUTF8(PFSPanel->TextW);
	E3dNodePanel*                    LPanel = (E3dNodePanel*)PClientData;
	E3dNode*                         LNode = LPanel->Node;if(LNode == NULL) return;
	E3dBackgroundImageInfo*          LInfo = (E3dBackgroundImageInfo*)E3dNodeComponent_ByClass(LNode, E3dBackgroundImageNodeComponent_class, FALSE);if(LInfo == NULL) return;
	XeComponentClassDrawer*          LDrawer = XeInfoPanel_GetFolderByClass((XeInfoPanel*)LPanel, E3dBackgroundImageNodeComponent_class);if(LDrawer == NULL) return;
	E3dBackgroundImageResourcesGUI*  LResourcesGUI = (E3dBackgroundImageResourcesGUI*)LDrawer->ResourcesGUI;if(LResourcesGUI == NULL) return;

	switch(PCallData->Reason)
	{
// OK was clicked in file selection dialog etc.
//
		case XeCR_OK:
			if(LText)
			{
// Empty string here means "do nothing"
//
				if(LText[0])
				{
					EFreeChk(LInfo->FileName);
					LInfo->FileName = LText;
					E_DeleteChk(LInfo->Image);
					_BackgroundImageUpdate(LInfo, LResourcesGUI);
					_ClearScaledBGImages();
				}
				else EFree(LText);
			}
		break;

// Enter was hit on the TextField
//
		case XeCR_ACTIVATE:
			if(LText)
			{
// Empty string here means "do nothing"
//
				if(LText[0])
				{
					EFreeChk(LInfo->FileName);LInfo->FileName = LText;
					E_DeleteChk(LInfo->Image);
					_BackgroundImageUpdate(LInfo, LResourcesGUI);
					_ClearScaledBGImages();
				}
				else EFree(LText);
			}
		break;
	}
}


static void _DCB_BackgroundImageShow(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dNodePanel*  LPanel = (E3dNodePanel*)PClientData;
	E3dNode*       LNode = LPanel->Node;E3dModel*  LModel = E_DynamicCast(LNode, E3dModel);if(LModel)
	{
		E3dCamera*  LCamera = (E3dCamera*)E3dModel_GetObjectByClass(LModel, E3dCamera_class);if(LCamera)
		{
			E3dBackgroundImageInfo*  LInfo = (E3dBackgroundImageInfo*)E3dNodeComponent_ByClass(LNode, E3dBackgroundImageNodeComponent_class, FALSE);if(LInfo)
			{
				LInfo->Show = ((XeToggleButtonCallbackStruct*)PCallData)->Set;
				E3dWindows_RedrawByCamera(E3d_Scene,  LCamera, LModel,  0);
			}
		}
	}
}


static void _BackgroundImageGUI(E3dNode* PNode, E3dNodeComponent* PInfo, XeInfoPanel* PPanel, void* PDrawer, const int PAction)
{
	XeComponentClassDrawer*          LDrawer = (XeComponentClassDrawer*)PDrawer;
	E3dBackgroundImageInfo*          LInfo = (E3dBackgroundImageInfo*)PInfo;
	E3dBackgroundImageResourcesGUI*  LResourcesGUI;

	switch(PAction)
	{
		case E_UPDATE:
			if(LInfo)
			{
				if(LDrawer->ResourcesGUI)
				{
					LResourcesGUI = (E3dBackgroundImageResourcesGUI*)(LDrawer->ResourcesGUI);

					XeFileSelectionPanel_Update(&LResourcesGUI->FSPanel, LInfo->FileName);

					XeToggleButton_SetState(LResourcesGUI->ShowW, LInfo->Show, FALSE);

					_BackgroundImageUpdate(LInfo, LResourcesGUI);
				}
				else
				{
					XeWidget  LTopMatrixW = LDrawer->Drawer.DrawerW;

					ENew(LResourcesGUI);LDrawer->ResourcesGUI = LResourcesGUI;

					XeFileSelectionPanel_New(&LResourcesGUI->FSPanel, "File", LTopMatrixW, E3dp_TopShellW, XeFSPanelCREATE_HMATRIX|XeFSPanelCREATE_TEXTFIELD|XeFSPanelAUTOFILL_TEXTFIELD,  0, 5, 3,  _CB_BackgroundImageFileSelectionPanel, (EPointer)PPanel);
					XeFileSelectionPanel_Update(&LResourcesGUI->FSPanel, LInfo->FileName);

					if(LInfo->Image == NULL)
					{
						if(LInfo->FileName)
						{
							E_New(EpImage, LInfo->Image);
							if(EpImage_LoadFromFile(LInfo->FileName, EpFileFormatAUTOMATIC, NULL,  LInfo->Image, NULL) == E_SUCCESS);
							else E_DeleteChk(LInfo->Image);
						}
					}

					EIndex    LSizeX = 0, LSizeY = 0;
					EpImage*  LImage = LInfo->Image;if(LImage)
					{
						const E3dCoordinate  LAspect = (E3dCoordinate)LImage->Size.X / (E3dCoordinate)LImage->Size.Y;
						if(LImage->Size.X > LImage->Size.Y)
						{
							LSizeX = _ImageSwatchMaxSizeX;
							LSizeY = (int)((float)_ImageSwatchMaxSizeX / LAspect);
						}
						else
						{
							LSizeX = (int)((float)_ImageSwatchMaxSizeY * LAspect);
							LSizeY = _ImageSwatchMaxSizeY;
						}
					}

					EArg  Args[256];int  ArgCnt;

					LResourcesGUI->ImageSwatch = XeImageSwatch_New("CameraBGImageSwatchW", LTopMatrixW,  LSizeX, LSizeY,  XeNImage, LImage,  NULL);

					EXeStart;
					EXeSetArg(XeNResizableY, FALSE);
					Xe_SetValues(Xe_Parent(LResourcesGUI->ImageSwatch->DrawW),  Args, ArgCnt);

					XeWidget  LW;

					EXeStart;
					EXeSetArg(XeNShadowThickness, 0);
					EXeSetArg(XeNHighlightThickness, 0);EXeSetArg(XeNMarginSizeY, 0);
					EXeSetArg(XeNMarginTop, 2);EXeSetArg(XeNMarginBottom, 0);
					EXeSetArg(XeNSet, LInfo->Show);
					EXeSetArg(XeNResizableX, FALSE);EXeSetArg(XeNAlignmentX, XeAlignBEGINNING);
					XeToolTip8("Turning this off will not show the image in the corresponding 3D window.\nThis allows you to temporarity disable the image without removing this attribute.");
					LResourcesGUI->ShowW = LW = XeWidget_ManagedNew("Show", XeToggleButton_class, LTopMatrixW,  Args, ArgCnt);
					XeAddCallback(LW, XeNValueChangedCallback, _DCB_BackgroundImageShow, (EPointer)PPanel);
				}
			}
		break;

		case E_FREE:
			if(LDrawer && LDrawer->ResourcesGUI)
			{
				LResourcesGUI = (E3dBackgroundImageResourcesGUI*)(LDrawer->ResourcesGUI);
				if(LResourcesGUI->ImageSwatch) { XeImageSwatch_Free(LResourcesGUI->ImageSwatch, TRUE);LResourcesGUI->ImageSwatch = NULL; }
			}
		break;
	}
}


//========================================
// Icon/menu callback
//========================================
static void _MCB_AddBackgroundImage(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	EIndex                  LCnt = 0;
	E3dNodeComponentClass*  LClass = E3dBackgroundImageNodeComponent_class;

	EListAI_s(, E3dNodePtr, LSelectedNodes);const EIndex  LNSelectedNodes = E3dScene_GetSelectedNodes(E3d_Scene,  E3dNodeSelANY, FALSE,  &LSelectedNodes);if(LNSelectedNodes > 0)
	{
		EIndex  LI = 0;const EIndex  LN = LNSelectedNodes;for(;LI < LN;LI++)
		{
			E3dNode*  LNode = LSelectedNodes.Items[LI];
			EBool     LCameraFound = FALSE;

			{
				E3dModel*  LModel = E_DynamicCast(LNode, E3dModel);if(LModel)
				{
					EIndex  LObjI = 0;const EIndex  LObjN = LModel->Objects.Count;for(;LObjI < LObjN;LObjI++)
					{
						if(EObject_IsSubClass(LModel->Objects.Items[LObjI], E3dCamera)) { LCameraFound = TRUE;break; }
					}
				}
			}

			if(LCameraFound)
			{
				E3dBackgroundImageInfo*  LInfo = (E3dBackgroundImageInfo*)E3dNodeComponent_ByClass(LNode, LClass, FALSE);if(LInfo == NULL)
				{
					LInfo = (E3dBackgroundImageInfo*)E3dNodeComponent_Add(LNode, LClass);

// If this Node is being edited, update its NodePanel
//
					if(E3dNode_LockFind(LNode, &E3dNode_EditLock) >= 0) E3dNode_Edit(LNode, NULL);
					LCnt++;
				}
			}
		}
	}

	{
		const E3dpPrefs*  LPrefs = &E3dp_Prefs;
		const uint32_t    LLabelRGBAp8Color = Xe_ForegroundRGBA8pColor;

		switch(LCnt)
		{
			case 0:   E3dp_PrintMessage(ELogWARNING, 5000, "Select at least one Model with a Camera");break;
			case 1:   E3dp_PrintCompoundMessageVa(ELogMESSAGE, 5000,   XeFONT, XeFontDEFAULT,  XeTEXT, "Added '",  XeCOLOR_RGBA, LPrefs->VariableRGBAp8Color,  XeTEXT, LClass->Name,  XeCOLOR_RGBA, LLabelRGBAp8Color,  XeTEXT, "' to 1 Model",  0);break;
			default:  E3dp_PrintCompoundMessageVa(ELogMESSAGE, 5000,   XeFONT, XeFontDEFAULT,  XeTEXT, "Added '",  XeCOLOR_RGBA, LPrefs->VariableRGBAp8Color,  XeTEXT, LClass->Name,  XeCOLOR_RGBA, LLabelRGBAp8Color,  XeFTEXT, "' to %d Models", LCnt,  0);break;
		}

		if(LCnt)
		{
if(0) { printf("%s() %s:%d  %d %d\n",  __FUNCTION__, __FILE__, __LINE__,  ((XePushButtonCallbackStruct*)PCallData)->Reason, XeCR_ACTIVATE_OPTIONS);fflush(stdout); }
//assert(0);
			if(((XePushButtonCallbackStruct*)PCallData)->Reason == XeCR_ACTIVATE_OPTIONS && E3dNode_LockFind(LSelectedNodes.Items[0], &E3dNode_EditLock) < 0)
			{
				E3dNode_Edit(LSelectedNodes.Items[0], LClass);
			}
			EList_FreeA_s(LSelectedNodes);

			E3dScene*  LScene = E3d_Scene;
			E3dWindows_RedrawByChangeFlags(LScene, E3dChgSELECTION);
		}
	}
}


//================================================================================
// Compute the bounding box of a Model in Screen coordinates
//
// Arguments
//  PModel    Pointer to the Model structure
//  PBBMin    First corner of bounding box
//  PBBMax    Opposite corner of bounding box
//
// Description
//  Computes the world-relative bounding box of a Model and returns it in
//  LBBMin and LBBMax.
//
// Return value
//  TRUE if there is a valid bounding box, FALSE if there isn't one (e.g. the
//  Model has no Geometries).
//================================================================================
EBool E3dModel_GetPerspectiveProjectedBoundingBox(E3dModel* PModel, const E3dMatrix PMatrix, E3dCoord2* PBBMin, E3dCoord2* PBBMax, E3dCoord4* PMaxProjectedXYAbsValPoint, const uint32_t PWhatToInclude)
{
printf("%s() %s:%d  %sGREEN%s\n", __FUNCTION__, __FILE__, __LINE__, ECodeGreen, ECodeNormal);fflush(stdout);
	E3dCoord2      LTBBMin, LTBBMax;
	E3dCoord4      LTMaxProjectedXYAbsValPoint;
	E3dCoordinate  LMaxXYAbsVal = 0.0;
	EBool          LResult = FALSE, LFirst = TRUE;

	EIndex  LObjI = 0;const EIndex  LObjN = PModel->Objects.Count;E3dObject**  LObjects = PModel->Objects.Items;for(;LObjI < LObjN;LObjI++)
	{
		E3dObject*  LObject = LObjects[LObjI];

		if(EObject_IsSubClass(LObject, E3dGeometry))
		{
			E3dGeometry*  LGeometry = (E3dGeometry*)LObject;

			if(LFirst)
			{
				E3dGeometry_GetPerspectiveProjectedBoundingBox(LGeometry, PMatrix, PWhatToInclude,  PBBMin, PBBMax,  PMaxProjectedXYAbsValPoint);LResult = TRUE;

				LMaxXYAbsVal = PMaxProjectedXYAbsValPoint->W;
				LFirst = FALSE;
			}
			else
			{
				if(E3dGeometry_GetPerspectiveProjectedBoundingBox(LGeometry, PMatrix, PWhatToInclude,  &LTBBMin, &LTBBMax, &LTMaxProjectedXYAbsValPoint))
				{
					if(LTBBMin.X < PBBMin->X) PBBMin->X = LTBBMin.X;
					if(LTBBMax.X > PBBMax->X) PBBMax->X = LTBBMax.X;

					if(LTBBMin.Y < PBBMin->Y) PBBMin->Y = LTBBMin.Y;
					if(LTBBMax.Y > PBBMax->Y) PBBMax->Y = LTBBMax.Y;

					if(LTMaxProjectedXYAbsValPoint.W > LMaxXYAbsVal)
					{
						PMaxProjectedXYAbsValPoint->X = LTMaxProjectedXYAbsValPoint.X;
						PMaxProjectedXYAbsValPoint->Y = LTMaxProjectedXYAbsValPoint.Y;
						PMaxProjectedXYAbsValPoint->Z = LTMaxProjectedXYAbsValPoint.Z;
						PMaxProjectedXYAbsValPoint->W = LTMaxProjectedXYAbsValPoint.W;
						LMaxXYAbsVal = LTMaxProjectedXYAbsValPoint.W;
					}

					LResult = TRUE;
				}
			}
		}
	}

	return(LResult);
}




//================================================================================
// Compute the perspective-pjojected bounding box (bounding frustum) of a Mesh
//
// PMaxProjectedXYAbsValPoint will have the non-transformed XYZ of the Vertex
// with the largest projected absolute value of X or Y.
// The W will have that maximum X or Y absolute value.
//================================================================================
static EBool _PointsGetPerspectiveProjectedBoundingBox(const E3dCoord3* PPoints, const EIndex PPoints_Count, const E3dMatrix PMatrix,  E3dCoord2* PBBMin, E3dCoord2* PBBMax,  E3dCoord4* PMaxProjectedXYAbsValPoint)
{
	if(PPoints_Count > 0)
	{
		E3dCoordinate  LXOfMaxXYAbsVal = 0.0, LYOfMaxXYAbsVal = 0.0, LZOfMaxXYAbsVal = 0.0, LMaxXYAbsVal = 0.0;

		const E3dCoord3*  LPoint = PPoints;

		{
			E3dCoord4  LF;

// Must initialize BBox on first Point
//
			EMatrix_Transform4x4_hpv(PMatrix,  LPoint,  LF);
			{ const E3dCoordinate  LW = 1.0 / LF.W;LF.X *= LW;LF.Y *= LW; }

			PBBMin->X = PBBMax->X = LF.X;
			PBBMin->Y = PBBMax->Y = LF.Y;

			LXOfMaxXYAbsVal = LPoint->X;
			LYOfMaxXYAbsVal = LPoint->Y;
			LZOfMaxXYAbsVal = LPoint->Z;
			if(E3d_Abs(LF.X) > E3d_Abs(LF.Y)) LMaxXYAbsVal = E3d_Abs(LF.X);
			else LMaxXYAbsVal = E3d_Abs(LF.Y);

			LPoint++;

			EIndex  LI = 1;for(;LI < PPoints_Count;LI++)
			{
				EMatrix_Transform4x4_hpv(PMatrix,  LPoint,  LF);
				{ const E3dCoordinate  LW = 1.0 / LF.W;LF.X *= LW;LF.Y *= LW; }

				if(LF.X < PBBMin->X) PBBMin->X = LF.X;
				if(LF.X > PBBMax->X) PBBMax->X = LF.X;

				if(LF.Y < PBBMin->Y) PBBMin->Y = LF.Y;
				if(LF.Y > PBBMax->Y) PBBMax->Y = LF.Y;

				if(E3d_Abs(LF.X) > E3d_Abs(LF.Y))
				{
					if(E3d_Abs(LF.X) > LMaxXYAbsVal)
					{
						LXOfMaxXYAbsVal = LPoint->X;
						LYOfMaxXYAbsVal = LPoint->Y;
						LZOfMaxXYAbsVal = LPoint->Z;
						LMaxXYAbsVal = E3d_Abs(LF.X);
					}
				}
				else
				{
					if(E3d_Abs(LF.Y) > LMaxXYAbsVal)
					{
						LXOfMaxXYAbsVal = LPoint->X;
						LYOfMaxXYAbsVal = LPoint->Y;
						LZOfMaxXYAbsVal = LPoint->Z;
						LMaxXYAbsVal = E3d_Abs(LF.Y);
					}
				}

				LPoint++;
			}
		}

		PMaxProjectedXYAbsValPoint->X = LXOfMaxXYAbsVal;
		PMaxProjectedXYAbsValPoint->Y = LYOfMaxXYAbsVal;
		PMaxProjectedXYAbsValPoint->Z = LZOfMaxXYAbsVal;
		PMaxProjectedXYAbsValPoint->W = LMaxXYAbsVal;

		return(TRUE);
	}

	return(FALSE);
}


//========================================================
// Get projected bounds of the selection
// EUNUSED_CODE
//========================================================
EBool E3dScene_GetProjectedBounds(E3dScene* PScene, E3dCamera* PCamera, E3dModel* PCameraModel, const E3dCoordinate PAspectRatio, E3dCoord2* PAllBBMin, E3dCoord2* PAllBBMax, E3dCoord4* PAllMaxProjectedXYAbsValPoint, unsigned int PWhatToInclude)
{
	E3dCoord3      LF = E3dCoord3_s(0.0, 0.0, 0.0);
	E3dCoordinate  LMaxXYAbsVal = 0.0;
	EBool          LSelFound = FALSE;

	E3dMatrix      LWorldToViewerMatrix;E3dMatrix_Invert3x4(PCameraModel->LocalToWorldMatrix, LWorldToViewerMatrix);
	E3dMatrix      LWorldToScreenMatrix;E3dMatrix_Copy(PCamera->ProjectionMatrix, LWorldToScreenMatrix);

	E3dMatrix_Mult4x4(LWorldToViewerMatrix, LWorldToScreenMatrix);

	EListAI(, E3dNode*, LEmptyNodes);

	printf("%s() %s:%d\n", __FUNCTION__, __FILE__, __LINE__);fflush(stdout);

// Go through all the selected Nodes in the scene and get their common bounding box
//
	E3dNode**  LRootNodes = PScene->RootNodes.Items;

	{
		EBool  LFirst = TRUE;

		EIndex  LRI = 0;const EIndex  LRN = PScene->RootNodes.Count;for(;LRI < LRN;LRI++)
		{
			E3dNode*  LNode = LRootNodes[LRI];for(;LNode;LNode = LNode->Next)
			{
				unsigned int  LWhatToInclude = E3dBB_NONE;
				switch(LNode->Selection)
				{
					case E3dNodeSelNONE:  if(PWhatToInclude == E3dBB_ALL) LWhatToInclude = E3dBB_ALL;break;

					case E3dNodeSelNODE:
					case E3dNodeSelBRANCH:
					case E3dNodeSelBRANCH_ROOT:
						if(PWhatToInclude & E3dBB_SELECTED_GEOMETRY) LWhatToInclude = E3dBB_ALL;
					break;

					case E3dNodeSelOBJECT:
						LWhatToInclude = PWhatToInclude;
					break;
				}

				if(LWhatToInclude)
				{
					E3dModel*  LModel = E_DynamicCast(LNode, E3dModel);

					if(LModel)
					{
						E3dMatrix  LMatrix;E3dMatrix_Copy(LWorldToScreenMatrix, LMatrix);E3dMatrix_Mult4x4(LModel->LocalToWorldMatrix, LMatrix);
						E3dMatrix  LLocalToViewerMatrix;E3dMatrix_Copy(LWorldToViewerMatrix, LLocalToViewerMatrix);E3dMatrix_Mult4x4(LModel->LocalToWorldMatrix, LLocalToViewerMatrix);

						if(LFirst)
						{
							if(E3dModel_GetPerspectiveProjectedBoundingBox(LModel, LMatrix,  PAllBBMin, PAllBBMax,  PAllMaxProjectedXYAbsValPoint, LWhatToInclude))
							{
printf("%s() %s:%d  FIRST\n", __FUNCTION__, __FILE__, __LINE__);fflush(stdout);
								LSelFound = TRUE;
								LMaxXYAbsVal = PAllMaxProjectedXYAbsValPoint->W;

// Transform this point into Viewer-space
//
								EMatrix_Transform3x4_pv(LLocalToViewerMatrix, PAllMaxProjectedXYAbsValPoint,  LF);

								LFirst = FALSE;
							}
							else EList_PointerAddA(LEmptyNodes, 16, LNode);
						}
						else
						{
							E3dCoord2  LBBMin, LBBMax;E3dCoord4  LMaxProjectedXYAbsValPoint;
							if(E3dModel_GetPerspectiveProjectedBoundingBox(LModel, LMatrix,  &LBBMin, &LBBMax,  &LMaxProjectedXYAbsValPoint, LWhatToInclude))
							{
printf("%s() %s:%d\n", __FUNCTION__, __FILE__, __LINE__);fflush(stdout);
								if(LBBMin.X < PAllBBMin->X) PAllBBMin->X = LBBMin.X;
								if(LBBMax.X > PAllBBMax->X) PAllBBMax->X = LBBMax.X;

								if(LBBMin.Y < PAllBBMin->Y) PAllBBMin->Y = LBBMin.Y;
								if(LBBMax.Y > PAllBBMax->Y) PAllBBMax->Y = LBBMax.Y;

								if(LMaxProjectedXYAbsValPoint.W > LMaxXYAbsVal)
								{
// Transform this point into Viewer-space
//
									EMatrix_Transform3x4_vv(LLocalToViewerMatrix,  LMaxProjectedXYAbsValPoint,  LF);

									LMaxXYAbsVal = LMaxProjectedXYAbsValPoint.W;
								}

								LSelFound = TRUE;
							}
							else EList_PointerAddA(LEmptyNodes, 16, LNode);
						}
					}

				}
			}
		}
	}

printf("%s() %s:%d\n", __FUNCTION__, __FILE__, __LINE__);fflush(stdout);

	if(!LSelFound)
	{
		EBool  LFirst = TRUE;

printf("%s() %s:%d\n", __FUNCTION__, __FILE__, __LINE__);fflush(stdout);
// If there were empty Nodes, return the bbox of the unit boxes transformed by each Node's LocalToWorldMatrix
//
		EIndex  LNC = 0;const EIndex  LNN = LEmptyNodes_Count;for(;LNC < LNN;LNC++)
		{
			const E3dNode*  LNode = LEmptyNodes[LNC];

			E3dMatrix  LMatrix;E3dMatrix_Copy(LWorldToScreenMatrix, LMatrix);E3dMatrix_Mult4x4(LNode->LocalToWorldMatrix, LMatrix);
			E3dMatrix  LLocalToViewerMatrix;E3dMatrix_Copy(LWorldToViewerMatrix, LLocalToViewerMatrix);E3dMatrix_Mult4x4(LNode->LocalToWorldMatrix, LLocalToViewerMatrix);

			if(LFirst)
			{
				_PointsGetPerspectiveProjectedBoundingBox(E3d_UnitBoxPoints, 8, LMatrix,  PAllBBMin, PAllBBMax,  PAllMaxProjectedXYAbsValPoint);
				LMaxXYAbsVal = PAllMaxProjectedXYAbsValPoint->W;

// Transform this point into Viewer-space
//
				EMatrix_Transform3x4_pv(LLocalToViewerMatrix, PAllMaxProjectedXYAbsValPoint,  LF);
				LSelFound = TRUE;
			}
			else
			{
				E3dCoord2  LBBMin, LBBMax;E3dCoord4  LMaxProjectedXYAbsValPoint;
				_PointsGetPerspectiveProjectedBoundingBox(E3d_UnitBoxPoints, 8, LMatrix,  &LBBMin, &LBBMax, &LMaxProjectedXYAbsValPoint);

				if(LBBMin.X < PAllBBMin->X) PAllBBMin->X = LBBMin.X;
				if(LBBMax.X > PAllBBMax->X) PAllBBMax->X = LBBMax.X;

				if(LBBMin.Y < PAllBBMin->Y) PAllBBMin->Y = LBBMin.Y;
				if(LBBMax.Y > PAllBBMax->Y) PAllBBMax->Y = LBBMax.Y;

				if(LMaxProjectedXYAbsValPoint.W > LMaxXYAbsVal)
				{
// Transform this point into Viewer-space
//
					EMatrix_Transform3x4_vv(LLocalToViewerMatrix,  LMaxProjectedXYAbsValPoint,  LF);

					LMaxXYAbsVal = LMaxProjectedXYAbsValPoint.W;
				}
				LSelFound = TRUE;
			}
		}
	}


	if(LSelFound)
	{
		PAllMaxProjectedXYAbsValPoint->X = LF.X;
		PAllMaxProjectedXYAbsValPoint->Y = LF.Y;
		PAllMaxProjectedXYAbsValPoint->Z = LF.Z;
		PAllMaxProjectedXYAbsValPoint->W = LMaxXYAbsVal;
	}

	EFreeChk(LEmptyNodes);

	return(LSelFound);
}


//========================================
// Reset Camera
//========================================
static void _MCB_ResetCamera(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dWindows_ResetCameras(E3d_Scene, TRUE);
}


static void _CameraSet(E3dScene* PScene,  E3dCamera* PCamera, E3dModel* PCameraModel,
                             const E3dCoord3* P3Position,
                             const E3dCoord3* PAimPoint,
                             const E3dCoord3* PUpVector,
                             const E3dCoordinate PFieldOfView, const E3dCoordinate PMag, const E3dCoordinate PZNear, const E3dCoordinate PZFar,
                             const E3dCoordinate PEyeDistance, const EBool PComputeUpVector, const EBool PFlyTo)
{
		PCamera->FieldOfView = PFieldOfView;
		PCamera->Mag.X = PCamera->Mag.Y = PMag;
		PCamera->ZNear = PZNear;
		PCamera->ZFar  = PZFar;
		PCamera->EyeDistance = PEyeDistance;

//printf("%s() %s:%d  [%s]  % 7.2f % 7.2f % 7.2f  % 7.2f % 7.2f % 7.2f  % 7.2f % 7.2f % 7.2f\n", __FUNCTION__, __FILE__, __LINE__, P3DWindow->Name, XYZ_p(P3Position), XYZ_p(PAimPoint), XYZ_p(PUpVector));fflush(stdout);
		const E3dCoord3  LAimVector = E3dCoord3_s(0.0, 0.0, -1.0);
		E3d_AimConstraintUpdateMatrices(PCameraModel,  P3Position, PAimPoint,  &LAimVector, PUpVector, 0.0,  TRUE);
		E3d_AimConstraintUpdate(PScene, PCameraModel,  P3Position, PAimPoint, PUpVector, 0.0);
}

//========================================
// Create a new Camera
//========================================
static void _MCB_NewCamera(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dModel*  LCameraModel;E3dCamera*  LCamera = E3dCamera_NewWithModel("3DCamera", &LCameraModel);

	if(LCamera && LCameraModel)
	{
		EObject_SetName(LCamera, EStrDup("camera"), FALSE);
		E3dScene*  LScene = E3d_Scene;
		E3dModel*  LAimPointModel;

//printf("%s() %s:%d  LCamera %p RefCnt %d\n", __FUNCTION__, __FILE__, __LINE__, LCamera, LCamera->RefCnt);fflush(stdout);
		E3d_SceneAddCameraModels(E3d_Scene,  LCamera, LCameraModel,  &LAimPointModel, "New Camerarig");
//printf("%s() %s:%d  LCamera %p RefCnt %d\n", __FUNCTION__, __FILE__, __LINE__, LCamera, LCamera->RefCnt);fflush(stdout);
		LCameraModel->Visible = FALSE;LAimPointModel->Visible = FALSE;

		E3dScene_ObjectsUpdateModelList(LScene);E3dScene_LayoutOnSchematics(LScene, TRUE);

		const E3dCoordinate  LZNear = E3dp_Prefs.DefZNear;
		const E3dCoord3      LAimPoint = E3dCoord3_s(0.0, 0.0, 0.0), LUpVectorY = E3dCoord3_s(0.0, 1.0, 0.0);
		_CameraSet(LScene,  LCamera, LCameraModel,  &E3dp_Prefs.DefaultCameraPosition,  &LAimPoint,  &LUpVectorY,  E3dp_Prefs.DefFieldOfView, E3dp_Prefs.DefMag, LZNear, LCamera->ZFar,  E3dp_Prefs.DefEyeDistance, TRUE, FALSE);
	}
}


typedef struct
{
	EIndex   Selections;
	E3dBBox  BBox;
} e3dViewSelBBox;


//========================================
// Frame selection or tagged points
//========================================
static void _MCB_FrameCameraTo(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dScene*        LScene = E3d_Scene;
	E3dBBox          L3DBBox;
	int              L3DSelFound = 0;
	uint16_t         LWhatToInclude;
	e3dViewSelBBox*  LViewSelBBoxes = NULL;

	EListI(, E3dViewClass*, LViewClasses);
	EListI(, E3dWindow*, LFlyToWindows);
	EBool            LDo3D = FALSE, LSelFound = FALSE;

	switch((EPointerInt)PClientData)
	{
		case CR_FRAME_ALL_ON_CURRENT_WINDOW:          // Frame all
		case CR_FRAME_ALL_ON_ALL_WINDOWS:
			LWhatToInclude = E3dBB_ALL;
		break;

		case CR_FRAME_SELECTION_ON_CURRENT_WINDOW:    // Frame selection
		case CR_FRAME_SELECTION_ON_ALL_WINDOWS:
			LWhatToInclude = E3dBB_SELECTED_GEOMETRY;
		break;

		case CR_FRAME_SELECTED_POINTS_ON_CURRENT_WINDOW:          // Frame tag
		case CR_FRAME_SELECTED_POINTS_ON_ALL_WINDOWS:
			LWhatToInclude = E3dBB_SELECTED_POINTS;
		break;

		default:
			LWhatToInclude = E3dBB_NONE;
		break;
	}

	switch((EPointerInt)PClientData)
	{
		case CR_FRAME_ALL_ON_CURRENT_WINDOW:
		case CR_FRAME_SELECTION_ON_CURRENT_WINDOW:
		case CR_FRAME_SELECTED_POINTS_ON_CURRENT_WINDOW:
			{
				E3dWindow*  L3DWindow = E3dp_Active3DWindow;if(L3DWindow)
				{
					if(E3dWindow_IsNormal3DView(L3DWindow)) LDo3D = TRUE;
					else
					{
						E3dViewClass*  LViewClass = L3DWindow->ViewMode->Class;

						if(LViewClass) EList_PointerAdd(LViewClasses, LViewClass);
					}
				}
			}
		break;

		case CR_FRAME_ALL_ON_ALL_WINDOWS:
		case CR_FRAME_SELECTION_ON_ALL_WINDOWS:
		case CR_FRAME_SELECTED_POINTS_ON_ALL_WINDOWS:
			{
//			int  LPI = 0;for(;LPI < E3dp_Panels.Count;LPI++)
				{
//				E3dPanel*  L3DPanel = E3dp_Panels.Items[LPI];

					E3dpWindow*  L3DpActiveWindow = E_DynamicCast(E3dp_Active3DWindow, E3dpWindow);if(L3DpActiveWindow)
					{
						E3dPanel*  L3DPanel = L3DpActiveWindow->Panel;
						int  LWI = 0;const int	LWN = L3DPanel->Windows.Count;for(;LWI < LWN;LWI++)
						{
							E3dWindow*  L3DWindow = L3DPanel->Windows.Items[LWI];E3dpWindow*  L3DpWindow = E_DynamicCast(L3DWindow, E3dpWindow);if(L3DpWindow)
							{
								if(E3dWindow_IsNormal3DView(L3DWindow)) LDo3D = TRUE;
								else
								{
									E3dViewClass*  LViewClass = L3DpWindow->ViewMode->Class;

									if(LViewClass) EList_PointerAdd(LViewClasses, LViewClass);
								}
							}
						}
					}
				}
			}
		break;
	}


// Go through all the selected Nodes in the Scene and get their common bounding box
//
	if(LDo3D) L3DSelFound = E3dp_View3DSelectionBBox(LScene, &L3DBBox, LWhatToInclude);

	if(LViewClasses)
	{
		e3dViewSelBBox*  LViewSelBBox;

		ENewN(LViewClasses_Count, LViewSelBBoxes);
		LViewSelBBox = LViewSelBBoxes;

		int  LI = 0;for(;LI < LViewClasses_Count;LI++, LViewSelBBox++)
		{
			E3dViewClass*  LViewClass = LViewClasses[LI];

			if(LViewClass->SelectionBBox) LViewSelBBox->Selections = LViewClass->SelectionBBox(LScene, LWhatToInclude,  &LViewSelBBox->BBox);
			else LViewSelBBox->Selections = 0;

			if(LViewSelBBox->Selections) LSelFound = TRUE;
		}
	}

//printf("Frame to %f %f %f   %f %f %f\n", LAllBBMin.X, LAllBBMin.Y, LAllBBMin.Z, LAllBBMax.X, LAllBBMax.Y, LAllBBMax.Z);fflush(stdout);

	if(L3DSelFound || LSelFound)
	{
		float  LFlyToTimeLeft = 0.0;

		switch((EPointerInt)PClientData)
		{
			case CR_FRAME_ALL_ON_CURRENT_WINDOW:
			case CR_FRAME_SELECTION_ON_CURRENT_WINDOW:
			case CR_FRAME_SELECTED_POINTS_ON_CURRENT_WINDOW:
				{
					E3dWindow*  L3DWindow = E3dp_Active3DWindow;

/*
					{
						E3dCamera*  LCamera = L3DWindow->PerspectiveView.VCamera;

// If the main Camera or its interest point are visible, update all other
// 3DWindows, during the "fly-to", but without changing their Cameras.
//
						if(L3DSelFound && LCamera->Visible && LCamera->Models)
						{
							E3dModel*	LCameraModel = L3DWindow->PerspectiveView.CameraModel;
							if(LCameraModel->Visible) _E3dp_AddOther3DWindowsNoCameraMove(L3DWindow, &LFlyToWindows, &LFlyToWindows_Count);
						}
					}
*/

					if(E3dWindow_IsNormal3DView(L3DWindow))
					{
						if(L3DSelFound)
						{
							LFlyToTimeLeft = E3dScene_FrameCameras(LScene, L3DWindow, &LFlyToWindows, &LFlyToWindows_Count,  &L3DBBox,  LWhatToInclude);
							if(LFlyToTimeLeft == 0.0) E3dWindow_Redraw(L3DWindow, LScene,  E3dChgCAMERA, 0,  TRUE);
						}
					}
					else
					{
						const E3dViewClass*  LViewClass = L3DWindow->ViewMode->Class;

// If this is a Schematic view, and we are not doing af "frame all", turn off AutoFrame
//
						if(LWhatToInclude != E3dBB_ALL && LViewClass == E3dSchematicsView_class)
						{
							E3dpWindow*  L3DpWindow = E_DynamicCast(L3DWindow, E3dpWindow);if(L3DpWindow)
							{
								((E3dSchematicsView*)L3DpWindow->SchematicsView)->AutoFrame = FALSE;
							}
						}

						int  LI = 0;for(;LI < LViewClasses_Count;LI++)
						{
							if(LViewClass == LViewClasses[LI])
							{
								e3dViewSelBBox*  LViewSelBBox = LViewSelBBoxes + LI;

								if(LViewSelBBox->Selections)
								{
									LFlyToTimeLeft = E3dScene_FrameCameras(LScene, L3DWindow, &LFlyToWindows, &LFlyToWindows_Count,  &LViewSelBBox->BBox,  LWhatToInclude);
									if(LFlyToTimeLeft == 0.0) E3dWindow_Redraw(L3DWindow, LScene,  E3dChgCAMERA, 0,  TRUE);
								}
							}
						}
					}
				}
			break;

			case CR_FRAME_ALL_ON_ALL_WINDOWS:
			case CR_FRAME_SELECTION_ON_ALL_WINDOWS:
			case CR_FRAME_SELECTED_POINTS_ON_ALL_WINDOWS:
				{
//				int  LPI = 0;for(;LPI < E3dp_Panels_Count;LPI++)
					{
//					E3dPanel*  L3DPanel = E3dp_Panels[LPI];

						E3dpWindow*  LActive3DpWindow = E_DynamicCast(E3dp_Active3DWindow, E3dpWindow);if(LActive3DpWindow)
						{
							E3dPanel*  L3DPanel = LActive3DpWindow->Panel;
							EIndex  LWI = 0;const EIndex  LWN = L3DPanel->Windows.Count;for(;LWI < LWN;LWI++)
							{
								E3dWindow*  L3DWindow = L3DPanel->Windows.Items[LWI];
								{
									if(E3dWindow_IsNormal3DView(L3DWindow))
									{
										if(L3DSelFound)
										{
											LFlyToTimeLeft = E3dScene_FrameCameras(LScene, L3DWindow,  &LFlyToWindows, &LFlyToWindows_Count,  &L3DBBox,  LWhatToInclude);
											if(LFlyToTimeLeft == 0.0) E3dWindow_Redraw(L3DWindow, LScene,  E3dChgCAMERA, 0,  TRUE);
										}
									}
									else
									{
										E3dpWindow*  L3DpWindow = E_DynamicCast(L3DWindow, E3dpWindow);if(L3DpWindow)
										{
											E3dViewClass*  LViewClass = L3DWindow->ViewMode->Class;

// If this is a Schematic view, and we are not doing af "frame all", turn off AutoFrame
//
											if(LWhatToInclude != E3dBB_ALL && LViewClass == E3dSchematicsView_class) ((E3dSchematicsView*)L3DpWindow->SchematicsView)->AutoFrame = FALSE;

											int  LI = 0;for(;LI < LViewClasses_Count;LI++)
											{
												if(LViewClass == LViewClasses[LI])
												{
													e3dViewSelBBox*  LViewSelBBox = LViewSelBBoxes + LI;

													if(LViewSelBBox->Selections)
													{
														LFlyToTimeLeft = E3dScene_FrameCameras(LScene, L3DWindow,  &LFlyToWindows, &LFlyToWindows_Count,  &LViewSelBBox->BBox,  LWhatToInclude);
														if(LFlyToTimeLeft == 0.0) E3dWindow_Redraw(L3DWindow, LScene,  E3dChgCAMERA, 0,  TRUE);
													}
												}
											}
										}
									}
								}
							}
						}
					}
				}
			break;
		}

		if(LFlyToTimeLeft > 0.0)
		{
			if(LFlyToWindows)
			{
//printf("%s() %s:%d  LFlyToWindows_Count %d\n", __FUNCTION__, __FILE__, __LINE__, LFlyToWindows_Count);fflush(stdout);
				E3dp_CameraFlyTo(LFlyToWindows, LFlyToWindows_Count,  LScene,  LFlyToTimeLeft, E3dp_Prefs.FrameToTimeStep, NULL);
				EList_ObjectsFree(LFlyToWindows);
			}
		}
	}
	else E3dp_PrintMessage(ELogWARNING, 2500, "Select something to frame");

	EFreeChk(LViewClasses);
	EFreeChk(LViewSelBBoxes);
}


//========================================
// Key callback
//========================================
static void _KCB_FrameCameraTo(const EKeyCode PKey, const unsigned int PModifiers, const int PKeyEventType, EPointer PClientData)
{
	_MCB_FrameCameraTo(NULL, PClientData, (EPointer)NULL);
}


//========================================
// Key callback
//========================================
static void _KCB_ResetCamera(const EKeyCode PKey, const unsigned int PModifiers, const int PKeyEventType, EPointer PClientData)
{
	E3dScene*   LScene = E3d_Scene;

	switch((EPointerInt)PClientData)
	{
		case CR_RESET_WINDOW_CAMERA:
			if(E3dp_Active3DWindow)
			{
				E3dWindow*  L3DWindow = E3dp_Active3DWindow;
				EListI(, E3dWindow*, LFlyToWindows);

				if(E3dWindow_ResetCameras(L3DWindow, LScene, TRUE))
				{
					EList_ObjectAddChk(LFlyToWindows, L3DWindow);

					const E3dViewClass*  LViewClass = L3DWindow->ViewMode->Class;
					if(L3DWindow == E3dp_Main3DWindow && LViewClass == E3dPerspectiveView_class)
					{
						E3dCamera*  LCamera = L3DWindow->PerspectiveView->VCamera;

// FIXMECAMERA
						if(LCamera->Visible && LCamera->Models.Items[0]->Visible)
						{
							_E3dp_AddOther3DWindowsNoCameraMove(L3DWindow, LScene,  &LFlyToWindows, &LFlyToWindows_Count);
						}
					}

					if(LFlyToWindows)
					{
						E3dp_CameraFlyTo(LFlyToWindows, LFlyToWindows_Count,  LScene,  E3dp_Prefs.FrameToTime, E3dp_Prefs.FrameToTimeStep,  NULL);
						EList_ObjectsFree(LFlyToWindows);
					}
				}
				else E3dWindow_Redraw((E3dWindow*)E3dp_Active3DWindow, LScene,  E3dChgCAMERA, 0,  TRUE);
			}
		break;

		case CR_RESET_CAMERA_ON_ALL_WINDOWS:
			E3dWindows_ResetCameras(LScene, TRUE);
			E3dWindows_RedrawByChangeFlags(LScene, E3dChgCAMERA);
		break;
	}
}


//========================================
// Key callback
//========================================
static void _KCB_CenterCameraTo(const EKeyCode PKey, const unsigned int PModifiers, const int PKeyEventType, EPointer PClientData)
{
	E3dWindow*  L3DWindow = E3dp_Active3DWindow;

	int  LX, LY;if(E3dWindow_GetPointerPosition(L3DWindow, &LX, &LY))
	{
		E3dRay  LRay;E3dModel*  LCameraModel;E3dCamera*  LCamera = E3dWindow_CastPrimaryRay(L3DWindow,  LX, LY,  &LRay, &LCameraModel);if(LCamera)
		{
			E3dMatrix      LViewerToWorldMatrix;E3dMatrix_Copy(LCameraModel->LocalToWorldMatrix, LViewerToWorldMatrix);
			E3dMatrix      LWorldToViewerMatrix;E3dMatrix_Invert3x4(LViewerToWorldMatrix, LWorldToViewerMatrix);
			E3dCoordinate  LT = -1.0;
			E3dCoord3      LIntr = E3dCoord3_s(0.0, 0.0, 0.0);

			{
				E3dItem*  LPickedItems;ENewN(E3dp_PickBufferSize, LPickedItems);if(LPickedItems)
				{
					const int  LPickHits = E3dWindow_RectanglePickItems(L3DWindow, E3d_Scene, E3dItemANYOBJECT, LPickedItems,  LX, LY,  1, 1);if(LPickHits)
					{
						E3dObjItem*    LHItem = (E3dObjItem*)LPickedItems;
						E3dCoordinate  LZNear = (E3dCoordinate)LHItem->ZNear / 4294967295.0, LXF = (E3dCoordinate)LX, LYF = (E3dCoordinate)(L3DWindow->Size.Y - LY);
						E3dGLMatrix    LGLProjectionMatrix;E3dGL_GetFloatv(GL_PROJECTION_MATRIX, LGLProjectionMatrix);
						E3dMatrix      LProjectionMatrix;E3dMatrix_FromGL(LGLProjectionMatrix, LProjectionMatrix);
						GLint          LGLViewPort[4];EpglGetIntegerv(GL_VIEWPORT, LGLViewPort);

printf("%s() Ray-cast %f,%f,%f T = %f\n", __FUNCTION__, LIntr.X, LIntr.Y, LIntr.Z, LT);fflush(stdout);

						const EIndex  LViewPort[4] = { LGLViewPort[0], LGLViewPort[1], LGLViewPort[2], LGLViewPort[3] };
						E3d_UnProject(LXF, LYF - (E3dCoordinate)L3DWindow->Size.Y, LZNear,  LViewPort, LProjectionMatrix, LWorldToViewerMatrix,  &LIntr);

//printf("                            GL %f,%f,%f (ZNear %f)\n\n", LIntrX, LIntrY, LIntrZ,  LZNear);fflush(stdout);

						LT = 1.0;
//E3dItemWORLD_POSITION
					}

					EFree(LPickedItems);
				}
			}


//		if(0)
			if(LT > 0.0)
			{
				E3dpWindow*  L3DpWindow = E_DynamicCast(L3DWindow, E3dpWindow);if(L3DpWindow)
				{
					E3dScene*        LScene = E3d_Scene;
					const E3dCoord3  LPosition = E3dCoord3_s(LCameraModel->LocalToWorldMatrix[M30], LCameraModel->LocalToWorldMatrix[M31], LCameraModel->LocalToWorldMatrix[M32]);
					E3dCoord3        LAimPoint, LUpVector;E3dCoordinate  LBankAngle;E3dModel_GetAim(LScene, LCameraModel,  &LAimPoint, NULL, NULL, &LUpVector, &LBankAngle);
					E3dFlyTo*        LFlyTo = &L3DpWindow->CameraFlyTo;

					LFlyTo->TransformStart.Position = LPosition;
					LFlyTo->TransformStart.FieldOfView = LCamera->FieldOfView;
					LFlyTo->TransformStart.Mag = LCamera->Mag;

					const E3dCoordinate  LZFar = E3dp_Prefs.DefZFar;

					const E3dViewClass*  LViewClass = L3DpWindow->ViewMode->Class;if(LViewClass == E3dPerspectiveView_class)
					{
						LCameraModel = L3DpWindow->PerspectiveView->CameraModel;

						LFlyTo->TransformEnd.Position = LPosition;
						LFlyTo->TransformEnd.AimPoint = LIntr;
						LFlyTo->TransformEnd.UpVector = LUpVector;
						LFlyTo->TransformStart.UpVector = LUpVector;
						LFlyTo->TransformStart.BankAngle = LBankAngle;
					}
					else if(LViewClass == E3dRightView_class)
					{
						LFlyTo->TransformEnd.Position.X = LZFar * 0.5;
						LFlyTo->TransformEnd.Position.Y = LIntr.Y;
						LFlyTo->TransformEnd.Position.Z = LIntr.Z;

						LFlyTo->TransformStart.AimPoint.X = 0.0;
						LFlyTo->TransformStart.AimPoint.Y = LPosition.Y;
						LFlyTo->TransformStart.AimPoint.Z = LPosition.Z;

						LFlyTo->TransformEnd.AimPoint.X = 0.0;
						LFlyTo->TransformEnd.AimPoint.Y = LIntr.Y;
						LFlyTo->TransformEnd.AimPoint.Z = LIntr.Z;

						LFlyTo->TransformStart.UpVector = LFlyTo->TransformEnd.UpVector = LUpVector;
						LFlyTo->TransformStart.BankAngle = LBankAngle;
					}
					else if(LViewClass == E3dTopView_class)
					{
						LFlyTo->TransformEnd.Position.X = LIntr.X;
						LFlyTo->TransformEnd.Position.Y = LZFar * 0.5;
						LFlyTo->TransformEnd.Position.Z = LIntr.Z;

						LFlyTo->TransformStart.AimPoint.X = LPosition.X;
						LFlyTo->TransformStart.AimPoint.Y = 0.0;
						LFlyTo->TransformStart.AimPoint.Z = LPosition.Y;

						LFlyTo->TransformEnd.AimPoint.X = LIntr.X;
						LFlyTo->TransformEnd.AimPoint.Y = 0.0;
						LFlyTo->TransformEnd.AimPoint.Z = LIntr.Z;

						LFlyTo->TransformStart.UpVector = LFlyTo->TransformEnd.UpVector = LUpVector;
						LFlyTo->TransformStart.BankAngle = LBankAngle;
					}
					else
					{
						LFlyTo->TransformEnd.Position.X = LIntr.X;
						LFlyTo->TransformEnd.Position.Y = LIntr.Y;
						LFlyTo->TransformEnd.Position.Z = LZFar * 0.5;

						LFlyTo->TransformStart.AimPoint.X = LPosition.X;
						LFlyTo->TransformStart.AimPoint.Y = LPosition.Y;
						LFlyTo->TransformStart.AimPoint.Z = 0.0;

						LFlyTo->TransformEnd.AimPoint.X = LIntr.X;
						LFlyTo->TransformEnd.AimPoint.Y = LIntr.Y;
						LFlyTo->TransformEnd.AimPoint.Z = 0.0;

						LFlyTo->TransformStart.UpVector = LFlyTo->TransformEnd.UpVector = LUpVector;
						LFlyTo->TransformStart.BankAngle = LBankAngle;
					}

					LFlyTo->TransformEnd.FieldOfView = LCamera->FieldOfView;
					LFlyTo->TransformEnd.Mag = LCamera->Mag;
					LFlyTo->TransformEnd.BankAngle = LBankAngle;

					{
						E3dWindow*  L3DWindows[1] = { NULL };

						E_Assign(E3dp_Active3DWindow, L3DWindows[0]);

						E3dp_CameraFlyTo(L3DWindows, 1,  LScene,  E3dp_Prefs.FrameToTime, E3dp_Prefs.FrameToTimeStep, NULL);

						E_Delete(L3DWindows[0]);
					}
				}
			}
		}
	}
}


//========================================
// Dialog-box callback
//========================================
static void _CB_FieldOfView(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dObjectPanel*  LPanel = (E3dObjectPanel*)PClientData;
	E3dCamera*       LCamera = (E3dCamera*)(LPanel->Object);

	LCamera->FieldOfView = ((XeScaleCallbackStruct*)PCallData)->Value.m_Coordinate;

	if(LCamera->DepthOfFieldOn)
	{
		XeComponentClassDrawer*  LDrawer = XeInfoPanel_GetFolderByClass((XeInfoPanel*)LPanel, E3dCamera_class);if(LDrawer)
		{
			E3dResourcesGUI*  LResourcesGUI = (E3dResourcesGUI*)(LDrawer->ResourcesGUI);

			LCamera->FocalLength = 1.0 / (E3d_Tan(LCamera->FieldOfView / 2.0 / E3dRADIANS_TO_DEGREES) / (LCamera->SensorSize.Y / 2.0));

			XeScale_SetValue_Coord(LResourcesGUI->FocalLengthW, LCamera->FocalLength, FALSE);
		}
	}

	E3dWindows_RedrawByCamera(E3d_Scene,  LCamera, NULL,  0);
}


static void _CB_Zoom(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dObjectPanel*  LPanel = (E3dObjectPanel*)PClientData;
	E3dCamera*       LCamera = (E3dCamera*)(LPanel->Object);

	LCamera->Mag.X = LCamera->Mag.Y = ((XeScaleCallbackStruct*)PCallData)->Value.m_Coordinate;

	E3dWindows_RedrawByCamera(E3d_Scene,  LCamera, NULL,  0);
}


static void _CB_DOF(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dObjectPanel*          LPanel = (E3dObjectPanel*)PClientData;
	E3dCamera*               LCamera = (E3dCamera*)(LPanel->Object);
	XeComponentClassDrawer*  LDrawer = XeInfoPanel_GetFolderByClass((XeInfoPanel*)LPanel, E3dCamera_class);if(LDrawer)
	{
		E3dResourcesGUI*  LResourcesGUI = (E3dResourcesGUI*)(LDrawer->ResourcesGUI);

		LCamera->DepthOfFieldOn = ((XeToggleButtonCallbackStruct*)PCallData)->Set;

		if(LCamera->DepthOfFieldOn) XeWidget_Manage(LResourcesGUI->DOFMatrixW);
		else XeWidget_Unmanage(LResourcesGUI->DOFMatrixW);
	}

	E3dWindows_RedrawByCamera(E3d_Scene,  LCamera, NULL,  0);
}


static void _CB_FocalLength(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dObjectPanel*  LPanel = (E3dObjectPanel*)PClientData;
	E3dCamera*       LCamera = (E3dCamera*)(LPanel->Object);

	LCamera->FocalLength = ((XeScaleCallbackStruct*)PCallData)->Value.m_Coordinate;

	if(LCamera->DepthOfFieldOn)
	{
		XeComponentClassDrawer*  LDrawer = XeInfoPanel_GetFolderByClass((XeInfoPanel*)LPanel, E3dCamera_class);if(LDrawer)
		{
			E3dResourcesGUI*  LResourcesGUI = (E3dResourcesGUI*)(LDrawer->ResourcesGUI);

			LCamera->FieldOfView = 2.0 * E3d_ATan((LCamera->SensorSize.Y / 2.0) / LCamera->FocalLength) * E3dRADIANS_TO_DEGREES;

			XeScale_SetValue_Coord(LResourcesGUI->FieldOfViewW, LCamera->FieldOfView, FALSE);
		}
	}

	E3dWindows_RedrawByCamera(E3d_Scene,  LCamera, NULL,  0);
}


static void _CB_Aperture(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dObjectPanel*  LPanel = (E3dObjectPanel*)PClientData;
	E3dCamera*       LCamera = (E3dCamera*)(LPanel->Object);

	LCamera->Aperture = ((XeScaleCallbackStruct*)PCallData)->Value.m_Coordinate;

	E3dWindows_RedrawByCamera(E3d_Scene,  LCamera, NULL,  0);
}


static void _CB_FocalDistance(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dObjectPanel*  LPanel = (E3dObjectPanel*)PClientData;
	E3dCamera*       LCamera = (E3dCamera*)(LPanel->Object);

	LCamera->FocalDistance = ((XeScaleCallbackStruct*)PCallData)->Value.m_Coordinate;

	E3dWindows_RedrawByCamera(E3d_Scene,  LCamera, NULL,  0);
}


static void _CB_FocusAtAimPoint(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dObjectPanel*          LPanel = (E3dObjectPanel*)PClientData;
	E3dCamera*               LCamera = (E3dCamera*)(LPanel->Object);
	XeComponentClassDrawer*  LDrawer = XeInfoPanel_GetFolderByClass((XeInfoPanel*)LPanel, E3dCamera_class);if(LDrawer)
	{
		E3dResourcesGUI*  LResourcesGUI = (E3dResourcesGUI*)(LDrawer->ResourcesGUI);

		if(((XeToggleButtonCallbackStruct*)PCallData)->Set)
		{
			LCamera->FocusingMode = E3dFOCUS_AT_AIMPOINT;
		}
		else LCamera->FocusingMode = 0;

		XeScale_SetValue_Coord(LResourcesGUI->FocalDistanceW, LCamera->FocalDistance, FALSE);
	}

	E3dWindows_RedrawByCamera(E3d_Scene,  LCamera, NULL,  0);
}


static void _CB_SensorSizeY(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dObjectPanel*  LPanel = (E3dObjectPanel*)PClientData;
	E3dCamera*       LCamera = (E3dCamera*)(LPanel->Object);

	LCamera->SensorSize.Y = ((XeScaleCallbackStruct*)PCallData)->Value.m_Coordinate;

	XeComponentClassDrawer*  LDrawer = XeInfoPanel_GetFolderByClass((XeInfoPanel*)LPanel, E3dCamera_class);if(LDrawer)
	{
		E3dResourcesGUI*  LResourcesGUI = (E3dResourcesGUI*)(LDrawer->ResourcesGUI);

		LCamera->FieldOfView = 2.0 * E3d_ATan((LCamera->SensorSize.Y / 2.0) / LCamera->FocalLength) * E3dRADIANS_TO_DEGREES;

		XeScale_SetValue_Coord(LResourcesGUI->FieldOfViewW, LCamera->FieldOfView, FALSE);
	}

	E3dWindows_RedrawByCamera(E3d_Scene,  LCamera, NULL,  0);
}


//========================================
// Dialog-box callback
//========================================
static void _CB_NearPlane(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dObjectPanel*          LPanel = (E3dObjectPanel*)PClientData;
	E3dCamera*               LCamera = (E3dCamera*)(LPanel->Object);
	XeComponentClassDrawer*  LDrawer = XeInfoPanel_GetFolderByClass((XeInfoPanel*)LPanel, E3dCamera_class);if(LDrawer)
	{
		E3dResourcesGUI*  LResourcesGUI = (E3dResourcesGUI*)(LDrawer->ResourcesGUI);

		LCamera->ZNear = ((XeScaleCallbackStruct*)PCallData)->Value.m_Coordinate;
		if(LCamera->ZNear >= LCamera->ZFar) { LCamera->ZFar = LCamera->ZNear + 1.0;XeScale_SetValue_Coord(LResourcesGUI->FarPlaneW, LCamera->ZFar, FALSE); }

		E3dWindows_RedrawByCamera(E3d_Scene,  LCamera, NULL,  0);
	}
}


//========================================
// Dialog-box callback
//========================================
static void _CB_FarPlane(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dObjectPanel*          LPanel = (E3dObjectPanel*)PClientData;
	E3dCamera*               LCamera = (E3dCamera*)(LPanel->Object);
	XeComponentClassDrawer*  LDrawer = XeInfoPanel_GetFolderByClass((XeInfoPanel*)LPanel, E3dCamera_class);if(LDrawer)
	{
		E3dResourcesGUI*  LResourcesGUI = (E3dResourcesGUI*)(LDrawer->ResourcesGUI);

		LCamera->ZFar = ((XeScaleCallbackStruct*)PCallData)->Value.m_Coordinate;
		if(LCamera->ZFar <= LCamera->ZNear)
		{
			LCamera->ZNear = LCamera->ZFar - 1.0;if(LCamera->ZNear < E3d_ZNearMin) LCamera->ZNear = E3d_ZNearMin;
			LCamera->ZNear = XeScale_SetValue_Coord(LResourcesGUI->NearPlaneW, LCamera->ZNear, FALSE);
		}

		E3dWindows_RedrawByCamera(E3d_Scene,  LCamera, NULL,  0);
	}
}


//================================================
// Manage / unmanage lens parameter widgets
//================================================
static void _ManageLensWidgets(E3dObjectPanel* PPanel, E3dCamera* PCamera)
{
	XeComponentClassDrawer*  LDrawer = XeInfoPanel_GetFolderByClass((XeInfoPanel*)PPanel, E3dCamera_class);if(LDrawer)
	{
		E3dResourcesGUI*  LResourcesGUI = (E3dResourcesGUI*)(LDrawer->ResourcesGUI);
		XeChangeManage    LChildren[16];XeChangeManage*  LChild = LChildren;

		if(PCamera->OpticsClass != E3dOpticsPerspective_class)
		{
			LChild->W = LResourcesGUI->DOFBW;LChild->Manage = FALSE;LChild++;
			LChild->W = LResourcesGUI->DOFMatrixW;LChild->Manage = FALSE;LChild++;
		}

		if(PCamera->OpticsClass != E3dOpticsOrthographic_class)
		{
			LChild->W = LResourcesGUI->ZoomW;LChild->Manage = FALSE;LChild++;
		}

		if(PCamera->OpticsClass == E3dOpticsOrthographic_class)
		{
			LChild->W = LResourcesGUI->FieldOfViewW;LChild->Manage = FALSE;LChild++;
			LChild->W = LResourcesGUI->ZoomW;LChild->Manage = TRUE;LChild++;
		}
		else if(PCamera->OpticsClass == E3dOpticsPerspective_class)
		{
			LChild->W = LResourcesGUI->FieldOfViewW;LChild->Manage = TRUE;LChild++;
			LChild->W = LResourcesGUI->DOFBW;LChild->Manage = TRUE;LChild++;


			LChild->W = LResourcesGUI->DOFMatrixW;
			if(PCamera->DepthOfFieldOn) LChild->Manage = TRUE;
			else LChild->Manage = FALSE;
			LChild++;
		}
		else if(PCamera->OpticsClass == E3dOpticsFishEye_class || PCamera->OpticsClass == E3dOpticsFishEye_class)
		{
			LChild->W = LResourcesGUI->FieldOfViewW;LChild->Manage = FALSE;LChild++;
		}
		else if(PCamera->OpticsClass == E3dOpticsPanorama_class)
		{
			LChild->W = LResourcesGUI->FieldOfViewW;LChild->Manage = FALSE;LChild++;
		}
		else if(PCamera->OpticsClass == E3dOpticsSpherical_class)
		{
			LChild->W = LResourcesGUI->FieldOfViewW;LChild->Manage = FALSE;LChild++;
		}
		else if(PCamera->OpticsClass == E3dOpticsAngleMap_class)
		{
			LChild->W = LResourcesGUI->FieldOfViewW;LChild->Manage = FALSE;LChild++;
		}
		else if(PCamera->OpticsClass == E3dOpticsCubeMap_class)
		{
			LChild->W = LResourcesGUI->FieldOfViewW;LChild->Manage = FALSE;LChild++;
			LChild->W = LResourcesGUI->DOFBW;LChild->Manage = TRUE;LChild++;

			LChild->W = LResourcesGUI->DOFMatrixW;
			if(PCamera->DepthOfFieldOn) LChild->Manage = TRUE;
			else LChild->Manage = FALSE;
			LChild++;
		}

		if(LChild - LChildren) XeWidgets_ChangeManage(LChildren, LChild - LChildren);
	}
}


static void _CB_LensPerspective(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dObjectPanel*  LPanel = (E3dObjectPanel*)PClientData;
	E3dCamera*       LCamera = (E3dCamera*)(LPanel->Object);

	LCamera->OpticsClass = E3dOpticsPerspective_class;
	_ManageLensWidgets(LPanel, LCamera);
	E3dWindows_RedrawByCamera(E3d_Scene,  LCamera, NULL,  0);
}


static void _CB_LensOrthographic(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dObjectPanel*  LPanel = (E3dObjectPanel*)PClientData;
	E3dCamera*       LCamera = (E3dCamera*)(LPanel->Object);

	LCamera->OpticsClass = E3dOpticsOrthographic_class;
	_ManageLensWidgets(LPanel, LCamera);
	E3dWindows_RedrawByCamera(E3d_Scene,  LCamera, NULL,  0);
}


static void _CB_LensFishEye(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dObjectPanel*  LPanel = (E3dObjectPanel*)PClientData;
	E3dCamera*       LCamera = (E3dCamera*)(LPanel->Object);

	LCamera->OpticsClass = E3dOpticsFishEye_class;
	_ManageLensWidgets(LPanel, LCamera);
	E3dWindows_RedrawByCamera(E3d_Scene,  LCamera, NULL,  0);
}


static void _CB_LensRearFishEye(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dObjectPanel*  LPanel = (E3dObjectPanel*)PClientData;
	E3dCamera*       LCamera = (E3dCamera*)(LPanel->Object);

	LCamera->OpticsClass = E3dOpticsRearFishEye_class;
	_ManageLensWidgets(LPanel, LCamera);
	E3dWindows_RedrawByCamera(E3d_Scene,  LCamera, NULL,  0);
}


static void _CB_LensAngleMap(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dObjectPanel*  LPanel = (E3dObjectPanel*)PClientData;
	E3dCamera*       LCamera = (E3dCamera*)(LPanel->Object);

	LCamera->OpticsClass = E3dOpticsAngleMap_class;
	_ManageLensWidgets(LPanel, LCamera);
	E3dWindows_RedrawByCamera(E3d_Scene,  LCamera, NULL,  0);
}


static void _CB_LensPanorama(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dObjectPanel*  LPanel = (E3dObjectPanel*)PClientData;
	E3dCamera*       LCamera = (E3dCamera*)(LPanel->Object);

	LCamera->OpticsClass = E3dOpticsPanorama_class;
	_ManageLensWidgets(LPanel, LCamera);
	E3dWindows_RedrawByCamera(E3d_Scene,  LCamera, NULL,  0);
}


static void _CB_LensSpherical(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dObjectPanel*  LPanel = (E3dObjectPanel*)PClientData;
	E3dCamera*       LCamera = (E3dCamera*)(LPanel->Object);

	LCamera->OpticsClass = E3dOpticsSpherical_class;
	_ManageLensWidgets(LPanel, LCamera);
	E3dWindows_RedrawByCamera(E3d_Scene,  LCamera, NULL,  0);
}


static void _CB_LensCubeMap(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dObjectPanel*  LPanel = (E3dObjectPanel*)PClientData;
	E3dCamera*       LCamera = (E3dCamera*)(LPanel->Object);

	LCamera->OpticsClass = E3dOpticsCubeMap_class;
	_ManageLensWidgets(LPanel, LCamera);
	E3dWindows_RedrawByCamera(E3d_Scene,  LCamera, NULL,  0);
}


//========================================
// Key callback
//========================================
static void _KCB_ZoomWindow(const EKeyCode PKey, const unsigned int PModifiers, const int PKeyEventType, EPointer PClientData)
{
	static EBool  LChanging = FALSE;

	if(LChanging) return;

	LChanging = TRUE;
	if(E3dp_Active3DWindow)
	{
		E3dWindow*  L3DWindow = E3dp_Active3DWindow;

		E3dpWindow*  L3DpWindow = E_DynamicCast(L3DWindow, E3dpWindow);if(L3DpWindow)
		{
			L3DpWindow->SizeFlags ^= E3dZOOM;
			E3dWindows_Layout(L3DpWindow->Panel);

			E3dScene*  LScene = E3d_Scene;
			E3dPanel_RedrawWindows(L3DpWindow->Panel, E3dVM_ALL, E3dDF_ALL,  0, E3dRendererChgWINDOW_SIZE | E3dRendererChgWINDOW_EXPOSE, LScene);
		}
	}
	LChanging = FALSE;
//printf("%s()\n", __FUNCTION__);fflush(stdout);
}


static void _UpdateViewDistance(E3dObjectPanel* PPanel, E3dScene* PScene, E3dCamera* PCamera)
{
	XeComponentClassDrawer*  LDrawer = XeInfoPanel_GetFolderByClass((XeInfoPanel*)PPanel, E3dCamera_class);
	E3dResourcesGUI*         LResourcesGUI = (E3dResourcesGUI*)(LDrawer->ResourcesGUI);

	const E3dpPrefs*  LPrefs = &E3dp_Prefs;
	XeString  LXeStr;
	if(PCamera->Models.Count > 0)
	{
		if(PCamera->Models.Count > 1) LXeStr = XeString_NewVa(TRUE,  XeFONT, XeFontDEFAULT,  XeTEXT, "at aim point (multiple instances)",  0);
		else
		{
			E3dModel*  LCameraModel = PCamera->Models.Items[0];E3dAimConstraintNodeComponent*  LAimInfo;E3dNode*  LAimNode = E3d_AimNodeFind(PScene, (E3dNode*)LCameraModel, &LAimInfo);if(LAimNode)
			{
				LXeStr = XeString_NewVa(TRUE,  XeFONT, XeFontDEFAULT,  XeTEXT, "at aim point (",  XeCOLOR_RGBA, LPrefs->VariableRGBAp8Color,  XeFTEXT, "%7.4f", LAimInfo->AimDistance,  XeCOLOR_RGBA, Xe_ForegroundRGBA8pColor,  XeTEXT, ")",  0);
			}
			else LXeStr = XeString_NewVa(TRUE,  XeFONT, XeFontDEFAULT,  XeTEXT, "at aim point",  0);
		}
	}
	else LXeStr = XeString_NewVa(TRUE,  XeFONT, XeFontDEFAULT,  XeTEXT, "at aim point",  0);

	Xe_SetValuesVa(LResourcesGUI->FocusAtAimPointBW,  XeNLabelString, LXeStr,  0);
}


static void _GUI(ECoreObject* PObject, XeInfoPanel* PPanel, void* PDrawer, const int PAction)
{
	E3dCamera*               LCamera = (E3dCamera*)PObject;
	E3dObjectPanel*          LPanel = (E3dObjectPanel*)PPanel;
	XeComponentClassDrawer*  LDrawer = (XeComponentClassDrawer*)PDrawer;

	switch(PAction)
	{
		case E_UPDATE:
			if(LDrawer->ResourcesGUI)
			{
				E3dResourcesGUI*  LResourcesGUI = (E3dResourcesGUI*)(LDrawer->ResourcesGUI);

				if(LResourcesGUI->PerspectiveBW)
				{
					XeToggleButton_SetState(LResourcesGUI->PerspectiveBW, (EBool)(LCamera->OpticsClass == E3dOpticsPerspective_class),  FALSE);
					XeToggleButton_SetState(LResourcesGUI->OrthoBW,       (EBool)(LCamera->OpticsClass == E3dOpticsOrthographic_class), FALSE);
					XeToggleButton_SetState(LResourcesGUI->FisheyeBW,     (EBool)(LCamera->OpticsClass == E3dOpticsFishEye_class),      FALSE);
					XeToggleButton_SetState(LResourcesGUI->RearFisheyeBW, (EBool)(LCamera->OpticsClass == E3dOpticsRearFishEye_class),  FALSE);
					XeToggleButton_SetState(LResourcesGUI->AngleMapBW,    (EBool)(LCamera->OpticsClass == E3dOpticsAngleMap_class),     FALSE);
					XeToggleButton_SetState(LResourcesGUI->PanoramaBW,    (EBool)(LCamera->OpticsClass == E3dOpticsPanorama_class),     FALSE);
					XeToggleButton_SetState(LResourcesGUI->SphericalBW,   (EBool)(LCamera->OpticsClass == E3dOpticsSpherical_class),    FALSE);
					XeToggleButton_SetState(LResourcesGUI->CubeMapBW,     (EBool)(LCamera->OpticsClass == E3dOpticsCubeMap_class),      FALSE);

					XeScale_SetValue_Coord(LResourcesGUI->NearPlaneW, LCamera->ZNear, FALSE);
					XeScale_SetValue_Coord(LResourcesGUI->FarPlaneW,  LCamera->ZFar,  FALSE);

					XeScale_SetValue_Coord(LResourcesGUI->FieldOfViewW, LCamera->FieldOfView, FALSE);
					XeScale_SetValue_Coord(LResourcesGUI->ZoomW,        LCamera->Mag.X,       FALSE);

					XeToggleButton_SetState(LResourcesGUI->DOFBW, LCamera->DepthOfFieldOn, FALSE);

					XeScale_SetValue_Coord(LResourcesGUI->FocalLengthW, LCamera->FocalLength,  FALSE);
					XeScale_SetValue_Coord(LResourcesGUI->ApertureW,    LCamera->Aperture,     FALSE);
					XeScale_SetValue_Coord(LResourcesGUI->SensorSizeYW, LCamera->SensorSize.Y, FALSE);

//				XeScale_SetValue_Coord(LResourcesGUI->FocalDistanceW, LCamera->FocalDistance, FALSE);
//				E3dCoordinate	LAimDistance;E3dModel_GetAim(LCameraModel,  NULL, NULL, &LAimDistance, NULL);
//				XeScale_SetValueAndRanges_Coord(LResourcesGUI->FocalDistanceW,  LCamera->FocalDistance,  0.001, 640000.0,  LAimDistance * 0.1, LAimDistance * 1000.0,  FALSE);
					XeScale_SetValueAndRanges_Coord(LResourcesGUI->FocalDistanceW,  LCamera->FocalDistance,  0.001, 640000.0,  0.001, 1000.0,  FALSE);
					XeToggleButton_SetState(LResourcesGUI->FocusAtAimPointBW, (EBool)(LCamera->FocusingMode == E3dFOCUS_AT_AIMPOINT), FALSE);

					_ManageLensWidgets(LPanel, LCamera);
					_UpdateViewDistance(LPanel, E3d_Scene, LCamera);
				}
			}
			else
			{
				XeWidget  LTopMatrixW = LDrawer->Drawer.DrawerW, LMatrixW, LW;
				XeWidget  LIndentWs[8];  int  LIndentN;      // For indenting the Widgets
				XeWidget  LTraverseWs[8];int  LTraverseN;    // For TextField traversal
				EArg      Args[256];int  ArgCnt;


				E3dResourcesGUI*  LResourcesGUI;ENew(LResourcesGUI);LDrawer->ResourcesGUI = LResourcesGUI;

				LIndentN = LTraverseN = 0;
				EXeStart;
				EXeSetArg(XeNTextFieldDigits, 9);EXeSetArg(XeNDecimalPoints, 4);
//			EXeSetArg(XeNValueType, XeDOUBLE);
				LIndentWs[LIndentN++] = LTraverseWs[LTraverseN++] = LResourcesGUI->NearPlaneW = LW = XeScale_New_Coord(LTopMatrixW, "Near plane",  Args, ArgCnt);
				XeScale_SetValueAndRange_Coord(LW,  LCamera->ZNear,  E3d_ZNearMin, 1000000.0,  FALSE);
				XeAddCallback(LW, XeNValueChangedCallback, _CB_NearPlane, (EPointer)LPanel);

				EXeStart;
				EXeSetArg(XeNTextFieldDigits, 9);EXeSetArg(XeNDecimalPoints, 4);
//			EXeSetArg(XeNValueType, XeDOUBLE);
				LIndentWs[LIndentN++] = LTraverseWs[LTraverseN++] = LResourcesGUI->FarPlaneW = LW = XeScale_New_Coord(LTopMatrixW, "Far plane",  Args, ArgCnt);
				XeScale_SetValueAndRanges_Coord(LW,  LCamera->ZFar,  E3d_ZNearMin + 0.0001, 2000000000.0,  E3d_ZNearMin + 0.0001, 1000000.0,  FALSE);
				XeAddCallback(LW, XeNValueChangedCallback, _CB_FarPlane, (EPointer)LPanel);

				XeWidgets_IndentFromLeft(LIndentWs, LIndentN, 4);



				EXeStart;
				EXeSetArg(XeNMarginTop, 3);
				EXeSetArg(XeNMarginSizeX, 0);
				EXeSetArg(XeNResizableX, FALSE);EXeSetArg(XeNAlignmentX, XeAlignBEGINNING);
				XeWidget_ManagedNew("Optics type", XeLabelWidget_class, LTopMatrixW,  Args, ArgCnt);

				EXeStart;
				EXeSetArg(XeNOrientation, XeVERTICAL);
				EXeSetArg(XeNColumns, 2);
				EXeSetArg(XeNMarginSizeX, 0);
				EXeSetArg(XeNSpacingX, 4);
				EXeSetArg(XeNRadioBehavior, TRUE);
				EXeSetArg(XeNRadioAlwaysOne, TRUE);
				EXeSetArg(XeNResizableX, FALSE);EXeSetArg(XeNAlignmentX, XeAlignBEGINNING);
				LMatrixW = XeWidget_ManagedNew("MatrixW", XeMatrixWidget_class, LTopMatrixW,  Args, ArgCnt);

				EXeStart;
				EXeSetArg(XeNShadowThickness, 0);
				EXeSetArg(XeNHighlightThickness, 0);
				EXeSetArg(XeNActivateOnArm, TRUE);
//				EXeSetArg(XeNIndicatorOn, FALSE);
//				EXeSetArg(XeNLabelType, XeIMAGE_STRING_HORIZONTAL);
//				EXeSetImages3(_Image_LensPerspective);

				EXeSetArg(XeNSet, (EBool)(LCamera->OpticsClass == E3dOpticsPerspective_class));
				XeToolTip8("Your every-day perspective camera / lens.");
				LResourcesGUI->PerspectiveBW = LW = XeWidget_ManagedNew("Perspective", XeToggleButton_class, LMatrixW,  Args, ArgCnt);
				XeAddCallback(LW, XeNValueChangedCallback, _CB_LensPerspective, (EPointer)LPanel);

				EXeStart;
				EXeSetArg(XeNShadowThickness, 0);
				EXeSetArg(XeNHighlightThickness, 0);
				EXeSetArg(XeNActivateOnArm, TRUE);
//				EXeSetArg(XeNIndicatorOn, FALSE);
//				EXeSetArg(XeNLabelType, XeIMAGE_STRING_HORIZONTAL);
//				EXeSetImages3(_Image_LensOrtho);

				EXeSetArg(XeNSet, (EBool)(LCamera->OpticsClass == E3dOpticsOrthographic_class));
				XeToolTip8("Parallel projection. This 'lens' has an infinite focal length and thus provides no perspective.\nIt is very useful for creating textures and other images where perspective is an 'unwanted feature'.");
				LResourcesGUI->OrthoBW = LW = XeWidget_ManagedNew("Orthographic", XeToggleButton_class, LMatrixW,  Args, ArgCnt);
				XeAddCallback(LW, XeNValueChangedCallback, _CB_LensOrthographic, (EPointer)LPanel);

				EXeStart;
				EXeSetArg(XeNShadowThickness, 0);
				EXeSetArg(XeNHighlightThickness, 0);
				EXeSetArg(XeNActivateOnArm, TRUE);
				EXeSetArg(XeNSet, (EBool)(LCamera->OpticsClass == E3dOpticsFishEye_class));
				XeToolTip8("This lens has a 180-degree field-of-view\nIt lets you create environment-maps and illumination-maps (see HDR).\nAn image created this way, when mapped on a hemisphere (with XY\ntexture-mapping), will yield a 180-degree view of the scene.");
				LResourcesGUI->FisheyeBW = LW = XeWidget_ManagedNew("Fish-eye", XeToggleButton_class, LMatrixW,  Args, ArgCnt);
				XeAddCallback(LW, XeNValueChangedCallback, _CB_LensFishEye, (EPointer)LPanel);

				EXeStart;
				EXeSetArg(XeNShadowThickness, 0);
				EXeSetArg(XeNHighlightThickness, 0);
				EXeSetArg(XeNActivateOnArm, TRUE);
				EXeSetArg(XeNSet, (EBool)(LCamera->OpticsClass == E3dOpticsRearFishEye_class));
				XeToolTip8("This allows you to make two matching 'fish-eye' images for a full, 360 degree\nview of the scene, without having to manually turn the camera 180 degrees.");
				LResourcesGUI->RearFisheyeBW = LW = XeWidget_ManagedNew("Rear-view fish-eye", XeToggleButton_class, LMatrixW,  Args, ArgCnt);
				XeAddCallback(LW, XeNValueChangedCallback, _CB_LensRearFishEye, (EPointer)LPanel);

				EXeStart;
				EXeSetArg(XeNShadowThickness, 0);
				EXeSetArg(XeNHighlightThickness, 0);
				EXeSetArg(XeNActivateOnArm, TRUE);
				EXeSetArg(XeNSet, (EBool)(LCamera->OpticsClass == E3dOpticsAngleMap_class));
				XeToolTip8( 
"Similar to fish-eye, but it has a 360 by 360-degree field-of-view.\n\
Points behind the camera will be projected around the edge of the 'image circle'.\n\
This is (approximately) the projection you get if you take a photograph of a\n\
reflective sphere (\"chrome ball\")");
				LResourcesGUI->AngleMapBW = LW = XeWidget_ManagedNew("Angle map", XeToggleButton_class, LMatrixW,  Args, ArgCnt);
				XeAddCallback(LW, XeNValueChangedCallback, _CB_LensAngleMap, (EPointer)LPanel);

				EXeStart;
				EXeSetArg(XeNShadowThickness, 0);
				EXeSetArg(XeNHighlightThickness, 0);
				EXeSetArg(XeNActivateOnArm, TRUE);
				EXeSetArg(XeNSet, (EBool)(LCamera->OpticsClass == E3dOpticsPanorama_class));
				XeToolTip8("360-degree panorama (cylindrical mapping).\nCreates an image that, when mapped on the inside of a cylinder (with cylindrical\ntexture-mapping), will yield a 360-degree panoramic view of the scene.");
				LResourcesGUI->PanoramaBW = LW = XeWidget_ManagedNew("Panorama", XeToggleButton_class, LMatrixW,  Args, ArgCnt);
				XeAddCallback(LW, XeNValueChangedCallback, _CB_LensPanorama, (EPointer)LPanel);

				EXeStart;
				EXeSetArg(XeNShadowThickness, 0);
				EXeSetArg(XeNHighlightThickness, 0);
				EXeSetArg(XeNActivateOnArm, TRUE);
				EXeSetArg(XeNSet, (EBool)(LCamera->OpticsClass == E3dOpticsSpherical_class));
				XeToolTip8("Full 360-degree view (longitude/latitude mapping).\nCreates an image that, when mapped on the inside of a sphere (with spherical\ntexture-mapping), will yield a full 360-degree view of the scene.");
				LResourcesGUI->SphericalBW = LW = XeWidget_ManagedNew("Spherical", XeToggleButton_class, LMatrixW,  Args, ArgCnt);
				XeAddCallback(LW, XeNValueChangedCallback, _CB_LensSpherical, (EPointer)LPanel);

				EXeStart;
				EXeSetArg(XeNShadowThickness, 0);
				EXeSetArg(XeNHighlightThickness, 0);
				EXeSetArg(XeNActivateOnArm, TRUE);
				EXeSetArg(XeNSet, (EBool)(LCamera->OpticsClass == E3dOpticsCubeMap_class));
				XeToolTip8("Full 360-degree view\nCreates an image that, when mapped on the inside of a cube, will yield a full 360-degree view of the scene.\n\
The image produced is a 3x2 grid of the 6 sides of the cube in the directions (relative to the camera):\n\
top row:    -X, -Y, -Z\n\
bottom row: +X, +Y, +Z");
				LResourcesGUI->CubeMapBW = LW = XeWidget_ManagedNew("Cube-map", XeToggleButton_class, LMatrixW,  Args, ArgCnt);
				XeAddCallback(LW, XeNValueChangedCallback, _CB_LensCubeMap, (EPointer)LPanel);

				LIndentN = 0;
				EXeStart;
				EXeSetArg(XeNTextFieldDigits, 6);EXeSetArg(XeNDecimalPoints, 4);
				EXeSetArg(XeNMarginTop, 8);
				XeToolTip8("Field of view in degrees, along the vertical axis.");
				LIndentWs[LIndentN++] = LTraverseWs[LTraverseN++] = LResourcesGUI->FieldOfViewW = LW = XeScale_New_Coord(LTopMatrixW, "Field of view",  Args, ArgCnt);
				XeScale_SetValueAndRange_Coord(LW,  LCamera->FieldOfView,  0.0001, 179.99999,  FALSE);
				XeAddCallback(LW, XeNValueChangedCallback, _CB_FieldOfView, (EPointer)LPanel);


				LIndentN = 0;
				EXeStart;
				EXeSetArg(XeNMarginTop, 8);
				EXeSetArg(XeNTextFieldDigits, 8);EXeSetArg(XeNDecimalPoints, 6);
				LIndentWs[LIndentN++] = LTraverseWs[LTraverseN++] = LResourcesGUI->ZoomW = LW = XeScale_New_Coord(LTopMatrixW, "Zoom",  Args, ArgCnt);
				XeScale_SetValueAndRange_Coord(LW,  LCamera->Mag.X,  0.000001, 1000.0,  FALSE);
				XeAddCallback(LW, XeNValueChangedCallback, _CB_Zoom, (EPointer)LPanel);


// Depth of field
//
				{
					XeWidget  LDOFMatrixW;

					EXeStart;
					EXeSetArg(XeNShadowThickness, 0);EXeSetArg(XeNMarginSizeX, 0);
					EXeSetArg(XeNHighlightThickness, 0);
					EXeSetArg(XeNMarginTop, 8);
					EXeSetArg(XeNMarginSizeY, 1);
					EXeSetArg(XeNSet, LCamera->DepthOfFieldOn);
					EXeSetArg(XeNResizableY, FALSE);
					XeToolTip8("Simulate a realistic camera lens that allows for objects to be in focus or out of focus.");
					LResourcesGUI->DOFBW = LW = XeWidget_ManagedNew("Depth of field", XeToggleButton_class, LTopMatrixW,  Args, ArgCnt);
					XeAddCallback(LW, XeNValueChangedCallback, _CB_DOF, (EPointer)LPanel);

					EXeStart;
					EXeSetArg(XeNOrientation, XeHORIZONTAL);
					EXeSetArg(XeNSpacingLeft, 4);EXeSetArg(XeNMarginSizeX, 0);
					EXeSetArg(XeNColumns, 2);
					EXeSetArg(XeNSpacingX, 2);
					EXeSetArg(XeNResizableY, FALSE);
					if(LCamera->DepthOfFieldOn) LResourcesGUI->DOFMatrixW = LDOFMatrixW = XeWidget_ManagedNew("DOFMatrixW", XeMatrixWidget_class, LTopMatrixW,  Args, ArgCnt);
					else LResourcesGUI->DOFMatrixW = LDOFMatrixW = XeWidget_New("DOFMatrixW", XeMatrixWidget_class, LTopMatrixW,  Args, ArgCnt);

					LIndentN = 0;
					EXeStart;
					EXeSetArg(XeNTextFieldDigits, 5);EXeSetArg(XeNDecimalPoints, 4);
					EXeSetArg(XeNIncrement, 10);
					XeToolTip8("Focal length of the lens (not the same as focal distance!).\nThis, divided by the aperture (f value) provides the physical diameter of the lens.\nThe larger the diameter, the narrower the depth of field.\n- Wide depht-of-field (DOF): everything is in focus.\n- Narrow DOF: only objects close to the focal plane are in focus.");
					LIndentWs[LIndentN++] = LTraverseWs[LTraverseN++] = LResourcesGUI->FocalLengthW = LW = XeScale_New_Coord(LDOFMatrixW, "Focal length",  Args, ArgCnt);
					XeScale_SetValueAndRange_Coord(LW,  LCamera->FocalLength,  0.001, 10.0,  FALSE);
					XeAddCallback(LW, XeNValueChangedCallback, _CB_FocalLength, (EPointer)LPanel);

					LIndentN = 0;
					EXeStart;
					EXeSetArg(XeNTextFieldDigits, 5);EXeSetArg(XeNDecimalPoints, 3);
					XeToolTip8("\"f\" value. Focal length divided by this number provides the physical diameter of the lens.\nThe larger the diameter, the narrower the depth of field.\n- Wide depht-of-field (DOF): everything is in focus.\n- Narrow DOF: only objects close to the focal plane are in focus.");
					LIndentWs[LIndentN++] = LTraverseWs[LTraverseN++] = LResourcesGUI->ApertureW = LW = XeScale_New_Coord(LDOFMatrixW, "Aperture",  Args, ArgCnt);
					XeScale_SetValueAndRange_Coord(LW,  LCamera->Aperture,  0.01, 256.0,  FALSE);
					XeAddCallback(LW, XeNValueChangedCallback, _CB_Aperture, (EPointer)LPanel);

					EXeStart;
					EXeSetArg(XeNTextFieldDigits, 5);EXeSetArg(XeNDecimalPoints, 4);
					XeToolTip8("Distance from the lens to the focal plane (the distance at which the optics is focused).");
					LIndentWs[LIndentN++] = LTraverseWs[LTraverseN++] = LResourcesGUI->FocalDistanceW = LW = XeScale_New_Coord(LDOFMatrixW, "Focal distance",  Args, ArgCnt);

//				E3dCoordinate	LAimDistance;E3dModel_GetAim(LCameraModel,  NULL, NULL, &LAimDistance, NULL);
//				XeScale_SetValueAndRanges_Coord(LW,  LCamera->FocalDistance,  0.001, 640000.0,  LAimDistance * 0.1, LAimDistance * 1000.0,  FALSE);
					XeScale_SetValueAndRanges_Coord(LW,  LCamera->FocalDistance,  0.001, 640000.0,  0.001, 1000.0,  FALSE);
					XeAddCallback(LW, XeNValueChangedCallback, _CB_FocalDistance, (EPointer)LPanel);

					EXeStart;
					EXeSetArg(XeNShadowThickness, 0);
					EXeSetArg(XeNHighlightThickness, 0);
					EXeSetArg(XeNActivateOnArm, TRUE);
					EXeSetArg(XeNSet, (EBool)(LCamera->FocusingMode == E3dFOCUS_AT_AIMPOINT));
					XeToolTip8("Automatically focus at the Camera's aim point.");
					LResourcesGUI->FocusAtAimPointBW = LW = XeWidget_ManagedNew("at aimpoint", XeToggleButton_class, LDOFMatrixW,  Args, ArgCnt);
					XeAddCallback(LW, XeNValueChangedCallback, _CB_FocusAtAimPoint, (EPointer)LPanel);
					_UpdateViewDistance(LPanel, E3d_Scene, LCamera);

					LIndentN = 0;
					EXeStart;
					EXeSetArg(XeNTextFieldDigits, 5);EXeSetArg(XeNDecimalPoints, 4);
					XeToolTip8("Image sensor height.");
					LIndentWs[LIndentN++] = LTraverseWs[LTraverseN++] = LResourcesGUI->SensorSizeYW = LW = XeScale_New_Coord(LDOFMatrixW, "Sensor height",  Args, ArgCnt);
					XeScale_SetValueAndRange_Coord(LW,  LCamera->SensorSize.Y,  0.001, 50.0,  FALSE);
					XeAddCallback(LW, XeNValueChangedCallback, _CB_SensorSizeY, (EPointer)LPanel);
				}

				XeTraversal_SetRing(LTraverseWs, LTraverseN);

/*
				EXeStart;
				EXeSetArg(XeNTextFieldDigits, 5);EXeSetArg(XeNDecimalPoints, 2);
				EXeSetArg(XeNIncrement, 10);
				LIndentWs[LIndentN++] = LTraverseWs[LTraverseN++] = _XZoomW = LW = XeScale_New_Coord(LTopMatrixW, "X zoom",  Args, ArgCnt);
				XeScale_SetValueAndRange_Coord(LW,  LCamera->XZoom,  0.01, 100.0,  FALSE);
				XeAddCallback(LW, XeNValueChangedCallback, _DCB_CameraXMag, (EPointer)LSettingsPanel);

				EXeStart;
				EXeSetArg(XeNTextFieldDigits, 5);EXeSetArg(XeNDecimalPoints, 2);
				EXeSetArg(XeNIncrement, 10);
				LIndentWs[LIndentN++] = LTraverseWs[LTraverseN++] = _YZoomW = LW = XeScale_New_Coord(LTopMatrixW, "Y zoom",  Args, ArgCnt);
				XeScale_SetValueAndRange_Coord(LW,  LCamera->YZoom,  0.01, 100.0,  FALSE);
				XeAddCallback(LW, XeNValueChangedCallback, _DCB_CameraYMag, (EPointer)LSettingsPanel);
*/


/*
				EXeStart;
				EXeSetArg(XeNShadowThickness, 0);
				EXeSetArg(XeNHighlightThickness, 0);
				EXeSetArg(XeNSet, LCamera->Glow);
				XeToolTip8("Also called 'bloom effect'. With this option turned on, a halo will appear around bright objects.\nIt adds a nice ambience to images and makes them look more realistic.");
				LSettingsPanel->GlowBW = LW = XeWidget_ManagedNew("Glow", XeToggleButton_class, LTopMatrixW,  Args, ArgCnt);
				XeAddCallback(LW, XeNValueChangedCallback, _DCB_Camera, (EPointer)CR_GLOW);
*/
			}
			_ManageLensWidgets(LPanel, LCamera);
		break;
	}
}


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

	E3dCamera_class->GUI = _GUI;

	{
		E3dNodeComponentClass  LNodeComponentClass=
		{
			.Name=        "Background image",
			.StructSize=  sizeof(E3dBackgroundImageInfo),
			.Resources=   EListS_s(_BackgroundImageResources),
			.Init=        _BackgroundImageInit,
			.Free=        _BackgroundImageFree,
			.GUI=         _BackgroundImageGUI
		};
		E3dBackgroundImageNodeComponent_class = E3dNodeComponent_ClassRegister(&LNodeComponentClass);
	}

	if(E3dBackgroundImageNodeComponent_class)
	{
		_MenuButtons[_MenuButtons_Count++] = XePushButton_Add(Xe_App, "Menu->Camera", "Add / edit background image", '\0', NULL, NULL, TRUE, NULL, _MCB_AddBackgroundImage, (EPointer)0);
	}

	_MenuButtons[_MenuButtons_Count++] = XePushButton_Add(Xe_App, "Menu->Camera", "Reset", '\0', "Shift ~Ctrl <Key>Home", "Home / Shft+Home", FALSE, NULL, _MCB_ResetCamera, (EPointer)CR_RESET_CAMERA_ON_ALL_WINDOWS);
	_MenuButtons[_MenuButtons_Count++] = XePushButton_Add(Xe_App, "Menu->Camera", "Frame all", '\0', "~Shift ~Ctrl <Key>A", "A / Shift+A", FALSE, NULL, _MCB_FrameCameraTo, (EPointer)CR_FRAME_ALL_ON_ALL_WINDOWS);
	_MenuButtons[_MenuButtons_Count++] = XePushButton_Add(Xe_App, "Menu->Camera", "Frame selection", '\0', "~Shift ~Ctrl <Key>F", "F / Shift+F", FALSE, NULL, _MCB_FrameCameraTo, (EPointer)CR_FRAME_SELECTION_ON_ALL_WINDOWS);
	_MenuButtons[_MenuButtons_Count++] = XePushButton_Add(Xe_App, "Menu->Camera", "New Camera", '\0', NULL, NULL, FALSE, NULL, _MCB_NewCamera, (EPointer)0);

//	E3dp_FrameTagMenuButton = XePushButton_Add("Menu->Camera", "Frame tag", '\0', "~Shift Ctrl <Key>F", "F", FALSE, NULL, _MCB_FrameCameraTo, (EPointer)CR_FRAME_SELECTED_POINTS_ON_ALL_WINDOWS);

	E_KeySetCallback("Reset camera",                   EKeyHOME, 0,          EKeyPRESS, _KCB_ResetCamera,    (EPointer)CR_RESET_WINDOW_CAMERA);
	E_KeySetCallback("Reset cameras",                  EKeyHOME, EShiftMask, EKeyPRESS, _KCB_ResetCamera,    (EPointer)CR_RESET_CAMERA_ON_ALL_WINDOWS);

// A lot of Apple laptops don't have a "Home" key (WTF?), but reset camera is pretty important, so we'll add it to Ctrl+H as well.
// Command+H is 'Hide X11', so we'll use Ctrl+H, even though Apple fanboys and girls prefer the Command key...
#ifdef __APPLE__
	E_KeySetCallback("Reset camera",                   'H', EControlMask,            EKeyPRESS, _KCB_ResetCamera,    (EPointer)CR_RESET_WINDOW_CAMERA);
	E_KeySetCallback("Reset cameras",                  'H', EControlMask|EShiftMask, EKeyPRESS, _KCB_ResetCamera,    (EPointer)CR_RESET_CAMERA_ON_ALL_WINDOWS);
#endif

	E_KeySetCallback("Frame all",                      'a',      0,          EKeyPRESS, _KCB_FrameCameraTo,  (EPointer)CR_FRAME_ALL_ON_CURRENT_WINDOW);
	E_KeySetCallback("Frame all on all windows",       'a',      EShiftMask, EKeyPRESS, _KCB_FrameCameraTo,  (EPointer)CR_FRAME_ALL_ON_ALL_WINDOWS);
	E_KeySetCallback("Frame selection",                'f',      0,          EKeyPRESS, _KCB_FrameCameraTo,  (EPointer)CR_FRAME_SELECTION_ON_CURRENT_WINDOW);
	E_KeySetCallback("Frame selection on all windows", 'f',      EShiftMask, EKeyPRESS, _KCB_FrameCameraTo,  (EPointer)CR_FRAME_SELECTION_ON_ALL_WINDOWS);
	E_KeySetCallback("Center to point",                'i',      0,          EKeyPRESS, _KCB_CenterCameraTo, (EPointer)0);

	E_KeySetCallback("Zoom window",                    EKeyF8,   0,          EKeyPRESS, _KCB_ZoomWindow,     (EPointer)0);

	return(E_SUCCESS);
}


//========================================
// Exit method of the plugin
//========================================
int E_PluginExit(const EBool PExiting)
{
	if(!PExiting || Xe_GUIFreeOnExit)
	{
		EIndex  LI = 0;for(;LI < _MenuButtons_Count;LI++) { XeItemFree(_MenuButtons[LI]); }
		_MenuButtons_Count = 0;
	}

	E3dCamera_class->GUI = NULL;

	if(E3dBackgroundImageNodeComponent_class) { E3dNodeComponent_ClassDeactivate(E3dBackgroundImageNodeComponent_class);E3dBackgroundImageNodeComponent_class = NULL; }

	E_KeyRemoveCallback(EKeyHOME, 0,                       EKeyPRESS, _KCB_ResetCamera,    (EPointer)CR_RESET_WINDOW_CAMERA);
	E_KeyRemoveCallback(EKeyHOME, EShiftMask,              EKeyPRESS, _KCB_ResetCamera,    (EPointer)CR_RESET_CAMERA_ON_ALL_WINDOWS);

// A lot of Apple laptops don't have a "Home" key (WTF?), but reset camera is pretty important, so we'll add it to Ctrl+H as well.
// Command+H is 'Hide X11', so we'll use Ctrl+H, even though Apple fanboys and girls prefer the Command key...
#ifdef __APPLE__
	E_KeyRemoveCallback('H',      EControlMask,            EKeyPRESS, _KCB_ResetCamera,    (EPointer)CR_RESET_WINDOW_CAMERA);
	E_KeyRemoveCallback('H',      EControlMask|EShiftMask, EKeyPRESS, _KCB_ResetCamera,    (EPointer)CR_RESET_CAMERA_ON_ALL_WINDOWS);
#endif

	E_KeyRemoveCallback('a',      0,                       EKeyPRESS, _KCB_FrameCameraTo,  (EPointer)CR_FRAME_ALL_ON_CURRENT_WINDOW);
	E_KeyRemoveCallback('a',      EShiftMask,              EKeyPRESS, _KCB_FrameCameraTo,  (EPointer)CR_FRAME_ALL_ON_ALL_WINDOWS);
	E_KeyRemoveCallback('f',      0,                       EKeyPRESS, _KCB_FrameCameraTo,  (EPointer)CR_FRAME_SELECTION_ON_CURRENT_WINDOW);
	E_KeyRemoveCallback('f',      EShiftMask,              EKeyPRESS, _KCB_FrameCameraTo,  (EPointer)CR_FRAME_SELECTION_ON_ALL_WINDOWS);
	E_KeyRemoveCallback('i',      0,                       EKeyPRESS, _KCB_CenterCameraTo, (EPointer)0);

	E_KeyRemoveCallback(EKeyF8,   0,                       EKeyPRESS, _KCB_ZoomWindow,     (EPointer)0);

	return(E_SUCCESS);
}
