// ---------------------------------------------------------------------------
// Copyright (c) Allan Petersen, 2010.
/*
 * This is a implementation of an OpenGL VCL Component for Embarcadero Rad Studio XE.

 * (c) Copyright 2010, Allan Petersen
 * ALL RIGHTS RESERVED
 * Permission to use, copy, modify, and distribute this software for
 * any purpose and without fee is hereby granted, provided that the above
 * copyright notice appear in all copies and that both the copyright notice
 * and this permission notice appear in supporting documentation, and that
 * the name of Allan Petersen not be used in advertising
 * or publicity pertaining to distribution of the software without specific,
 * written prior permission.
 *
 * THE MATERIAL EMBODIED ON THIS SOFTWARE IS PROVIDED TO YOU "AS-IS"
 * AND WITHOUT WARRANTY OF ANY KIND, EXPRESS, IMPLIED OR OTHERWISE,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY OR
 * FITNESS FOR A PARTICULAR PURPOSE.  IN NO EVENT SHALL ALLAN
 * PETERSEN BE LIABLE TO YOU OR ANYONE ELSE FOR ANY DIRECT,
 * SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY
 * KIND, OR ANY DAMAGES WHATSOEVER, INCLUDING WITHOUT LIMITATION,
 * LOSS OF PROFIT, LOSS OF USE, SAVINGS OR REVENUE, OR THE CLAIMS OF
 * THIRD PARTIES, WHETHER OR NOT ALLAN PETERSEN HAS BEEN
 * ADVISED OF THE POSSIBILITY OF SUCH LOSS, HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE
 * POSSESSION, USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 * www.allanpetersen.com
 *
 */

// ---------------------------------------------------------------------------
// Version History (sorry this is not very updated)
// 1.3.0, 2010-10-19  -   Support for Embarcadero RAD Studio XE, changed OnError event, FMultiSampleSettings, Version String
// 1.2.0, 2010-06-20  -   Fixed typecasting and copy paste error in DrawText2D DrawText3D and Get3DTextSize. (sorry about this one again...)
// 1.1.9, 2010-05-02  -   Fixed Length bug in DrawText2D DrawText3D and Get3DTextSize. (sorry about this one)
// 1.1.8, 2010-02-25  -   Fixed unicode bug in DrawText2D DrawText3D and Get3DTextSize.
// 1.1.7, 2010-01-20  -   Embarcadero Rad Studio 2010 Support.
// Unicode strings.
// QueryOpenGLExtention ()
// 1.1.6, 2008-09-25  -   Codegear Rad Studio 2009 Support.
// for the moment keep ansistring in events, in the future use UnicodeString
// 1.1.5, 2008-09-25  -   Codegear Rad Studio 2009 Support.
// 1.1.4, 2007-11-02  -   Windows Vista Support with f_PFD_SUPPORT_COMPOSITION.
// 1.1.3, 2006-05-25  -   Fixed _T errors in compile.
// 1.1.2, 2006-02-06  -   Added multi Sample Support.
// 1.1.1, 2006-01-06  -   Support for BDS 2006
// 1.1.0, 2003-09-02  -   Removed a few memory leaks with help from Heath Raftery
// 1.0.9, 2003-05-27  -   Added WMEraseBkgnd, with a paint call.
// 1.0.8, 2003-05-09  -   Removed WMEraseBkgnd, it caused components to dissapear when
// added on top of this component in design mode.
// Added public PIXELFORMATDESCRIPTOR dpf for external test for
// hardware rendering etc.
//
// ?.?.?, 2002.01.07  -   Cleaning up the code a bit
// Changing wglMakeCurrent to be independent of the event
// FOnInit changed to initialize independent of an existing event.
// ?.?.?, 2000.08.15  -   Added LoadTGA () to load targa files.

// ---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include <stdio.h>
#include <algorithm>
#include "TOpenGLAPPanel.h"
#include "wglext.h"
#pragma package(smart_init)
// ----
#define MULTISAMPLE_OPENGL_CLASS ("MultiSampleOpenGLClass")

using std::max;

// ---- Definition on targa header
typedef struct {
	unsigned char id_length, colormap_type, image_type;
	unsigned short colormap_index, colormap_length;
	unsigned char colormap_size;
	unsigned short x_origin, y_origin, width, height;
	unsigned char pixel_size, attributes;
} targaHeader;

// ---------------------------------------------------------------------------
// Compontent Registration
// ---------------------------------------------------------------------------
// ValidCtrCheck is used to assure that the components created do not have
// any pure virtual functions.
//
static inline void ValidCtrCheck(TOpenGLAPPanel *) {
	new TOpenGLAPPanel(NULL);
}

// ---------------------------------------------------------------------------
namespace Topenglappanel {
	void __fastcall PACKAGE Register() {
		TComponentClass classes[1] = {__classid(TOpenGLAPPanel)};
		RegisterComponents("allanpetersen.com", classes, 0);
	}
}

// ---------------------------------------------------------------------------
// Constructor and Destructor
// ---------------------------------------------------------------------------
__fastcall TOpenGLAPPanel::TOpenGLAPPanel(TComponent* Owner)
	: TCustomPanel(Owner) {

	FVersion = "1.3.0";
	AllowCreateParms = false;
	DoubleBuffered = false;
	FdwFlags.Clear();
	FdwFlags << f_PFD_DRAW_TO_WINDOW << f_PFD_SUPPORT_OPENGL <<
		f_PFD_DOUBLEBUFFER << f_PFD_SUPPORT_COMPOSITION;

	FiPixelType = f_PFD_TYPE_RGBA;
	SavedPixelType = PFD_TYPE_RGBA;
	FcColorBits = 32;
	FcDepthBits = 32;
	FSwapBuffers = Auto;

	FOpenGLPrintScale = pglProportional;
	FOpenGLPixelsPerInch = 96;

	// ---- 3D Font Initialization
	Font3DEnabled = false;
	Font3DType = new TFont;
	Font3DType->Name = "Arial";
	Font3DType->Style << fsBold;
	Font3DType->Height = -9;
	Font3DType->Charset = ANSI_CHARSET;
	Font3DFirstGylph = 32;
	Font3DNumGylph = 96;
	Font3DMaxDeviation = 1.0;
	Font3DExtrusion = 0.1;
	Font3DFormat = f_WGL_FONT_POLYGONS;
	SavedFont3DFormat = WGL_FONT_POLYGONS;
	Font3DDefault = NULL;

	// ---- 2D Font Initialization
	Font2DEnabled = false;
	Font2DType = new TFont;
	Font2DType->Name = "Arial";
	Font2DType->Style << fsBold;
	Font2DType->Height = -9;
	Font2DType->Charset = ANSI_CHARSET;
	Font2DFirstGylph = 32;
	Font2DNumGylph = 96;
	Font2DDefault = NULL;

	Ffps_Enabled = false;

	FMultiSample = false;
	arbMultisampleSupported = false;
	FMultiSampleSettings = 4;

	QueryPerformanceFrequency(&Frequency);
}

// ---------------------------------------------------------------------------
__fastcall TOpenGLAPPanel::~TOpenGLAPPanel() {
	// ---- Cleaning up Font Types AP-2003-09-02
	delete Font2DType;
	delete Font3DType;
}

// ---------------------------------------------------------------------------
// don't do anything
static LRESULT CALLBACK MultiSampleOpenGLProc(HWND hWnd, UINT msg,
	WPARAM wParam, LPARAM lParam) {
	return DefWindowProc(hWnd, msg, wParam, lParam);
}

// ===========================================================================
// Name	 	: MultiSamplePixelFormat
// Purpose	 	: Creating a dummy window to determine a pixel format for multi sample
// Return		:
// ---------------------------------------------------------------------------
// 2006-02-24	Created by AP.
// ===========================================================================
int TOpenGLAPPanel::MultiSamplePixelFormat(void) {

	HDC LocalDisplayDeviceContext;
	HGLRC LocalGLRenderingContext;
	int arbMultisampleFormat;
	int PixelFormatIndex;
	int valid;
	WNDCLASSEX wcex;
	UINT numFormats;
	PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB;
	float fAttributes[] = {0, 0};
	int iAttributes[] = {
		WGL_DRAW_TO_WINDOW_ARB, GL_TRUE, WGL_SUPPORT_OPENGL_ARB, GL_TRUE,
		WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB, WGL_COLOR_BITS_ARB, 24,
		WGL_ALPHA_BITS_ARB, 8, WGL_DEPTH_BITS_ARB, 16, WGL_STENCIL_BITS_ARB, 0,
		WGL_DOUBLE_BUFFER_ARB, GL_TRUE, WGL_SAMPLE_BUFFERS_ARB, GL_TRUE,
		WGL_SAMPLES_ARB, FMultiSampleSettings, // number of samples
		0, 0};

	PIXELFORMATDESCRIPTOR pfd = {sizeof(PIXELFORMATDESCRIPTOR), // size
		1, // version 1
		0, // Flags
		SavedPixelType, // Color Mode
		FcColorBits, // Number of color bits
		0, 0, 0, 0, 0, 0, // RGB color bits with shift (not used)
		1, 0, // Alpha bits with shift     (not used)
		FcAccBits, // Number of accumulator bits
		0, 0, 0, 0, // RGBA accumulator bits
		FcDepthBits, // Number of bits for Z-Buffer Depth
		0, // Stencil buffer depth
		0, // Auxiliary buffer depth
		PFD_MAIN_PLANE, // Layer type
		0, // Reserved
		0, // Layer mask
		0, // Visible mask
		0 // Damage mask
	};
	if (FdwFlags.Contains(f_PFD_DRAW_TO_WINDOW))
		pfd.dwFlags |= PFD_DRAW_TO_WINDOW;

	if (FdwFlags.Contains(f_PFD_DRAW_TO_BITMAP))
		pfd.dwFlags |= PFD_DRAW_TO_BITMAP;

	if (FdwFlags.Contains(f_PFD_SUPPORT_GDI))
		pfd.dwFlags |= PFD_SUPPORT_GDI;
	else
		pfd.dwFlags |= PFD_DOUBLEBUFFER;

	if (FdwFlags.Contains(f_PFD_SUPPORT_OPENGL))
		pfd.dwFlags |= PFD_SUPPORT_OPENGL;

	if (FdwFlags.Contains(f_PFD_GENERIC_ACCELERATED))
		pfd.dwFlags |= PFD_GENERIC_ACCELERATED;

	if (FdwFlags.Contains(f_PFD_GENERIC_FORMAT))
		pfd.dwFlags |= PFD_GENERIC_FORMAT;

	if (FdwFlags.Contains(f_PFD_NEED_PALETTE))
		pfd.dwFlags |= PFD_NEED_PALETTE;

	if (FdwFlags.Contains(f_PFD_NEED_SYSTEM_PALETTE))
		pfd.dwFlags |= PFD_NEED_SYSTEM_PALETTE;

	if (FdwFlags.Contains(f_PFD_STEREO))
		pfd.dwFlags |= PFD_STEREO;

	if (FdwFlags.Contains(f_PFD_SWAP_LAYER_BUFFERS))
		pfd.dwFlags |= PFD_SWAP_LAYER_BUFFERS;

	if (FdwFlags.Contains(f_PFD_SUPPORT_COMPOSITION))
		pfd.dwFlags |= PFD_SUPPORT_COMPOSITION;

	try {
		// ---- registering the multi sample window
		ZeroMemory(&wcex, sizeof(wcex));
		wcex.cbSize = sizeof(wcex);
		wcex.style = CS_OWNDC;
		wcex.cbWndExtra = 0; /* space for our pointer */
		wcex.lpfnWndProc = MultiSampleOpenGLProc;
		wcex.hbrBackground = NULL;
		wcex.hInstance = Handle;
		wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
		wcex.lpszClassName = MULTISAMPLE_OPENGL_CLASS;

		if (RegisterClassEx(&wcex) == 0) {
			FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 0,
				SystemMessage, 255, NULL);
			Error (("RegisterClass:" + String(SystemMessage)));
			return (-1);
		}

		// create dummy window
		HWND hMultiSampleWindow = CreateWindowEx(0L, MULTISAMPLE_OPENGL_CLASS,
			(""), WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 0, 0, 1, 1,
			Handle, 0, NULL, NULL);
		if (!hMultiSampleWindow) {
			FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 0,
				SystemMessage, 255, NULL);
			Error ("MultiSample: " + String(SystemMessage));
			return (-1);
		}

		LocalDisplayDeviceContext = GetDC(hMultiSampleWindow);

		if ((PixelFormatIndex = ChoosePixelFormat(LocalDisplayDeviceContext,
			&pfd)) == 0) {
			ReleaseDC(hMultiSampleWindow, DisplayDeviceContext);
			DestroyWindow(hMultiSampleWindow);
			return (-1);
		}
		if (SetPixelFormat(LocalDisplayDeviceContext, PixelFormatIndex,
			&pfd) != true) {
			ReleaseDC(hMultiSampleWindow, LocalDisplayDeviceContext);
			DestroyWindow(hMultiSampleWindow);
			return (-1);
		}
		if ((LocalGLRenderingContext =
			wglCreateContext(LocalDisplayDeviceContext)) == NULL) {
			ReleaseDC(hMultiSampleWindow, LocalDisplayDeviceContext);
			DestroyWindow(hMultiSampleWindow);
			return (-1);
		}
		if (wglMakeCurrent(LocalDisplayDeviceContext, LocalGLRenderingContext)
			== false) {
			wglMakeCurrent(NULL, NULL);
			wglDeleteContext(LocalGLRenderingContext);
			ReleaseDC(hMultiSampleWindow, LocalDisplayDeviceContext);
			return (-1);
		}

		wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC)
			wglGetProcAddress("wglChoosePixelFormatARB");
		if (!wglChoosePixelFormatARB) {
			wglMakeCurrent(NULL, NULL);
			wglDeleteContext(LocalGLRenderingContext);
			ReleaseDC(hMultiSampleWindow, LocalDisplayDeviceContext);
			return (-1);
		}

		if (!wglChoosePixelFormatARB) {
			wglMakeCurrent(NULL, NULL);
			wglDeleteContext(LocalGLRenderingContext);
			ReleaseDC(hMultiSampleWindow, LocalDisplayDeviceContext);
			return (-1);
		}
		// First We Check To See If We Can Get A Pixel Format For specified Samples
		valid = wglChoosePixelFormatARB(LocalDisplayDeviceContext, iAttributes,
			fAttributes, 1, &arbMultisampleFormat, &numFormats);
		// If We Returned True, And Our Format Count Is Greater Than 1
		if (valid && numFormats >= 1) {
			// ok
			wglMakeCurrent(NULL, NULL);
			wglDeleteContext(LocalGLRenderingContext);
			ReleaseDC(hMultiSampleWindow, LocalDisplayDeviceContext);
			DestroyWindow(hMultiSampleWindow);
			return (arbMultisampleFormat);
		}
		// Our Pixel Format With User Samples Failed, Test For 2 Samples
		// Dropping the fall back to 2 samples
		/*
		iAttributes[19] = 2;
		valid = wglChoosePixelFormatARB(LocalDisplayDeviceContext, iAttributes,
			fAttributes, 1, &arbMultisampleFormat, &numFormats);
		// If We Returned True, And Our Format Count Is Greater Than 1
		if (valid && numFormats >= 1) {
			// ok
			wglMakeCurrent(NULL, NULL);
			wglDeleteContext(LocalGLRenderingContext);
			ReleaseDC(hMultiSampleWindow, LocalDisplayDeviceContext);
			DestroyWindow(hMultiSampleWindow);
			return (arbMultisampleFormat);
		}
		*/
		// error
		wglMakeCurrent(NULL, NULL);
		wglDeleteContext(LocalGLRenderingContext);
		ReleaseDC(hMultiSampleWindow, LocalDisplayDeviceContext);
		DestroyWindow(hMultiSampleWindow);
		return (-1);
	}
	catch (Exception &Except) {
		Error (Except.Message);
		return (-1);
	}
}

// ---------------------------------------------------------------------------
// Function    :   CreateGLContext ()
// Description :   Creates the OpenGL Context handles
//
// Author      :   AP
// Date        :   07.01.2002
// Comments    :
// ---------------------------------------------------------------------------
bool TOpenGLAPPanel::CreateGLContext(void) {
	int FirstTickCount;
	int arbMultisampleFormat;
	int PixelFormatIndex;
	PIXELFORMATDESCRIPTOR pfd = {sizeof(PIXELFORMATDESCRIPTOR), // size
		1, // version 1
		0, // Flags
		SavedPixelType, // Color Mode
		FcColorBits, // Number of color bits
		0, 0, 0, 0, 0, 0, // RGB color bits with shift (not used)
		1, 0, // Alpha bits with shift     (not used)
		FcAccBits, // Number of accumulator bits
		0, 0, 0, 0, // RGBA accumulator bits
		FcDepthBits, // Number of bits for Z-Buffer Depth
		0, // Stencil buffer depth
		0, // Auxiliary buffer depth
		PFD_MAIN_PLANE, // Layer type
		0, // Reserved
		0, // Layer mask
		0, // Visible mask
		0 // Damage mask
	};
	if (FdwFlags.Contains(f_PFD_DRAW_TO_WINDOW))
		pfd.dwFlags |= PFD_DRAW_TO_WINDOW;

	if (FdwFlags.Contains(f_PFD_DRAW_TO_BITMAP))
		pfd.dwFlags |= PFD_DRAW_TO_BITMAP;

	if (FdwFlags.Contains(f_PFD_SUPPORT_GDI))
		pfd.dwFlags |= PFD_SUPPORT_GDI;
	else
		pfd.dwFlags |= PFD_DOUBLEBUFFER;

	if (FdwFlags.Contains(f_PFD_SUPPORT_OPENGL))
		pfd.dwFlags |= PFD_SUPPORT_OPENGL;

	if (FdwFlags.Contains(f_PFD_GENERIC_ACCELERATED))
		pfd.dwFlags |= PFD_GENERIC_ACCELERATED;

	if (FdwFlags.Contains(f_PFD_GENERIC_FORMAT))
		pfd.dwFlags |= PFD_GENERIC_FORMAT;

	if (FdwFlags.Contains(f_PFD_NEED_PALETTE))
		pfd.dwFlags |= PFD_NEED_PALETTE;

	if (FdwFlags.Contains(f_PFD_NEED_SYSTEM_PALETTE))
		pfd.dwFlags |= PFD_NEED_SYSTEM_PALETTE;

	if (FdwFlags.Contains(f_PFD_STEREO))
		pfd.dwFlags |= PFD_STEREO;

	if (FdwFlags.Contains(f_PFD_SWAP_LAYER_BUFFERS))
		pfd.dwFlags |= PFD_SWAP_LAYER_BUFFERS;

	arbMultisampleSupported = false;
	if (FMultiSample) {
		arbMultisampleFormat = MultiSamplePixelFormat();
		if (arbMultisampleFormat != -1) {
			PixelFormatIndex = arbMultisampleFormat;
			arbMultisampleSupported = true;
		}
		else {
			FMultiSample = false; // ---- Not supported
		}
	}

	DisplayDeviceContext = GetDC(this->Handle);

	if (!arbMultisampleSupported) {
		if ((PixelFormatIndex = ChoosePixelFormat(DisplayDeviceContext,
			&pfd)) == 0) {
			FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 0,
				SystemMessage, 255, NULL);
			Error("ChoosePixelFormat:" + String(SystemMessage));
			ReleaseDC(this->Handle, DisplayDeviceContext);
			return false;
		}
	}
	if (SetPixelFormat(DisplayDeviceContext, PixelFormatIndex, &pfd) != true) {
		FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 0,
			SystemMessage, 255, NULL);
		Error("SetPixelFormat:" + String(SystemMessage));
		ReleaseDC(this->Handle, DisplayDeviceContext);
		return false;
	}
	// ----
	DoubleBuffer = (pfd.dwFlags & PFD_DOUBLEBUFFER) ? true : false;
	// ----
	DescribePixelFormat(DisplayDeviceContext, PixelFormatIndex,
		sizeof(PIXELFORMATDESCRIPTOR), &pfd);
	dpf = pfd;
	if (dpf.dwFlags & PFD_NEED_PALETTE) {
		if (ConstructPalette(dpf) == false) {
			ReleaseDC(this->Handle, DisplayDeviceContext);
			return false;
		}
	}
	else {
		Palette = NULL;
	}

	if ((GLRenderingContext = wglCreateContext(DisplayDeviceContext)) == NULL) {
		FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 0,
			SystemMessage, 255, NULL);
		Error("wglRenderingContext:" + String(SystemMessage));
		if (Palette) {
			SelectPalette(DisplayDeviceContext,
				GetStockObject(DEFAULT_PALETTE), false);
			if (DeleteObject(Palette) == false) {
				Error("DeleteObject: Failed");
			}
		}
		ReleaseDC(this->Handle, DisplayDeviceContext);
		return false;
	}

	// ---- Activate this panel
	if (MakeOpenGLPanelCurrent() == false) {
		return false;
	}

	return true;
}

// ---------------------------------------------------------------------------
void TOpenGLAPPanel::DestroyGLContext(void) {
	if (Font3DDefault) {
		delete Font3DDefault;
	}
	if (Font2DDefault) {
		delete Font2DDefault;
	}
	wglMakeCurrent(NULL, NULL);
	if (GLRenderingContext != NULL) {
		if (wglDeleteContext(GLRenderingContext) == false) {
			FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 0,
				SystemMessage, 255, NULL);
			Error("wglDeleteContext:" + String(SystemMessage));
		}
		if ((Palette) && (DeleteObject(Palette) == false)) {
			FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 0,
				SystemMessage, 255, NULL);
			Error("DeleteObject Failed:" + String(SystemMessage));
		}
	}
	// ---- Deleting DisplayDeviceContext AP.2003.09.02
	if (DisplayDeviceContext != NULL) {
		if (ReleaseDC(Handle, DisplayDeviceContext) == 0) {
			FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 0,
				SystemMessage, 255, NULL);
			Error("ReleaseDC Failed:" + String(SystemMessage));
		}
	}
	return;
}

// ---------------------------------------------------------------------------
bool TOpenGLAPPanel::ConstructPalette(PIXELFORMATDESCRIPTOR &pfd) {
	LOGPALETTE *LPalettep;
	WORD NumColors = (WORD)(1 << pfd.cColorBits);

	if ((LPalettep = (LOGPALETTE*)malloc(sizeof(LOGPALETTE) +
		(sizeof(PALETTEENTRY) * NumColors))) == NULL) {
		Error("ConstructPalette: Malloc failure");
		return false;
	}
	LPalettep->palVersion = 0x300;
	LPalettep->palNumEntries = NumColors;

	int RedMask = (1 << pfd.cRedBits) - 1;
	int GreenMask = (1 << pfd.cGreenBits) - 1;
	int BlueMask = (1 << pfd.cBlueBits) - 1;

	for (int i = 0; i < NumColors; i++) {
		LPalettep->palPalEntry[i].peRed =
			(BYTE)((((i >> pfd.cRedShift) & RedMask) * 255) / RedMask);
		LPalettep->palPalEntry[i].peGreen =
			(BYTE)((((i >> pfd.cGreenShift) & GreenMask) * 255) / GreenMask);
		LPalettep->palPalEntry[i].peBlue =
			(BYTE)((((i >> pfd.cBlueShift) & BlueMask) * 255) / BlueMask);
	}
	if ((Palette = CreatePalette(LPalettep)) == NULL) {
		FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 0,
			SystemMessage, 255, NULL);
		Error("CreatePalette:" + String(SystemMessage));
		return false;
	}
	free(LPalettep);
	if (Palette) {
		SelectPalette(DisplayDeviceContext, Palette, FALSE);
		RealizePalette(DisplayDeviceContext);
	}
	return true;
}

// ---------------------------------------------------------------------------
void TOpenGLAPPanel::Create3DFont(void) {
	if (Font3DDefault) {
		delete Font3DDefault;
	}
	Font3DDefault = Create3DFont(Font3DType, Font3DFirstGylph, Font3DNumGylph,
		Font3DMaxDeviation, Font3DExtrusion, SavedFont3DFormat);
}

// ---------------------------------------------------------------------------
void TOpenGLAPPanel::Create2DFont(void) {
	if (Font2DDefault) {
		delete Font2DDefault;
	}
	Font2DDefault = Create2DFont(Font2DType, Font2DFirstGylph, Font2DNumGylph);
}

// ---------------------------------------------------------------------------
// Private Methods (Property)
// ---------------------------------------------------------------------------
void __fastcall TOpenGLAPPanel::SetPFDPixelType(const TPFDPixelTypes Value) {
	if (Value == f_PFD_TYPE_RGBA) {
		SavedPixelType = PFD_TYPE_RGBA;
	}
	else {
		SavedPixelType = PFD_TYPE_COLORINDEX;
	}
	FiPixelType = Value;
	// return;
}

// ---------------------------------------------------------------------------
void __fastcall TOpenGLAPPanel::SetToolWindow(const bool Value) {
	int NewStyle;

	FToolWindow = Value;

	if (!AllowCreateParms)
		return;
	if (FToolWindow) {
		SetWindowLong(Handle, GWL_STYLE, GetWindowLong(Handle,
			GWL_STYLE) | WS_CAPTION);
		SetWindowLong(Handle, GWL_EXSTYLE, GetWindowLong(Handle,
			GWL_EXSTYLE) | WS_EX_TOOLWINDOW);
	}
	else {
		SetWindowLong(Handle, GWL_STYLE, GetWindowLong(Handle,
			GWL_STYLE) &~WS_CAPTION);
		SetWindowLong(Handle, GWL_EXSTYLE, GetWindowLong(Handle,
			GWL_EXSTYLE) &~WS_EX_TOOLWINDOW);
	}
	// ---- Updating the control
	Width += 1;
	Width -= 1;
}

// ---------------------------------------------------------------------------
void __fastcall TOpenGLAPPanel::SetBorder(const bool Value) {
	FBorder = Value;

	if (!AllowCreateParms)
		return;
	if (FBorder) {
		SetWindowLong(Handle, GWL_EXSTYLE, GetWindowLong(Handle,
			GWL_EXSTYLE) | WS_EX_CLIENTEDGE);
	}
	else {
		SetWindowLong(Handle, GWL_EXSTYLE, GetWindowLong(Handle,
			GWL_EXSTYLE) &~WS_EX_CLIENTEDGE);
	}
	// ---- Updating the control
	SetWindowPos(Handle, 0, Left, Top, Width, Height, SWP_DRAWFRAME);
}

// ---------------------------------------------------------------------------
void __fastcall TOpenGLAPPanel::SetAllowResize(const bool Value) {
	FAllowResize = Value;

	if (!AllowCreateParms)
		return;
	if (FAllowResize) {
		SetWindowLong(Handle, GWL_STYLE, GetWindowLong(Handle,
			GWL_STYLE) | WS_SIZEBOX);
	}
	else {
		SetWindowLong(Handle, GWL_STYLE, GetWindowLong(Handle,
			GWL_STYLE) &~WS_SIZEBOX);
	}
	// ---- Updating the control
	SetWindowPos(Handle, 0, Left, Top, Width, Height, SWP_DRAWFRAME);
}

// ---------------------------------------------------------------------------
void __fastcall TOpenGLAPPanel::SetPFDFlags(const TPFDFlagsSet Value) {
	TPFDFlagsSet Temp = Value;

	if ((Value.Contains(f_PFD_SUPPORT_GDI)) &&
		(Value.Contains(f_PFD_DOUBLEBUFFER))) {
		if (FdwFlags.Contains(f_PFD_SUPPORT_GDI)) {
			Temp >> f_PFD_SUPPORT_GDI;
		}
		else {
			Temp >> f_PFD_DOUBLEBUFFER;
		}
	}
	else {
		if ((!Value.Contains(f_PFD_SUPPORT_GDI)) &&
			(!Value.Contains(f_PFD_DOUBLEBUFFER))) {
			if (FdwFlags.Contains(f_PFD_SUPPORT_GDI)) {
				Temp << f_PFD_DOUBLEBUFFER;
			}
			else {
				Temp << f_PFD_SUPPORT_GDI;
			}
		}
	}
	FdwFlags = Temp;
	return;
}

// ---------------------------------------------------------------------------
// Function    :   SetPFDNumAccBits ()
// Description :   Setting the number of accumulator bits
//
// Author      :   AP
// Date        :   07.01.2002
// Comments    :
// ---------------------------------------------------------------------------
void __fastcall TOpenGLAPPanel::SetPFDNumAccBits(const BYTE Value) {
	FcAccBits = Value;
}

// --------------------------------------------------------------------------
void __fastcall TOpenGLAPPanel::SetPFDNumColorBits(const BYTE Value) {
	FcColorBits = Value;
}

// ---------------------------------------------------------------------------
void __fastcall TOpenGLAPPanel::SetPFDNumDepthBits(const BYTE Value) {
	FcDepthBits = Value;
}

// ---------------------------------------------------------------------------
void __fastcall TOpenGLAPPanel::SetOpenGLPrintScale
	(const TOpenGLPrintScale Value) {
	FOpenGLPrintScale = Value;
}

// ---------------------------------------------------------------------------
void __fastcall TOpenGLAPPanel::SetOpenGLPixelsPerInch(const int Value) {
	FOpenGLPixelsPerInch = Value;
}

// ---------------------------------------------------------------------------
void __fastcall TOpenGLAPPanel::SetFont3DType(TFont* Value) {
	Font3DType->Assign(Value);
	return;
}

// ---------------------------------------------------------------------------
void __fastcall TOpenGLAPPanel::SetFont3DFormat(const TFont3DFormat Value) {
	if (Value == f_WGL_FONT_LINES)
		SavedFont3DFormat = f_WGL_FONT_LINES;
	else
		SavedFont3DFormat = WGL_FONT_POLYGONS;
	Font3DFormat = Value;
	return;
}

// ---------------------------------------------------------------------------
void __fastcall TOpenGLAPPanel::SetFont(TFont* Value) {
	Font2DType->Assign(Value);
	return;
}

// ---------------------------------------------------------------------------
// Protected Methods
// ---------------------------------------------------------------------------
void __fastcall TOpenGLAPPanel::WMCreate(TWMCreate &Message) {
	try {
		if (!CreateGLContext()) {
			Error("Open GL Context Create Failed");
			return;
		}

		if (Font3DEnabled)
			Create3DFont();
		if (Font2DEnabled)
			Create2DFont();

		SetToolWindow(FToolWindow);
		SetAllowResize(FAllowResize);
		SetBorder(FBorder);

		if (FOnInit) {
			FOnInit(this);
		}
	}
	catch (...) {
	}

	return;
}

// ---------------------------------------------------------------------------
void __fastcall TOpenGLAPPanel::WMPaletteChanged(TWMPaletteChanged &Message) {
	TWMQueryNewPalette Dummy;
	if (Message.PalChg == Handle)
		return;
	WMQueryNewPalette(Dummy);
	return;
}

// ---------------------------------------------------------------------------
void __fastcall TOpenGLAPPanel::WMQueryNewPalette(TWMQueryNewPalette &Message) {
	UINT num;
	HPALETTE OldPalette = SelectPalette(DisplayDeviceContext, Palette, FALSE);
	if ((num = RealizePalette(DisplayDeviceContext)) == GDI_ERROR) {
		FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 0,
			SystemMessage, 255, NULL);
		Error("RealizePalette:" + String(SystemMessage));
		return;
	}
	if (MakeOpenGLPanelCurrent() == false) {
		Message.Result = 0;
		return;
	}
	wglMakeCurrent(NULL, NULL);
	SelectPalette(GLRenderingContext, OldPalette, TRUE);
	if (num)
		InvalidateRect(Handle, NULL, TRUE); // Force Repaint
	Message.Result = num;
	return;
}

// ---------------------------------------------------------------------------
void __fastcall TOpenGLAPPanel::WMDestroy(TWMDestroy &Message) {
	if (!ComponentState.Contains(csDesigning)) {
		DestroyGLContext();
	}
	TCustomPanel::Dispatch(&Message);
}

// ---------------------------------------------------------------------------
void __fastcall TOpenGLAPPanel::WMEraseBkgnd(TWMEraseBkgnd &Message) {
	if (ComponentState.Contains(csDesigning)) {
		TCustomPanel::Dispatch(&Message);
	}
}

// ---------------------------------------------------------------------------
void __fastcall TOpenGLAPPanel::WMMove(TWMMove &Message) {
	if (!FAllowMoving) {
		ReleaseCapture();
	}
}

// ---------------------------------------------------------------------------
void __fastcall TOpenGLAPPanel::CreateParams(TCreateParams &Params) {
	TCustomPanel::CreateParams(Params);

	Params.Style |= (WS_CLIPCHILDREN | WS_CLIPSIBLINGS);
	Params.WindowClass.style |= CS_OWNDC; // Place Holder

	AllowCreateParms = true;

	if (FOnPreInit)
		FOnPreInit(this);
}

// ---------------------------------------------------------------------------
void __fastcall TOpenGLAPPanel::Error (UnicodeString LastError) {
	if (FOnError) {
		FOnError(LastError);
	}
}

// ---------------------------------------------------------------------------
void __fastcall TOpenGLAPPanel::Paint(void) {
	HPALETTE OldPalette;
	LARGE_INTEGER StartTime;
	LARGE_INTEGER StopTime;

	if (ComponentState.Contains(csDesigning)) {
		return;
	}

	if (Ffps_Enabled)
		QueryPerformanceCounter(&StartTime);

	if (FOnPaint) {
		if (Palette) {
			OldPalette = SelectPalette(DisplayDeviceContext, Palette, FALSE);
			if (!OldPalette) {
				FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(),
					0, SystemMessage, 255, NULL);
				Error("SelectPalette:" + String(SystemMessage));
				return;
			}
			if (RealizePalette(DisplayDeviceContext) == GDI_ERROR) {
				FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(),
					0, SystemMessage, 255, NULL);
				Error("RealizePalette:" + String(SystemMessage));
				return;
			}
		}
		FOnPaint(this);
		if ((FSwapBuffers == Auto) && (DoubleBuffer)) {
			// With multithread applications, flush the drawing commands in any other threads drawing to the same window before calling SwapBuffers
			glFinish();
			::SwapBuffers(DisplayDeviceContext);
		}
		if (Palette) {
			SelectPalette(DisplayDeviceContext, OldPalette, TRUE);
		}
	}
	if (Ffps_Enabled) {
		QueryPerformanceCounter(&StopTime);
		Fps = 1. / (((float)StopTime.QuadPart - (float)StartTime.QuadPart) /
			(float)Frequency.QuadPart);
	}
}

// ---------------------------------------------------------------------------
void __fastcall TOpenGLAPPanel::Resize(void) {
	if (FOnResize) {
		FOnResize(this);
	}
}

// ---------------------------------------------------------------------------
// Public Methods
// ---------------------------------------------------------------------------
bool __fastcall TOpenGLAPPanel::MakeOpenGLPanelCurrent(void) {
	if (GLRenderingContext == wglGetCurrentContext()
		&& DisplayDeviceContext == wglGetCurrentDC())
		return true;
	if (wglMakeCurrent(DisplayDeviceContext, GLRenderingContext) == false) {
		FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 0,
			SystemMessage, 255, NULL);
		Error("wglMakeCurrent:" + String(SystemMessage));
		return false;
	}
	return true;
}

// ---------------------------------------------------------------------------
void __fastcall TOpenGLAPPanel::MakeOpenGLPanelNotCurrent(void) {
	wglMakeCurrent(NULL, NULL);
}

// ---------------------------------------------------------------------------
void __fastcall TOpenGLAPPanel::SwapBuffers(void) {
	if (DoubleBuffer)
		::SwapBuffers(DisplayDeviceContext);
}

// ---------------------------------------------------------------------------
bool __fastcall TOpenGLAPPanel::PanelShareLists(TOpenGLAPPanel *WithOpenGLPanel)
{
	return (wglShareLists(this->GLRenderingContext,
		WithOpenGLPanel->GLRenderingContext));
}

// ---------------------------------------------------------------------------
BMPTexture * __fastcall TOpenGLAPPanel::LoadBMPTexture(UnicodeString FileName,
	Boolean WithAlpha) {
	Graphics::TBitmap * Bitmap = new Graphics::TBitmap();
	BMPTexture * BMPTex = new BMPTexture(); ;
	try {
		int Onedarray;

		Bitmap->LoadFromFile(FileName);
		if (WithAlpha)
			BMPTex->Components = 4;
		else
			BMPTex->Components = 3;
		BMPTex->Width = Bitmap->Width;
		BMPTex->Height = Bitmap->Height;
		BMPTex->Format = WithAlpha ? GL_RGBA : GL_RGB;
		BMPTex->Type = GL_UNSIGNED_BYTE;
		BMPTex->Pixels =
			new GLubyte[BMPTex->Width * BMPTex->Height * BMPTex->Components];
		for (long i = 0; i < BMPTex->Height; i++)
			for (long j = 0; j < BMPTex->Width; j++) {
				Onedarray = i * BMPTex->Width * BMPTex->Components +
					j * BMPTex->Components;
				BMPTex->Pixels[Onedarray + 0] =
					GetRValue(Bitmap->Canvas->Pixels[j][BMPTex->Height -
					i - 1]);
				BMPTex->Pixels[Onedarray + 1] =
					GetGValue(Bitmap->Canvas->Pixels[j][BMPTex->Height -
					i - 1]);
				BMPTex->Pixels[Onedarray + 2] =
					GetBValue(Bitmap->Canvas->Pixels[j][BMPTex->Height -
					i - 1]);
				if (WithAlpha)
					BMPTex->Pixels[Onedarray + 3] = 255;
			}
	}
	catch (...) {
		delete Bitmap;
		delete BMPTex;
		return (NULL);
	}
	delete Bitmap;
	return (BMPTex);

}

//
// ---------------------------------------------------------------------------
// Function    :   LoadTGA ()
// Description :   Loads a Truevision Targa image file
// Input       :
// Output      :
// Return Code :   0 : OK
// -1 : Failed to open file.
// -2 : Targa file version
// -3 : Targa color map not of 24/32 bit type
//
// Author      :   AP
// Date        :   15.08.2000
// Comments    :   Remember to release "pixels" allocation
// ---------------------------------------------------------------------------
int __fastcall TOpenGLAPPanel::LoadTGA(UnicodeString FileName, // ---- Filename
	byte **pixels, unsigned int *width, unsigned int *height,
	unsigned int *format) {
	int columns, rows, numPixels;
	byte *ppixbuf;
	int row, column;
	FILE *pfile;
	byte *ptarga_rgba;
	targaHeader targa_header;
	unsigned char red, green, blue, alphabyte;
	unsigned char packetHeader, packetSize, jj;

	pfile = _wfopen(FileName.c_str(), L"rb");
	if (!pfile) {
		return (-3);
	}

	// read targa header
	fread(&targa_header.id_length, sizeof(unsigned char), 1, pfile);
	fread(&targa_header.colormap_type, sizeof(unsigned char), 1, pfile);
	fread(&targa_header.image_type, sizeof(unsigned char), 1, pfile);

	fread(&targa_header.colormap_index, sizeof(unsigned short), 1, pfile);
	fread(&targa_header.colormap_length, sizeof(unsigned short), 1, pfile);

	fread(&targa_header.colormap_size, sizeof(unsigned char), 1, pfile);

	fread(&targa_header.x_origin, sizeof(unsigned short), 1, pfile);
	fread(&targa_header.y_origin, sizeof(unsigned short), 1, pfile);
	fread(&targa_header.width, sizeof(unsigned short), 1, pfile);
	fread(&targa_header.height, sizeof(unsigned short), 1, pfile);

	fread(&targa_header.pixel_size, sizeof(unsigned char), 1, pfile);
	fread(&targa_header.attributes, sizeof(unsigned char), 1, pfile);

	// ---- Verifying version, only type 2 and 10 targa images supported
	if (targa_header.image_type != 2 && targa_header.image_type != 10) {
		// ----
		return (-1);
	}

	// ---- Checking color map types, only 24/32 bit supported
	if (targa_header.colormap_type != 0 ||
		(targa_header.pixel_size != 32 && targa_header.pixel_size != 24)) {
		return (-2);
	}

	*format = targa_header.pixel_size;

	columns = targa_header.width;
	rows = targa_header.height;
	numPixels = columns * rows;

	if (width)
		* width = columns;

	if (height)
		* height = rows;

	ptarga_rgba = (unsigned char*)malloc(numPixels * 4);
	// new unsigned char[numPixels*4];
	*pixels = ptarga_rgba;

	if (targa_header.id_length != 0) {
		fseek(pfile, targa_header.id_length, SEEK_CUR);
		// skip TARGA image comment
	}

	switch (targa_header.image_type) {
	case 2:
			// Uncompressed, RGB images
		for (row = rows - 1; row >= 0; row--) {
			ppixbuf = ptarga_rgba + row * columns * 4;
			for (column = 0; column < columns; column++) {
				switch (targa_header.pixel_size) {
				case 24:
					blue = getc(pfile);
					green = getc(pfile);
					red = getc(pfile);
					*ppixbuf++ = red;
					*ppixbuf++ = green;
					*ppixbuf++ = blue;
					*ppixbuf++ = 255;
					break;
				case 32:
					blue = getc(pfile);
					green = getc(pfile);
					red = getc(pfile);
					alphabyte = getc(pfile);
					*ppixbuf++ = red;
					*ppixbuf++ = green;
					*ppixbuf++ = blue;
					*ppixbuf++ = alphabyte;
					break;
				}
			}
		}
		break;
	case 10:
			// Runlength encoded RGB images
		for (row = rows - 1; row >= 0; row--) {
			ppixbuf = ptarga_rgba + row * columns * 4;
			for (column = 0; column < columns;) {
				packetHeader = getc(pfile);
				packetSize = 1 + (packetHeader & 0x7f);
				if (packetHeader & 0x80) {
					// run-length packet
					switch (targa_header.pixel_size) {
					case 24:
						blue = getc(pfile);
						green = getc(pfile);
						red = getc(pfile);
						alphabyte = 255;
						break;

					case 32:
						blue = getc(pfile);
						green = getc(pfile);
						red = getc(pfile);
						alphabyte = getc(pfile);
						break;
					}
					for (jj = 0; jj < packetSize; jj++) {
						*ppixbuf++ = red;
						*ppixbuf++ = green;
						*ppixbuf++ = blue;
						*ppixbuf++ = alphabyte;
						column++;
						if (column == columns) {
							// run spans across rows
							column = 0;
							if (row > 0)
								row--;
							else
								goto breakOut;
							ppixbuf = ptarga_rgba + row * columns * 4;
						}
					}
				}
				else {
					// non run-length packet
					for (jj = 0; jj < packetSize; jj++) {
						switch (targa_header.pixel_size) {
						case 24:
							blue = getc(pfile);
							green = getc(pfile);
							red = getc(pfile);
							*ppixbuf++ = red;
							*ppixbuf++ = green;
							*ppixbuf++ = blue;
							*ppixbuf++ = 255;
							break;

						case 32:
							blue = getc(pfile);
							green = getc(pfile);
							red = getc(pfile);
							alphabyte = getc(pfile);
							*ppixbuf++ = red;
							*ppixbuf++ = green;
							*ppixbuf++ = blue;
							*ppixbuf++ = alphabyte;
							break;
						}
						column++;
						if (column == columns) {
							// pixel packet run spans across rows
							column = 0;
							if (row > 0)
								row--;
							else
								goto breakOut;
							ppixbuf = ptarga_rgba + row * columns * 4;
						}
					}
				}
			}
		breakOut: ;
		}

	}

	fclose(pfile);
	return 0;
}

// ---------------------------------------------------------------------------
void __fastcall TOpenGLAPPanel::PrinTOpenGLAPPanel(void) {
	Graphics::TBitmap *BitMap = new Graphics::TBitmap();
	unsigned int BitMapInfoSize, BitMapImageSize, XPixelsPerInch,
		YPixelsPerInch, XSizeInPixels, YSizeInPixels;
	void *BitMapImage;
	BITMAPINFO *BitMapInfo;
	RECT Rect;

	BitMap->Height = Height;
	BitMap->Width = Width;
	BringToFront();
	Paint();
	BitMap->Canvas->CopyRect(ClientRect, Canvas, ClientRect);
	XPixelsPerInch = GetDeviceCaps(Printer()->Handle, LOGPIXELSX);
	YPixelsPerInch = GetDeviceCaps(Printer()->Handle, LOGPIXELSY);

	Rect.left = 0.18 * XPixelsPerInch;
	Rect.top = 0.18 * YPixelsPerInch;

	if (FOpenGLPrintScale == pglPrintToFit) {
		XSizeInPixels = GetDeviceCaps(Printer()->Handle, HORZRES) -
			2 * Rect.left;
		YSizeInPixels = GetDeviceCaps(Printer()->Handle, VERTRES) -
			2 * Rect.top;
		if (XSizeInPixels > YSizeInPixels) {
			Rect.right = BitMap->Width * (YSizeInPixels / BitMap->Height);
			Rect.bottom = YSizeInPixels;
		}
		else {
			Rect.right = XSizeInPixels;
			Rect.bottom = BitMap->Height * (XSizeInPixels / BitMap->Width);
		}
	}
	else if (FOpenGLPrintScale == pglProportional) {
		if (FOpenGLPixelsPerInch <= 0)
			FOpenGLPixelsPerInch = 96;

		Rect.right =
			(int)(((float)BitMap->Width / (float)FOpenGLPixelsPerInch) * (float)
			XPixelsPerInch) + Rect.left;
		Rect.bottom =
			(int)(((float)BitMap->Height / (float)FOpenGLPixelsPerInch) *
			(float)YPixelsPerInch) + Rect.top;
	}
	else {
		Rect.right = BitMap->Width + Rect.left;
		Rect.bottom = BitMap->Height + Rect.right;
	}

	GetDIBSizes(BitMap->Handle, BitMapInfoSize, BitMapImageSize);
	BitMapInfo = (BITMAPINFO*)malloc(BitMapInfoSize);
	BitMapImage = malloc(BitMapImageSize);
	if ((BitMapInfo == NULL) || (BitMapImage == NULL)) {
		Error("Print: Malloc Error");
		if (BitMapInfo)
			free(BitMapInfo);
		if (BitMapImage)
			free(BitMapImage);
		return;
	}
	Printer()->BeginDoc();
	GetDIB(BitMap->Handle, BitMap->Palette, BitMapInfo, BitMapImage);
	StretchDIBits(Printer()->Canvas->Handle, Rect.left, Rect.top,
		Rect.right - Rect.left, Rect.bottom - Rect.top, 0, 0, BitMap->Width,
		BitMap->Height, BitMapImage, BitMapInfo, DIB_RGB_COLORS, SRCCOPY);
	free(BitMapInfo);
	free(BitMapImage);
	Printer()->EndDoc();
	delete BitMap;
	return;
}

// ---------------------------------------------------------------------------
void __fastcall TOpenGLAPPanel::SaveOpenGLPanelToFile
	(const UnicodeString FileName) {
	Graphics::TBitmap *BitMap = new Graphics::TBitmap();
	BitMap->Height = Height;
	BitMap->Width = Width;
	BringToFront();
	Paint();
	BitMap->Canvas->CopyRect(ClientRect, Canvas, ClientRect);
	BitMap->SaveToFile(FileName);
	delete BitMap;
}

// ---------------------------------------------------------------------------
OpenGLAPFont3D * __fastcall TOpenGLAPPanel::Create3DFont(TFont *Font,
	int FirstGylph, int NumGylph, float Deviation, float Extrusion, int Format)
{
	HFONT HFont;
	LOGFONT LogFont;
	GLuint Base;
	OpenGLAPFont3D *Font3D;

	if (Font == NULL)
		return (NULL);
	if ((Base = glGenLists(NumGylph)) == 0)
		return (NULL);

	Font3D = new OpenGLAPFont3D();

	Font3D->FirstGylph = FirstGylph;
	Font3D->NumGylph = NumGylph;
	Font3D->ListBase = Base;
	Font3D->Extrusion = Extrusion;
	Font3D->GlyphMetrics = new GLYPHMETRICSFLOAT[NumGylph];
	GetObject(Font->Handle, sizeof(LOGFONT), &LogFont);
	HFont = CreateFontIndirect(&LogFont);
	SelectObject(DisplayDeviceContext, HFont);
	wglUseFontOutlines(DisplayDeviceContext, FirstGylph, NumGylph, Base,
		Deviation, Extrusion, Format, Font3D->GlyphMetrics);
	DeleteObject(HFont);
	return (Font3D);
}

// ---------------------------------------------------------------------------
void __fastcall TOpenGLAPPanel::Get3DTextSize(char *Text, GLdouble *XYZ) {
	Get3DTextSize(Font3DDefault, Text, XYZ);
}

// ---------------------------------------------------------------------------
void __fastcall TOpenGLAPPanel::Get3DTextSize(OpenGLAPFont3D *Font, char *Text,
	GLdouble *XYZ) {
	int ix, Index, Length;

	Length = strlen(Text);

	XYZ[0] = 0.0;
	XYZ[1] = 0.0;
	XYZ[2] = 0.0;
	if ((Font == NULL) || (XYZ == NULL) || (Length == 0))
		return;
	XYZ[2] = Font->Extrusion;

	for (ix = 0; ix < Length; ix++) {
		Index = (unsigned char)Text[ix] - Font->FirstGylph;
		if ((Index < 0) || (Index > Font->NumGylph - 1)) {
			XYZ[0] = XYZ[1] = XYZ[2] = 0.0;
			return;
		}
		// ---- Using gmfCellIncX to increase size by each character length
		XYZ[0] += Font->GlyphMetrics[Index].gmfCellIncX;
		// ---- gmfBlackBoxY to collect correct height
		XYZ[1] = max(XYZ[1], (GLdouble)Font->GlyphMetrics[Index].gmfBlackBoxY);
	}
}

// ---------------------------------------------------------------------------
void __fastcall TOpenGLAPPanel::Draw3DText(char *Text) {
	Draw3DText(Font3DDefault, Text);
}

// ---------------------------------------------------------------------------
void __fastcall TOpenGLAPPanel::Draw3DText(OpenGLAPFont3D *Font, char * Text) {
	GLint FaceMode;
	int Index, Length;

	Length = strlen(Text);
	if ((Font == NULL) || (Length == 0))
		return;
	for (int i = 0; i < Length; i++) {
		Index = (unsigned char)Text[i] - Font->FirstGylph;
		if ((Index < 0) || (Index > Font->NumGylph - 1))
			return;
	}
	glPushAttrib(GL_LIST_BIT);
	glGetIntegerv(GL_FRONT_FACE, &FaceMode);
	glListBase(Font->ListBase - Font->FirstGylph);
	glCallLists(Length, GL_UNSIGNED_BYTE, Text);
	glFrontFace(FaceMode);
	glPopAttrib();
	return;
}

// ---------------------------------------------------------------------------
OpenGLAPFont2D * __fastcall TOpenGLAPPanel::Create2DFont(TFont *Font,
	int FirstGylph, int NumGylph) {
	HFONT HFont;
	LOGFONT LogFont;
	GLuint Base;

	if (Font == NULL)
		return (NULL);
	if ((Base = glGenLists(NumGylph)) == 0)
		return (NULL);
	OpenGLAPFont2D *Font2D = new OpenGLAPFont2D();
	Font2D->FirstGylph = FirstGylph;
	Font2D->NumGylph = NumGylph;
	Font2D->ListBase = Base;
	GetObject(Font->Handle, sizeof(LOGFONT), &LogFont);
	// FYI To go from LogFont -> Handle use:
	// Font->Handle =CreateFontIndirect(LogFont);
	HFont = CreateFontIndirect(&LogFont);
	SelectObject(DisplayDeviceContext, HFont);
	wglUseFontBitmaps(DisplayDeviceContext, FirstGylph, NumGylph, Base);
	DeleteObject(HFont);
	return (Font2D);
}

// ---------------------------------------------------------------------------
// Hint : Use glRasterPos3f() instead of glTranslatef
// to position 2d text.
// ---------------------------------------------------------------------------
void __fastcall TOpenGLAPPanel::Draw2DText(char *Text) {
	Draw2DText(Font2DDefault, Text);
}

// ---------------------------------------------------------------------------
void __fastcall TOpenGLAPPanel::Draw2DText(OpenGLAPFont2D *Font, char *Text) {
	int Index, Length;

	Length = strlen(Text);

	if ((Font == NULL) || (Length == 0))
		return;
	for (int i = 0; i < Length; i++) {
		Index = (unsigned char)Text[i] - Font->FirstGylph;
		if ((Index < 0) || (Index > Font->NumGylph - 1))
			return;
	}
	glPushAttrib(GL_LIST_BIT);
	glListBase(Font->ListBase - Font->FirstGylph);
	glCallLists(Length, GL_UNSIGNED_BYTE, Text);
	glPopAttrib();
	return;
}

// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
bool __fastcall TOpenGLAPPanel::QueryOpenGLExtention
	(UnicodeString ExtentionName) {
	char *GLExtentions;

	GLExtentions = glGetString(GL_EXTENSIONS);
	// ---- no extensions reported
	if (!GLExtentions) {
		return false;
	}
	// ---- search for specific extension
	if (UnicodeString(GLExtentions).Pos(ExtentionName) > 0) {
		return true;
	}
	return false;
}

// ---------------------------------------------------------------------------
int __fastcall TOpenGLAPPanel::GetDisplayBPP(void) {
	int BPP;
	HDC DeviceContext = GetDC(NULL);
	BPP = GetDeviceCaps(DeviceContext, BITSPIXEL);
	ReleaseDC(NULL, DeviceContext);
	return (BPP);
}

// ---------------------------------------------------------------------------
bool __fastcall TOpenGLAPPanel::SetDisplayBPP(int BPP) {
	// Warning:: this code does not work correctly
	// It was intended to be executed in the OnPreInit Event
	// The display mode changes but then GL Initializaion Fails??
	DEVMODE LPDevMode;
	ZeroMemory(&LPDevMode, sizeof(LPDevMode));
	LPDevMode.dmSize = sizeof(LPDevMode);
	LPDevMode.dmBitsPerPel = BPP;
	LPDevMode.dmFields = DM_BITSPERPEL;
	return (ChangeDisplaySettings(&LPDevMode, 0) == DISP_CHANGE_SUCCESSFUL ?
		true : false);
}
