mirror of
https://github.com/raysan5/raylib.git
synced 2026-02-20 20:49:17 -05:00
Compare commits
13 Commits
c0829bc69e
...
efda35b309
| Author | SHA1 | Date | |
|---|---|---|---|
| efda35b309 | |||
| 3b647c85e1 | |||
| b39cc6bce7 | |||
| 7e59e1d93d | |||
| 84f75785ee | |||
| f190c6a4d4 | |||
| 9861baf4b7 | |||
| e67dc15a52 | |||
| f6910bc1e0 | |||
| a654beb565 | |||
| eba1fca933 | |||
| 49cd2ddaa1 | |||
| c4baa5b81d |
@ -148,6 +148,7 @@ int main(void)
|
|||||||
//--------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------
|
||||||
UnloadRenderTexture(observerTarget);
|
UnloadRenderTexture(observerTarget);
|
||||||
UnloadRenderTexture(subjectTarget);
|
UnloadRenderTexture(subjectTarget);
|
||||||
|
|
||||||
CloseWindow(); // Close window and OpenGL context
|
CloseWindow(); // Close window and OpenGL context
|
||||||
//--------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|||||||
279
src/external/win32_clipboard.h
vendored
279
src/external/win32_clipboard.h
vendored
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
#ifndef WIN32_CLIPBOARD_
|
#ifndef WIN32_CLIPBOARD_
|
||||||
#define WIN32_CLIPBOARD_
|
#define WIN32_CLIPBOARD_
|
||||||
unsigned char* Win32GetClipboardImageData(int* width, int* height, unsigned long long int *dataSize);
|
unsigned char *Win32GetClipboardImageData(int *width, int *height, unsigned long long int *dataSize);
|
||||||
#endif // WIN32_CLIPBOARD_
|
#endif // WIN32_CLIPBOARD_
|
||||||
|
|
||||||
#ifdef WIN32_CLIPBOARD_IMPLEMENTATION
|
#ifdef WIN32_CLIPBOARD_IMPLEMENTATION
|
||||||
@ -16,38 +16,38 @@ unsigned char* Win32GetClipboardImageData(int* width, int* height, unsigned long
|
|||||||
// NOTE: These search for architecture is taken from "Windows.h", and it's necessary if we really don't wanna import windows.h
|
// NOTE: These search for architecture is taken from "Windows.h", and it's necessary if we really don't wanna import windows.h
|
||||||
// and still make it compile on msvc, because import indirectly importing "winnt.h" (e.g. <minwindef.h>) can cause problems is these are not defined.
|
// and still make it compile on msvc, because import indirectly importing "winnt.h" (e.g. <minwindef.h>) can cause problems is these are not defined.
|
||||||
#if !defined(_X86_) && !defined(_68K_) && !defined(_MPPC_) && !defined(_IA64_) && !defined(_AMD64_) && !defined(_ARM_) && !defined(_ARM64_) && !defined(_ARM64EC_) && defined(_M_IX86)
|
#if !defined(_X86_) && !defined(_68K_) && !defined(_MPPC_) && !defined(_IA64_) && !defined(_AMD64_) && !defined(_ARM_) && !defined(_ARM64_) && !defined(_ARM64EC_) && defined(_M_IX86)
|
||||||
#define _X86_
|
#define _X86_
|
||||||
#if !defined(_CHPE_X86_ARM64_) && defined(_M_HYBRID)
|
#if !defined(_CHPE_X86_ARM64_) && defined(_M_HYBRID)
|
||||||
#define _CHPE_X86_ARM64_
|
#define _CHPE_X86_ARM64_
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if !defined(_AMD64_) && !defined(_68K_) && !defined(_MPPC_) && !defined(_X86_) && !defined(_IA64_) && !defined(_AMD64_) && !defined(_ARM_) && !defined(_ARM64_) && (defined(_M_AMD64) || defined(_M_ARM64EC))
|
#if !defined(_AMD64_) && !defined(_68K_) && !defined(_MPPC_) && !defined(_X86_) && !defined(_IA64_) && !defined(_AMD64_) && !defined(_ARM_) && !defined(_ARM64_) && (defined(_M_AMD64) || defined(_M_ARM64EC))
|
||||||
#define _AMD64_
|
#define _AMD64_
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if !defined(_ARM_) && !defined(_68K_) && !defined(_MPPC_) && !defined(_X86_) && !defined(_IA64_) && !defined(_AMD64_) && !defined(_ARM64_) && !defined(_ARM64EC_) && defined(_M_ARM)
|
#if !defined(_ARM_) && !defined(_68K_) && !defined(_MPPC_) && !defined(_X86_) && !defined(_IA64_) && !defined(_AMD64_) && !defined(_ARM64_) && !defined(_ARM64EC_) && defined(_M_ARM)
|
||||||
#define _ARM_
|
#define _ARM_
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if !defined(_ARM64_) && !defined(_68K_) && !defined(_MPPC_) && !defined(_X86_) && !defined(_IA64_) && !defined(_AMD64_) && !defined(_ARM_) && !defined(_ARM64EC_) && defined(_M_ARM64)
|
#if !defined(_ARM64_) && !defined(_68K_) && !defined(_MPPC_) && !defined(_X86_) && !defined(_IA64_) && !defined(_AMD64_) && !defined(_ARM_) && !defined(_ARM64EC_) && defined(_M_ARM64)
|
||||||
#define _ARM64_
|
#define _ARM64_
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if !defined(_68K_) && !defined(_MPPC_) && !defined(_X86_) && !defined(_IA64_) && !defined(_ARM_) && !defined(_ARM64_) && !defined(_ARM64EC_) && defined(_M_ARM64EC)
|
#if !defined(_68K_) && !defined(_MPPC_) && !defined(_X86_) && !defined(_IA64_) && !defined(_ARM_) && !defined(_ARM64_) && !defined(_ARM64EC_) && defined(_M_ARM64EC)
|
||||||
#define _ARM64EC_
|
#define _ARM64EC_
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if !defined(_68K_) && !defined(_MPPC_) && !defined(_X86_) && !defined(_IA64_) && !defined(_AMD64_) && !defined(_ARM_) && !defined(_ARM64_) && !defined(_ARM64EC_) && defined(_M_M68K)
|
#if !defined(_68K_) && !defined(_MPPC_) && !defined(_X86_) && !defined(_IA64_) && !defined(_AMD64_) && !defined(_ARM_) && !defined(_ARM64_) && !defined(_ARM64EC_) && defined(_M_M68K)
|
||||||
#define _68K_
|
#define _68K_
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if !defined(_68K_) && !defined(_MPPC_) && !defined(_X86_) && !defined(_IA64_) && !defined(_AMD64_) && !defined(_ARM_) && !defined(_ARM64_) && !defined(_ARM64EC_) && defined(_M_MPPC)
|
#if !defined(_68K_) && !defined(_MPPC_) && !defined(_X86_) && !defined(_IA64_) && !defined(_AMD64_) && !defined(_ARM_) && !defined(_ARM64_) && !defined(_ARM64EC_) && defined(_M_MPPC)
|
||||||
#define _MPPC_
|
#define _MPPC_
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if !defined(_IA64_) && !defined(_68K_) && !defined(_MPPC_) && !defined(_X86_) && !defined(_M_IX86) && !defined(_AMD64_) && !defined(_ARM_) && !defined(_ARM64_) && !defined(_ARM64EC_) && defined(_M_IA64)
|
#if !defined(_IA64_) && !defined(_68K_) && !defined(_MPPC_) && !defined(_X86_) && !defined(_M_IX86) && !defined(_AMD64_) && !defined(_ARM_) && !defined(_ARM64_) && !defined(_ARM64EC_) && defined(_M_IA64)
|
||||||
#define _IA64_
|
#define _IA64_
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
@ -59,40 +59,39 @@ unsigned char* Win32GetClipboardImageData(int* width, int* height, unsigned long
|
|||||||
// #include <minwinbase.h>
|
// #include <minwinbase.h>
|
||||||
|
|
||||||
#ifndef WINAPI
|
#ifndef WINAPI
|
||||||
#if defined(_ARM_)
|
#if defined(_ARM_)
|
||||||
#define WINAPI
|
#define WINAPI
|
||||||
#else
|
#else
|
||||||
#define WINAPI __stdcall
|
#define WINAPI __stdcall
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef WINAPI
|
#ifndef WINAPI
|
||||||
#if defined(_ARM_)
|
#if defined(_ARM_)
|
||||||
#define WINAPI
|
#define WINAPI
|
||||||
#else
|
#else
|
||||||
#define WINAPI __stdcall
|
#define WINAPI __stdcall
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef WINBASEAPI
|
#ifndef WINBASEAPI
|
||||||
#ifndef _KERNEL32_
|
#ifndef _KERNEL32_
|
||||||
#define WINBASEAPI DECLSPEC_IMPORT
|
#define WINBASEAPI DECLSPEC_IMPORT
|
||||||
#else
|
#else
|
||||||
#define WINBASEAPI
|
#define WINBASEAPI
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef WINUSERAPI
|
#ifndef WINUSERAPI
|
||||||
#ifndef _USER32_
|
#ifndef _USER32_
|
||||||
#define WINUSERAPI __declspec (dllimport)
|
#define WINUSERAPI __declspec (dllimport)
|
||||||
#else
|
#else
|
||||||
#define WINUSERAPI
|
#define WINUSERAPI
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef int WINBOOL;
|
typedef int WINBOOL;
|
||||||
|
|
||||||
|
|
||||||
#if !defined(_WINUSER_) || !defined(WINUSER_ALREADY_INCLUDED)
|
#if !defined(_WINUSER_) || !defined(WINUSER_ALREADY_INCLUDED)
|
||||||
WINUSERAPI WINBOOL WINAPI OpenClipboard(HWND hWndNewOwner);
|
WINUSERAPI WINBOOL WINAPI OpenClipboard(HWND hWndNewOwner);
|
||||||
WINUSERAPI WINBOOL WINAPI CloseClipboard(VOID);
|
WINUSERAPI WINBOOL WINAPI CloseClipboard(VOID);
|
||||||
@ -116,7 +115,7 @@ WINUSERAPI HWND WINAPI GetOpenClipboardWindow(VOID);
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef HGLOBAL
|
#ifndef HGLOBAL
|
||||||
#define HGLOBAL void*
|
#define HGLOBAL void*
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if !defined(_WINBASE_) || !defined(WINBASE_ALREADY_INCLUDED)
|
#if !defined(_WINBASE_) || !defined(WINBASE_ALREADY_INCLUDED)
|
||||||
@ -125,7 +124,6 @@ WINBASEAPI LPVOID WINAPI GlobalLock (HGLOBAL hMem);
|
|||||||
WINBASEAPI WINBOOL WINAPI GlobalUnlock (HGLOBAL hMem);
|
WINBASEAPI WINBOOL WINAPI GlobalUnlock (HGLOBAL hMem);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#if !defined(_WINGDI_) || !defined(WINGDI_ALREADY_INCLUDED)
|
#if !defined(_WINGDI_) || !defined(WINGDI_ALREADY_INCLUDED)
|
||||||
#ifndef BITMAPINFOHEADER_ALREADY_DEFINED
|
#ifndef BITMAPINFOHEADER_ALREADY_DEFINED
|
||||||
#define BITMAPINFOHEADER_ALREADY_DEFINED
|
#define BITMAPINFOHEADER_ALREADY_DEFINED
|
||||||
@ -170,8 +168,7 @@ typedef struct tagRGBQUAD {
|
|||||||
} RGBQUAD, *LPRGBQUAD;
|
} RGBQUAD, *LPRGBQUAD;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// REF: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-wmf/4e588f70-bd92-4a6f-b77f-35d0feaf7a57
|
||||||
// https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-wmf/4e588f70-bd92-4a6f-b77f-35d0feaf7a57
|
|
||||||
#define BI_RGB 0x0000
|
#define BI_RGB 0x0000
|
||||||
#define BI_RLE8 0x0001
|
#define BI_RLE8 0x0001
|
||||||
#define BI_RLE4 0x0002
|
#define BI_RLE4 0x0002
|
||||||
@ -182,12 +179,15 @@ typedef struct tagRGBQUAD {
|
|||||||
#define BI_CMYKRLE8 0x000C
|
#define BI_CMYKRLE8 0x000C
|
||||||
#define BI_CMYKRLE4 0x000D
|
#define BI_CMYKRLE4 0x000D
|
||||||
|
|
||||||
|
// Bitmap not compressed and that the color table consists of four DWORD color masks,
|
||||||
|
// that specify the red, green, blue, and alpha components of each pixel
|
||||||
|
#define BI_ALPHABITFIELDS 0x0006
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// https://learn.microsoft.com/en-us/windows/win32/dataxchg/standard-clipboard-formats
|
// REF: https://learn.microsoft.com/en-us/windows/win32/dataxchg/standard-clipboard-formats
|
||||||
#define CF_DIB 8
|
#define CF_DIB 8
|
||||||
|
|
||||||
// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setsystemcursor
|
// REF: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setsystemcursor
|
||||||
// #define OCR_NORMAL 32512 // Normal select
|
// #define OCR_NORMAL 32512 // Normal select
|
||||||
// #define OCR_IBEAM 32513 // Text select
|
// #define OCR_IBEAM 32513 // Text select
|
||||||
// #define OCR_WAIT 32514 // Busy
|
// #define OCR_WAIT 32514 // Busy
|
||||||
@ -202,164 +202,137 @@ typedef struct tagRGBQUAD {
|
|||||||
// #define OCR_HAND 32649 // Link select
|
// #define OCR_HAND 32649 // Link select
|
||||||
// #define OCR_APPSTARTING 32650 //
|
// #define OCR_APPSTARTING 32650 //
|
||||||
|
|
||||||
|
|
||||||
//----------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------
|
||||||
// Module Internal Functions Declaration
|
// Module Internal Functions Declaration
|
||||||
//----------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------
|
||||||
|
static BOOL OpenClipboardRetrying(HWND handle); // Open clipboard with a number of retries
|
||||||
|
static int GetPixelDataOffset(BITMAPINFOHEADER bih); // Get pixel data offset from DIB image
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------------
|
||||||
static BOOL OpenClipboardRetrying(HWND handle); // Open clipboard with a number of retries
|
// Module Functions Definition
|
||||||
static int GetPixelDataOffset(BITMAPINFOHEADER bih);
|
//----------------------------------------------------------------------------------
|
||||||
|
unsigned char *Win32GetClipboardImageData(int *width, int *height, unsigned long long int *dataSize)
|
||||||
unsigned char* Win32GetClipboardImageData(int* width, int* height, unsigned long long int *dataSize)
|
|
||||||
{
|
{
|
||||||
HWND win = NULL; // Get from somewhere but is doesnt seem to matter
|
unsigned char *bmpData = NULL;
|
||||||
const char* msgString = "";
|
|
||||||
int severity = LOG_INFO;
|
if (OpenClipboardRetrying(NULL))
|
||||||
BYTE* bmpData = NULL;
|
{
|
||||||
if (!OpenClipboardRetrying(win)) {
|
HGLOBAL clipHandle = (HGLOBAL)GetClipboardData(CF_DIB);
|
||||||
severity = LOG_ERROR;
|
if (clipHandle != NULL)
|
||||||
msgString = "Couldn't open clipboard";
|
{
|
||||||
goto end;
|
BITMAPINFOHEADER *bmpInfoHeader = (BITMAPINFOHEADER *)GlobalLock(clipHandle);
|
||||||
|
if (bmpInfoHeader)
|
||||||
|
{
|
||||||
|
*width = bmpInfoHeader->biWidth;
|
||||||
|
*height = bmpInfoHeader->biHeight;
|
||||||
|
SIZE_T clipDataSize = GlobalSize(clipHandle);
|
||||||
|
if (clipDataSize >= sizeof(BITMAPINFOHEADER))
|
||||||
|
{
|
||||||
|
int pixelOffset = GetPixelDataOffset(*bmpInfoHeader);
|
||||||
|
|
||||||
|
// Create the bytes for a correct BMP file and copy the data to a pointer
|
||||||
|
//------------------------------------------------------------------------
|
||||||
|
BITMAPFILEHEADER bmpFileHeader = { 0 };
|
||||||
|
SIZE_T bmpFileSize = sizeof(bmpFileHeader) + clipDataSize;
|
||||||
|
*dataSize = bmpFileSize;
|
||||||
|
|
||||||
|
bmpFileHeader.bfType = 0x4D42; // BMP fil type constant
|
||||||
|
bmpFileHeader.bfSize = (DWORD)bmpFileSize; // Up to 4GB works fine
|
||||||
|
bmpFileHeader.bfOffBits = sizeof(bmpFileHeader) + pixelOffset;
|
||||||
|
|
||||||
|
bmpData = (unsigned char *)RL_MALLOC(sizeof(bmpFileHeader) + clipDataSize);
|
||||||
|
memcpy(bmpData, &bmpFileHeader, sizeof(bmpFileHeader)); // Add BMP file header data
|
||||||
|
memcpy(bmpData + sizeof(bmpFileHeader), bmpInfoHeader, clipDataSize); // Add BMP info header data
|
||||||
|
|
||||||
|
GlobalUnlock(clipHandle);
|
||||||
|
CloseClipboard();
|
||||||
|
|
||||||
|
TRACELOG(LOG_INFO, "Clipboad image acquired successfully");
|
||||||
|
//------------------------------------------------------------------------
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
TRACELOG(LOG_WARNING, "Clipboard data is malformed");
|
||||||
|
GlobalUnlock(clipHandle);
|
||||||
|
CloseClipboard();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
TRACELOG(LOG_WARNING, "Clipboard data failed to be locked");
|
||||||
|
GlobalUnlock(clipHandle);
|
||||||
|
CloseClipboard();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
TRACELOG(LOG_WARNING, "Clipboard data is not an image");
|
||||||
|
CloseClipboard();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
else TRACELOG(LOG_WARNING, "Clipboard can not be opened");
|
||||||
|
|
||||||
HGLOBAL clipHandle = (HGLOBAL)GetClipboardData(CF_DIB);
|
|
||||||
if (!clipHandle) {
|
|
||||||
severity = LOG_ERROR;
|
|
||||||
msgString = "Clipboard data is not an Image";
|
|
||||||
goto close;
|
|
||||||
}
|
|
||||||
|
|
||||||
BITMAPINFOHEADER *bmpInfoHeader = (BITMAPINFOHEADER *)GlobalLock(clipHandle);
|
|
||||||
if (!bmpInfoHeader) {
|
|
||||||
// Mapping from HGLOBAL to our local *address space* failed
|
|
||||||
severity = LOG_ERROR;
|
|
||||||
msgString = "Clipboard data failed to be locked";
|
|
||||||
goto unlock;
|
|
||||||
}
|
|
||||||
|
|
||||||
*width = bmpInfoHeader->biWidth;
|
|
||||||
*height = bmpInfoHeader->biHeight;
|
|
||||||
|
|
||||||
SIZE_T clipDataSize = GlobalSize(clipHandle);
|
|
||||||
if (clipDataSize < sizeof(BITMAPINFOHEADER)) {
|
|
||||||
// Format CF_DIB needs space for BITMAPINFOHEADER struct.
|
|
||||||
msgString = "Clipboard has Malformed data";
|
|
||||||
severity = LOG_ERROR;
|
|
||||||
goto unlock;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Denotes where the pixel data starts from the bmpInfoHeader pointer
|
|
||||||
int pixelOffset = GetPixelDataOffset(*bmpInfoHeader);
|
|
||||||
|
|
||||||
//--------------------------------------------------------------------------------//
|
|
||||||
//
|
|
||||||
// The rest of the section is about create the bytes for a correct BMP file
|
|
||||||
// Then we copy the data and to a pointer
|
|
||||||
//
|
|
||||||
//--------------------------------------------------------------------------------//
|
|
||||||
|
|
||||||
BITMAPFILEHEADER bmpFileHeader = {0};
|
|
||||||
SIZE_T bmpFileSize = sizeof(bmpFileHeader) + clipDataSize;
|
|
||||||
*dataSize = bmpFileSize;
|
|
||||||
|
|
||||||
bmpFileHeader.bfType = 0x4D42; //https://stackoverflow.com/questions/601430/multibyte-character-constants-and-bitmap-file-header-type-constants#601536
|
|
||||||
|
|
||||||
bmpFileHeader.bfSize = (DWORD)bmpFileSize; // Up to 4GB works fine
|
|
||||||
bmpFileHeader.bfOffBits = sizeof(bmpFileHeader) + pixelOffset;
|
|
||||||
|
|
||||||
//
|
|
||||||
// Each process has a default heap provided by the system
|
|
||||||
// Memory objects allocated by GlobalAlloc and LocalAlloc are in private,
|
|
||||||
// committed pages with read/write access that cannot be accessed by other processes.
|
|
||||||
//
|
|
||||||
// This may be wrong since we might be allocating in a DLL and freeing from another module, the main application
|
|
||||||
// that may cause heap corruption. We could create a FreeImage function
|
|
||||||
//
|
|
||||||
bmpData = (BYTE *)malloc(sizeof(bmpFileHeader) + clipDataSize);
|
|
||||||
// First we add the header for a bmp file
|
|
||||||
memcpy(bmpData, &bmpFileHeader, sizeof(bmpFileHeader));
|
|
||||||
// Then we add the header for the bmp itself + the pixel data
|
|
||||||
memcpy(bmpData + sizeof(bmpFileHeader), bmpInfoHeader, clipDataSize);
|
|
||||||
msgString = "Clipboad image acquired successfully";
|
|
||||||
|
|
||||||
|
|
||||||
unlock:
|
|
||||||
GlobalUnlock(clipHandle);
|
|
||||||
close:
|
|
||||||
CloseClipboard();
|
|
||||||
end:
|
|
||||||
|
|
||||||
TRACELOG(severity, msgString);
|
|
||||||
return bmpData;
|
return bmpData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------------
|
||||||
|
// Module Internal Functions Definition
|
||||||
|
//----------------------------------------------------------------------------------
|
||||||
|
// Open clipboard with several tries
|
||||||
|
// NOTE: If parameter is NULL, the open clipboard is associated with the current task
|
||||||
static BOOL OpenClipboardRetrying(HWND hWnd)
|
static BOOL OpenClipboardRetrying(HWND hWnd)
|
||||||
{
|
{
|
||||||
static const int maxTries = 20;
|
static const int maxTries = 20;
|
||||||
static const int sleepTimeMS = 60;
|
static const int sleepTimeMS = 60;
|
||||||
for (int _ = 0; _ < maxTries; ++_)
|
|
||||||
|
for (int i = 0; i < maxTries; i++)
|
||||||
{
|
{
|
||||||
// Might be being hold by another process
|
// Might be being hold by another process
|
||||||
// Or yourself forgot to CloseClipboard
|
// Or yourself forgot to CloseClipboard
|
||||||
if (OpenClipboard(hWnd)) {
|
if (OpenClipboard(hWnd)) return true;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
Sleep(sleepTimeMS);
|
Sleep(sleepTimeMS);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Based off of researching microsoft docs and reponses from this question https://stackoverflow.com/questions/30552255/how-to-read-a-bitmap-from-the-windows-clipboard#30552856
|
|
||||||
// https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader
|
|
||||||
// Get the byte offset where does the pixels data start (from a packed DIB)
|
// Get the byte offset where does the pixels data start (from a packed DIB)
|
||||||
|
// REF: https://stackoverflow.com/questions/30552255/how-to-read-a-bitmap-from-the-windows-clipboard#30552856
|
||||||
|
// REF: https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader
|
||||||
static int GetPixelDataOffset(BITMAPINFOHEADER bih)
|
static int GetPixelDataOffset(BITMAPINFOHEADER bih)
|
||||||
{
|
{
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
const unsigned int rgbaSize = sizeof(RGBQUAD);
|
const unsigned int rgbaSize = sizeof(RGBQUAD);
|
||||||
|
|
||||||
// biSize Specifies the number of bytes required by the structure
|
// NOTE: biSize specifies the number of bytes required by the structure
|
||||||
// We expect to always be 40 because it should be packed
|
// We expect to always be 40 because it should be packed
|
||||||
if (40 == bih.biSize && 40 == sizeof(BITMAPINFOHEADER))
|
if ((bih.biSize == 40) && (sizeof(BITMAPINFOHEADER) == 40))
|
||||||
{
|
{
|
||||||
//
|
// NOTE: biBitCount specifies the number of bits per pixel
|
||||||
// biBitCount Specifies the number of bits per pixel.
|
|
||||||
// Might exist some bit masks *after* the header and *before* the pixel offset
|
// Might exist some bit masks *after* the header and *before* the pixel offset
|
||||||
// we're looking, but only if we have more than
|
// we're looking, but only if we have more than
|
||||||
// 8 bits per pixel, so we need to ajust for that
|
// 8 bits per pixel, so we need to ajust for that
|
||||||
//
|
|
||||||
if (bih.biBitCount > 8)
|
if (bih.biBitCount > 8)
|
||||||
{
|
{
|
||||||
// if bih.biCompression is RBG we should NOT offset more
|
// If (bih.biCompression == BI_RGB) we should NOT offset more
|
||||||
|
|
||||||
if (bih.biCompression == BI_BITFIELDS)
|
if (bih.biCompression == BI_BITFIELDS) offset += 3*rgbaSize;
|
||||||
{
|
else if (bih.biCompression == BI_ALPHABITFIELDS) offset += 4*rgbaSize; // Not widely supported, but valid
|
||||||
offset += 3 * rgbaSize;
|
|
||||||
} else if (bih.biCompression == 6 /* BI_ALPHABITFIELDS */)
|
|
||||||
{
|
|
||||||
// Not widely supported, but valid.
|
|
||||||
offset += 4 * rgbaSize;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
// NOTE: biClrUsed specifies the number of color indices in the color table that are actually used by the bitmap
|
||||||
// biClrUsed Specifies the number of color indices in the color table that are actually used by the bitmap.
|
|
||||||
// If this value is zero, the bitmap uses the maximum number of colors
|
// If this value is zero, the bitmap uses the maximum number of colors
|
||||||
// corresponding to the value of the biBitCount member for the compression mode specified by biCompression.
|
// corresponding to the value of the biBitCount member for the compression mode specified by biCompression
|
||||||
// If biClrUsed is nonzero and the biBitCount member is less than 16
|
// If biClrUsed is nonzero and the biBitCount member is less than 16
|
||||||
// the biClrUsed member specifies the actual number of colors
|
// the biClrUsed member specifies the actual number of colors
|
||||||
//
|
if (bih.biClrUsed > 0) offset += bih.biClrUsed*rgbaSize;
|
||||||
if (bih.biClrUsed > 0) {
|
else
|
||||||
offset += bih.biClrUsed * rgbaSize;
|
{
|
||||||
} else {
|
if (bih.biBitCount < 16) offset = offset + (rgbaSize << bih.biBitCount);
|
||||||
if (bih.biBitCount < 16)
|
|
||||||
{
|
|
||||||
offset = offset + (rgbaSize << bih.biBitCount);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return bih.biSize + offset;
|
return bih.biSize + offset;
|
||||||
}
|
}
|
||||||
#endif // WIN32_CLIPBOARD_IMPLEMENTATION
|
#endif // WIN32_CLIPBOARD_IMPLEMENTATION
|
||||||
// EOF
|
|
||||||
|
|||||||
@ -1049,14 +1049,14 @@ Image GetClipboardImage(void)
|
|||||||
#if defined(SUPPORT_CLIPBOARD_IMAGE)
|
#if defined(SUPPORT_CLIPBOARD_IMAGE)
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
unsigned long long int dataSize = 0;
|
unsigned long long int dataSize = 0;
|
||||||
void *fileData = NULL;
|
void *bmpData = NULL;
|
||||||
int width = 0;
|
int width = 0;
|
||||||
int height = 0;
|
int height = 0;
|
||||||
|
|
||||||
fileData = (void *)Win32GetClipboardImageData(&width, &height, &dataSize);
|
bmpData = (void *)Win32GetClipboardImageData(&width, &height, &dataSize);
|
||||||
|
|
||||||
if (fileData == NULL) TRACELOG(LOG_WARNING, "Clipboard image: Couldn't get clipboard data.");
|
if (bmpData == NULL) TRACELOG(LOG_WARNING, "Clipboard image: Couldn't get clipboard data.");
|
||||||
else image = LoadImageFromMemory(".bmp", (const unsigned char *)fileData, (int)dataSize);
|
else image = LoadImageFromMemory(".bmp", (const unsigned char *)bmpData, (int)dataSize);
|
||||||
#else
|
#else
|
||||||
TRACELOG(LOG_WARNING, "GetClipboardImage() not implemented on target platform");
|
TRACELOG(LOG_WARNING, "GetClipboardImage() not implemented on target platform");
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -1469,7 +1469,7 @@ int InitPlatform(void)
|
|||||||
if (!eglChooseConfig(platform.device, framebufferAttribs, configs, numConfigs, &matchingNumConfigs))
|
if (!eglChooseConfig(platform.device, framebufferAttribs, configs, numConfigs, &matchingNumConfigs))
|
||||||
{
|
{
|
||||||
TRACELOG(LOG_WARNING, "DISPLAY: Failed to choose EGL config: 0x%x", eglGetError());
|
TRACELOG(LOG_WARNING, "DISPLAY: Failed to choose EGL config: 0x%x", eglGetError());
|
||||||
free(configs);
|
RL_FREE(configs);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
17
src/raylib.h
17
src/raylib.h
@ -4,13 +4,12 @@
|
|||||||
*
|
*
|
||||||
* FEATURES:
|
* FEATURES:
|
||||||
* - NO external dependencies, all required libraries included with raylib
|
* - NO external dependencies, all required libraries included with raylib
|
||||||
* - Multiplatform: Windows, Linux, FreeBSD, OpenBSD, NetBSD, DragonFly,
|
* - Multiplatform: Windows, Linux, macOS, FreeBSD, Web, Android, Raspberry Pi, DRM native...
|
||||||
* MacOS, Haiku, Android, Raspberry Pi, DRM native, HTML5
|
|
||||||
* - Written in plain C code (C99) in PascalCase/camelCase notation
|
* - Written in plain C code (C99) in PascalCase/camelCase notation
|
||||||
* - Hardware accelerated with OpenGL (1.1, 2.1, 3.3, 4.3, ES2, ES3 - choose at compile)
|
* - Hardware accelerated with OpenGL (1.1, 2.1, 3.3, 4.3, ES2, ES3 - choose at compile)
|
||||||
* - Unique OpenGL abstraction layer (usable as standalone module): [rlgl]
|
* - Custom OpenGL abstraction layer (usable as standalone module): [rlgl]
|
||||||
* - Multiple Fonts formats supported (TTF, OTF, FNT, BDF, Sprite fonts)
|
* - Multiple Fonts formats supported (TTF, OTF, FNT, BDF, Sprite fonts)
|
||||||
* - Outstanding texture formats support, including compressed formats (DXT, ETC, ASTC)
|
* - Many texture formats supportted, including compressed formats (DXT, ETC, ASTC)
|
||||||
* - Full 3d support for 3d Shapes, Models, Billboards, Heightmaps and more!
|
* - Full 3d support for 3d Shapes, Models, Billboards, Heightmaps and more!
|
||||||
* - Flexible Materials system, supporting classic maps and PBR maps
|
* - Flexible Materials system, supporting classic maps and PBR maps
|
||||||
* - Animated 3D models supported (skeletal bones animation) (IQM, M3D, GLTF)
|
* - Animated 3D models supported (skeletal bones animation) (IQM, M3D, GLTF)
|
||||||
@ -26,10 +25,9 @@
|
|||||||
* - One default Shader is loaded on rlglInit()->rlLoadShaderDefault() [rlgl] (OpenGL 3.3 or ES2)
|
* - One default Shader is loaded on rlglInit()->rlLoadShaderDefault() [rlgl] (OpenGL 3.3 or ES2)
|
||||||
* - One default RenderBatch is loaded on rlglInit()->rlLoadRenderBatch() [rlgl] (OpenGL 3.3 or ES2)
|
* - One default RenderBatch is loaded on rlglInit()->rlLoadRenderBatch() [rlgl] (OpenGL 3.3 or ES2)
|
||||||
*
|
*
|
||||||
* DEPENDENCIES (included):
|
* DEPENDENCIES:
|
||||||
* [rcore][GLFW] rglfw (Camilla Löwy - github.com/glfw/glfw) for window/context management and input
|
* [rcore] Depends on the selected platform backend, check rcore.c header for details
|
||||||
* [rcore][RGFW] rgfw (ColleagueRiley - github.com/ColleagueRiley/RGFW) for window/context management and input
|
* [rlgl] glad/glad_gles2 (David Herberth - github.com/Dav1dde/glad) for OpenGL extensions loading
|
||||||
* [rlgl] glad/glad_gles2 (David Herberth - github.com/Dav1dde/glad) for OpenGL 3.3 extensions loading
|
|
||||||
* [raudio] miniaudio (David Reid - github.com/mackron/miniaudio) for audio device/context management
|
* [raudio] miniaudio (David Reid - github.com/mackron/miniaudio) for audio device/context management
|
||||||
*
|
*
|
||||||
* OPTIONAL DEPENDENCIES (included):
|
* OPTIONAL DEPENDENCIES (included):
|
||||||
@ -41,6 +39,7 @@
|
|||||||
* [rtextures] stb_image_write (Sean Barret) for image writing (BMP, TGA, PNG, JPG)
|
* [rtextures] stb_image_write (Sean Barret) for image writing (BMP, TGA, PNG, JPG)
|
||||||
* [rtextures] stb_image_resize2 (Sean Barret) for image resizing algorithms
|
* [rtextures] stb_image_resize2 (Sean Barret) for image resizing algorithms
|
||||||
* [rtextures] stb_perlin (Sean Barret) for Perlin Noise image generation
|
* [rtextures] stb_perlin (Sean Barret) for Perlin Noise image generation
|
||||||
|
* [rtextures] rl_gputex (Ramon Santamaria) for GPU-compressed texture formats
|
||||||
* [rtext] stb_truetype (Sean Barret) for ttf fonts loading
|
* [rtext] stb_truetype (Sean Barret) for ttf fonts loading
|
||||||
* [rtext] stb_rect_pack (Sean Barret) for rectangles packing
|
* [rtext] stb_rect_pack (Sean Barret) for rectangles packing
|
||||||
* [rmodels] par_shapes (Philip Rideout) for parametric 3d shapes generation
|
* [rmodels] par_shapes (Philip Rideout) for parametric 3d shapes generation
|
||||||
@ -1102,7 +1101,7 @@ RLAPI void SetTraceLogLevel(int logLevel); // Set the curre
|
|||||||
RLAPI void TraceLog(int logLevel, const char *text, ...); // Show trace log messages (LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERROR...)
|
RLAPI void TraceLog(int logLevel, const char *text, ...); // Show trace log messages (LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERROR...)
|
||||||
RLAPI void SetTraceLogCallback(TraceLogCallback callback); // Set custom trace log
|
RLAPI void SetTraceLogCallback(TraceLogCallback callback); // Set custom trace log
|
||||||
|
|
||||||
// Memory management, using internal allocators
|
// Memory management, using internal allocators
|
||||||
RLAPI void *MemAlloc(unsigned int size); // Internal memory allocator
|
RLAPI void *MemAlloc(unsigned int size); // Internal memory allocator
|
||||||
RLAPI void *MemRealloc(void *ptr, unsigned int size); // Internal memory reallocator
|
RLAPI void *MemRealloc(void *ptr, unsigned int size); // Internal memory reallocator
|
||||||
RLAPI void MemFree(void *ptr); // Internal memory free
|
RLAPI void MemFree(void *ptr); // Internal memory free
|
||||||
|
|||||||
@ -164,13 +164,19 @@ typedef struct Matrix {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// NOTE: Helper types to be used instead of array return types for *ToFloat functions
|
// NOTE: Helper types to be used instead of array return types for *ToFloat functions
|
||||||
|
#if !defined(RL_FLOAT3_TYPE)
|
||||||
typedef struct float3 {
|
typedef struct float3 {
|
||||||
float v[3];
|
float v[3];
|
||||||
} float3;
|
} float3;
|
||||||
|
#define RL_FLOAT3_TYPE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(RL_FLOAT16_TYPE)
|
||||||
typedef struct float16 {
|
typedef struct float16 {
|
||||||
float v[16];
|
float v[16];
|
||||||
} float16;
|
} float16;
|
||||||
|
#define RL_FLOAT16_TYPE
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <math.h> // Required for: sinf(), cosf(), tan(), atan2f(), sqrtf(), floor(), fminf(), fmaxf(), fabsf()
|
#include <math.h> // Required for: sinf(), cosf(), tan(), atan2f(), sqrtf(), floor(), fminf(), fmaxf(), fabsf()
|
||||||
|
|
||||||
|
|||||||
18
src/rcore.c
18
src/rcore.c
@ -17,19 +17,23 @@
|
|||||||
* - Linux (X11/Wayland desktop mode)
|
* - Linux (X11/Wayland desktop mode)
|
||||||
* - macOS/OSX (x64, arm64)
|
* - macOS/OSX (x64, arm64)
|
||||||
* - Others (not tested)
|
* - Others (not tested)
|
||||||
* > PLATFORM_WEB_RGFW:
|
* > PLATFORM_DESKTOP_WIN32 (native Win32):
|
||||||
|
* - Windows (Win32, Win64)
|
||||||
|
* > PLATFORM_WEB (GLFW + Emscripten):
|
||||||
* - HTML5 (WebAssembly)
|
* - HTML5 (WebAssembly)
|
||||||
* > PLATFORM_WEB:
|
* > PLATFORM_WEB_EMSCRIPTEN (Emscripten):
|
||||||
* - HTML5 (WebAssembly)
|
* - HTML5 (WebAssembly)
|
||||||
* > PLATFORM_DRM:
|
* > PLATFORM_WEB_RGFW (Emscripten):
|
||||||
|
* - HTML5 (WebAssembly)
|
||||||
|
* > PLATFORM_DRM (native DRM):
|
||||||
* - Raspberry Pi 0-5 (DRM/KMS)
|
* - Raspberry Pi 0-5 (DRM/KMS)
|
||||||
* - Linux DRM subsystem (KMS mode)
|
* - Linux DRM subsystem (KMS mode)
|
||||||
* > PLATFORM_ANDROID:
|
* - Embedded devices (with GPU)
|
||||||
|
* > PLATFORM_ANDROID (native NDK):
|
||||||
* - Android (ARM, ARM64)
|
* - Android (ARM, ARM64)
|
||||||
* > PLATFORM_DESKTOP_WIN32 (Native Win32):
|
|
||||||
* - Windows (Win32, Win64)
|
|
||||||
* > PLATFORM_MEMORY
|
* > PLATFORM_MEMORY
|
||||||
* - Memory framebuffer output, using software renderer, no OS required
|
* - Memory framebuffer output, using software renderer, no OS required
|
||||||
|
*
|
||||||
* CONFIGURATION:
|
* CONFIGURATION:
|
||||||
* #define SUPPORT_DEFAULT_FONT (default)
|
* #define SUPPORT_DEFAULT_FONT (default)
|
||||||
* Default font is loaded on window initialization to be available for the user to render simple text
|
* Default font is loaded on window initialization to be available for the user to render simple text
|
||||||
@ -277,7 +281,7 @@
|
|||||||
#define FILE_FILTER_TAG_DIR_ONLY "DIR*" // Filter to include directories on directory scan
|
#define FILE_FILTER_TAG_DIR_ONLY "DIR*" // Filter to include directories on directory scan
|
||||||
#endif // NOTE: Used in ScanDirectoryFiles(), LoadDirectoryFilesEx() and GetDirectoryFileCountEx()
|
#endif // NOTE: Used in ScanDirectoryFiles(), LoadDirectoryFilesEx() and GetDirectoryFileCountEx()
|
||||||
|
|
||||||
// Flags operation macros
|
// Flags bitwise operation macros
|
||||||
#define FLAG_SET(n, f) ((n) |= (f))
|
#define FLAG_SET(n, f) ((n) |= (f))
|
||||||
#define FLAG_CLEAR(n, f) ((n) &= ~(f))
|
#define FLAG_CLEAR(n, f) ((n) &= ~(f))
|
||||||
#define FLAG_TOGGLE(n, f) ((n) ^= (f))
|
#define FLAG_TOGGLE(n, f) ((n) ^= (f))
|
||||||
|
|||||||
87
src/rlgl.h
87
src/rlgl.h
@ -204,9 +204,9 @@
|
|||||||
#define RL_DEFAULT_BATCH_BUFFER_ELEMENTS 8192
|
#define RL_DEFAULT_BATCH_BUFFER_ELEMENTS 8192
|
||||||
#endif
|
#endif
|
||||||
#if defined(GRAPHICS_API_OPENGL_ES2)
|
#if defined(GRAPHICS_API_OPENGL_ES2)
|
||||||
// We reduce memory sizes for embedded systems (RPI and HTML5)
|
// Reducing memory sizes for embedded systems (RPI and HTML5)
|
||||||
// NOTE: On HTML5 (emscripten) this is allocated on heap,
|
// NOTE: On HTML5 (emscripten) this is allocated on heap,
|
||||||
// by default it's only 16MB!...just take care...
|
// by default heap is only 16MB!...just take care...
|
||||||
#define RL_DEFAULT_BATCH_BUFFER_ELEMENTS 2048
|
#define RL_DEFAULT_BATCH_BUFFER_ELEMENTS 2048
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
@ -1277,7 +1277,7 @@ void rlTranslatef(float x, float y, float z)
|
|||||||
matTranslation.m13 = y;
|
matTranslation.m13 = y;
|
||||||
matTranslation.m14 = z;
|
matTranslation.m14 = z;
|
||||||
|
|
||||||
// NOTE: We transpose matrix with multiplication order
|
// NOTE: Transposing matrix by multiplication order
|
||||||
*RLGL.State.currentMatrix = rlMatrixMultiply(matTranslation, *RLGL.State.currentMatrix);
|
*RLGL.State.currentMatrix = rlMatrixMultiply(matTranslation, *RLGL.State.currentMatrix);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1322,7 +1322,7 @@ void rlRotatef(float angle, float x, float y, float z)
|
|||||||
matRotation.m14 = 0.0f;
|
matRotation.m14 = 0.0f;
|
||||||
matRotation.m15 = 1.0f;
|
matRotation.m15 = 1.0f;
|
||||||
|
|
||||||
// NOTE: We transpose matrix with multiplication order
|
// NOTE: Transposing matrix by multiplication order
|
||||||
*RLGL.State.currentMatrix = rlMatrixMultiply(matRotation, *RLGL.State.currentMatrix);
|
*RLGL.State.currentMatrix = rlMatrixMultiply(matRotation, *RLGL.State.currentMatrix);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1336,7 +1336,7 @@ void rlScalef(float x, float y, float z)
|
|||||||
matScale.m5 = y;
|
matScale.m5 = y;
|
||||||
matScale.m10 = z;
|
matScale.m10 = z;
|
||||||
|
|
||||||
// NOTE: We transpose matrix with multiplication order
|
// NOTE: Transposing matrix by multiplication order
|
||||||
*RLGL.State.currentMatrix = rlMatrixMultiply(matScale, *RLGL.State.currentMatrix);
|
*RLGL.State.currentMatrix = rlMatrixMultiply(matScale, *RLGL.State.currentMatrix);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1418,7 +1418,6 @@ void rlOrtho(double left, double right, double bottom, double top, double znear,
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Set the viewport area (transformation from normalized device coordinates to window coordinates)
|
// Set the viewport area (transformation from normalized device coordinates to window coordinates)
|
||||||
// NOTE: We store current viewport dimensions
|
|
||||||
void rlViewport(int x, int y, int width, int height)
|
void rlViewport(int x, int y, int width, int height)
|
||||||
{
|
{
|
||||||
glViewport(x, y, width, height);
|
glViewport(x, y, width, height);
|
||||||
@ -1529,9 +1528,9 @@ void rlVertex3f(float x, float y, float z)
|
|||||||
tz = RLGL.State.transform.m2*x + RLGL.State.transform.m6*y + RLGL.State.transform.m10*z + RLGL.State.transform.m14;
|
tz = RLGL.State.transform.m2*x + RLGL.State.transform.m6*y + RLGL.State.transform.m10*z + RLGL.State.transform.m14;
|
||||||
}
|
}
|
||||||
|
|
||||||
// WARNING: We can't break primitives when launching a new batch
|
// WARNING: Be careful with primitives breaking when launching a new batch!
|
||||||
// RL_LINES comes in pairs, RL_TRIANGLES come in groups of 3 vertices and RL_QUADS come in groups of 4 vertices
|
// RL_LINES comes in pairs, RL_TRIANGLES come in groups of 3 vertices and RL_QUADS come in groups of 4 vertices
|
||||||
// We must check current draw.mode when a new vertex is required and finish the batch only if the draw.mode draw.vertexCount is %2, %3 or %4
|
// Checking current draw.mode when a new vertex is required and finish the batch only if the draw.mode draw.vertexCount is %2, %3 or %4
|
||||||
if (RLGL.State.vertexCounter > (RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].elementCount*4 - 4))
|
if (RLGL.State.vertexCounter > (RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].elementCount*4 - 4))
|
||||||
{
|
{
|
||||||
if ((RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].mode == RL_LINES) &&
|
if ((RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].mode == RL_LINES) &&
|
||||||
@ -1539,7 +1538,7 @@ void rlVertex3f(float x, float y, float z)
|
|||||||
{
|
{
|
||||||
// Reached the maximum number of vertices for RL_LINES drawing
|
// Reached the maximum number of vertices for RL_LINES drawing
|
||||||
// Launch a draw call but keep current state for next vertices comming
|
// Launch a draw call but keep current state for next vertices comming
|
||||||
// NOTE: We add +1 vertex to the check for security
|
// NOTE: Adding +1 vertex to the check for some safety
|
||||||
rlCheckRenderBatchLimit(2 + 1);
|
rlCheckRenderBatchLimit(2 + 1);
|
||||||
}
|
}
|
||||||
else if ((RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].mode == RL_TRIANGLES) &&
|
else if ((RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].mode == RL_TRIANGLES) &&
|
||||||
@ -1659,7 +1658,7 @@ void rlSetTexture(unsigned int id)
|
|||||||
#if defined(GRAPHICS_API_OPENGL_11)
|
#if defined(GRAPHICS_API_OPENGL_11)
|
||||||
rlDisableTexture();
|
rlDisableTexture();
|
||||||
#else
|
#else
|
||||||
// NOTE: If quads batch limit is reached, we force a draw call and next batch starts
|
// NOTE: If quads batch limit is reached, force a draw call and next batch starts
|
||||||
if (RLGL.State.vertexCounter >=
|
if (RLGL.State.vertexCounter >=
|
||||||
RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].elementCount*4)
|
RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].elementCount*4)
|
||||||
{
|
{
|
||||||
@ -2485,7 +2484,7 @@ void rlLoadExtensions(void *loader)
|
|||||||
const char **extList = (const char **)RL_CALLOC(512, sizeof(const char *)); // Allocate 512 strings pointers (2 KB)
|
const char **extList = (const char **)RL_CALLOC(512, sizeof(const char *)); // Allocate 512 strings pointers (2 KB)
|
||||||
const char *extensions = (const char *)glGetString(GL_EXTENSIONS); // One big const string
|
const char *extensions = (const char *)glGetString(GL_EXTENSIONS); // One big const string
|
||||||
|
|
||||||
// NOTE: We have to duplicate string because glGetString() returns a const string
|
// NOTE: String duplication rquired because glGetString() returns a const string
|
||||||
int extensionsLength = (int)strlen(extensions); // Get extensions string size in bytes
|
int extensionsLength = (int)strlen(extensions); // Get extensions string size in bytes
|
||||||
char *extensionsDup = (char *)RL_CALLOC(extensionsLength + 1, sizeof(char)); // Allocate space for copy with additional EOL byte
|
char *extensionsDup = (char *)RL_CALLOC(extensionsLength + 1, sizeof(char)); // Allocate space for copy with additional EOL byte
|
||||||
strncpy(extensionsDup, extensions, extensionsLength);
|
strncpy(extensionsDup, extensions, extensionsLength);
|
||||||
@ -2970,19 +2969,20 @@ void rlUnloadRenderBatch(rlRenderBatch batch)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Draw render batch
|
// Draw render batch
|
||||||
// NOTE: We require a pointer to reset batch and increase current buffer (multi-buffer)
|
// NOTE: Batch is reseted and current buffer is updated (for multi-buffer config)
|
||||||
void rlDrawRenderBatch(rlRenderBatch *batch)
|
void rlDrawRenderBatch(rlRenderBatch *batch)
|
||||||
{
|
{
|
||||||
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
|
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
|
||||||
// Update batch vertex buffers
|
// Update batch vertex buffers
|
||||||
//------------------------------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------------------------------
|
||||||
// NOTE: If there is not vertex data, buffers doesn't need to be updated (vertexCount > 0)
|
// NOTE: If there is not vertex data, buffers doesn't need to be updated (vertexCount > 0)
|
||||||
// TODO: If no data changed on the CPU arrays there is no need to re-upload data to GPU,
|
|
||||||
// a flag can be used to detect changes but it would imply keeping a copy buffer and memcmp() both, does it worth it?
|
|
||||||
if (RLGL.State.vertexCounter > 0)
|
if (RLGL.State.vertexCounter > 0)
|
||||||
{
|
{
|
||||||
// Activate elements VAO
|
// Activate elements VAO
|
||||||
if (RLGL.ExtSupported.vao) glBindVertexArray(batch->vertexBuffer[batch->currentBuffer].vaoId);
|
if (RLGL.ExtSupported.vao) glBindVertexArray(batch->vertexBuffer[batch->currentBuffer].vaoId);
|
||||||
|
|
||||||
|
// TODO: If no data changed on the CPU arrays there is no need to re-upload data to GPU,
|
||||||
|
// a flag can be used to detect changes but it would imply keeping a copy buffer and memcmp() both, does it worth it?
|
||||||
|
|
||||||
// Vertex positions buffer
|
// Vertex positions buffer
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, batch->vertexBuffer[batch->currentBuffer].vboId[0]);
|
glBindBuffer(GL_ARRAY_BUFFER, batch->vertexBuffer[batch->currentBuffer].vboId[0]);
|
||||||
@ -3006,18 +3006,17 @@ void rlDrawRenderBatch(rlRenderBatch *batch)
|
|||||||
|
|
||||||
// NOTE: glMapBuffer() causes sync issue
|
// NOTE: glMapBuffer() causes sync issue
|
||||||
// If GPU is working with this buffer, glMapBuffer() will wait(stall) until GPU to finish its job
|
// If GPU is working with this buffer, glMapBuffer() will wait(stall) until GPU to finish its job
|
||||||
// To avoid waiting (idle), you can call first glBufferData() with NULL pointer before glMapBuffer()
|
// To avoid waiting (idle), glBufferData() can bee called first with NULL pointer before glMapBuffer()
|
||||||
// If you do that, the previous data in PBO will be discarded and glMapBuffer() returns a new
|
// Doing that, the previous data in PBO will be discarded and glMapBuffer() returns a new
|
||||||
// allocated pointer immediately even if GPU is still working with the previous data
|
// allocated pointer immediately even if GPU is still working with the previous data
|
||||||
|
|
||||||
// Another option: map the buffer object into client's memory
|
// Another option: map the buffer object into client's memory
|
||||||
// Probably this code could be moved somewhere else...
|
//batch->vertexBuffer[batch->currentBuffer].vertices = (float *)glMapBuffer(GL_ARRAY_BUFFER, GL_READ_WRITE);
|
||||||
// batch->vertexBuffer[batch->currentBuffer].vertices = (float *)glMapBuffer(GL_ARRAY_BUFFER, GL_READ_WRITE);
|
//if (batch->vertexBuffer[batch->currentBuffer].vertices)
|
||||||
// if (batch->vertexBuffer[batch->currentBuffer].vertices)
|
//{
|
||||||
// {
|
// Update vertex data
|
||||||
// Update vertex data
|
//}
|
||||||
// }
|
//glUnmapBuffer(GL_ARRAY_BUFFER);
|
||||||
// glUnmapBuffer(GL_ARRAY_BUFFER);
|
|
||||||
|
|
||||||
// Unbind the current VAO
|
// Unbind the current VAO
|
||||||
if (RLGL.ExtSupported.vao) glBindVertexArray(0);
|
if (RLGL.ExtSupported.vao) glBindVertexArray(0);
|
||||||
@ -3132,7 +3131,7 @@ void rlDrawRenderBatch(rlRenderBatch *batch)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
#if defined(GRAPHICS_API_OPENGL_33)
|
#if defined(GRAPHICS_API_OPENGL_33)
|
||||||
// We need to define the number of indices to be processed: elementCount*6
|
// The number of indices to be processed needs to be defined: elementCount*6
|
||||||
// NOTE: The final parameter tells the GPU the offset in bytes from the
|
// NOTE: The final parameter tells the GPU the offset in bytes from the
|
||||||
// start of the index buffer to the location of the first index to process
|
// start of the index buffer to the location of the first index to process
|
||||||
glDrawElements(GL_TRIANGLES, batch->draws[i].vertexCount/4*6, GL_UNSIGNED_INT, (GLvoid *)(vertexOffset/4*6*sizeof(GLuint)));
|
glDrawElements(GL_TRIANGLES, batch->draws[i].vertexCount/4*6, GL_UNSIGNED_INT, (GLvoid *)(vertexOffset/4*6*sizeof(GLuint)));
|
||||||
@ -3233,7 +3232,7 @@ bool rlCheckRenderBatchLimit(int vCount)
|
|||||||
|
|
||||||
rlDrawRenderBatch(RLGL.currentBatch); // NOTE: Stereo rendering is checked inside
|
rlDrawRenderBatch(RLGL.currentBatch); // NOTE: Stereo rendering is checked inside
|
||||||
|
|
||||||
// Restore state of last batch so we can continue adding vertices
|
// Restore state of last batch so new vertices can be added
|
||||||
RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].mode = currentMode;
|
RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].mode = currentMode;
|
||||||
RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].textureId = currentTexture;
|
RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].textureId = currentTexture;
|
||||||
}
|
}
|
||||||
@ -3395,7 +3394,7 @@ unsigned int rlLoadTexture(const void *data, int width, int height, int format,
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// At this point we have the texture loaded in GPU and texture parameters configured
|
// At this point texture is loaded in GPU and texture parameters configured
|
||||||
|
|
||||||
// NOTE: If mipmaps were not in data, they are not generated automatically
|
// NOTE: If mipmaps were not in data, they are not generated automatically
|
||||||
|
|
||||||
@ -3416,10 +3415,10 @@ unsigned int rlLoadTextureDepth(int width, int height, bool useRenderBuffer)
|
|||||||
if (!isGpuReady) { TRACELOG(RL_LOG_WARNING, "GL: GPU is not ready to load data, trying to load before InitWindow()?"); return id; }
|
if (!isGpuReady) { TRACELOG(RL_LOG_WARNING, "GL: GPU is not ready to load data, trying to load before InitWindow()?"); return id; }
|
||||||
|
|
||||||
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
|
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
|
||||||
// In case depth textures not supported, we force renderbuffer usage
|
// In case depth textures were not supported, force renderbuffer usage
|
||||||
if (!RLGL.ExtSupported.texDepth) useRenderBuffer = true;
|
if (!RLGL.ExtSupported.texDepth) useRenderBuffer = true;
|
||||||
|
|
||||||
// NOTE: We let the implementation to choose the best bit-depth
|
// NOTE: Letting the implementation to choose the best bit-depth
|
||||||
// Possible formats: GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT32 and GL_DEPTH_COMPONENT32F
|
// Possible formats: GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT32 and GL_DEPTH_COMPONENT32F
|
||||||
unsigned int glInternalFormat = GL_DEPTH_COMPONENT;
|
unsigned int glInternalFormat = GL_DEPTH_COMPONENT;
|
||||||
|
|
||||||
@ -3565,7 +3564,7 @@ unsigned int rlLoadTextureCubemap(const void *data, int size, int format, int mi
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update already loaded texture in GPU with new data
|
// Update already loaded texture in GPU with new data
|
||||||
// NOTE: We don't know safely if internal texture format is the expected one...
|
// WARNING: Not possible to know safely if internal texture format is the expected one...
|
||||||
void rlUpdateTexture(unsigned int id, int offsetX, int offsetY, int width, int height, int format, const void *data)
|
void rlUpdateTexture(unsigned int id, int offsetX, int offsetY, int width, int height, int format, const void *data)
|
||||||
{
|
{
|
||||||
glBindTexture(GL_TEXTURE_2D, id);
|
glBindTexture(GL_TEXTURE_2D, id);
|
||||||
@ -3699,7 +3698,7 @@ void *rlReadTexturePixels(unsigned int id, int width, int height, int format)
|
|||||||
#if defined(GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_33)
|
#if defined(GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_33)
|
||||||
glBindTexture(GL_TEXTURE_2D, id);
|
glBindTexture(GL_TEXTURE_2D, id);
|
||||||
|
|
||||||
// NOTE: Using texture id, we can retrieve some texture info (but not on OpenGL ES 2.0)
|
// NOTE: Using texture id, some texture info can be retrieved (but not on OpenGL ES 2.0)
|
||||||
// Possible texture info: GL_TEXTURE_RED_SIZE, GL_TEXTURE_GREEN_SIZE, GL_TEXTURE_BLUE_SIZE, GL_TEXTURE_ALPHA_SIZE
|
// Possible texture info: GL_TEXTURE_RED_SIZE, GL_TEXTURE_GREEN_SIZE, GL_TEXTURE_BLUE_SIZE, GL_TEXTURE_ALPHA_SIZE
|
||||||
//int width, height, format;
|
//int width, height, format;
|
||||||
//glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &width);
|
//glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &width);
|
||||||
@ -3742,7 +3741,7 @@ void *rlReadTexturePixels(unsigned int id, int width, int height, int format)
|
|||||||
// Attach our texture to FBO
|
// Attach our texture to FBO
|
||||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, id, 0);
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, id, 0);
|
||||||
|
|
||||||
// We read data as RGBA because FBO texture is configured as RGBA, despite binding another texture format
|
// Reading data as RGBA because FBO texture is configured as RGBA, despite binding another texture format
|
||||||
pixels = RL_CALLOC(rlGetPixelDataSize(width, height, RL_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8), 1);
|
pixels = RL_CALLOC(rlGetPixelDataSize(width, height, RL_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8), 1);
|
||||||
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
|
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
|
||||||
|
|
||||||
@ -3778,12 +3777,12 @@ unsigned char *rlReadScreenPixels(int width, int height)
|
|||||||
{
|
{
|
||||||
unsigned char *imgData = (unsigned char *)RL_CALLOC(width*height*4, sizeof(unsigned char));
|
unsigned char *imgData = (unsigned char *)RL_CALLOC(width*height*4, sizeof(unsigned char));
|
||||||
|
|
||||||
// NOTE 1: glReadPixels returns image flipped vertically -> (0,0) is the bottom left corner of the framebuffer
|
// NOTE: glReadPixels() returns image flipped vertically -> (0,0) is the bottom left corner of the framebuffer
|
||||||
// NOTE 2: We are getting alpha channel! Be careful, it can be transparent if not cleared properly!
|
// WARNING: Getting alpha channel! Be careful, it can be transparent if not cleared properly!
|
||||||
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, imgData);
|
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, imgData);
|
||||||
|
|
||||||
// Flip image vertically!
|
// Flip image vertically
|
||||||
// NOTE: Alpha value has already been applied to RGB in framebuffer, we don't need it!
|
// NOTE: Alpha value has already been applied to RGB in framebuffer, not needed anymore
|
||||||
for (int y = height - 1; y >= height/2; y--)
|
for (int y = height - 1; y >= height/2; y--)
|
||||||
{
|
{
|
||||||
for (int x = 0; x < (width*4); x += 4)
|
for (int x = 0; x < (width*4); x += 4)
|
||||||
@ -3904,13 +3903,13 @@ void rlUnloadFramebuffer(unsigned int id)
|
|||||||
{
|
{
|
||||||
#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2))
|
#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2))
|
||||||
// Query depth attachment to automatically delete texture/renderbuffer
|
// Query depth attachment to automatically delete texture/renderbuffer
|
||||||
int depthType = 0, depthId = 0;
|
int depthType = 0;
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, id); // Bind framebuffer to query depth texture type
|
glBindFramebuffer(GL_FRAMEBUFFER, id); // Bind framebuffer to query depth texture type
|
||||||
glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &depthType);
|
glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &depthType);
|
||||||
|
|
||||||
// TODO: Review warning retrieving object name in WebGL
|
|
||||||
// WARNING: WebGL: INVALID_ENUM: getFramebufferAttachmentParameter: invalid parameter name
|
// WARNING: WebGL: INVALID_ENUM: getFramebufferAttachmentParameter: invalid parameter name
|
||||||
// REF: https://registry.khronos.org/webgl/specs/latest/1.0/
|
// REF: https://registry.khronos.org/webgl/specs/latest/1.0/
|
||||||
|
int depthId = 0;
|
||||||
glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &depthId);
|
glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &depthId);
|
||||||
|
|
||||||
unsigned int depthIdU = (unsigned int)depthId;
|
unsigned int depthIdU = (unsigned int)depthId;
|
||||||
@ -4190,15 +4189,15 @@ unsigned int rlLoadShaderCode(const char *vsCode, const char *fsCode)
|
|||||||
if (fsCode != NULL) fragmentShaderId = rlCompileShader(fsCode, GL_FRAGMENT_SHADER);
|
if (fsCode != NULL) fragmentShaderId = rlCompileShader(fsCode, GL_FRAGMENT_SHADER);
|
||||||
else fragmentShaderId = RLGL.State.defaultFShaderId;
|
else fragmentShaderId = RLGL.State.defaultFShaderId;
|
||||||
|
|
||||||
// In case vertex and fragment shader are the default ones, no need to recompile, we can just assign the default shader program id
|
// In case vertex and fragment shader are the default ones, no need to recompile, just assign the default shader program id
|
||||||
if ((vertexShaderId == RLGL.State.defaultVShaderId) && (fragmentShaderId == RLGL.State.defaultFShaderId)) id = RLGL.State.defaultShaderId;
|
if ((vertexShaderId == RLGL.State.defaultVShaderId) && (fragmentShaderId == RLGL.State.defaultFShaderId)) id = RLGL.State.defaultShaderId;
|
||||||
else if ((vertexShaderId > 0) && (fragmentShaderId > 0))
|
else if ((vertexShaderId > 0) && (fragmentShaderId > 0))
|
||||||
{
|
{
|
||||||
// One of or both shader are new, we need to compile a new shader program
|
// One of or both shader are new, a new shader program needs to be compiled
|
||||||
id = rlLoadShaderProgram(vertexShaderId, fragmentShaderId);
|
id = rlLoadShaderProgram(vertexShaderId, fragmentShaderId);
|
||||||
|
|
||||||
// We can detach and delete vertex/fragment shaders (if not default ones)
|
// Detaching and deleting vertex/fragment shaders (if not default ones)
|
||||||
// NOTE: We detach shader before deletion to make sure memory is freed
|
// WARNING: Detach shader before deletion to make sure memory is freed
|
||||||
if (vertexShaderId != RLGL.State.defaultVShaderId)
|
if (vertexShaderId != RLGL.State.defaultVShaderId)
|
||||||
{
|
{
|
||||||
// WARNING: Shader program linkage could fail and returned id is 0
|
// WARNING: Shader program linkage could fail and returned id is 0
|
||||||
@ -4212,10 +4211,10 @@ unsigned int rlLoadShaderCode(const char *vsCode, const char *fsCode)
|
|||||||
glDeleteShader(fragmentShaderId);
|
glDeleteShader(fragmentShaderId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// In case shader program loading failed, we assign default shader
|
// In case shader program loading failed, assign default shader
|
||||||
if (id == 0)
|
if (id == 0)
|
||||||
{
|
{
|
||||||
// In case shader loading fails, we return the default shader
|
// In case shader loading fails, reassigning default shader
|
||||||
TRACELOG(RL_LOG_WARNING, "SHADER: Failed to load custom shader code, using default shader");
|
TRACELOG(RL_LOG_WARNING, "SHADER: Failed to load custom shader code, using default shader");
|
||||||
id = RLGL.State.defaultShaderId;
|
id = RLGL.State.defaultShaderId;
|
||||||
}
|
}
|
||||||
@ -4737,9 +4736,9 @@ Matrix rlGetMatrixTransform(void)
|
|||||||
Matrix mat = rlMatrixIdentity();
|
Matrix mat = rlMatrixIdentity();
|
||||||
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
|
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
|
||||||
// TODO: Consider possible transform matrices in the RLGL.State.stack
|
// TODO: Consider possible transform matrices in the RLGL.State.stack
|
||||||
// Is this the right order? or should we start with the first stored matrix instead of the last one?
|
|
||||||
//Matrix matStackTransform = rlMatrixIdentity();
|
//Matrix matStackTransform = rlMatrixIdentity();
|
||||||
//for (int i = RLGL.State.stackCounter; i > 0; i--) matStackTransform = rlMatrixMultiply(RLGL.State.stack[i], matStackTransform);
|
//for (int i = RLGL.State.stackCounter; i > 0; i--) matStackTransform = rlMatrixMultiply(RLGL.State.stack[i], matStackTransform);
|
||||||
|
|
||||||
mat = RLGL.State.transform;
|
mat = RLGL.State.transform;
|
||||||
#endif
|
#endif
|
||||||
return mat;
|
return mat;
|
||||||
|
|||||||
Reference in New Issue
Block a user