Remove end-line spaces
This commit is contained in:
parent
e340517a73
commit
fc1c9505ba
170
src/audio.c
170
src/audio.c
@ -80,7 +80,7 @@
|
||||
#endif
|
||||
|
||||
#include "external/mini_al.h" // mini_al audio library
|
||||
// NOTE: Cannot be implement here because it conflicts with
|
||||
// NOTE: Cannot be implement here because it conflicts with
|
||||
// Win32 APIs: Rectangle, CloseWindow(), ShowCursor(), PlaySoundA()
|
||||
|
||||
#include <stdlib.h> // Required for: malloc(), free()
|
||||
@ -132,12 +132,12 @@
|
||||
// Types and Structures Definition
|
||||
//----------------------------------------------------------------------------------
|
||||
|
||||
typedef enum {
|
||||
MUSIC_AUDIO_OGG = 0,
|
||||
MUSIC_AUDIO_FLAC,
|
||||
MUSIC_AUDIO_MP3,
|
||||
MUSIC_MODULE_XM,
|
||||
MUSIC_MODULE_MOD
|
||||
typedef enum {
|
||||
MUSIC_AUDIO_OGG = 0,
|
||||
MUSIC_AUDIO_FLAC,
|
||||
MUSIC_AUDIO_MP3,
|
||||
MUSIC_MODULE_XM,
|
||||
MUSIC_MODULE_MOD
|
||||
} MusicContextType;
|
||||
|
||||
// Music type (file streaming from memory)
|
||||
@ -167,12 +167,12 @@ typedef struct MusicData {
|
||||
} MusicData;
|
||||
|
||||
#if defined(AUDIO_STANDALONE)
|
||||
typedef enum {
|
||||
LOG_INFO = 0,
|
||||
LOG_ERROR,
|
||||
LOG_WARNING,
|
||||
LOG_DEBUG,
|
||||
LOG_OTHER
|
||||
typedef enum {
|
||||
LOG_INFO = 0,
|
||||
LOG_ERROR,
|
||||
LOG_WARNING,
|
||||
LOG_DEBUG,
|
||||
LOG_OTHER
|
||||
} TraceLogType;
|
||||
#endif
|
||||
|
||||
@ -214,7 +214,7 @@ typedef enum { AUDIO_BUFFER_USAGE_STATIC = 0, AUDIO_BUFFER_USAGE_STREAM } AudioB
|
||||
|
||||
// Audio buffer structure
|
||||
// NOTE: Slightly different logic is used when feeding data to the playback device depending on whether or not data is streamed
|
||||
typedef struct AudioBuffer AudioBuffer;
|
||||
typedef struct AudioBuffer AudioBuffer;
|
||||
struct AudioBuffer {
|
||||
mal_dsp dsp; // Required for format conversion
|
||||
float volume;
|
||||
@ -239,7 +239,7 @@ static bool isAudioInitialized = MAL_FALSE;
|
||||
static float masterVolume = 1.0f;
|
||||
|
||||
// Audio buffers are tracked in a linked list
|
||||
static AudioBuffer *firstAudioBuffer = NULL;
|
||||
static AudioBuffer *firstAudioBuffer = NULL;
|
||||
static AudioBuffer *lastAudioBuffer = NULL;
|
||||
|
||||
// mini_al functions declaration
|
||||
@ -268,7 +268,7 @@ static void OnLog(mal_context *pContext, mal_device *pDevice, const char *messag
|
||||
{
|
||||
(void)pContext;
|
||||
(void)pDevice;
|
||||
|
||||
|
||||
TraceLog(LOG_ERROR, message); // All log messages from mini_al are errors
|
||||
}
|
||||
|
||||
@ -291,30 +291,30 @@ static mal_uint32 OnSendAudioDataToDevice(mal_device *pDevice, mal_uint32 frameC
|
||||
if (!audioBuffer->playing || audioBuffer->paused) continue;
|
||||
|
||||
mal_uint32 framesRead = 0;
|
||||
for (;;)
|
||||
for (;;)
|
||||
{
|
||||
if (framesRead > frameCount)
|
||||
if (framesRead > frameCount)
|
||||
{
|
||||
TraceLog(LOG_DEBUG, "Mixed too many frames from audio buffer");
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if (framesRead == frameCount) break;
|
||||
|
||||
// Just read as much data as we can from the stream.
|
||||
mal_uint32 framesToRead = (frameCount - framesRead);
|
||||
while (framesToRead > 0)
|
||||
while (framesToRead > 0)
|
||||
{
|
||||
float tempBuffer[1024]; // 512 frames for stereo.
|
||||
|
||||
mal_uint32 framesToReadRightNow = framesToRead;
|
||||
if (framesToReadRightNow > sizeof(tempBuffer)/sizeof(tempBuffer[0])/DEVICE_CHANNELS)
|
||||
if (framesToReadRightNow > sizeof(tempBuffer)/sizeof(tempBuffer[0])/DEVICE_CHANNELS)
|
||||
{
|
||||
framesToReadRightNow = sizeof(tempBuffer)/sizeof(tempBuffer[0])/DEVICE_CHANNELS;
|
||||
}
|
||||
|
||||
mal_uint32 framesJustRead = (mal_uint32)mal_dsp_read(&audioBuffer->dsp, framesToReadRightNow, tempBuffer, audioBuffer->dsp.pUserData);
|
||||
if (framesJustRead > 0)
|
||||
if (framesJustRead > 0)
|
||||
{
|
||||
float *framesOut = (float *)pFramesOut + (framesRead*device.channels);
|
||||
float *framesIn = tempBuffer;
|
||||
@ -325,16 +325,16 @@ static mal_uint32 OnSendAudioDataToDevice(mal_device *pDevice, mal_uint32 frameC
|
||||
}
|
||||
|
||||
// If we weren't able to read all the frames we requested, break.
|
||||
if (framesJustRead < framesToReadRightNow)
|
||||
if (framesJustRead < framesToReadRightNow)
|
||||
{
|
||||
if (!audioBuffer->looping)
|
||||
if (!audioBuffer->looping)
|
||||
{
|
||||
StopAudioBuffer(audioBuffer);
|
||||
break;
|
||||
}
|
||||
else
|
||||
}
|
||||
else
|
||||
{
|
||||
// Should never get here, but just for safety,
|
||||
// Should never get here, but just for safety,
|
||||
// move the cursor position back to the start and continue the loop.
|
||||
audioBuffer->frameCursorPos = 0;
|
||||
continue;
|
||||
@ -342,13 +342,13 @@ static mal_uint32 OnSendAudioDataToDevice(mal_device *pDevice, mal_uint32 frameC
|
||||
}
|
||||
}
|
||||
|
||||
// If for some reason we weren't able to read every frame we'll need to break from the loop.
|
||||
// If for some reason we weren't able to read every frame we'll need to break from the loop.
|
||||
// Not doing this could theoretically put us into an infinite loop.
|
||||
if (framesToRead > 0) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
mal_mutex_unlock(&audioLock);
|
||||
|
||||
return frameCount; // We always output the same number of frames that were originally requested.
|
||||
@ -361,8 +361,8 @@ static mal_uint32 OnAudioBufferDSPRead(mal_dsp *pDSP, mal_uint32 frameCount, voi
|
||||
|
||||
mal_uint32 subBufferSizeInFrames = audioBuffer->bufferSizeInFrames/2;
|
||||
mal_uint32 currentSubBufferIndex = audioBuffer->frameCursorPos/subBufferSizeInFrames;
|
||||
|
||||
if (currentSubBufferIndex > 1)
|
||||
|
||||
if (currentSubBufferIndex > 1)
|
||||
{
|
||||
TraceLog(LOG_DEBUG, "Frame cursor position moved too far forward in audio stream");
|
||||
return 0;
|
||||
@ -381,11 +381,11 @@ static mal_uint32 OnAudioBufferDSPRead(mal_dsp *pDSP, mal_uint32 frameCount, voi
|
||||
{
|
||||
// We break from this loop differently depending on the buffer's usage. For static buffers, we simply fill as much data as we can. For
|
||||
// streaming buffers we only fill the halves of the buffer that are processed. Unprocessed halves must keep their audio data in-tact.
|
||||
if (audioBuffer->usage == AUDIO_BUFFER_USAGE_STATIC)
|
||||
if (audioBuffer->usage == AUDIO_BUFFER_USAGE_STATIC)
|
||||
{
|
||||
if (framesRead >= frameCount) break;
|
||||
}
|
||||
else
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isSubBufferProcessed[currentSubBufferIndex]) break;
|
||||
}
|
||||
@ -394,11 +394,11 @@ static mal_uint32 OnAudioBufferDSPRead(mal_dsp *pDSP, mal_uint32 frameCount, voi
|
||||
if (totalFramesRemaining == 0) break;
|
||||
|
||||
mal_uint32 framesRemainingInOutputBuffer;
|
||||
if (audioBuffer->usage == AUDIO_BUFFER_USAGE_STATIC)
|
||||
if (audioBuffer->usage == AUDIO_BUFFER_USAGE_STATIC)
|
||||
{
|
||||
framesRemainingInOutputBuffer = audioBuffer->bufferSizeInFrames - audioBuffer->frameCursorPos;
|
||||
}
|
||||
else
|
||||
}
|
||||
else
|
||||
{
|
||||
mal_uint32 firstFrameIndexOfThisSubBuffer = subBufferSizeInFrames * currentSubBufferIndex;
|
||||
framesRemainingInOutputBuffer = subBufferSizeInFrames - (audioBuffer->frameCursorPos - firstFrameIndexOfThisSubBuffer);
|
||||
@ -412,7 +412,7 @@ static mal_uint32 OnAudioBufferDSPRead(mal_dsp *pDSP, mal_uint32 frameCount, voi
|
||||
framesRead += framesToRead;
|
||||
|
||||
// If we've read to the end of the buffer, mark it as processed.
|
||||
if (framesToRead == framesRemainingInOutputBuffer)
|
||||
if (framesToRead == framesRemainingInOutputBuffer)
|
||||
{
|
||||
audioBuffer->isSubBufferProcessed[currentSubBufferIndex] = true;
|
||||
isSubBufferProcessed[currentSubBufferIndex] = true;
|
||||
@ -420,7 +420,7 @@ static mal_uint32 OnAudioBufferDSPRead(mal_dsp *pDSP, mal_uint32 frameCount, voi
|
||||
currentSubBufferIndex = (currentSubBufferIndex + 1)%2;
|
||||
|
||||
// We need to break from this loop if we're not looping.
|
||||
if (!audioBuffer->looping)
|
||||
if (!audioBuffer->looping)
|
||||
{
|
||||
StopAudioBuffer(audioBuffer);
|
||||
break;
|
||||
@ -430,7 +430,7 @@ static mal_uint32 OnAudioBufferDSPRead(mal_dsp *pDSP, mal_uint32 frameCount, voi
|
||||
|
||||
// Zero-fill excess.
|
||||
mal_uint32 totalFramesRemaining = (frameCount - framesRead);
|
||||
if (totalFramesRemaining > 0)
|
||||
if (totalFramesRemaining > 0)
|
||||
{
|
||||
memset((unsigned char *)pFramesOut + (framesRead*frameSizeInBytes), 0, totalFramesRemaining*frameSizeInBytes);
|
||||
|
||||
@ -447,9 +447,9 @@ static mal_uint32 OnAudioBufferDSPRead(mal_dsp *pDSP, mal_uint32 frameCount, voi
|
||||
// NOTE: framesOut is both an input and an output. It will be initially filled with zeros outside of this function.
|
||||
static void MixAudioFrames(float *framesOut, const float *framesIn, mal_uint32 frameCount, float localVolume)
|
||||
{
|
||||
for (mal_uint32 iFrame = 0; iFrame < frameCount; ++iFrame)
|
||||
for (mal_uint32 iFrame = 0; iFrame < frameCount; ++iFrame)
|
||||
{
|
||||
for (mal_uint32 iChannel = 0; iChannel < device.channels; ++iChannel)
|
||||
for (mal_uint32 iChannel = 0; iChannel < device.channels; ++iChannel)
|
||||
{
|
||||
float *frameOut = framesOut + (iFrame*device.channels);
|
||||
const float *frameIn = framesIn + (iFrame*device.channels);
|
||||
@ -519,7 +519,7 @@ void InitAudioDevice(void)
|
||||
// Close the audio device for all contexts
|
||||
void CloseAudioDevice(void)
|
||||
{
|
||||
if (!isAudioInitialized)
|
||||
if (!isAudioInitialized)
|
||||
{
|
||||
TraceLog(LOG_WARNING, "Could not close audio device because it is not currently initialized");
|
||||
return;
|
||||
@ -543,7 +543,7 @@ void SetMasterVolume(float volume)
|
||||
{
|
||||
if (volume < 0.0f) volume = 0.0f;
|
||||
else if (volume > 1.0f) volume = 1.0f;
|
||||
|
||||
|
||||
masterVolume = volume;
|
||||
}
|
||||
|
||||
@ -574,7 +574,7 @@ AudioBuffer *CreateAudioBuffer(mal_format format, mal_uint32 channels, mal_uint3
|
||||
dspConfig.pUserData = audioBuffer;
|
||||
dspConfig.allowDynamicSampleRate = MAL_TRUE; // <-- Required for pitch shifting.
|
||||
mal_result resultMAL = mal_dsp_init(&dspConfig, &audioBuffer->dsp);
|
||||
if (resultMAL != MAL_SUCCESS)
|
||||
if (resultMAL != MAL_SUCCESS)
|
||||
{
|
||||
TraceLog(LOG_ERROR, "CreateAudioBuffer() : Failed to create data conversion pipeline");
|
||||
free(audioBuffer);
|
||||
@ -716,10 +716,10 @@ void SetAudioBufferPitch(AudioBuffer *audioBuffer, float pitch)
|
||||
void TrackAudioBuffer(AudioBuffer *audioBuffer)
|
||||
{
|
||||
mal_mutex_lock(&audioLock);
|
||||
|
||||
|
||||
{
|
||||
if (firstAudioBuffer == NULL) firstAudioBuffer = audioBuffer;
|
||||
else
|
||||
else
|
||||
{
|
||||
lastAudioBuffer->next = audioBuffer;
|
||||
audioBuffer->prev = lastAudioBuffer;
|
||||
@ -727,7 +727,7 @@ void TrackAudioBuffer(AudioBuffer *audioBuffer)
|
||||
|
||||
lastAudioBuffer = audioBuffer;
|
||||
}
|
||||
|
||||
|
||||
mal_mutex_unlock(&audioLock);
|
||||
}
|
||||
|
||||
@ -735,7 +735,7 @@ void TrackAudioBuffer(AudioBuffer *audioBuffer)
|
||||
void UntrackAudioBuffer(AudioBuffer *audioBuffer)
|
||||
{
|
||||
mal_mutex_lock(&audioLock);
|
||||
|
||||
|
||||
{
|
||||
if (audioBuffer->prev == NULL) firstAudioBuffer = audioBuffer->next;
|
||||
else audioBuffer->prev->next = audioBuffer->next;
|
||||
@ -746,7 +746,7 @@ void UntrackAudioBuffer(AudioBuffer *audioBuffer)
|
||||
audioBuffer->prev = NULL;
|
||||
audioBuffer->next = NULL;
|
||||
}
|
||||
|
||||
|
||||
mal_mutex_unlock(&audioLock);
|
||||
}
|
||||
|
||||
@ -816,7 +816,7 @@ Sound LoadSoundFromWave(Wave wave)
|
||||
{
|
||||
// When using mini_al we need to do our own mixing. To simplify this we need convert the format of each sound to be consistent with
|
||||
// the format used to open the playback device. We can do this two ways:
|
||||
//
|
||||
//
|
||||
// 1) Convert the whole sound in one go at load time (here).
|
||||
// 2) Convert the audio data in chunks at mixing time.
|
||||
//
|
||||
@ -861,7 +861,7 @@ void UnloadSound(Sound sound)
|
||||
void UpdateSound(Sound sound, const void *data, int samplesCount)
|
||||
{
|
||||
AudioBuffer *audioBuffer = (AudioBuffer *)sound.audioBuffer;
|
||||
|
||||
|
||||
if (audioBuffer == NULL)
|
||||
{
|
||||
TraceLog(LOG_ERROR, "UpdateSound() : Invalid sound - no audio buffer");
|
||||
@ -878,9 +878,9 @@ void UpdateSound(Sound sound, const void *data, int samplesCount)
|
||||
void ExportWave(Wave wave, const char *fileName)
|
||||
{
|
||||
bool success = false;
|
||||
|
||||
|
||||
if (IsFileExtension(fileName, ".wav")) success = SaveWAV(wave, fileName);
|
||||
else if (IsFileExtension(fileName, ".raw"))
|
||||
else if (IsFileExtension(fileName, ".raw"))
|
||||
{
|
||||
// Export raw sample data (without header)
|
||||
// NOTE: It's up to the user to track wave parameters
|
||||
@ -888,7 +888,7 @@ void ExportWave(Wave wave, const char *fileName)
|
||||
success = fwrite(wave.data, wave.sampleCount*wave.channels*wave.sampleSize/8, 1, rawFile);
|
||||
fclose(rawFile);
|
||||
}
|
||||
|
||||
|
||||
if (success) TraceLog(LOG_INFO, "Wave exported successfully: %s", fileName);
|
||||
else TraceLog(LOG_WARNING, "Wave could not be exported.");
|
||||
}
|
||||
@ -897,12 +897,12 @@ void ExportWave(Wave wave, const char *fileName)
|
||||
void ExportWaveAsCode(Wave wave, const char *fileName)
|
||||
{
|
||||
#define BYTES_TEXT_PER_LINE 20
|
||||
|
||||
|
||||
char varFileName[256] = { 0 };
|
||||
int dataSize = wave.sampleCount*wave.channels*wave.sampleSize/8;
|
||||
|
||||
|
||||
FILE *txtFile = fopen(fileName, "wt");
|
||||
|
||||
|
||||
fprintf(txtFile, "\n//////////////////////////////////////////////////////////////////////////////////\n");
|
||||
fprintf(txtFile, "// //\n");
|
||||
fprintf(txtFile, "// WaveAsCode exporter v1.0 - Wave data exported as an array of bytes //\n");
|
||||
@ -917,7 +917,7 @@ void ExportWaveAsCode(Wave wave, const char *fileName)
|
||||
// Get file name from path and convert variable name to uppercase
|
||||
strcpy(varFileName, GetFileNameWithoutExt(fileName));
|
||||
for (int i = 0; varFileName[i] != '\0'; i++) if (varFileName[i] >= 'a' && varFileName[i] <= 'z') { varFileName[i] = varFileName[i] - 32; }
|
||||
|
||||
|
||||
fprintf(txtFile, "// Wave data information\n");
|
||||
fprintf(txtFile, "#define %s_SAMPLE_COUNT %i\n", varFileName, wave.sampleCount);
|
||||
fprintf(txtFile, "#define %s_SAMPLE_RATE %i\n", varFileName, wave.sampleRate);
|
||||
@ -983,7 +983,7 @@ void WaveFormat(Wave *wave, int sampleRate, int sampleSize, int channels)
|
||||
mal_uint32 frameCountIn = wave->sampleCount; // Is wave->sampleCount actually the frame count? That terminology needs to change, if so.
|
||||
|
||||
mal_uint32 frameCount = (mal_uint32)mal_convert_frames(NULL, formatOut, channels, sampleRate, NULL, formatIn, wave->channels, wave->sampleRate, frameCountIn);
|
||||
if (frameCount == 0)
|
||||
if (frameCount == 0)
|
||||
{
|
||||
TraceLog(LOG_ERROR, "WaveFormat() : Failed to get frame count for format conversion.");
|
||||
return;
|
||||
@ -992,7 +992,7 @@ void WaveFormat(Wave *wave, int sampleRate, int sampleSize, int channels)
|
||||
void *data = malloc(frameCount*channels*(sampleSize/8));
|
||||
|
||||
frameCount = (mal_uint32)mal_convert_frames(data, formatOut, channels, sampleRate, wave->data, formatIn, wave->channels, wave->sampleRate, frameCountIn);
|
||||
if (frameCount == 0)
|
||||
if (frameCount == 0)
|
||||
{
|
||||
TraceLog(LOG_ERROR, "WaveFormat() : Format conversion failed.");
|
||||
return;
|
||||
@ -1130,16 +1130,16 @@ Music LoadMusicStream(const char *fileName)
|
||||
TraceLog(LOG_INFO, "[%s] MP3 sample rate: %i", fileName, music->ctxMp3.sampleRate);
|
||||
TraceLog(LOG_INFO, "[%s] MP3 bits per sample: %i", fileName, 32);
|
||||
TraceLog(LOG_INFO, "[%s] MP3 channels: %i", fileName, music->ctxMp3.channels);
|
||||
|
||||
|
||||
music->stream = InitAudioStream(music->ctxMp3.sampleRate, 32, music->ctxMp3.channels);
|
||||
|
||||
|
||||
// TODO: There is not an easy way to compute the total number of samples available
|
||||
// in an MP3, frames size could be variable... we tried with a 60 seconds music... but crashes...
|
||||
music->totalSamples = drmp3_get_pcm_frame_count(&music->ctxMp3)*music->ctxMp3.channels;
|
||||
music->samplesLeft = music->totalSamples;
|
||||
music->ctxType = MUSIC_AUDIO_MP3;
|
||||
music->loopCount = -1; // Infinite loop by default
|
||||
|
||||
|
||||
TraceLog(LOG_INFO, "[%s] MP3 total samples: %i", fileName, music->totalSamples);
|
||||
}
|
||||
}
|
||||
@ -1186,7 +1186,7 @@ Music LoadMusicStream(const char *fileName)
|
||||
}
|
||||
#endif
|
||||
else musicLoaded = false;
|
||||
|
||||
|
||||
if (!musicLoaded)
|
||||
{
|
||||
if (music->ctxType == MUSIC_AUDIO_OGG) stb_vorbis_close(music->ctxOgg);
|
||||
@ -1238,7 +1238,7 @@ void UnloadMusicStream(Music music)
|
||||
void PlayMusicStream(Music music)
|
||||
{
|
||||
AudioBuffer *audioBuffer = (AudioBuffer *)music->stream.audioBuffer;
|
||||
|
||||
|
||||
if (audioBuffer == NULL)
|
||||
{
|
||||
TraceLog(LOG_ERROR, "PlayMusicStream() : No audio buffer");
|
||||
@ -1250,7 +1250,7 @@ void PlayMusicStream(Music music)
|
||||
// // just make sure to play again on window restore
|
||||
// if (IsMusicPlaying(music)) PlayMusicStream(music);
|
||||
mal_uint32 frameCursorPos = audioBuffer->frameCursorPos;
|
||||
|
||||
|
||||
PlayAudioStream(music->stream); // <-- This resets the cursor position.
|
||||
|
||||
audioBuffer->frameCursorPos = frameCursorPos;
|
||||
@ -1273,7 +1273,7 @@ void ResumeMusicStream(Music music)
|
||||
void StopMusicStream(Music music)
|
||||
{
|
||||
StopAudioStream(music->stream);
|
||||
|
||||
|
||||
// Restart music context
|
||||
switch (music->ctxType)
|
||||
{
|
||||
@ -1332,18 +1332,18 @@ void UpdateMusicStream(Music music)
|
||||
} break;
|
||||
#endif
|
||||
#if defined(SUPPORT_FILEFORMAT_MP3)
|
||||
case MUSIC_AUDIO_MP3:
|
||||
case MUSIC_AUDIO_MP3:
|
||||
{
|
||||
// NOTE: samplesCount, actually refers to framesCount and returns the number of frames processed
|
||||
unsigned int numFramesMp3 = (unsigned int)drmp3_read_pcm_frames_f32(&music->ctxMp3, samplesCount/music->stream.channels, (float *)pcm);
|
||||
drmp3_read_pcm_frames_f32(&music->ctxMp3, samplesCount/music->stream.channels, (float *)pcm);
|
||||
|
||||
} break;
|
||||
#endif
|
||||
#if defined(SUPPORT_FILEFORMAT_XM)
|
||||
case MUSIC_MODULE_XM:
|
||||
case MUSIC_MODULE_XM:
|
||||
{
|
||||
// NOTE: Internally this function considers 2 channels generation, so samplesCount/2 --> WEIRD
|
||||
jar_xm_generate_samples_16bit(music->ctxXm, (short *)pcm, samplesCount/2);
|
||||
jar_xm_generate_samples_16bit(music->ctxXm, (short *)pcm, samplesCount/2);
|
||||
} break;
|
||||
#endif
|
||||
#if defined(SUPPORT_FILEFORMAT_MOD)
|
||||
@ -1369,7 +1369,7 @@ void UpdateMusicStream(Music music)
|
||||
if (streamEnding)
|
||||
{
|
||||
StopMusicStream(music); // Stop music (and reset)
|
||||
|
||||
|
||||
// Decrease loopCount to stop when required
|
||||
if (music->loopCount > 0)
|
||||
{
|
||||
@ -1475,7 +1475,7 @@ AudioStream InitAudioStream(unsigned int sampleRate, unsigned int sampleSize, un
|
||||
void CloseAudioStream(AudioStream stream)
|
||||
{
|
||||
DeleteAudioBuffer((AudioBuffer *)stream.audioBuffer);
|
||||
|
||||
|
||||
TraceLog(LOG_INFO, "[AUD ID %i] Unloaded audio stream data", stream.source);
|
||||
}
|
||||
|
||||
@ -1494,7 +1494,7 @@ void UpdateAudioStream(AudioStream stream, const void *data, int samplesCount)
|
||||
if (audioBuffer->isSubBufferProcessed[0] || audioBuffer->isSubBufferProcessed[1])
|
||||
{
|
||||
mal_uint32 subBufferToUpdate;
|
||||
|
||||
|
||||
if (audioBuffer->isSubBufferProcessed[0] && audioBuffer->isSubBufferProcessed[1])
|
||||
{
|
||||
// Both buffers are available for updating. Update the first one and make sure the cursor is moved back to the front.
|
||||
@ -1514,7 +1514,7 @@ void UpdateAudioStream(AudioStream stream, const void *data, int samplesCount)
|
||||
if (subBufferSizeInFrames >= (mal_uint32)samplesCount/stream.channels)
|
||||
{
|
||||
mal_uint32 framesToWrite = subBufferSizeInFrames;
|
||||
|
||||
|
||||
if (framesToWrite > ((mal_uint32)samplesCount/stream.channels)) framesToWrite = (mal_uint32)samplesCount/stream.channels;
|
||||
|
||||
mal_uint32 bytesToWrite = framesToWrite*stream.channels*(stream.sampleSize/8);
|
||||
@ -1522,8 +1522,8 @@ void UpdateAudioStream(AudioStream stream, const void *data, int samplesCount)
|
||||
|
||||
// Any leftover frames should be filled with zeros.
|
||||
mal_uint32 leftoverFrameCount = subBufferSizeInFrames - framesToWrite;
|
||||
|
||||
if (leftoverFrameCount > 0)
|
||||
|
||||
if (leftoverFrameCount > 0)
|
||||
{
|
||||
memset(subBuffer + bytesToWrite, 0, leftoverFrameCount*stream.channels*(stream.sampleSize/8));
|
||||
}
|
||||
@ -1723,7 +1723,7 @@ static int SaveWAV(Wave wave, const char *fileName)
|
||||
{
|
||||
int success = 0;
|
||||
int dataSize = wave.sampleCount*wave.channels*wave.sampleSize/8;
|
||||
|
||||
|
||||
// Basic WAV headers structs
|
||||
typedef struct {
|
||||
char chunkID[4];
|
||||
@ -1748,7 +1748,7 @@ static int SaveWAV(Wave wave, const char *fileName)
|
||||
} WaveData;
|
||||
|
||||
FILE *wavFile = fopen(fileName, "wb");
|
||||
|
||||
|
||||
if (wavFile == NULL) TraceLog(LOG_WARNING, "[%s] WAV audio file could not be created", fileName);
|
||||
else
|
||||
{
|
||||
@ -1784,7 +1784,7 @@ static int SaveWAV(Wave wave, const char *fileName)
|
||||
waveData.subChunkID[2] = 't';
|
||||
waveData.subChunkID[3] = 'a';
|
||||
waveData.subChunkSize = dataSize;
|
||||
|
||||
|
||||
success = fwrite(&riffHeader, sizeof(RiffHeader), 1, wavFile);
|
||||
success = fwrite(&waveFormat, sizeof(WaveFormat), 1, wavFile);
|
||||
success = fwrite(&waveData, sizeof(WaveData), 1, wavFile);
|
||||
@ -1793,7 +1793,7 @@ static int SaveWAV(Wave wave, const char *fileName)
|
||||
|
||||
fclose(wavFile);
|
||||
}
|
||||
|
||||
|
||||
// If all data has been written correctly to file, success = 1
|
||||
return success;
|
||||
}
|
||||
@ -1812,7 +1812,7 @@ static Wave LoadOGG(const char *fileName)
|
||||
else
|
||||
{
|
||||
stb_vorbis_info info = stb_vorbis_get_info(oggFile);
|
||||
|
||||
|
||||
wave.sampleRate = info.sample_rate;
|
||||
wave.sampleSize = 16; // 16 bit per sample (short)
|
||||
wave.channels = info.channels;
|
||||
@ -1872,7 +1872,7 @@ static Wave LoadMP3(const char *fileName)
|
||||
uint64_t totalFrameCount = 0;
|
||||
drmp3_config config = { 0 };
|
||||
wave.data = drmp3_open_file_and_read_f32(fileName, &config, &totalFrameCount);
|
||||
|
||||
|
||||
wave.channels = config.outputChannels;
|
||||
wave.sampleRate = config.outputSampleRate;
|
||||
wave.sampleCount = (int)totalFrameCount*wave.channels;
|
||||
@ -1895,7 +1895,7 @@ bool IsFileExtension(const char *fileName, const char *ext)
|
||||
{
|
||||
bool result = false;
|
||||
const char *fileExt;
|
||||
|
||||
|
||||
if ((fileExt = strrchr(fileName, '.')) != NULL)
|
||||
{
|
||||
if (strcmp(fileExt, ext) == 0) result = true;
|
||||
|
@ -45,7 +45,7 @@
|
||||
// Use busy wait loop for timing sync, if not defined, a high-resolution timer is setup and used
|
||||
#define SUPPORT_BUSY_WAIT_LOOP 1
|
||||
// Wait for events passively (sleeping while no events) instead of polling them actively every frame
|
||||
//SUPPORT_EVENTS_WAITING 1
|
||||
//#define SUPPORT_EVENTS_WAITING 1
|
||||
// Allow automatic screen capture of current screen pressing F12, defined in KeyCallback()
|
||||
#define SUPPORT_SCREEN_CAPTURE 1
|
||||
// Allow automatic gif recording of current screen pressing CTRL+F12, defined in KeyCallback()
|
||||
|
277
src/core.c
277
src/core.c
@ -2,12 +2,12 @@
|
||||
*
|
||||
* raylib.core - Basic functions to manage windows, OpenGL context and input on multiple platforms
|
||||
*
|
||||
* PLATFORMS SUPPORTED:
|
||||
* PLATFORMS SUPPORTED:
|
||||
* - PLATFORM_DESKTOP: Windows (Win32, Win64)
|
||||
* - PLATFORM_DESKTOP: Linux (X11 desktop mode)
|
||||
* - PLATFORM_DESKTOP: FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop)
|
||||
* - PLATFORM_DESKTOP: OSX/macOS
|
||||
* - PLATFORM_ANDROID: Android 4.0 (ARM, ARM64)
|
||||
* - PLATFORM_ANDROID: Android 4.0 (ARM, ARM64)
|
||||
* - PLATFORM_RPI: Raspberry Pi 0,1,2,3 (Raspbian)
|
||||
* - PLATFORM_WEB: HTML5 with asm.js (Chrome, Firefox)
|
||||
* - PLATFORM_UWP: Windows 10 App, Windows Phone, Xbox One
|
||||
@ -23,7 +23,7 @@
|
||||
* NOTE: OpenGL ES 2.0 is required and graphic device is managed by EGL
|
||||
*
|
||||
* #define PLATFORM_RPI
|
||||
* Windowing and input system configured for Raspberry Pi i native mode (no X.org required, tested on Raspbian),
|
||||
* Windowing and input system configured for Raspberry Pi i native mode (no X.org required, tested on Raspbian),
|
||||
* graphic device is managed by EGL and inputs are processed is raw mode, reading from /dev/input/
|
||||
*
|
||||
* #define PLATFORM_WEB
|
||||
@ -155,7 +155,7 @@
|
||||
#if defined(_WIN32)
|
||||
#define GLFW_EXPOSE_NATIVE_WIN32
|
||||
#include <GLFW/glfw3native.h> // WARNING: It requires customization to avoid windows.h inclusion!
|
||||
|
||||
|
||||
#if !defined(SUPPORT_BUSY_WAIT_LOOP)
|
||||
// NOTE: Those functions require linking with winmm library
|
||||
unsigned int __stdcall timeBeginPeriod(unsigned int uPeriod);
|
||||
@ -164,7 +164,7 @@
|
||||
|
||||
#elif defined(__linux__)
|
||||
#include <sys/time.h> // Required for: timespec, nanosleep(), select() - POSIX
|
||||
|
||||
|
||||
//#define GLFW_EXPOSE_NATIVE_X11 // WARNING: Exposing Xlib.h > X.h results in dup symbols for Font type
|
||||
//#define GLFW_EXPOSE_NATIVE_WAYLAND
|
||||
//#define GLFW_EXPOSE_NATIVE_MIR
|
||||
@ -172,7 +172,7 @@
|
||||
#elif defined(__APPLE__)
|
||||
#include <unistd.h> // Required for: usleep()
|
||||
#include <objc/message.h> // Required for: objc_msgsend(), sel_registerName()
|
||||
|
||||
|
||||
//#define GLFW_EXPOSE_NATIVE_COCOA // WARNING: Fails due to type redefinition
|
||||
#define GLFW_EXPOSE_NATIVE_NSGL
|
||||
#include <GLFW/glfw3native.h> // Required for: glfwGetCocoaWindow(), glfwGetNSGLContext()
|
||||
@ -225,7 +225,7 @@
|
||||
#define GLFW_INCLUDE_ES2 // GLFW3: Enable OpenGL ES 2.0 (translated to WebGL)
|
||||
#include <GLFW/glfw3.h> // GLFW3 library: Windows, OpenGL context and Input management
|
||||
#include <sys/time.h> // Required for: timespec, nanosleep(), select() - POSIX
|
||||
|
||||
|
||||
#include <emscripten/emscripten.h> // Emscripten library - LLVM to JavaScript compiler
|
||||
#include <emscripten/html5.h> // Emscripten HTML5 library
|
||||
#endif
|
||||
@ -492,7 +492,7 @@ static void *GamepadThread(void *arg); // Mouse reading thread
|
||||
//----------------------------------------------------------------------------------
|
||||
|
||||
#if defined(PLATFORM_ANDROID)
|
||||
// To allow easier porting to android, we allow the user to define a
|
||||
// To allow easier porting to android, we allow the user to define a
|
||||
// main function which we call from android_main, defined by ourselves
|
||||
extern int main(int argc, char *argv[]);
|
||||
|
||||
@ -596,10 +596,10 @@ void InitWindow(int width, int height, const char *title)
|
||||
|
||||
#if defined(PLATFORM_WEB)
|
||||
emscripten_set_fullscreenchange_callback(0, 0, 1, EmscriptenFullscreenChangeCallback);
|
||||
|
||||
|
||||
// Support keyboard events
|
||||
emscripten_set_keypress_callback("#canvas", NULL, 1, EmscriptenKeyboardCallback);
|
||||
|
||||
|
||||
// Support mouse events
|
||||
emscripten_set_click_callback("#canvas", NULL, 1, EmscriptenMouseCallback);
|
||||
|
||||
@ -638,7 +638,7 @@ void CloseWindow(void)
|
||||
gifRecording = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(SUPPORT_DEFAULT_FONT)
|
||||
UnloadDefaultFont();
|
||||
#endif
|
||||
@ -715,7 +715,7 @@ bool WindowShouldClose(void)
|
||||
emscripten_sleep(16);
|
||||
return false;
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(PLATFORM_DESKTOP)
|
||||
if (windowReady)
|
||||
{
|
||||
@ -766,7 +766,7 @@ void SetWindowIcon(Image image)
|
||||
ImageFormat(&image, UNCOMPRESSED_R8G8B8A8);
|
||||
|
||||
GLFWimage icon[1];
|
||||
|
||||
|
||||
icon[0].width = image.width;
|
||||
icon[0].height = image.height;
|
||||
icon[0].pixels = (unsigned char *)image.data;
|
||||
@ -800,8 +800,8 @@ void SetWindowMonitor(int monitor)
|
||||
#if defined(PLATFORM_DESKTOP)
|
||||
int monitorCount;
|
||||
GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
|
||||
|
||||
if ((monitor >= 0) && (monitor < monitorCount))
|
||||
|
||||
if ((monitor >= 0) && (monitor < monitorCount))
|
||||
{
|
||||
//glfwSetWindowMonitor(window, monitors[monitor], 0, 0, screenWidth, screenHeight, GLFW_DONT_CARE);
|
||||
TraceLog(LOG_INFO, "Selected fullscreen monitor: [%i] %s", monitor, glfwGetMonitorName(monitors[monitor]));
|
||||
@ -873,12 +873,12 @@ int GetMonitorCount(void)
|
||||
|
||||
// Get primary monitor width
|
||||
int GetMonitorWidth(int monitor)
|
||||
{
|
||||
{
|
||||
#if defined(PLATFORM_DESKTOP)
|
||||
int monitorCount;
|
||||
GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
|
||||
|
||||
if ((monitor >= 0) && (monitor < monitorCount))
|
||||
|
||||
if ((monitor >= 0) && (monitor < monitorCount))
|
||||
{
|
||||
const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]);
|
||||
return mode->width;
|
||||
@ -890,12 +890,12 @@ int GetMonitorWidth(int monitor)
|
||||
|
||||
// Get primary monitor width
|
||||
int GetMonitorHeight(int monitor)
|
||||
{
|
||||
{
|
||||
#if defined(PLATFORM_DESKTOP)
|
||||
int monitorCount;
|
||||
GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
|
||||
|
||||
if ((monitor >= 0) && (monitor < monitorCount))
|
||||
|
||||
if ((monitor >= 0) && (monitor < monitorCount))
|
||||
{
|
||||
const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]);
|
||||
return mode->height;
|
||||
@ -911,8 +911,8 @@ int GetMonitorPhysicalWidth(int monitor)
|
||||
#if defined(PLATFORM_DESKTOP)
|
||||
int monitorCount;
|
||||
GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
|
||||
|
||||
if ((monitor >= 0) && (monitor < monitorCount))
|
||||
|
||||
if ((monitor >= 0) && (monitor < monitorCount))
|
||||
{
|
||||
int physicalWidth;
|
||||
glfwGetMonitorPhysicalSize(monitors[monitor], &physicalWidth, NULL);
|
||||
@ -929,8 +929,8 @@ int GetMonitorPhysicalHeight(int monitor)
|
||||
#if defined(PLATFORM_DESKTOP)
|
||||
int monitorCount;
|
||||
GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
|
||||
|
||||
if ((monitor >= 0) && (monitor < monitorCount))
|
||||
|
||||
if ((monitor >= 0) && (monitor < monitorCount))
|
||||
{
|
||||
int physicalHeight;
|
||||
glfwGetMonitorPhysicalSize(monitors[monitor], NULL, &physicalHeight);
|
||||
@ -944,11 +944,11 @@ int GetMonitorPhysicalHeight(int monitor)
|
||||
// Get the human-readable, UTF-8 encoded name of the primary monitor
|
||||
const char *GetMonitorName(int monitor)
|
||||
{
|
||||
#if defined(PLATFORM_DESKTOP)
|
||||
#if defined(PLATFORM_DESKTOP)
|
||||
int monitorCount;
|
||||
GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
|
||||
|
||||
if ((monitor >= 0) && (monitor < monitorCount))
|
||||
|
||||
if ((monitor >= 0) && (monitor < monitorCount))
|
||||
{
|
||||
return glfwGetMonitorName(monitors[monitor]);
|
||||
}
|
||||
@ -1038,7 +1038,7 @@ void EndDrawing(void)
|
||||
if (gifRecording)
|
||||
{
|
||||
gifFramesCounter++;
|
||||
|
||||
|
||||
// NOTE: We record one gif frame every 10 game frames
|
||||
if ((gifFramesCounter%GIF_RECORD_FRAMERATE) == 0)
|
||||
{
|
||||
@ -1046,20 +1046,20 @@ void EndDrawing(void)
|
||||
// NOTE: This process is very slow... :(
|
||||
unsigned char *screenData = rlReadScreenPixels(screenWidth, screenHeight);
|
||||
GifWriteFrame(screenData, screenWidth, screenHeight, 10, 8, false);
|
||||
|
||||
|
||||
free(screenData); // Free image data
|
||||
}
|
||||
|
||||
|
||||
if (((gifFramesCounter/15)%2) == 1)
|
||||
{
|
||||
DrawCircle(30, screenHeight - 20, 10, RED);
|
||||
DrawText("RECORDING", 50, screenHeight - 25, 10, MAROON);
|
||||
}
|
||||
|
||||
|
||||
rlglDraw(); // Draw RECORDING message
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
SwapBuffers(); // Copy back buffer to front buffer
|
||||
PollInputEvents(); // Poll user events
|
||||
|
||||
@ -1067,7 +1067,7 @@ void EndDrawing(void)
|
||||
currentTime = GetTime();
|
||||
drawTime = currentTime - previousTime;
|
||||
previousTime = currentTime;
|
||||
|
||||
|
||||
frameTime = updateTime + drawTime;
|
||||
|
||||
// Wait for some milliseconds...
|
||||
@ -1113,14 +1113,14 @@ void EndMode2D(void)
|
||||
void BeginMode3D(Camera3D camera)
|
||||
{
|
||||
rlglDraw(); // Draw Buffers (Only OpenGL 3+ and ES2)
|
||||
|
||||
|
||||
rlMatrixMode(RL_PROJECTION); // Switch to projection matrix
|
||||
rlPushMatrix(); // Save previous matrix, which contains the settings for the 2d ortho projection
|
||||
rlLoadIdentity(); // Reset current matrix (PROJECTION)
|
||||
|
||||
float aspect = (float)screenWidth/(float)screenHeight;
|
||||
|
||||
if (camera.type == CAMERA_PERSPECTIVE)
|
||||
if (camera.type == CAMERA_PERSPECTIVE)
|
||||
{
|
||||
// Setup perspective projection
|
||||
double top = 0.01*tan(camera.fovy*0.5*DEG2RAD);
|
||||
@ -1169,7 +1169,7 @@ void BeginTextureMode(RenderTexture2D target)
|
||||
rlglDraw(); // Draw Buffers (Only OpenGL 3+ and ES2)
|
||||
|
||||
rlEnableRenderTexture(target.id); // Enable render target
|
||||
|
||||
|
||||
rlClearScreenBuffers(); // Clear render texture buffers
|
||||
|
||||
// Set viewport to framebuffer size
|
||||
@ -1230,7 +1230,7 @@ Ray GetMouseRay(Vector2 mousePosition, Camera camera)
|
||||
|
||||
Matrix matProj = MatrixIdentity();
|
||||
|
||||
if (camera.type == CAMERA_PERSPECTIVE)
|
||||
if (camera.type == CAMERA_PERSPECTIVE)
|
||||
{
|
||||
// Calculate projection matrix from perspective
|
||||
matProj = MatrixPerspective(camera.fovy*DEG2RAD, ((double)GetScreenWidth()/(double)GetScreenHeight()), 0.01, 1000.0);
|
||||
@ -1240,7 +1240,7 @@ Ray GetMouseRay(Vector2 mousePosition, Camera camera)
|
||||
float aspect = (float)screenWidth/(float)screenHeight;
|
||||
double top = camera.fovy/2.0;
|
||||
double right = top*aspect;
|
||||
|
||||
|
||||
// Calculate projection matrix from orthographic
|
||||
matProj = MatrixOrtho(-right, right, -top, top, 0.01, 1000.0);
|
||||
}
|
||||
@ -1250,7 +1250,7 @@ Ray GetMouseRay(Vector2 mousePosition, Camera camera)
|
||||
Vector3 farPoint = rlUnproject((Vector3){ deviceCoords.x, deviceCoords.y, 1.0f }, matProj, matView);
|
||||
|
||||
// Unproject the mouse cursor in the near plane.
|
||||
// We need this as the source position because orthographic projects, compared to perspect doesn't have a
|
||||
// We need this as the source position because orthographic projects, compared to perspect doesn't have a
|
||||
// convergence point, meaning that the "eye" of the camera is more like a plane than a point.
|
||||
Vector3 cameraPlanePointerPos = rlUnproject((Vector3){ deviceCoords.x, deviceCoords.y, -1.0f }, matProj, matView);
|
||||
|
||||
@ -1282,7 +1282,7 @@ Vector2 GetWorldToScreen(Vector3 position, Camera camera)
|
||||
float aspect = (float)screenWidth/(float)screenHeight;
|
||||
double top = camera.fovy/2.0;
|
||||
double right = top*aspect;
|
||||
|
||||
|
||||
// Calculate projection matrix from orthographic
|
||||
matProj = MatrixOrtho(-right, right, -top, top, 0.01, 1000.0);
|
||||
}
|
||||
@ -1369,7 +1369,7 @@ Vector4 ColorNormalize(Color color)
|
||||
result.y = (float)color.g/255.0f;
|
||||
result.z = (float)color.b/255.0f;
|
||||
result.w = (float)color.a/255.0f;
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -1389,27 +1389,27 @@ Vector3 ColorToHSV(Color color)
|
||||
|
||||
hsv.z = max; // Value
|
||||
delta = max - min;
|
||||
|
||||
|
||||
if (delta < 0.00001f)
|
||||
{
|
||||
hsv.y = 0.0f;
|
||||
hsv.x = 0.0f; // Undefined, maybe NAN?
|
||||
return hsv;
|
||||
}
|
||||
|
||||
if (max > 0.0f)
|
||||
|
||||
if (max > 0.0f)
|
||||
{
|
||||
// NOTE: If max is 0, this divide would cause a crash
|
||||
hsv.y = (delta/max); // Saturation
|
||||
}
|
||||
else
|
||||
}
|
||||
else
|
||||
{
|
||||
// NOTE: If max is 0, then r = g = b = 0, s = 0, h is undefined
|
||||
hsv.y = 0.0f;
|
||||
hsv.x = NAN; // Undefined
|
||||
return hsv;
|
||||
}
|
||||
|
||||
|
||||
// NOTE: Comparing float values could not work properly
|
||||
if (rgb.x >= max) hsv.x = (rgb.y - rgb.z)/delta; // Between yellow & magenta
|
||||
else
|
||||
@ -1417,7 +1417,7 @@ Vector3 ColorToHSV(Color color)
|
||||
if (rgb.y >= max) hsv.x = 2.0f + (rgb.z - rgb.x)/delta; // Between cyan & yellow
|
||||
else hsv.x = 4.0f + (rgb.x - rgb.y)/delta; // Between magenta & cyan
|
||||
}
|
||||
|
||||
|
||||
hsv.x *= 60.0f; // Convert to degrees
|
||||
|
||||
if (hsv.x < 0.0f) hsv.x += 360.0f;
|
||||
@ -1519,7 +1519,7 @@ bool IsFileExtension(const char *fileName, const char *ext)
|
||||
#if defined(_WIN32)
|
||||
result = true;
|
||||
int extLen = strlen(ext);
|
||||
|
||||
|
||||
if (strlen(fileExt) == extLen)
|
||||
{
|
||||
for (int i = 0; i < extLen; i++)
|
||||
@ -1544,9 +1544,9 @@ bool IsFileExtension(const char *fileName, const char *ext)
|
||||
const char *GetExtension(const char *fileName)
|
||||
{
|
||||
const char *dot = strrchr(fileName, '.');
|
||||
|
||||
|
||||
if (!dot || dot == fileName) return NULL;
|
||||
|
||||
|
||||
return (dot + 1);
|
||||
}
|
||||
|
||||
@ -1572,19 +1572,19 @@ const char *GetFileName(const char *filePath)
|
||||
const char *GetFileNameWithoutExt(const char *filePath)
|
||||
{
|
||||
char *result, *lastDot, *lastSep;
|
||||
|
||||
|
||||
char nameDot = '.'; // Default filename to extension separator character
|
||||
char pathSep = '/'; // Default filepath separator character
|
||||
|
||||
// Error checks and allocate string
|
||||
if (filePath == NULL) return NULL;
|
||||
|
||||
|
||||
// Try to allocate new string, same size as original
|
||||
// NOTE: By default strlen() does not count the '\0' character
|
||||
if ((result = malloc(strlen(filePath) + 1)) == NULL) return NULL;
|
||||
|
||||
|
||||
strcpy(result, filePath); // Make a copy of the string
|
||||
|
||||
|
||||
// NOTE: strrchr() returns a pointer to the last occurrence of character
|
||||
lastDot = strrchr(result, nameDot);
|
||||
lastSep = (pathSep == 0) ? NULL : strrchr(result, pathSep);
|
||||
@ -1593,11 +1593,11 @@ const char *GetFileNameWithoutExt(const char *filePath)
|
||||
{
|
||||
if (lastSep != NULL) // ...and it's before the extenstion separator...
|
||||
{
|
||||
if (lastSep < lastDot)
|
||||
if (lastSep < lastDot)
|
||||
{
|
||||
*lastDot = '\0'; // ...then remove it
|
||||
}
|
||||
}
|
||||
}
|
||||
else *lastDot = '\0'; // Has extension separator with no path separator
|
||||
}
|
||||
|
||||
@ -1626,9 +1626,9 @@ const char *GetWorkingDirectory(void)
|
||||
{
|
||||
static char currentDir[MAX_FILEPATH_LENGTH];
|
||||
memset(currentDir, 0, MAX_FILEPATH_LENGTH);
|
||||
|
||||
|
||||
GETCWD(currentDir, MAX_FILEPATH_LENGTH - 1);
|
||||
|
||||
|
||||
return currentDir;
|
||||
}
|
||||
|
||||
@ -1637,36 +1637,36 @@ const char *GetWorkingDirectory(void)
|
||||
char **GetDirectoryFiles(const char *dirPath, int *fileCount)
|
||||
{
|
||||
#define MAX_DIRECTORY_FILES 512
|
||||
|
||||
|
||||
ClearDirectoryFiles();
|
||||
|
||||
// Memory allocation for MAX_DIRECTORY_FILES
|
||||
dirFilesPath = (char **)malloc(sizeof(char *)*MAX_DIRECTORY_FILES);
|
||||
for (int i = 0; i < MAX_DIRECTORY_FILES; i++) dirFilesPath[i] = (char *)malloc(sizeof(char)*MAX_FILEPATH_LENGTH);
|
||||
|
||||
for (int i = 0; i < MAX_DIRECTORY_FILES; i++) dirFilesPath[i] = (char *)malloc(sizeof(char)*MAX_FILEPATH_LENGTH);
|
||||
|
||||
int counter = 0;
|
||||
struct dirent *ent;
|
||||
DIR *dir = opendir(dirPath);
|
||||
|
||||
|
||||
if (dir != NULL) // It's a directory
|
||||
{
|
||||
// TODO: Reading could be done in two passes,
|
||||
// TODO: Reading could be done in two passes,
|
||||
// first one to count files and second one to read names
|
||||
// That way we can allocate required memory, instead of a limited pool
|
||||
|
||||
|
||||
while ((ent = readdir(dir)) != NULL)
|
||||
{
|
||||
strcpy(dirFilesPath[counter], ent->d_name);
|
||||
counter++;
|
||||
}
|
||||
|
||||
|
||||
closedir(dir);
|
||||
}
|
||||
else TraceLog(LOG_WARNING, "Can not open directory...\n"); // Maybe it's a file...
|
||||
|
||||
dirFilesCount = counter;
|
||||
*fileCount = dirFilesCount;
|
||||
|
||||
|
||||
return dirFilesPath;
|
||||
}
|
||||
|
||||
@ -1729,14 +1729,14 @@ void ClearDroppedFiles(void)
|
||||
RLAPI long GetFileModTime(const char *fileName)
|
||||
{
|
||||
struct stat result = { 0 };
|
||||
|
||||
|
||||
if (stat(fileName, &result) == 0)
|
||||
{
|
||||
time_t mod = result.st_mtime;
|
||||
|
||||
|
||||
return (long)mod;
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -2011,7 +2011,7 @@ bool IsMouseButtonPressed(int button)
|
||||
#else
|
||||
if ((currentMouseState[button] != previousMouseState[button]) && (currentMouseState[button] == 1)) pressed = true;
|
||||
#endif
|
||||
|
||||
|
||||
return pressed;
|
||||
}
|
||||
|
||||
@ -2259,7 +2259,7 @@ static bool InitGraphicsDevice(int width, int height)
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); // Choose OpenGL major version (just hint)
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); // Choose OpenGL minor version (just hint)
|
||||
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // Profiles Hint: Only 3.3 and above!
|
||||
// Other values: GLFW_OPENGL_ANY_PROFILE, GLFW_OPENGL_COMPAT_PROFILE
|
||||
// Values: GLFW_OPENGL_CORE_PROFILE, GLFW_OPENGL_ANY_PROFILE, GLFW_OPENGL_COMPAT_PROFILE
|
||||
#if defined(__APPLE__)
|
||||
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // OSX Requires fordward compatibility
|
||||
#else
|
||||
@ -2357,10 +2357,10 @@ static bool InitGraphicsDevice(int width, int height)
|
||||
#endif
|
||||
|
||||
glfwMakeContextCurrent(window);
|
||||
|
||||
|
||||
// Try to disable GPU V-Sync by default, set framerate using SetTargetFPS()
|
||||
// NOTE: V-Sync can be enabled by graphic driver configuration
|
||||
glfwSwapInterval(0);
|
||||
glfwSwapInterval(0);
|
||||
|
||||
#if defined(PLATFORM_DESKTOP)
|
||||
// Load OpenGL 3.3 extensions
|
||||
@ -2372,6 +2372,7 @@ static bool InitGraphicsDevice(int width, int height)
|
||||
// NOTE: V-Sync can be enabled by graphic driver configuration
|
||||
if (configFlags & FLAG_VSYNC_HINT)
|
||||
{
|
||||
// WARNING: It seems to hits a critical render path in Intel HD Graphics
|
||||
glfwSwapInterval(1);
|
||||
TraceLog(LOG_INFO, "Trying to enable VSYNC");
|
||||
}
|
||||
@ -2444,14 +2445,14 @@ static bool InitGraphicsDevice(int width, int height)
|
||||
// EGL_ANGLE_DISPLAY_ALLOW_RENDER_TO_BACK_BUFFER is an optimization that can have large performance benefits on mobile devices.
|
||||
// Its syntax is subject to change, though. Please update your Visual Studio templates if you experience compilation issues with it.
|
||||
EGL_ANGLE_DISPLAY_ALLOW_RENDER_TO_BACK_BUFFER, EGL_TRUE,
|
||||
|
||||
// EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE is an option that enables ANGLE to automatically call
|
||||
// the IDXGIDevice3::Trim method on behalf of the application when it gets suspended.
|
||||
|
||||
// EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE is an option that enables ANGLE to automatically call
|
||||
// the IDXGIDevice3::Trim method on behalf of the application when it gets suspended.
|
||||
// Calling IDXGIDevice3::Trim when an application is suspended is a Windows Store application certification requirement.
|
||||
EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE, EGL_TRUE,
|
||||
EGL_NONE,
|
||||
};
|
||||
|
||||
|
||||
const EGLint fl9_3DisplayAttributes[] =
|
||||
{
|
||||
// These can be used to request ANGLE's D3D11 renderer, with D3D11 Feature Level 9_3.
|
||||
@ -2474,7 +2475,7 @@ static bool InitGraphicsDevice(int width, int height)
|
||||
EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE, EGL_TRUE,
|
||||
EGL_NONE,
|
||||
};
|
||||
|
||||
|
||||
EGLConfig config = NULL;
|
||||
|
||||
// eglGetPlatformDisplayEXT is an alternative to eglGetDisplay. It allows us to pass in display attributes, used to configure D3D11.
|
||||
@ -2486,15 +2487,15 @@ static bool InitGraphicsDevice(int width, int height)
|
||||
}
|
||||
|
||||
//
|
||||
// To initialize the display, we make three sets of calls to eglGetPlatformDisplayEXT and eglInitialize, with varying
|
||||
// To initialize the display, we make three sets of calls to eglGetPlatformDisplayEXT and eglInitialize, with varying
|
||||
// parameters passed to eglGetPlatformDisplayEXT:
|
||||
// 1) The first calls uses "defaultDisplayAttributes" as a parameter. This corresponds to D3D11 Feature Level 10_0+.
|
||||
// 2) If eglInitialize fails for step 1 (e.g. because 10_0+ isn't supported by the default GPU), then we try again
|
||||
// 2) If eglInitialize fails for step 1 (e.g. because 10_0+ isn't supported by the default GPU), then we try again
|
||||
// using "fl9_3DisplayAttributes". This corresponds to D3D11 Feature Level 9_3.
|
||||
// 3) If eglInitialize fails for step 2 (e.g. because 9_3+ isn't supported by the default GPU), then we try again
|
||||
// 3) If eglInitialize fails for step 2 (e.g. because 9_3+ isn't supported by the default GPU), then we try again
|
||||
// using "warpDisplayAttributes". This corresponds to D3D11 Feature Level 11_0 on WARP, a D3D11 software rasterizer.
|
||||
//
|
||||
|
||||
|
||||
// This tries to initialize EGL to D3D11 Feature Level 10_0+. See above comment for details.
|
||||
display = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, EGL_DEFAULT_DISPLAY, defaultDisplayAttributes);
|
||||
if (display == EGL_NO_DISPLAY)
|
||||
@ -2502,7 +2503,7 @@ static bool InitGraphicsDevice(int width, int height)
|
||||
TraceLog(LOG_WARNING, "Failed to initialize EGL display");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (eglInitialize(display, NULL, NULL) == EGL_FALSE)
|
||||
{
|
||||
// This tries to initialize EGL to D3D11 Feature Level 9_3, if 10_0+ is unavailable (e.g. on some mobile devices).
|
||||
@ -2517,7 +2518,7 @@ static bool InitGraphicsDevice(int width, int height)
|
||||
{
|
||||
// This initializes EGL to D3D11 Feature Level 11_0 on WARP, if 9_3+ is unavailable on the default GPU.
|
||||
display = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, EGL_DEFAULT_DISPLAY, warpDisplayAttributes);
|
||||
if (display == EGL_NO_DISPLAY)
|
||||
if (display == EGL_NO_DISPLAY)
|
||||
{
|
||||
TraceLog(LOG_WARNING, "Failed to initialize EGL display");
|
||||
return false;
|
||||
@ -2545,7 +2546,7 @@ static bool InitGraphicsDevice(int width, int height)
|
||||
//PropertySet^ surfaceCreationProperties = ref new PropertySet();
|
||||
//surfaceCreationProperties->Insert(ref new String(EGLNativeWindowTypeProperty), window); // CoreWindow^ window
|
||||
|
||||
// You can configure the surface to render at a lower resolution and be scaled up to
|
||||
// You can configure the surface to render at a lower resolution and be scaled up to
|
||||
// the full window size. The scaling is often free on mobile hardware.
|
||||
//
|
||||
// One way to configure the SwapChainPanel is to specify precisely which resolution it should render at.
|
||||
@ -2557,20 +2558,20 @@ static bool InitGraphicsDevice(int width, int height)
|
||||
// float customResolutionScale = 0.5f;
|
||||
// surfaceCreationProperties->Insert(ref new String(EGLRenderResolutionScaleProperty), PropertyValue::CreateSingle(customResolutionScale));
|
||||
|
||||
|
||||
// eglCreateWindowSurface() requires a EGLNativeWindowType parameter,
|
||||
|
||||
// eglCreateWindowSurface() requires a EGLNativeWindowType parameter,
|
||||
// In Windows platform: typedef HWND EGLNativeWindowType;
|
||||
|
||||
|
||||
|
||||
|
||||
// Property: EGLNativeWindowTypeProperty
|
||||
// Type: IInspectable
|
||||
// Description: Set this property to specify the window type to use for creating a surface.
|
||||
// If this property is missing, surface creation will fail.
|
||||
//
|
||||
//const wchar_t EGLNativeWindowTypeProperty[] = L"EGLNativeWindowTypeProperty";
|
||||
|
||||
|
||||
//https://stackoverflow.com/questions/46550182/how-to-create-eglsurface-using-c-winrt-and-angle
|
||||
|
||||
|
||||
//surface = eglCreateWindowSurface(display, config, reinterpret_cast<IInspectable*>(surfaceCreationProperties), surfaceAttributes);
|
||||
surface = eglCreateWindowSurface(display, config, uwpWindow, surfaceAttributes);
|
||||
if (surface == EGL_NO_SURFACE)
|
||||
@ -2586,16 +2587,16 @@ static bool InitGraphicsDevice(int width, int height)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get EGL display window size
|
||||
// Get EGL display window size
|
||||
eglQuerySurface(display, surface, EGL_WIDTH, &screenWidth);
|
||||
eglQuerySurface(display, surface, EGL_HEIGHT, &screenHeight);
|
||||
|
||||
|
||||
#else // PLATFORM_ANDROID, PLATFORM_RPI
|
||||
EGLint numConfigs;
|
||||
|
||||
// Get an EGL display connection
|
||||
display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
|
||||
if (display == EGL_NO_DISPLAY)
|
||||
if (display == EGL_NO_DISPLAY)
|
||||
{
|
||||
TraceLog(LOG_WARNING, "Failed to initialize EGL display");
|
||||
return false;
|
||||
@ -2828,7 +2829,7 @@ static void SetupFramebuffer(int width, int height)
|
||||
static void InitTimer(void)
|
||||
{
|
||||
srand((unsigned int)time(NULL)); // Initialize random seed
|
||||
|
||||
|
||||
#if !defined(SUPPORT_BUSY_WAIT_LOOP) && defined(_WIN32)
|
||||
timeBeginPeriod(1); // Setup high-resolution timer to 1ms (granularity of 1-2 ms)
|
||||
#endif
|
||||
@ -2931,7 +2932,7 @@ static void PollInputEvents(void)
|
||||
// Register previous mouse states
|
||||
previousMouseWheelY = currentMouseWheelY;
|
||||
currentMouseWheelY = 0;
|
||||
for (int i = 0; i < 3; i++)
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
previousMouseState[i] = currentMouseState[i];
|
||||
currentMouseState[i] = currentMouseStateEvdev[i];
|
||||
@ -3097,11 +3098,11 @@ static void SwapBuffers(void)
|
||||
glfwSwapBuffers(window);
|
||||
#if __APPLE__
|
||||
// Workaround for missing/erroneous initial rendering on macOS
|
||||
if (windowNeedsUpdating)
|
||||
if (windowNeedsUpdating)
|
||||
{
|
||||
// Desugared version of Objective C: [glfwGetNSGLContext(window) update]
|
||||
((id (*)(id, SEL))objc_msgSend)(glfwGetNSGLContext(window), sel_registerName("update"));
|
||||
|
||||
|
||||
windowNeedsUpdating--;
|
||||
}
|
||||
#endif
|
||||
@ -3144,19 +3145,19 @@ static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, i
|
||||
{
|
||||
GifEnd();
|
||||
gifRecording = false;
|
||||
|
||||
|
||||
TraceLog(LOG_INFO, "End animated GIF recording");
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
gifRecording = true;
|
||||
gifFramesCounter = 0;
|
||||
|
||||
|
||||
// NOTE: delay represents the time between frames in the gif, if we capture a gif frame every
|
||||
// 10 game frames and each frame trakes 16.6ms (60fps), delay between gif frames should be ~16.6*10.
|
||||
GifBegin(FormatText("screenrec%03i.gif", screenshotCounter), screenWidth, screenHeight, (int)(GetFrameTime()*10.0f), 8, false);
|
||||
screenshotCounter++;
|
||||
|
||||
|
||||
TraceLog(LOG_INFO, "Begin animated GIF recording: %s", FormatText("screenrec%03i.gif", screenshotCounter));
|
||||
}
|
||||
}
|
||||
@ -3173,7 +3174,7 @@ static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, i
|
||||
else
|
||||
{
|
||||
currentKeyState[key] = action;
|
||||
|
||||
|
||||
// NOTE: lastKeyPressed already registered on CharCallback()
|
||||
//if (action == GLFW_PRESS) lastKeyPressed = key;
|
||||
}
|
||||
@ -3243,12 +3244,12 @@ static void MouseCursorPosCallback(GLFWwindow *window, double x, double y)
|
||||
|
||||
// GLFW3 Char Key Callback, runs on key down (get unicode char value)
|
||||
static void CharCallback(GLFWwindow *window, unsigned int key)
|
||||
{
|
||||
{
|
||||
// NOTE: Registers any key down considering OS keyboard layout but
|
||||
// do not detects action events, those should be managed by user...
|
||||
// https://github.com/glfw/glfw/issues/668#issuecomment-166794907
|
||||
// http://www.glfw.org/docs/latest/input_guide.html#input_char
|
||||
|
||||
|
||||
lastKeyPressed = key;
|
||||
}
|
||||
|
||||
@ -3349,7 +3350,7 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd)
|
||||
{
|
||||
// Init graphics device (display device and OpenGL context)
|
||||
InitGraphicsDevice(screenWidth, screenHeight);
|
||||
|
||||
|
||||
// Init hi-res timer
|
||||
InitTimer();
|
||||
|
||||
@ -3457,15 +3458,15 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event)
|
||||
// Get second touch position
|
||||
touchPosition[1].x = AMotionEvent_getX(event, 1);
|
||||
touchPosition[1].y = AMotionEvent_getY(event, 1);
|
||||
|
||||
|
||||
// Useful functions for gamepad inputs:
|
||||
//AMotionEvent_getAction()
|
||||
//AMotionEvent_getAxisValue()
|
||||
//AMotionEvent_getButtonState()
|
||||
|
||||
|
||||
// Gamepad dpad button presses capturing
|
||||
// TODO: That's weird, key input (or button)
|
||||
// shouldn't come as a TYPE_MOTION event...
|
||||
// shouldn't come as a TYPE_MOTION event...
|
||||
int32_t keycode = AKeyEvent_getKeyCode(event);
|
||||
if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_DOWN)
|
||||
{
|
||||
@ -3541,7 +3542,7 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event)
|
||||
// Normalize gestureEvent.position[x] for screenWidth and screenHeight
|
||||
gestureEvent.position[0].x /= (float)GetScreenWidth();
|
||||
gestureEvent.position[0].y /= (float)GetScreenHeight();
|
||||
|
||||
|
||||
gestureEvent.position[1].x /= (float)GetScreenWidth();
|
||||
gestureEvent.position[1].y /= (float)GetScreenHeight();
|
||||
|
||||
@ -3549,14 +3550,14 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event)
|
||||
ProcessGestureEvent(gestureEvent);
|
||||
}
|
||||
#else
|
||||
|
||||
|
||||
// Support only simple touch position
|
||||
if (flags == AMOTION_EVENT_ACTION_DOWN)
|
||||
{
|
||||
// Get first touch position
|
||||
touchPosition[0].x = AMotionEvent_getX(event, 0);
|
||||
touchPosition[0].y = AMotionEvent_getY(event, 0);
|
||||
|
||||
|
||||
touchPosition[0].x /= (float)GetScreenWidth();
|
||||
touchPosition[0].y /= (float)GetScreenHeight();
|
||||
}
|
||||
@ -3619,10 +3620,10 @@ static EM_BOOL EmscriptenMouseCallback(int eventType, const EmscriptenMouseEvent
|
||||
emscripten_get_pointerlock_status(&plce);
|
||||
//if (plce.isActive) TraceLog(LOG_WARNING, "Pointer lock exit did not work!");
|
||||
}
|
||||
|
||||
|
||||
toggleCursorLock = false;
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -3889,9 +3890,9 @@ static void InitMouse(void)
|
||||
|
||||
// Open the linux directory of "/dev/input"
|
||||
directory = opendir(DEFAULT_EVDEV_PATH);
|
||||
if (directory)
|
||||
if (directory)
|
||||
{
|
||||
while ((entity = readdir(directory)) != NULL)
|
||||
while ((entity = readdir(directory)) != NULL)
|
||||
{
|
||||
if (strncmp("event", entity->d_name, strlen("event")) == 0) // Search for devices named "event*"
|
||||
{
|
||||
@ -3899,7 +3900,7 @@ static void InitMouse(void)
|
||||
EventThreadSpawn(Path); // Identify the device and spawn a thread for it
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
closedir(directory);
|
||||
}
|
||||
else
|
||||
@ -3916,7 +3917,7 @@ static void EventThreadSpawn(char *device)
|
||||
#define BIT(x) (1UL<<OFF(x))
|
||||
#define LONG(x) ((x)/BITS_PER_LONG)
|
||||
#define TEST_BIT(array, bit) ((array[LONG(bit)] >> OFF(bit)) & 1)
|
||||
|
||||
|
||||
struct input_absinfo absinfo;
|
||||
unsigned long evBits[NBITS(EV_MAX)];
|
||||
unsigned long absBits[NBITS(ABS_MAX)];
|
||||
@ -3927,7 +3928,7 @@ static void EventThreadSpawn(char *device)
|
||||
bool hasAbsMulti = false;
|
||||
int freeWorkerId = -1;
|
||||
int fd = -1;
|
||||
|
||||
|
||||
InputEventWorker *worker;
|
||||
|
||||
/////////////////////////////////// Open the device and allocate worker /////////////////////////////////////////////
|
||||
@ -3967,15 +3968,15 @@ static void EventThreadSpawn(char *device)
|
||||
int devNum = 0;
|
||||
char *ptrDevName = strrchr(device, 't');
|
||||
worker->eventNum = -1;
|
||||
|
||||
|
||||
if (ptrDevName != NULL)
|
||||
{
|
||||
if (sscanf(ptrDevName, "t%d", &devNum) == 1)
|
||||
worker->eventNum = devNum;
|
||||
}
|
||||
|
||||
// At this point we have a connection to the device,
|
||||
// but we don't yet know what the device is (Could be
|
||||
// At this point we have a connection to the device,
|
||||
// but we don't yet know what the device is (Could be
|
||||
// many things, even as simple as a power button)
|
||||
|
||||
/////////////////////////////////// Identify the device /////////////////////////////////////////////
|
||||
@ -3983,15 +3984,15 @@ static void EventThreadSpawn(char *device)
|
||||
ioctl(fd, EVIOCGBIT(0, sizeof(evBits)), evBits); // Read a bitfield of the avalable device properties
|
||||
|
||||
// Check for absolute input devices
|
||||
if (TEST_BIT(evBits, EV_ABS))
|
||||
if (TEST_BIT(evBits, EV_ABS))
|
||||
{
|
||||
ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absBits)), absBits);
|
||||
|
||||
// Check for absolute movement support (usualy touchscreens, but also joysticks)
|
||||
if (TEST_BIT(absBits, ABS_X) && TEST_BIT(absBits, ABS_Y))
|
||||
if (TEST_BIT(absBits, ABS_X) && TEST_BIT(absBits, ABS_Y))
|
||||
{
|
||||
hasAbs = true;
|
||||
|
||||
|
||||
// Get the scaling values
|
||||
ioctl(fd, EVIOCGABS(ABS_X), &absinfo);
|
||||
worker->absRange.x = absinfo.minimum;
|
||||
@ -4000,12 +4001,12 @@ static void EventThreadSpawn(char *device)
|
||||
worker->absRange.y = absinfo.minimum;
|
||||
worker->absRange.height = absinfo.maximum - absinfo.minimum;
|
||||
}
|
||||
|
||||
|
||||
// Check for multiple absolute movement support (usualy multitouch touchscreens)
|
||||
if (TEST_BIT(absBits, ABS_MT_POSITION_X) && TEST_BIT(absBits, ABS_MT_POSITION_Y))
|
||||
if (TEST_BIT(absBits, ABS_MT_POSITION_X) && TEST_BIT(absBits, ABS_MT_POSITION_Y))
|
||||
{
|
||||
hasAbsMulti = true;
|
||||
|
||||
|
||||
// Get the scaling values
|
||||
ioctl(fd, EVIOCGABS(ABS_X), &absinfo);
|
||||
worker->absRange.x = absinfo.minimum;
|
||||
@ -4017,15 +4018,15 @@ static void EventThreadSpawn(char *device)
|
||||
}
|
||||
|
||||
// Check for relative movement support (usualy mouse)
|
||||
if (TEST_BIT(evBits, EV_REL))
|
||||
if (TEST_BIT(evBits, EV_REL))
|
||||
{
|
||||
ioctl(fd, EVIOCGBIT(EV_REL, sizeof(relBits)), relBits);
|
||||
|
||||
|
||||
if (TEST_BIT(relBits, REL_X) && TEST_BIT(relBits, REL_Y)) hasRel = true;
|
||||
}
|
||||
|
||||
// Check for button support to determine the device type(usualy on all input devices)
|
||||
if (TEST_BIT(evBits, EV_KEY))
|
||||
if (TEST_BIT(evBits, EV_KEY))
|
||||
{
|
||||
ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keyBits)), keyBits);
|
||||
|
||||
@ -4077,12 +4078,12 @@ static void EventThreadSpawn(char *device)
|
||||
#if defined(USE_LAST_TOUCH_DEVICE)
|
||||
// Find touchscreen with the highest index
|
||||
int maxTouchNumber = -1;
|
||||
|
||||
|
||||
for (int i = 0; i < sizeof(eventWorkers)/sizeof(InputEventWorker); ++i)
|
||||
{
|
||||
if (eventWorkers[i].isTouch && (eventWorkers[i].eventNum > maxTouchNumber)) maxTouchNumber = eventWorkers[i].eventNum;
|
||||
}
|
||||
|
||||
|
||||
// Find toucnscreens with lower indexes
|
||||
for (int i = 0; i < sizeof(eventWorkers)/sizeof(InputEventWorker); ++i)
|
||||
{
|
||||
@ -4134,7 +4135,7 @@ static void *EventThread(void *arg)
|
||||
if (event.code == REL_WHEEL)
|
||||
{
|
||||
currentMouseWheelY += event.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////// Absolute movement parsing ////////////////////////////////////
|
||||
@ -4231,9 +4232,9 @@ static void *EventThread(void *arg)
|
||||
usleep(5000); // Sleep for 5ms to avoid hogging CPU time
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
close(worker->fd);
|
||||
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -511,7 +511,7 @@ typedef struct Mesh {
|
||||
float *tangents; // Vertex tangents (XYZW - 4 components per vertex) (shader-location = 4)
|
||||
unsigned char *colors; // Vertex colors (RGBA - 4 components per vertex) (shader-location = 3)
|
||||
unsigned short *indices;// Vertex indices (in case vertex data comes indexed)
|
||||
|
||||
|
||||
// Animation vertex data
|
||||
float *baseVertices; // Vertex base position (required to apply bones transformations)
|
||||
float *baseNormals; // Vertex base normals (required to apply bones transformations)
|
||||
|
18
src/rlgl.h
18
src/rlgl.h
@ -199,7 +199,7 @@ typedef unsigned char byte;
|
||||
float *tangents; // vertex tangents (XYZW - 4 components per vertex) (shader-location = 4)
|
||||
unsigned char *colors; // vertex colors (RGBA - 4 components per vertex) (shader-location = 3)
|
||||
unsigned short *indices;// vertex indices (in case vertex data comes indexed)
|
||||
|
||||
|
||||
// Animation vertex data
|
||||
float *baseVertices; // Vertex base position (required to apply bones transformations)
|
||||
float *baseNormals; // Vertex base normals (required to apply bones transformations)
|
||||
@ -729,7 +729,7 @@ typedef struct VrStereoConfig {
|
||||
//----------------------------------------------------------------------------------
|
||||
#if !defined(GRAPHICS_API_OPENGL_11) && defined(SUPPORT_DISTORTION_SHADER)
|
||||
// Distortion shader embedded
|
||||
static char distortionFShaderStr[] =
|
||||
static char distortionFShaderStr[] =
|
||||
#if defined(GRAPHICS_API_OPENGL_21)
|
||||
"#version 120 \n"
|
||||
#elif defined(GRAPHICS_API_OPENGL_ES2)
|
||||
@ -1217,9 +1217,9 @@ void rlEnd(void)
|
||||
// WARNING: If we are between rlPushMatrix() and rlPopMatrix() and we need to force a rlglDraw(),
|
||||
// we need to call rlPopMatrix() before to recover *currentMatrix (modelview) for the next forced draw call!
|
||||
// Also noted that if we had multiple matrix pushed, it will require "stackCounter" pops before launching the draw
|
||||
|
||||
|
||||
// TODO: Undoubtely, current rlPushMatrix/rlPopMatrix should be redesigned... or removed... it's not working properly
|
||||
|
||||
|
||||
rlPopMatrix();
|
||||
rlglDraw();
|
||||
}
|
||||
@ -1507,7 +1507,7 @@ void rlDeleteRenderTextures(RenderTexture2D target)
|
||||
{
|
||||
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
|
||||
if (target.texture.id > 0) glDeleteTextures(1, &target.texture.id);
|
||||
if (target.depth.id > 0)
|
||||
if (target.depth.id > 0)
|
||||
{
|
||||
#if defined(GRAPHICS_API_OPENGL_21) || defined(GRAPHICS_API_OPENGL_ES2)
|
||||
glDeleteRenderbuffers(1, &target.depth.id);
|
||||
@ -1640,7 +1640,7 @@ void rlglInit(int width, int height)
|
||||
char *extensions = (char *)glGetString(GL_EXTENSIONS); // One big const string
|
||||
|
||||
// NOTE: We have to duplicate string because glGetString() returns a const string
|
||||
int len = strlen(extensions) + 1;
|
||||
int len = strlen(extensions) + 1;
|
||||
char *extensionsDup = (char *)malloc(len);
|
||||
strcpy(extensionsDup, extensions);
|
||||
|
||||
@ -2260,7 +2260,7 @@ RenderTexture2D rlLoadRenderTexture(int width, int height)
|
||||
}
|
||||
|
||||
if (target.texture.id > 0) glDeleteTextures(1, &target.texture.id);
|
||||
if (target.depth.id > 0)
|
||||
if (target.depth.id > 0)
|
||||
{
|
||||
#if defined(USE_DEPTH_RENDERBUFFER)
|
||||
glDeleteRenderbuffers(1, &target.depth.id);
|
||||
@ -2268,7 +2268,7 @@ RenderTexture2D rlLoadRenderTexture(int width, int height)
|
||||
glDeleteTextures(1, &target.depth.id);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
glDeleteFramebuffers(1, &target.id);
|
||||
}
|
||||
else TraceLog(LOG_INFO, "[FBO ID %i] Framebuffer object created successfully", target.id);
|
||||
@ -2736,7 +2736,7 @@ void rlUnloadMesh(Mesh *mesh)
|
||||
if (mesh->tangents != NULL) free(mesh->tangents);
|
||||
if (mesh->texcoords2 != NULL) free(mesh->texcoords2);
|
||||
if (mesh->indices != NULL) free(mesh->indices);
|
||||
|
||||
|
||||
if (mesh->baseVertices != NULL) free(mesh->baseVertices);
|
||||
if (mesh->baseNormals != NULL) free(mesh->baseNormals);
|
||||
if (mesh->weightBias != NULL) free(mesh->weightBias);
|
||||
|
92
src/shapes.c
92
src/shapes.c
@ -9,7 +9,7 @@
|
||||
* Allows drawing rectangles and text with a single draw call, very useful for GUI systems!
|
||||
*
|
||||
* #define SUPPORT_QUADS_DRAW_MODE
|
||||
* Use QUADS instead of TRIANGLES for drawing when possible.
|
||||
* Use QUADS instead of TRIANGLES for drawing when possible.
|
||||
* Some lines-based shapes could still use lines
|
||||
*
|
||||
* LICENSE: zlib/libpng
|
||||
@ -116,13 +116,13 @@ void DrawLineEx(Vector2 startPos, Vector2 endPos, float thick, Color color)
|
||||
startPos = endPos;
|
||||
endPos = tempPos;
|
||||
}
|
||||
|
||||
|
||||
float dx = endPos.x - startPos.x;
|
||||
float dy = endPos.y - startPos.y;
|
||||
|
||||
|
||||
float d = sqrtf(dx*dx + dy*dy);
|
||||
float angle = asinf(dy/d);
|
||||
|
||||
|
||||
rlEnableTexture(GetShapesTexture().id);
|
||||
|
||||
rlPushMatrix();
|
||||
@ -155,12 +155,12 @@ void DrawLineBezier(Vector2 startPos, Vector2 endPos, float thick, Color color)
|
||||
for (int i = 1; i <= LINE_DIVISIONS; i++)
|
||||
{
|
||||
// Cubic easing in-out
|
||||
// NOTE: Easing is calculated only for y position value
|
||||
// NOTE: Easing is calculated only for y position value
|
||||
current.y = EaseCubicInOut((float)i, startPos.y, endPos.y - startPos.y, (float)LINE_DIVISIONS);
|
||||
current.x = previous.x + (endPos.x - startPos.x)/ (float)LINE_DIVISIONS;
|
||||
|
||||
|
||||
DrawLineEx(previous, current, thick, color);
|
||||
|
||||
|
||||
previous = current;
|
||||
}
|
||||
}
|
||||
@ -176,7 +176,7 @@ void DrawCircle(int centerX, int centerY, float radius, Color color)
|
||||
void DrawCircleGradient(int centerX, int centerY, float radius, Color color1, Color color2)
|
||||
{
|
||||
if (rlCheckBufferLimit(RL_TRIANGLES, 3*36)) rlglDraw();
|
||||
|
||||
|
||||
rlBegin(RL_TRIANGLES);
|
||||
for (int i = 0; i < 360; i += 10)
|
||||
{
|
||||
@ -193,31 +193,31 @@ void DrawCircleGradient(int centerX, int centerY, float radius, Color color1, Co
|
||||
// Draw a color-filled circle (Vector version)
|
||||
// NOTE: On OpenGL 3.3 and ES2 we use QUADS to avoid drawing order issues (view rlglDraw)
|
||||
void DrawCircleV(Vector2 center, float radius, Color color)
|
||||
{
|
||||
{
|
||||
#if defined(SUPPORT_QUADS_DRAW_MODE)
|
||||
if (rlCheckBufferLimit(RL_QUADS, 4*(36/2))) rlglDraw();
|
||||
|
||||
|
||||
rlEnableTexture(GetShapesTexture().id);
|
||||
|
||||
rlBegin(RL_QUADS);
|
||||
for (int i = 0; i < 360; i += 20)
|
||||
{
|
||||
rlColor4ub(color.r, color.g, color.b, color.a);
|
||||
|
||||
|
||||
rlTexCoord2f(recTexShapes.x/texShapes.width, recTexShapes.y/texShapes.height);
|
||||
rlVertex2f(center.x, center.y);
|
||||
|
||||
|
||||
rlTexCoord2f(recTexShapes.x/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
|
||||
rlVertex2f(center.x + sinf(DEG2RAD*i)*radius, center.y + cosf(DEG2RAD*i)*radius);
|
||||
|
||||
|
||||
rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
|
||||
rlVertex2f(center.x + sinf(DEG2RAD*(i + 10))*radius, center.y + cosf(DEG2RAD*(i + 10))*radius);
|
||||
|
||||
|
||||
rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, recTexShapes.y/texShapes.height);
|
||||
rlVertex2f(center.x + sinf(DEG2RAD*(i + 20))*radius, center.y + cosf(DEG2RAD*(i + 20))*radius);
|
||||
}
|
||||
rlEnd();
|
||||
|
||||
|
||||
rlDisableTexture();
|
||||
#else
|
||||
if (rlCheckBufferLimit(RL_TRIANGLES, 3*(36/2))) rlglDraw();
|
||||
@ -226,7 +226,7 @@ void DrawCircleV(Vector2 center, float radius, Color color)
|
||||
for (int i = 0; i < 360; i += 10)
|
||||
{
|
||||
rlColor4ub(color.r, color.g, color.b, color.a);
|
||||
|
||||
|
||||
rlVertex2f(center.x, center.y);
|
||||
rlVertex2f(center.x + sinf(DEG2RAD*i)*radius, center.y + cosf(DEG2RAD*i)*radius);
|
||||
rlVertex2f(center.x + sinf(DEG2RAD*(i + 10))*radius, center.y + cosf(DEG2RAD*(i + 10))*radius);
|
||||
@ -239,7 +239,7 @@ void DrawCircleV(Vector2 center, float radius, Color color)
|
||||
void DrawCircleLines(int centerX, int centerY, float radius, Color color)
|
||||
{
|
||||
if (rlCheckBufferLimit(RL_LINES, 2*36)) rlglDraw();
|
||||
|
||||
|
||||
rlBegin(RL_LINES);
|
||||
rlColor4ub(color.r, color.g, color.b, color.a);
|
||||
|
||||
@ -263,7 +263,7 @@ void DrawRectangle(int posX, int posY, int width, int height, Color color)
|
||||
void DrawRectangleV(Vector2 position, Vector2 size, Color color)
|
||||
{
|
||||
Color colors[4] = { color, color, color, color };
|
||||
|
||||
|
||||
DrawRectanglePro((Rectangle){ position.x, position.y, size.x, size.y }, (Vector2){ 0.0f, 0.0f }, 0.0f, colors);
|
||||
}
|
||||
|
||||
@ -271,7 +271,7 @@ void DrawRectangleV(Vector2 position, Vector2 size, Color color)
|
||||
void DrawRectangleRec(Rectangle rec, Color color)
|
||||
{
|
||||
Color colors[4] = { color, color, color, color };
|
||||
|
||||
|
||||
DrawRectanglePro(rec, (Vector2){ 0.0f, 0.0f }, 0.0f, colors);
|
||||
}
|
||||
|
||||
@ -292,15 +292,15 @@ void DrawRectanglePro(Rectangle rec, Vector2 origin, float rotation, Color color
|
||||
rlColor4ub(colors[0].r, colors[0].g, colors[0].b, colors[0].a);
|
||||
rlTexCoord2f(recTexShapes.x/texShapes.width, recTexShapes.y/texShapes.height);
|
||||
rlVertex2f(rec.x, rec.y);
|
||||
|
||||
|
||||
rlColor4ub(colors[1].r, colors[1].g, colors[1].b, colors[1].a);
|
||||
rlTexCoord2f(recTexShapes.x/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
|
||||
rlVertex2f(rec.x, rec.y + rec.height);
|
||||
|
||||
|
||||
rlColor4ub(colors[2].r, colors[2].g, colors[2].b, colors[2].a);
|
||||
rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
|
||||
rlVertex2f(rec.x + rec.width, rec.y + rec.height);
|
||||
|
||||
|
||||
rlColor4ub(colors[3].r, colors[3].g, colors[3].b, colors[3].a);
|
||||
rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, recTexShapes.y/texShapes.height);
|
||||
rlVertex2f(rec.x + rec.width, rec.y);
|
||||
@ -329,14 +329,14 @@ void DrawRectangleGradientH(int posX, int posY, int width, int height, Color col
|
||||
void DrawRectangleGradientEx(Rectangle rec, Color col1, Color col2, Color col3, Color col4)
|
||||
{
|
||||
Color colors[4] = { col1, col2, col3, col4 };
|
||||
|
||||
|
||||
DrawRectanglePro(rec, (Vector2){ 0.0f, 0.0f }, 0.0f, colors);
|
||||
}
|
||||
|
||||
// Draw rectangle outline
|
||||
// NOTE: On OpenGL 3.3 and ES2 we use QUADS to avoid drawing order issues (view rlglDraw)
|
||||
void DrawRectangleLines(int posX, int posY, int width, int height, Color color)
|
||||
{
|
||||
{
|
||||
#if defined(SUPPORT_QUADS_DRAW_MODE)
|
||||
DrawRectangle(posX, posY, width, 1, color);
|
||||
DrawRectangle(posX + width - 1, posY + 1, 1, height - 2, color);
|
||||
@ -362,13 +362,13 @@ void DrawRectangleLines(int posX, int posY, int width, int height, Color color)
|
||||
|
||||
// Draw rectangle outline with extended parameters
|
||||
void DrawRectangleLinesEx(Rectangle rec, int lineThick, Color color)
|
||||
{
|
||||
{
|
||||
if (lineThick > rec.width || lineThick > rec.height)
|
||||
{
|
||||
if(rec.width > rec.height) lineThick = (int)rec.height/2;
|
||||
else if (rec.width < rec.height) lineThick = (int)rec.width/2;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
DrawRectangle( (int)rec.x, (int)rec.y, (int)rec.width, lineThick, color);
|
||||
DrawRectangle( (int)(rec.x - lineThick + rec.width), (int)(rec.y + lineThick), lineThick, (int)(rec.height - lineThick*2.0f), color);
|
||||
DrawRectangle( (int)rec.x, (int)(rec.y + rec.height - lineThick), (int)rec.width, lineThick, color);
|
||||
@ -383,20 +383,20 @@ void DrawTriangle(Vector2 v1, Vector2 v2, Vector2 v3, Color color)
|
||||
|
||||
rlBegin(RL_QUADS);
|
||||
rlColor4ub(color.r, color.g, color.b, color.a);
|
||||
|
||||
|
||||
rlTexCoord2f(recTexShapes.x/texShapes.width, recTexShapes.y/texShapes.height);
|
||||
rlVertex2f(v1.x, v1.y);
|
||||
|
||||
|
||||
rlTexCoord2f(recTexShapes.x/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
|
||||
rlVertex2f(v2.x, v2.y);
|
||||
|
||||
|
||||
rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
|
||||
rlVertex2f(v2.x, v2.y);
|
||||
|
||||
|
||||
rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, recTexShapes.y/texShapes.height);
|
||||
rlVertex2f(v3.x, v3.y);
|
||||
rlEnd();
|
||||
|
||||
|
||||
rlDisableTexture();
|
||||
#else
|
||||
rlBegin(RL_TRIANGLES);
|
||||
@ -428,13 +428,13 @@ void DrawTriangleLines(Vector2 v1, Vector2 v2, Vector2 v3, Color color)
|
||||
void DrawPoly(Vector2 center, int sides, float radius, float rotation, Color color)
|
||||
{
|
||||
if (sides < 3) sides = 3;
|
||||
|
||||
|
||||
if (rlCheckBufferLimit(RL_QUADS, 4*(360/sides))) rlglDraw();
|
||||
|
||||
rlPushMatrix();
|
||||
rlTranslatef(center.x, center.y, 0.0);
|
||||
rlRotatef(rotation, 0, 0, 1);
|
||||
|
||||
|
||||
#if defined(SUPPORT_QUADS_DRAW_MODE)
|
||||
rlEnableTexture(GetShapesTexture().id);
|
||||
|
||||
@ -445,13 +445,13 @@ void DrawPoly(Vector2 center, int sides, float radius, float rotation, Color col
|
||||
|
||||
rlTexCoord2f(recTexShapes.x/texShapes.width, recTexShapes.y/texShapes.height);
|
||||
rlVertex2f(0, 0);
|
||||
|
||||
|
||||
rlTexCoord2f(recTexShapes.x/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
|
||||
rlVertex2f(sinf(DEG2RAD*i)*radius, cosf(DEG2RAD*i)*radius);
|
||||
|
||||
|
||||
rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
|
||||
rlVertex2f(sinf(DEG2RAD*i)*radius, cosf(DEG2RAD*i)*radius);
|
||||
|
||||
|
||||
rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, recTexShapes.y/texShapes.height);
|
||||
rlVertex2f(sinf(DEG2RAD*(i + 360/sides))*radius, cosf(DEG2RAD*(i + 360/sides))*radius);
|
||||
}
|
||||
@ -478,7 +478,7 @@ void DrawPolyEx(Vector2 *points, int pointsCount, Color color)
|
||||
if (pointsCount >= 3)
|
||||
{
|
||||
if (rlCheckBufferLimit(RL_QUADS, pointsCount)) rlglDraw();
|
||||
|
||||
|
||||
#if defined(SUPPORT_QUADS_DRAW_MODE)
|
||||
rlEnableTexture(GetShapesTexture().id);
|
||||
|
||||
@ -577,7 +577,7 @@ bool CheckCollisionPointTriangle(Vector2 point, Vector2 p1, Vector2 p2, Vector2
|
||||
bool CheckCollisionRecs(Rectangle rec1, Rectangle rec2)
|
||||
{
|
||||
bool collision = false;
|
||||
|
||||
|
||||
if ((rec1.x <= (rec2.x + rec2.width) && (rec1.x + rec1.width) >= rec2.x) &&
|
||||
(rec1.y <= (rec2.y + rec2.height) && (rec1.y + rec1.height) >= rec2.y)) collision = true;
|
||||
|
||||
@ -605,7 +605,7 @@ bool CheckCollisionCircleRec(Vector2 center, float radius, Rectangle rec)
|
||||
{
|
||||
int recCenterX = (int)(rec.x + rec.width/2.0f);
|
||||
int recCenterY = (int)(rec.y + rec.height/2.0f);
|
||||
|
||||
|
||||
float dx = (float)fabs(center.x - recCenterX);
|
||||
float dy = (float)fabs(center.y - recCenterY);
|
||||
|
||||
@ -615,7 +615,7 @@ bool CheckCollisionCircleRec(Vector2 center, float radius, Rectangle rec)
|
||||
if (dx <= (rec.width/2.0f)) { return true; }
|
||||
if (dy <= (rec.height/2.0f)) { return true; }
|
||||
|
||||
float cornerDistanceSq = (dx - rec.width/2.0f)*(dx - rec.width/2.0f) +
|
||||
float cornerDistanceSq = (dx - rec.width/2.0f)*(dx - rec.width/2.0f) +
|
||||
(dy - rec.height/2.0f)*(dy - rec.height/2.0f);
|
||||
|
||||
return (cornerDistanceSq <= (radius*radius));
|
||||
@ -674,7 +674,7 @@ Rectangle GetCollisionRec(Rectangle rec1, Rectangle rec2)
|
||||
{
|
||||
if (retRec.width >= rec1.width) retRec.width = rec1.width;
|
||||
}
|
||||
|
||||
|
||||
if (rec1.height > rec2.height)
|
||||
{
|
||||
if (retRec.height >= rec2.height) retRec.height = rec2.height;
|
||||
@ -692,10 +692,10 @@ Rectangle GetCollisionRec(Rectangle rec1, Rectangle rec2)
|
||||
// Module specific Functions Definition
|
||||
//----------------------------------------------------------------------------------
|
||||
|
||||
// Cubic easing in-out
|
||||
// Cubic easing in-out
|
||||
// NOTE: Required for DrawLineBezier()
|
||||
static float EaseCubicInOut(float t, float b, float c, float d)
|
||||
{
|
||||
static float EaseCubicInOut(float t, float b, float c, float d)
|
||||
{
|
||||
if ((t /= 0.5f*d) < 1)
|
||||
return 0.5f*c*t*t*t + b;
|
||||
t -= 2;
|
||||
@ -715,6 +715,6 @@ static Texture2D GetShapesTexture(void)
|
||||
recTexShapes = { 0.0f, 0.0f, 1.0f, 1.0f };
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
return texShapes;
|
||||
}
|
||||
|
110
src/text.c
110
src/text.c
@ -200,10 +200,10 @@ extern void LoadDefaultFont(void)
|
||||
|
||||
// Reconstruct charSet using charsWidth[], charsHeight, charsDivisor, charsCount
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
// Allocate space for our characters info data
|
||||
// NOTE: This memory should be freed at end! --> CloseWindow()
|
||||
defaultFont.chars = (CharInfo *)malloc(defaultFont.charsCount*sizeof(CharInfo));
|
||||
defaultFont.chars = (CharInfo *)malloc(defaultFont.charsCount*sizeof(CharInfo));
|
||||
|
||||
int currentLine = 0;
|
||||
int currentPosX = charsDivisor;
|
||||
@ -238,7 +238,7 @@ extern void LoadDefaultFont(void)
|
||||
}
|
||||
|
||||
defaultFont.baseSize = (int)defaultFont.chars[0].rec.height;
|
||||
|
||||
|
||||
TraceLog(LOG_INFO, "[TEX ID %i] Default font loaded successfully", defaultFont.texture.id);
|
||||
}
|
||||
|
||||
@ -258,7 +258,7 @@ Font GetFontDefault()
|
||||
#else
|
||||
Font font = { 0 };
|
||||
return font;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
// Load Font from file into GPU memory (VRAM)
|
||||
@ -301,11 +301,11 @@ Font LoadFont(const char *fileName)
|
||||
Font LoadFontEx(const char *fileName, int fontSize, int charsCount, int *fontChars)
|
||||
{
|
||||
Font font = { 0 };
|
||||
|
||||
|
||||
font.baseSize = fontSize;
|
||||
font.charsCount = (charsCount > 0) ? charsCount : 95;
|
||||
font.chars = LoadFontData(fileName, font.baseSize, fontChars, font.charsCount, FONT_DEFAULT);
|
||||
|
||||
|
||||
if (font.chars != NULL)
|
||||
{
|
||||
Image atlas = GenImageFontAtlas(font.chars, font.charsCount, font.baseSize, 2, 0);
|
||||
@ -313,7 +313,7 @@ Font LoadFontEx(const char *fileName, int fontSize, int charsCount, int *fontCha
|
||||
UnloadImage(atlas);
|
||||
}
|
||||
else font = GetFontDefault();
|
||||
|
||||
|
||||
return font;
|
||||
}
|
||||
|
||||
@ -326,7 +326,7 @@ CharInfo *LoadFontData(const char *fileName, int fontSize, int *fontChars, int c
|
||||
#define SDF_CHAR_PADDING 4
|
||||
#define SDF_ON_EDGE_VALUE 128
|
||||
#define SDF_PIXEL_DIST_SCALE 64.0f
|
||||
|
||||
|
||||
#define BITMAP_ALPHA_THRESHOLD 80
|
||||
|
||||
CharInfo *chars = NULL;
|
||||
@ -335,15 +335,15 @@ CharInfo *LoadFontData(const char *fileName, int fontSize, int *fontChars, int c
|
||||
// NOTE: Loaded information should be enough to generate font image atlas,
|
||||
// using any packaging method
|
||||
FILE *fontFile = fopen(fileName, "rb"); // Load font file
|
||||
|
||||
|
||||
if (fontFile != NULL)
|
||||
{
|
||||
fseek(fontFile, 0, SEEK_END);
|
||||
long size = ftell(fontFile); // Get file size
|
||||
fseek(fontFile, 0, SEEK_SET); // Reset file pointer
|
||||
|
||||
|
||||
unsigned char *fontBuffer = (unsigned char *)malloc(size);
|
||||
|
||||
|
||||
fread(fontBuffer, size, 1, fontFile);
|
||||
fclose(fontFile);
|
||||
|
||||
@ -358,10 +358,10 @@ CharInfo *LoadFontData(const char *fileName, int fontSize, int *fontChars, int c
|
||||
// NOTE: ascent is equivalent to font baseline
|
||||
int ascent, descent, lineGap;
|
||||
stbtt_GetFontVMetrics(&fontInfo, &ascent, &descent, &lineGap);
|
||||
|
||||
|
||||
// In case no chars count provided, default to 95
|
||||
charsCount = (charsCount > 0) ? charsCount : 95;
|
||||
|
||||
|
||||
// Fill fontChars in case not provided externally
|
||||
// NOTE: By default we fill charsCount consecutevely, starting at 32 (Space)
|
||||
int genFontChars = false;
|
||||
@ -373,22 +373,22 @@ CharInfo *LoadFontData(const char *fileName, int fontSize, int *fontChars, int c
|
||||
}
|
||||
|
||||
chars = (CharInfo *)malloc(charsCount*sizeof(CharInfo));
|
||||
|
||||
|
||||
// NOTE: Using simple packaging, one char after another
|
||||
for (int i = 0; i < charsCount; i++)
|
||||
for (int i = 0; i < charsCount; i++)
|
||||
{
|
||||
int chw = 0, chh = 0; // Character width and height (on generation)
|
||||
int ch = fontChars[i]; // Character value to get info for
|
||||
chars[i].value = ch;
|
||||
|
||||
|
||||
// Render a unicode codepoint to a bitmap
|
||||
// stbtt_GetCodepointBitmap() -- allocates and returns a bitmap
|
||||
// stbtt_GetCodepointBitmapBox() -- how big the bitmap must be
|
||||
// stbtt_MakeCodepointBitmap() -- renders into bitmap you provide
|
||||
|
||||
|
||||
if (type != FONT_SDF) chars[i].data = stbtt_GetCodepointBitmap(&fontInfo, scaleFactor, scaleFactor, ch, &chw, &chh, &chars[i].offsetX, &chars[i].offsetY);
|
||||
else if (ch != 32) chars[i].data = stbtt_GetCodepointSDF(&fontInfo, scaleFactor, ch, SDF_CHAR_PADDING, SDF_ON_EDGE_VALUE, SDF_PIXEL_DIST_SCALE, &chw, &chh, &chars[i].offsetX, &chars[i].offsetY);
|
||||
|
||||
|
||||
if (type == FONT_BITMAP)
|
||||
{
|
||||
// Aliased bitmap (black & white) font generation, avoiding anti-aliasing
|
||||
@ -399,15 +399,15 @@ CharInfo *LoadFontData(const char *fileName, int fontSize, int *fontChars, int c
|
||||
else chars[i].data[p] = 255;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
chars[i].rec.width = (float)chw;
|
||||
chars[i].rec.height = (float)chh;
|
||||
chars[i].offsetY += (int)((float)ascent*scaleFactor);
|
||||
|
||||
|
||||
// Get bounding box for character (may be offset to account for chars that dip above or below the line)
|
||||
int chX1, chY1, chX2, chY2;
|
||||
stbtt_GetCodepointBitmapBox(&fontInfo, ch, scaleFactor, scaleFactor, &chX1, &chY1, &chX2, &chY2);
|
||||
|
||||
|
||||
TraceLog(LOG_DEBUG, "Character box measures: %i, %i, %i, %i", chX1, chY1, chX2 - chX1, chY2 - chY1);
|
||||
TraceLog(LOG_DEBUG, "Character offsetY: %i", (int)((float)ascent*scaleFactor) + chY1);
|
||||
|
||||
@ -419,7 +419,7 @@ CharInfo *LoadFontData(const char *fileName, int fontSize, int *fontChars, int c
|
||||
if (genFontChars) free(fontChars);
|
||||
}
|
||||
else TraceLog(LOG_WARNING, "[%s] TTF file could not be opened", fileName);
|
||||
|
||||
|
||||
return chars;
|
||||
}
|
||||
|
||||
@ -428,25 +428,25 @@ CharInfo *LoadFontData(const char *fileName, int fontSize, int *fontChars, int c
|
||||
Image GenImageFontAtlas(CharInfo *chars, int charsCount, int fontSize, int padding, int packMethod)
|
||||
{
|
||||
Image atlas = { 0 };
|
||||
|
||||
|
||||
// In case no chars count provided we suppose default of 95
|
||||
charsCount = (charsCount > 0) ? charsCount : 95;
|
||||
|
||||
|
||||
// Calculate image size based on required pixel area
|
||||
// NOTE 1: Image is forced to be squared and POT... very conservative!
|
||||
// NOTE 2: SDF font characters already contain an internal padding,
|
||||
// NOTE 2: SDF font characters already contain an internal padding,
|
||||
// so image size would result bigger than default font type
|
||||
float requiredArea = 0;
|
||||
for (int i = 0; i < charsCount; i++) requiredArea += ((chars[i].rec.width + 2*padding)*(chars[i].rec.height + 2*padding));
|
||||
float guessSize = sqrtf(requiredArea)*1.25f;
|
||||
int imageSize = (int)powf(2, ceilf(logf((float)guessSize)/logf(2))); // Calculate next POT
|
||||
|
||||
|
||||
atlas.width = imageSize; // Atlas bitmap width
|
||||
atlas.height = imageSize; // Atlas bitmap height
|
||||
atlas.data = (unsigned char *)calloc(1, atlas.width*atlas.height); // Create a bitmap to store characters (8 bpp)
|
||||
atlas.format = UNCOMPRESSED_GRAYSCALE;
|
||||
atlas.mipmaps = 1;
|
||||
|
||||
|
||||
// DEBUG: We can see padding in the generated image setting a gray background...
|
||||
//for (int i = 0; i < atlas.width*atlas.height; i++) ((unsigned char *)atlas.data)[i] = 100;
|
||||
|
||||
@ -454,9 +454,9 @@ Image GenImageFontAtlas(CharInfo *chars, int charsCount, int fontSize, int paddi
|
||||
{
|
||||
int offsetX = padding;
|
||||
int offsetY = padding;
|
||||
|
||||
|
||||
// NOTE: Using simple packaging, one char after another
|
||||
for (int i = 0; i < charsCount; i++)
|
||||
for (int i = 0; i < charsCount; i++)
|
||||
{
|
||||
// Copy pixel data from fc.data to atlas
|
||||
for (int y = 0; y < (int)chars[i].rec.height; y++)
|
||||
@ -466,22 +466,22 @@ Image GenImageFontAtlas(CharInfo *chars, int charsCount, int fontSize, int paddi
|
||||
((unsigned char *)atlas.data)[(offsetY + y)*atlas.width + (offsetX + x)] = chars[i].data[y*(int)chars[i].rec.width + x];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
chars[i].rec.x = (float)offsetX;
|
||||
chars[i].rec.y = (float)offsetY;
|
||||
|
||||
|
||||
// Move atlas position X for next character drawing
|
||||
offsetX += ((int)chars[i].rec.width + 2*padding);
|
||||
|
||||
|
||||
if (offsetX >= (atlas.width - (int)chars[i].rec.width - padding))
|
||||
{
|
||||
offsetX = padding;
|
||||
|
||||
// NOTE: Be careful on offsetY for SDF fonts, by default SDF
|
||||
|
||||
// NOTE: Be careful on offsetY for SDF fonts, by default SDF
|
||||
// use an internal padding of 4 pixels, it means char rectangle
|
||||
// height is bigger than fontSize, it could be up to (fontSize + 8)
|
||||
offsetY += (fontSize + 2*padding);
|
||||
|
||||
|
||||
if (offsetY > (atlas.height - fontSize - padding)) break;
|
||||
}
|
||||
}
|
||||
@ -489,13 +489,13 @@ Image GenImageFontAtlas(CharInfo *chars, int charsCount, int fontSize, int paddi
|
||||
else if (packMethod == 1) // Use Skyline rect packing algorythm (stb_pack_rect)
|
||||
{
|
||||
TraceLog(LOG_DEBUG, "Using Skyline packing algorythm!");
|
||||
|
||||
|
||||
stbrp_context *context = (stbrp_context *)malloc(sizeof(*context));
|
||||
stbrp_node *nodes = (stbrp_node *)malloc(charsCount*sizeof(*nodes));
|
||||
|
||||
stbrp_init_target(context, atlas.width, atlas.height, nodes, charsCount);
|
||||
stbrp_rect *rects = (stbrp_rect *)malloc(charsCount*sizeof(stbrp_rect));
|
||||
|
||||
|
||||
// Fill rectangles for packaging
|
||||
for (int i = 0; i < charsCount; i++)
|
||||
{
|
||||
@ -506,12 +506,12 @@ Image GenImageFontAtlas(CharInfo *chars, int charsCount, int fontSize, int paddi
|
||||
|
||||
// Package rectangles into atlas
|
||||
stbrp_pack_rects(context, rects, charsCount);
|
||||
|
||||
|
||||
for (int i = 0; i < charsCount; i++)
|
||||
{
|
||||
chars[i].rec.x = rects[i].x + (float)padding;
|
||||
chars[i].rec.y = rects[i].y + (float)padding;
|
||||
|
||||
|
||||
if (rects[i].was_packed)
|
||||
{
|
||||
// Copy pixel data from fc.data to atlas
|
||||
@ -530,9 +530,9 @@ Image GenImageFontAtlas(CharInfo *chars, int charsCount, int fontSize, int paddi
|
||||
free(nodes);
|
||||
free(context);
|
||||
}
|
||||
|
||||
|
||||
// TODO: Crop image if required for smaller size
|
||||
|
||||
|
||||
// Convert image data from GRAYSCALE to GRAY_ALPHA
|
||||
// WARNING: ImageAlphaMask(&atlas, atlas) does not work in this case, requires manual operation
|
||||
unsigned char *dataGrayAlpha = (unsigned char *)malloc(imageSize*imageSize*sizeof(unsigned char)*2); // Two channels
|
||||
@ -546,7 +546,7 @@ Image GenImageFontAtlas(CharInfo *chars, int charsCount, int fontSize, int paddi
|
||||
free(atlas.data);
|
||||
atlas.data = dataGrayAlpha;
|
||||
atlas.format = UNCOMPRESSED_GRAY_ALPHA;
|
||||
|
||||
|
||||
return atlas;
|
||||
}
|
||||
|
||||
@ -580,7 +580,7 @@ void DrawFPS(int posX, int posY)
|
||||
refreshRate = fps;
|
||||
counter = 0;
|
||||
}
|
||||
|
||||
|
||||
// NOTE: We have rounding errors every frame, so it oscillates a lot
|
||||
DrawText(FormatText("%2i FPS", fps), posX, posY, 20, LIME);
|
||||
}
|
||||
@ -645,7 +645,7 @@ void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, f
|
||||
i++;
|
||||
}
|
||||
else index = GetGlyphIndex(font, (unsigned char)text[i]);
|
||||
|
||||
|
||||
if ((unsigned char)text[i] != ' ')
|
||||
{
|
||||
DrawTexturePro(font.texture, font.chars[index].rec,
|
||||
@ -795,27 +795,27 @@ char **SplitText(char *text, char delimiter, int *strCount)
|
||||
char *strDup = (char *)malloc(len + 1);
|
||||
strcpy(strDup, text);
|
||||
int counter = 1;
|
||||
|
||||
|
||||
// Count how many substrings we have on string
|
||||
for (int i = 0; i < len; i++) if (text[i] == delimiter) counter++;
|
||||
|
||||
|
||||
// Memory allocation for substrings
|
||||
strings = (char **)malloc(sizeof(char *)*counter);
|
||||
for (int i = 0; i < counter; i++) strings[i] = (char *)malloc(sizeof(char)*MAX_SUBSTRING_LENGTH);
|
||||
|
||||
|
||||
char *substrPtr = NULL;
|
||||
char delimiters[1] = { delimiter }; // Only caring for one delimiter
|
||||
substrPtr = strtok(strDup, delimiters);
|
||||
|
||||
|
||||
for (int i = 0; (i < counter) && (substrPtr != NULL); i++)
|
||||
{
|
||||
strcpy(strings[i], substrPtr);
|
||||
substrPtr = strtok(NULL, delimiters);
|
||||
}
|
||||
|
||||
|
||||
*strCount = counter;
|
||||
free(strDup);
|
||||
|
||||
|
||||
return strings;
|
||||
}
|
||||
|
||||
@ -823,9 +823,9 @@ char **SplitText(char *text, char delimiter, int *strCount)
|
||||
bool IsEqualText(const char *text1, const char *text2)
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
|
||||
if (strcmp(text1, text2) == 0) result = true;
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -861,7 +861,7 @@ static Font LoadImageFont(Image image, Color key, int firstChar)
|
||||
{
|
||||
if (!COLOR_EQUAL(pixels[y*image.width + x], key)) break;
|
||||
}
|
||||
|
||||
|
||||
if (!COLOR_EQUAL(pixels[y*image.width + x], key)) break;
|
||||
}
|
||||
|
||||
@ -1031,10 +1031,10 @@ static Font LoadBMFont(const char *fileName)
|
||||
UnloadImage(imCopy);
|
||||
}
|
||||
else font.texture = LoadTextureFromImage(imFont);
|
||||
|
||||
|
||||
UnloadImage(imFont);
|
||||
free(texPath);
|
||||
|
||||
|
||||
|
||||
// Fill font characters info data
|
||||
font.baseSize = fontSize;
|
||||
|
190
src/textures.c
190
src/textures.c
@ -424,7 +424,7 @@ Color *GetImageData(Image image)
|
||||
if ((image.format == UNCOMPRESSED_R32) ||
|
||||
(image.format == UNCOMPRESSED_R32G32B32) ||
|
||||
(image.format == UNCOMPRESSED_R32G32B32A32)) TraceLog(LOG_WARNING, "32bit pixel format converted to 8bit per channel");
|
||||
|
||||
|
||||
for (int i = 0, k = 0; i < image.width*image.height; i++)
|
||||
{
|
||||
switch (image.format)
|
||||
@ -500,7 +500,7 @@ Color *GetImageData(Image image)
|
||||
pixels[i].g = 0;
|
||||
pixels[i].b = 0;
|
||||
pixels[i].a = 255;
|
||||
|
||||
|
||||
} break;
|
||||
case UNCOMPRESSED_R32G32B32:
|
||||
{
|
||||
@ -508,7 +508,7 @@ Color *GetImageData(Image image)
|
||||
pixels[i].g = (unsigned char)(((float *)image.data)[k + 1]*255.0f);
|
||||
pixels[i].b = (unsigned char)(((float *)image.data)[k + 2]*255.0f);
|
||||
pixels[i].a = 255;
|
||||
|
||||
|
||||
k += 3;
|
||||
}
|
||||
case UNCOMPRESSED_R32G32B32A32:
|
||||
@ -517,7 +517,7 @@ Color *GetImageData(Image image)
|
||||
pixels[i].g = (unsigned char)(((float *)image.data)[k]*255.0f);
|
||||
pixels[i].b = (unsigned char)(((float *)image.data)[k]*255.0f);
|
||||
pixels[i].a = (unsigned char)(((float *)image.data)[k]*255.0f);
|
||||
|
||||
|
||||
k += 4;
|
||||
}
|
||||
default: break;
|
||||
@ -532,7 +532,7 @@ Color *GetImageData(Image image)
|
||||
Vector4 *GetImageDataNormalized(Image image)
|
||||
{
|
||||
Vector4 *pixels = (Vector4 *)malloc(image.width*image.height*sizeof(Vector4));
|
||||
|
||||
|
||||
if (image.format >= COMPRESSED_DXT1_RGB) TraceLog(LOG_WARNING, "Pixel data retrieval not supported for compressed image formats");
|
||||
else
|
||||
{
|
||||
@ -611,7 +611,7 @@ Vector4 *GetImageDataNormalized(Image image)
|
||||
pixels[i].y = 0.0f;
|
||||
pixels[i].z = 0.0f;
|
||||
pixels[i].w = 1.0f;
|
||||
|
||||
|
||||
} break;
|
||||
case UNCOMPRESSED_R32G32B32:
|
||||
{
|
||||
@ -619,7 +619,7 @@ Vector4 *GetImageDataNormalized(Image image)
|
||||
pixels[i].y = ((float *)image.data)[k + 1];
|
||||
pixels[i].z = ((float *)image.data)[k + 2];
|
||||
pixels[i].w = 1.0f;
|
||||
|
||||
|
||||
k += 3;
|
||||
}
|
||||
case UNCOMPRESSED_R32G32B32A32:
|
||||
@ -628,14 +628,14 @@ Vector4 *GetImageDataNormalized(Image image)
|
||||
pixels[i].y = ((float *)image.data)[k + 1];
|
||||
pixels[i].z = ((float *)image.data)[k + 2];
|
||||
pixels[i].w = ((float *)image.data)[k + 3];
|
||||
|
||||
|
||||
k += 4;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return pixels;
|
||||
}
|
||||
|
||||
@ -720,16 +720,16 @@ void UpdateTexture(Texture2D texture, const void *pixels)
|
||||
void ExportImage(Image image, const char *fileName)
|
||||
{
|
||||
int success = 0;
|
||||
|
||||
|
||||
// NOTE: Getting Color array as RGBA unsigned char values
|
||||
unsigned char *imgData = (unsigned char *)GetImageData(image);
|
||||
|
||||
|
||||
if (IsFileExtension(fileName, ".png")) success = stbi_write_png(fileName, image.width, image.height, 4, imgData, image.width*4);
|
||||
else if (IsFileExtension(fileName, ".bmp")) success = stbi_write_bmp(fileName, image.width, image.height, 4, imgData);
|
||||
else if (IsFileExtension(fileName, ".tga")) success = stbi_write_tga(fileName, image.width, image.height, 4, imgData);
|
||||
else if (IsFileExtension(fileName, ".jpg")) success = stbi_write_jpg(fileName, image.width, image.height, 4, imgData, 80); // JPG quality: between 1 and 100
|
||||
else if (IsFileExtension(fileName, ".ktx")) success = SaveKTX(image, fileName);
|
||||
else if (IsFileExtension(fileName, ".raw"))
|
||||
else if (IsFileExtension(fileName, ".raw"))
|
||||
{
|
||||
// Export raw pixel data (without header)
|
||||
// NOTE: It's up to the user to track image parameters
|
||||
@ -740,7 +740,7 @@ void ExportImage(Image image, const char *fileName)
|
||||
|
||||
if (success != 0) TraceLog(LOG_INFO, "Image exported successfully: %s", fileName);
|
||||
else TraceLog(LOG_WARNING, "Image could not be exported.");
|
||||
|
||||
|
||||
free(imgData);
|
||||
}
|
||||
|
||||
@ -748,10 +748,10 @@ void ExportImage(Image image, const char *fileName)
|
||||
void ExportImageAsCode(Image image, const char *fileName)
|
||||
{
|
||||
#define BYTES_TEXT_PER_LINE 20
|
||||
|
||||
|
||||
char varFileName[256] = { 0 };
|
||||
int dataSize = GetPixelDataSize(image.width, image.height, image.format);
|
||||
|
||||
|
||||
FILE *txtFile = fopen(fileName, "wt");
|
||||
|
||||
fprintf(txtFile, "\n//////////////////////////////////////////////////////////////////////////////////////\n");
|
||||
@ -764,11 +764,11 @@ void ExportImageAsCode(Image image, const char *fileName)
|
||||
fprintf(txtFile, "// Copyright (c) 2018 Ramon Santamaria (@raysan5) //\n");
|
||||
fprintf(txtFile, "// //\n");
|
||||
fprintf(txtFile, "////////////////////////////////////////////////////////////////////////////////////////\n\n");
|
||||
|
||||
|
||||
// Get file name from path and convert variable name to uppercase
|
||||
strcpy(varFileName, GetFileNameWithoutExt(fileName));
|
||||
for (int i = 0; varFileName[i] != '\0'; i++) if (varFileName[i] >= 'a' && varFileName[i] <= 'z') { varFileName[i] = varFileName[i] - 32; }
|
||||
|
||||
|
||||
// Add image information
|
||||
fprintf(txtFile, "// Image data information\n");
|
||||
fprintf(txtFile, "#define %s_WIDTH %i\n", varFileName, image.width);
|
||||
@ -987,7 +987,7 @@ void ImageFormat(Image *image, int newFormat)
|
||||
case UNCOMPRESSED_R32:
|
||||
{
|
||||
// WARNING: Image is converted to GRAYSCALE eqeuivalent 32bit
|
||||
|
||||
|
||||
image->data = (float *)malloc(image->width*image->height*sizeof(float));
|
||||
|
||||
for (int i = 0; i < image->width*image->height; i++)
|
||||
@ -1023,7 +1023,7 @@ void ImageFormat(Image *image, int newFormat)
|
||||
|
||||
free(pixels);
|
||||
pixels = NULL;
|
||||
|
||||
|
||||
// In case original image had mipmaps, generate mipmaps for formated image
|
||||
// NOTE: Original mipmaps are replaced by new ones, if custom mipmaps were used, they are lost
|
||||
if (image->mipmaps > 1)
|
||||
@ -1087,14 +1087,14 @@ void ImageAlphaMask(Image *image, Image alphaMask)
|
||||
void ImageAlphaClear(Image *image, Color color, float threshold)
|
||||
{
|
||||
Color *pixels = GetImageData(*image);
|
||||
|
||||
|
||||
for (int i = 0; i < image->width*image->height; i++) if (pixels[i].a <= (unsigned char)(threshold*255.0f)) pixels[i] = color;
|
||||
|
||||
UnloadImage(*image);
|
||||
|
||||
|
||||
int prevFormat = image->format;
|
||||
*image = LoadImageEx(pixels, image->width, image->height);
|
||||
|
||||
|
||||
ImageFormat(image, prevFormat);
|
||||
}
|
||||
|
||||
@ -1102,13 +1102,13 @@ void ImageAlphaClear(Image *image, Color color, float threshold)
|
||||
void ImageAlphaCrop(Image *image, float threshold)
|
||||
{
|
||||
Rectangle crop = { 0 };
|
||||
|
||||
|
||||
Color *pixels = GetImageData(*image);
|
||||
|
||||
|
||||
int minx = 0;
|
||||
int miny = 0;
|
||||
|
||||
for (int i = 0; i < image->width*image->height; i++)
|
||||
for (int i = 0; i < image->width*image->height; i++)
|
||||
{
|
||||
if (pixels[i].a > (unsigned char)(threshold*255.0f))
|
||||
{
|
||||
@ -1127,18 +1127,18 @@ void ImageAlphaCrop(Image *image, float threshold)
|
||||
else if (crop.height < (float)miny) crop.height = (float)miny;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
crop.width -= (crop.x - 1);
|
||||
crop.height -= (crop.y - 1);
|
||||
|
||||
|
||||
TraceLog(LOG_INFO, "Crop rectangle: (%i, %i, %i, %i)", crop.x, crop.y, crop.width, crop.height);
|
||||
|
||||
|
||||
free(pixels);
|
||||
|
||||
|
||||
// NOTE: Added this weird check to avoid additional 1px crop to
|
||||
// image data that has already been cropped...
|
||||
if ((crop.x != 1) &&
|
||||
(crop.y != 1) &&
|
||||
if ((crop.x != 1) &&
|
||||
(crop.y != 1) &&
|
||||
(crop.width != image->width - 1) &&
|
||||
(crop.height != image->height - 1)) ImageCrop(image, crop);
|
||||
}
|
||||
@ -1148,8 +1148,8 @@ void ImageAlphaPremultiply(Image *image)
|
||||
{
|
||||
float alpha = 0.0f;
|
||||
Color *pixels = GetImageData(*image);
|
||||
|
||||
for (int i = 0; i < image->width*image->height; i++)
|
||||
|
||||
for (int i = 0; i < image->width*image->height; i++)
|
||||
{
|
||||
alpha = (float)pixels[i].a/255.0f;
|
||||
pixels[i].r = (unsigned char)((float)pixels[i].r*alpha);
|
||||
@ -1158,10 +1158,10 @@ void ImageAlphaPremultiply(Image *image)
|
||||
}
|
||||
|
||||
UnloadImage(*image);
|
||||
|
||||
|
||||
int prevFormat = image->format;
|
||||
*image = LoadImageEx(pixels, image->width, image->height);
|
||||
|
||||
|
||||
ImageFormat(image, prevFormat);
|
||||
}
|
||||
|
||||
@ -1282,9 +1282,9 @@ void ImageResizeCanvas(Image *image, int newWidth,int newHeight, int offsetX, in
|
||||
Image imTemp = GenImageColor(newWidth, newHeight, color);
|
||||
Rectangle srcRec = { 0.0f, 0.0f, (float)image->width, (float)image->height };
|
||||
Rectangle dstRec = { (float)offsetX, (float)offsetY, (float)srcRec.width, (float)srcRec.height };
|
||||
|
||||
|
||||
// TODO: Review different scaling situations
|
||||
|
||||
|
||||
if ((newWidth > image->width) && (newHeight > image->height))
|
||||
{
|
||||
ImageDraw(&imTemp, *image, srcRec, dstRec);
|
||||
@ -1314,7 +1314,7 @@ void ImageMipmaps(Image *image)
|
||||
{
|
||||
if (mipWidth != 1) mipWidth /= 2;
|
||||
if (mipHeight != 1) mipHeight /= 2;
|
||||
|
||||
|
||||
// Security check for NPOT textures
|
||||
if (mipWidth < 1) mipWidth = 1;
|
||||
if (mipHeight < 1) mipHeight = 1;
|
||||
@ -1332,8 +1332,8 @@ void ImageMipmaps(Image *image)
|
||||
if (image->mipmaps < mipCount)
|
||||
{
|
||||
void *temp = realloc(image->data, mipSize);
|
||||
|
||||
if (temp != NULL)
|
||||
|
||||
if (temp != NULL)
|
||||
{
|
||||
image->data = temp; // Assign new pointer (new size) to store mipmaps data
|
||||
TraceLog(LOG_DEBUG, "Image data memory point reallocated: 0x%x", temp);
|
||||
@ -1342,29 +1342,29 @@ void ImageMipmaps(Image *image)
|
||||
|
||||
// Pointer to allocated memory point where store next mipmap level data
|
||||
unsigned char *nextmip = (unsigned char *)image->data + GetPixelDataSize(image->width, image->height, image->format);
|
||||
|
||||
|
||||
mipWidth = image->width/2;
|
||||
mipHeight = image->height/2;
|
||||
mipSize = GetPixelDataSize(mipWidth, mipHeight, image->format);
|
||||
Image imCopy = ImageCopy(*image);
|
||||
|
||||
|
||||
for (int i = 1; i < mipCount; i++)
|
||||
{
|
||||
TraceLog(LOG_DEBUG, "Gen mipmap level: %i (%i x %i) - size: %i - offset: 0x%x", i, mipWidth, mipHeight, mipSize, nextmip);
|
||||
|
||||
|
||||
ImageResize(&imCopy, mipWidth, mipHeight); // Uses internally Mitchell cubic downscale filter
|
||||
|
||||
memcpy(nextmip, imCopy.data, mipSize);
|
||||
nextmip += mipSize;
|
||||
image->mipmaps++;
|
||||
|
||||
|
||||
mipWidth /= 2;
|
||||
mipHeight /= 2;
|
||||
|
||||
|
||||
// Security check for NPOT textures
|
||||
if (mipWidth < 1) mipWidth = 1;
|
||||
if (mipHeight < 1) mipHeight = 1;
|
||||
|
||||
|
||||
mipSize = GetPixelDataSize(mipWidth, mipHeight, image->format);
|
||||
}
|
||||
|
||||
@ -1487,10 +1487,10 @@ void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp)
|
||||
Color *ImageExtractPalette(Image image, int maxPaletteSize, int *extractCount)
|
||||
{
|
||||
#define COLOR_EQUAL(col1, col2) ((col1.r == col2.r)&&(col1.g == col2.g)&&(col1.b == col2.b)&&(col1.a == col2.a))
|
||||
|
||||
|
||||
Color *pixels = GetImageData(image);
|
||||
Color *palette = (Color *)malloc(maxPaletteSize*sizeof(Color));
|
||||
|
||||
|
||||
int palCount = 0;
|
||||
for (int i = 0; i < maxPaletteSize; i++) palette[i] = BLANK; // Set all colors to BLANK
|
||||
|
||||
@ -1499,23 +1499,23 @@ Color *ImageExtractPalette(Image image, int maxPaletteSize, int *extractCount)
|
||||
if (pixels[i].a > 0)
|
||||
{
|
||||
bool colorInPalette = false;
|
||||
|
||||
|
||||
// Check if the color is already on palette
|
||||
for (int j = 0; j < maxPaletteSize; j++)
|
||||
{
|
||||
if (COLOR_EQUAL(pixels[i], palette[j]))
|
||||
if (COLOR_EQUAL(pixels[i], palette[j]))
|
||||
{
|
||||
colorInPalette = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Store color if not on the palette
|
||||
if (!colorInPalette)
|
||||
{
|
||||
palette[palCount] = pixels[i]; // Add pixels[i] to palette
|
||||
palCount++;
|
||||
|
||||
|
||||
// We reached the limit of colors supported by palette
|
||||
if (palCount >= maxPaletteSize)
|
||||
{
|
||||
@ -1527,9 +1527,9 @@ Color *ImageExtractPalette(Image image, int maxPaletteSize, int *extractCount)
|
||||
}
|
||||
|
||||
free(pixels);
|
||||
|
||||
|
||||
*extractCount = palCount;
|
||||
|
||||
|
||||
return palette;
|
||||
}
|
||||
|
||||
@ -1595,7 +1595,7 @@ void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec)
|
||||
UnloadImage(srcCopy); // Source copy not required any more...
|
||||
|
||||
Vector4 fsrc, fdst, fout; // float based versions of pixel data
|
||||
|
||||
|
||||
// Blit pixels, copy source image into destination
|
||||
// TODO: Probably out-of-bounds blitting could be considered here instead of so much cropping...
|
||||
for (int j = (int)dstRec.y; j < (int)(dstRec.y + dstRec.height); j++)
|
||||
@ -1603,12 +1603,12 @@ void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec)
|
||||
for (int i = (int)dstRec.x; i < (int)(dstRec.x + dstRec.width); i++)
|
||||
{
|
||||
// Alpha blending (https://en.wikipedia.org/wiki/Alpha_compositing)
|
||||
|
||||
|
||||
fdst = ColorNormalize(dstPixels[j*(int)dst->width + i]);
|
||||
fsrc = ColorNormalize(srcPixels[(j - (int)dstRec.y)*(int)dstRec.width + (i - (int)dstRec.x)]);
|
||||
|
||||
fout.w = fsrc.w + fdst.w*(1.0f - fsrc.w);
|
||||
|
||||
|
||||
if (fout.w <= 0.0f)
|
||||
{
|
||||
fout.x = 0.0f;
|
||||
@ -1622,9 +1622,9 @@ void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec)
|
||||
fout.z = (fsrc.z*fsrc.w + fdst.z*fdst.w*(1 - fsrc.w))/fout.w;
|
||||
}
|
||||
|
||||
dstPixels[j*(int)dst->width + i] = (Color){ (unsigned char)(fout.x*255.0f),
|
||||
(unsigned char)(fout.y*255.0f),
|
||||
(unsigned char)(fout.z*255.0f),
|
||||
dstPixels[j*(int)dst->width + i] = (Color){ (unsigned char)(fout.x*255.0f),
|
||||
(unsigned char)(fout.y*255.0f),
|
||||
(unsigned char)(fout.z*255.0f),
|
||||
(unsigned char)(fout.w*255.0f) };
|
||||
|
||||
// TODO: Support other blending options
|
||||
@ -1662,16 +1662,16 @@ Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Co
|
||||
|
||||
// TODO: ISSUE: Measured text size does not seem to be correct... issue on ImageDraw()
|
||||
Vector2 imSize = MeasureTextEx(font, text, (float)font.baseSize, spacing);
|
||||
|
||||
|
||||
TraceLog(LOG_DEBUG, "Text Image size: %f, %f", imSize.x, imSize.y);
|
||||
|
||||
// NOTE: glGetTexImage() not available in OpenGL ES
|
||||
// TODO: This is horrible, retrieving font texture from GPU!!!
|
||||
// TODO: This is horrible, retrieving font texture from GPU!!!
|
||||
// Define ImageFont struct? or include Image spritefont in Font struct?
|
||||
Image imFont = GetTextureData(font.texture);
|
||||
|
||||
|
||||
ImageFormat(&imFont, UNCOMPRESSED_R8G8B8A8); // Make sure image format could be properly colored!
|
||||
|
||||
|
||||
ImageColorTint(&imFont, tint); // Apply color tint to font
|
||||
|
||||
// Create image to store text
|
||||
@ -1702,10 +1702,10 @@ Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Co
|
||||
else index = GetGlyphIndex(font, (unsigned char)text[i]);
|
||||
|
||||
CharInfo letter = font.chars[index];
|
||||
|
||||
|
||||
if ((unsigned char)text[i] != ' ')
|
||||
{
|
||||
ImageDraw(&imText, imFont, letter.rec, (Rectangle){ (float)(posX + letter.offsetX),
|
||||
ImageDraw(&imText, imFont, letter.rec, (Rectangle){ (float)(posX + letter.offsetX),
|
||||
(float)letter.offsetY, (float)letter.rec.width, (float)letter.rec.height });
|
||||
}
|
||||
|
||||
@ -1734,11 +1734,11 @@ Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Co
|
||||
void ImageDrawRectangle(Image *dst, Vector2 position, Rectangle rec, Color color)
|
||||
{
|
||||
Image imRec = GenImageColor((int)rec.width, (int)rec.height, color);
|
||||
|
||||
|
||||
Rectangle dstRec = { position.x, position.y, (float)imRec.width, (float)imRec.height };
|
||||
|
||||
ImageDraw(dst, imRec, rec, dstRec);
|
||||
|
||||
|
||||
UnloadImage(imRec);
|
||||
}
|
||||
|
||||
@ -2056,13 +2056,13 @@ void ImageColorReplace(Image *image, Color color, Color replace)
|
||||
Image GenImageColor(int width, int height, Color color)
|
||||
{
|
||||
Color *pixels = (Color *)calloc(width*height, sizeof(Color));
|
||||
|
||||
|
||||
for (int i = 0; i < width*height; i++) pixels[i] = color;
|
||||
|
||||
|
||||
Image image = LoadImageEx(pixels, width, height);
|
||||
|
||||
|
||||
free(pixels);
|
||||
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
@ -2120,17 +2120,17 @@ Image GenImageGradientRadial(int width, int height, float density, Color inner,
|
||||
|
||||
float centerX = (float)width/2.0f;
|
||||
float centerY = (float)height/2.0f;
|
||||
|
||||
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
float dist = hypotf((float)x - centerX, (float)y - centerY);
|
||||
float factor = (dist - radius*density)/(radius*(1.0f - density));
|
||||
|
||||
|
||||
factor = (float)fmax(factor, 0.f);
|
||||
factor = (float)fmin(factor, 1.f); // dist can be bigger than radius so we have to check
|
||||
|
||||
|
||||
pixels[y*width + x].r = (int)((float)outer.r*factor + (float)inner.r*(1.0f - factor));
|
||||
pixels[y*width + x].g = (int)((float)outer.g*factor + (float)inner.g*(1.0f - factor));
|
||||
pixels[y*width + x].b = (int)((float)outer.b*factor + (float)inner.b*(1.0f - factor));
|
||||
@ -2192,7 +2192,7 @@ Image GenImagePerlinNoise(int width, int height, int offsetX, int offsetY, float
|
||||
{
|
||||
float nx = (float)(x + offsetX)*scale/(float)width;
|
||||
float ny = (float)(y + offsetY)*scale/(float)height;
|
||||
|
||||
|
||||
// Typical values to start playing with:
|
||||
// lacunarity = ~2.0 -- spacing between successive octaves (use exactly 2.0 for wrapping output)
|
||||
// gain = 0.5 -- relative weighting applied to each successive octave
|
||||
@ -2200,7 +2200,7 @@ Image GenImagePerlinNoise(int width, int height, int offsetX, int offsetY, float
|
||||
|
||||
// NOTE: We need to translate the data from [-1..1] to [0..1]
|
||||
float p = (stb_perlin_fbm_noise3(nx, ny, 1.0f, 2.0f, 0.5f, 6, 0, 0, 0) + 1.0f)/2.0f;
|
||||
|
||||
|
||||
int intensity = (int)(p*255.0f);
|
||||
pixels[y*width + x] = (Color){intensity, intensity, intensity, 255};
|
||||
}
|
||||
@ -2233,7 +2233,7 @@ Image GenImageCellular(int width, int height, int tileSize)
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
int tileY = y/tileSize;
|
||||
|
||||
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
int tileX = x/tileSize;
|
||||
@ -2263,7 +2263,7 @@ Image GenImageCellular(int width, int height, int tileSize)
|
||||
pixels[y*width + x] = (Color){ intensity, intensity, intensity, 255 };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
free(seeds);
|
||||
|
||||
Image image = LoadImageEx(pixels, width, height);
|
||||
@ -2411,7 +2411,7 @@ void DrawTexturePro(Texture2D texture, Rectangle sourceRec, Rectangle destRec, V
|
||||
{
|
||||
float width = (float)texture.width;
|
||||
float height = (float)texture.height;
|
||||
|
||||
|
||||
if (sourceRec.width < 0) sourceRec.x -= sourceRec.width;
|
||||
if (sourceRec.height < 0) sourceRec.y -= sourceRec.height;
|
||||
|
||||
@ -2729,7 +2729,7 @@ static Image LoadDDS(const char *fileName)
|
||||
|
||||
image.width = ddsHeader.width;
|
||||
image.height = ddsHeader.height;
|
||||
|
||||
|
||||
if (ddsHeader.mipmapCount == 0) image.mipmaps = 1; // Parameter not used
|
||||
else image.mipmaps = ddsHeader.mipmapCount;
|
||||
|
||||
@ -2946,9 +2946,9 @@ static Image LoadKTX(const char *fileName)
|
||||
// KTX file Header (64 bytes)
|
||||
// v1.1 - https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/
|
||||
// v2.0 - http://github.khronos.org/KTX-Specification/
|
||||
|
||||
|
||||
// TODO: Support KTX 2.2 specs!
|
||||
|
||||
|
||||
typedef struct {
|
||||
char id[12]; // Identifier: "«KTX 11»\r\n\x1A\n"
|
||||
unsigned int endianness; // Little endian: 0x01 0x02 0x03 0x04
|
||||
@ -3028,11 +3028,11 @@ static Image LoadKTX(const char *fileName)
|
||||
static int SaveKTX(Image image, const char *fileName)
|
||||
{
|
||||
int success = 0;
|
||||
|
||||
|
||||
// KTX file Header (64 bytes)
|
||||
// v1.1 - https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/
|
||||
// v2.0 - http://github.khronos.org/KTX-Specification/ - still on draft, not ready for implementation
|
||||
|
||||
|
||||
typedef struct {
|
||||
char id[12]; // Identifier: "«KTX 11»\r\n\x1A\n" // KTX 2.0: "«KTX 22»\r\n\x1A\n"
|
||||
unsigned int endianness; // Little endian: 0x01 0x02 0x03 0x04
|
||||
@ -3060,11 +3060,11 @@ static int SaveKTX(Image image, const char *fileName)
|
||||
else
|
||||
{
|
||||
KTXHeader ktxHeader;
|
||||
|
||||
|
||||
// KTX identifier (v2.2)
|
||||
//unsigned char id[12] = { '«', 'K', 'T', 'X', ' ', '1', '1', '»', '\r', '\n', '\x1A', '\n' };
|
||||
//unsigned char id[12] = { 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A };
|
||||
|
||||
|
||||
// Get the image header
|
||||
strcpy(ktxHeader.id, "«KTX 11»\r\n\x1A\n"); // KTX 1.1 signature
|
||||
ktxHeader.endianness = 0;
|
||||
@ -3080,28 +3080,28 @@ static int SaveKTX(Image image, const char *fileName)
|
||||
ktxHeader.faces = 1;
|
||||
ktxHeader.mipmapLevels = image.mipmaps; // If it was 0, it means mipmaps should be generated on loading (not for compressed formats)
|
||||
ktxHeader.keyValueDataSize = 0; // No extra data after the header
|
||||
|
||||
|
||||
rlGetGlTextureFormats(image.format, &ktxHeader.glInternalFormat, &ktxHeader.glFormat, &ktxHeader.glType); // rlgl module function
|
||||
ktxHeader.glBaseInternalFormat = ktxHeader.glFormat; // KTX 1.1 only
|
||||
|
||||
|
||||
// NOTE: We can save into a .ktx all PixelFormats supported by raylib, including compressed formats like DXT, ETC or ASTC
|
||||
|
||||
|
||||
if (ktxHeader.glFormat == -1) TraceLog(LOG_WARNING, "Image format not supported for KTX export.");
|
||||
else
|
||||
{
|
||||
success = fwrite(&ktxHeader, sizeof(KTXHeader), 1, ktxFile);
|
||||
|
||||
|
||||
int width = image.width;
|
||||
int height = image.height;
|
||||
int dataOffset = 0;
|
||||
|
||||
|
||||
// Save all mipmaps data
|
||||
for (int i = 0; i < image.mipmaps; i++)
|
||||
{
|
||||
unsigned int dataSize = GetPixelDataSize(width, height, image.format);
|
||||
success = fwrite(&dataSize, sizeof(unsigned int), 1, ktxFile);
|
||||
success = fwrite((unsigned char *)image.data + dataOffset, dataSize, 1, ktxFile);
|
||||
|
||||
|
||||
width /= 2;
|
||||
height /= 2;
|
||||
dataOffset += dataSize;
|
||||
@ -3110,7 +3110,7 @@ static int SaveKTX(Image image, const char *fileName)
|
||||
|
||||
fclose(ktxFile); // Close file pointer
|
||||
}
|
||||
|
||||
|
||||
// If all data has been written correctly to file, success = 1
|
||||
return success;
|
||||
}
|
||||
@ -3321,7 +3321,7 @@ static Image LoadASTC(const char *fileName)
|
||||
TraceLog(LOG_DEBUG, "ASTC image width: %i", image.width);
|
||||
TraceLog(LOG_DEBUG, "ASTC image height: %i", image.height);
|
||||
TraceLog(LOG_DEBUG, "ASTC image blocks: %ix%i", astcHeader.blockX, astcHeader.blockY);
|
||||
|
||||
|
||||
image.mipmaps = 1; // NOTE: ASTC format only contains one mipmap level
|
||||
|
||||
// NOTE: Each block is always stored in 128bit so we can calculate the bpp
|
||||
|
Loading…
Reference in New Issue
Block a user