From 6db44500b75097a581587cba15ce703963d2ced8 Mon Sep 17 00:00:00 2001 From: Joshua Reisenauer Date: Wed, 11 May 2016 00:37:10 -0700 Subject: adding multiple music streams --- src/audio.c | 181 ++++++++++++++++++++++++++--------------------------------- src/audio.h | 9 +-- src/raylib.h | 9 +-- 3 files changed, 91 insertions(+), 108 deletions(-) (limited to 'src') diff --git a/src/audio.c b/src/audio.c index fbf53df6..39be4eeb 100644 --- a/src/audio.c +++ b/src/audio.c @@ -61,6 +61,7 @@ //---------------------------------------------------------------------------------- #define MAX_STREAM_BUFFERS 2 #define MAX_AUDIO_CONTEXTS 4 // Number of open AL sources +#define MAX_MUSIC_STREAMS 2 #if defined(PLATFORM_RPI) || defined(PLATFORM_ANDROID) // NOTE: On RPI and Android should be lower to avoid frame-stalls @@ -81,13 +82,8 @@ typedef struct Music { stb_vorbis *stream; jar_xm_context_t *chipctx; // Stores jar_xm context - - ALuint buffers[MAX_STREAM_BUFFERS]; - ALuint source; - ALenum format; - - int channels; - int sampleRate; + AudioContext_t *ctx; // audio context + int totalSamplesLeft; float totalLengthSeconds; bool loop; @@ -117,8 +113,8 @@ typedef enum { INFO = 0, ERROR, WARNING, DEBUG, OTHER } TraceLogType; //---------------------------------------------------------------------------------- static AudioContext_t* mixChannelsActive_g[MAX_AUDIO_CONTEXTS]; // What mix channels are currently active static bool musicEnabled = false; -static Music currentMusic; // Current music loaded - // NOTE: Only one music file playing at a time +static Music currentMusic[2]; // Current music loaded, up to two can play at the same time + //---------------------------------------------------------------------------------- // Module specific Functions Declaration //---------------------------------------------------------------------------------- @@ -769,151 +765,129 @@ void SetSoundPitch(Sound sound, float pitch) // Start music playing (open stream) void PlayMusicStream(char *fileName) { + int musicIndex; + int mixIndex; + for(musicIndex = 0; musicIndex < MAX_MUSIC_STREAMS; musicIndex++) // find empty music slot + { + if(currentMusic[musicIndex].stream == NULL && !currentMusic[musicIndex].chipTune) break; + else if(musicIndex = MAX_MUSIC_STREAMS - 1) return; + } + for(mixIndex = 0; mixIndex < MAX_AUDIO_CONTEXTS; mixIndex++) // find empty mix channel slot + { + if(mixChannelsActive_g[mixIndex] == NULL) break; + else if(musicIndex = MAX_AUDIO_CONTEXTS - 1) return; + } + + if (strcmp(GetExtension(fileName),"ogg") == 0) { - // Stop current music, clean buffers, unload current stream - StopMusicStream(); - // Open audio stream - currentMusic.stream = stb_vorbis_open_filename(fileName, NULL, NULL); + currentMusic[musicIndex].stream = stb_vorbis_open_filename(fileName, NULL, NULL); - if (currentMusic.stream == NULL) + if (currentMusic[musicIndex].stream == NULL) { TraceLog(WARNING, "[%s] OGG audio file could not be opened", fileName); } else { // Get file info - stb_vorbis_info info = stb_vorbis_get_info(currentMusic.stream); - - currentMusic.channels = info.channels; - currentMusic.sampleRate = info.sample_rate; + stb_vorbis_info info = stb_vorbis_get_info(currentMusic[musicIndex].stream); TraceLog(INFO, "[%s] Ogg sample rate: %i", fileName, info.sample_rate); TraceLog(INFO, "[%s] Ogg channels: %i", fileName, info.channels); TraceLog(DEBUG, "[%s] Temp memory required: %i", fileName, info.temp_memory_required); - if (info.channels == 2) currentMusic.format = AL_FORMAT_STEREO16; - else currentMusic.format = AL_FORMAT_MONO16; + if (info.channels == 2){ + currentMusic[musicIndex].ctx = InitAudioContext(info.sample_rate, mixIndex, 2, false); + } + else{ + currentMusic[musicIndex].ctx = InitAudioContext(info.sample_rate, mixIndex, 1, false); + } - currentMusic.loop = true; // We loop by default + currentMusic[musicIndex].loop = true; // We loop by default musicEnabled = true; - // Create an audio source - alGenSources(1, ¤tMusic.source); // Generate pointer to audio source - - alSourcef(currentMusic.source, AL_PITCH, 1); - alSourcef(currentMusic.source, AL_GAIN, 1); - alSource3f(currentMusic.source, AL_POSITION, 0, 0, 0); - alSource3f(currentMusic.source, AL_VELOCITY, 0, 0, 0); - //alSourcei(currentMusic.source, AL_LOOPING, AL_TRUE); // ERROR: Buffers do not queue! - - // Generate two OpenAL buffers - alGenBuffers(2, currentMusic.buffers); - - // Fill buffers with music... - BufferMusicStream(currentMusic.buffers[0]); - BufferMusicStream(currentMusic.buffers[1]); - - // Queue buffers and start playing - alSourceQueueBuffers(currentMusic.source, 2, currentMusic.buffers); - alSourcePlay(currentMusic.source); - - // NOTE: Regularly, we must check if a buffer has been processed and refill it: UpdateMusicStream() - - currentMusic.totalSamplesLeft = stb_vorbis_stream_length_in_samples(currentMusic.stream) * currentMusic.channels; - currentMusic.totalLengthSeconds = stb_vorbis_stream_length_in_seconds(currentMusic.stream); + currentMusic[musicIndex].totalSamplesLeft = stb_vorbis_stream_length_in_samples(currentMusic[musicIndex].stream) * currentMusic[musicIndex].channels; + currentMusic[musicIndex].totalLengthSeconds = stb_vorbis_stream_length_in_seconds(currentMusic[musicIndex].stream); } } else if (strcmp(GetExtension(fileName),"xm") == 0) { - // Stop current music, clean buffers, unload current stream - StopMusicStream(); - - // new song settings for xm chiptune - currentMusic.chipTune = true; - currentMusic.channels = 2; - currentMusic.sampleRate = 48000; - currentMusic.loop = true; - // only stereo is supported for xm - if(!jar_xm_create_context_from_file(¤tMusic.chipctx, currentMusic.sampleRate, fileName)) + if(!jar_xm_create_context_from_file(¤tMusic[musicIndex].chipctx, 48000, fileName)) { - currentMusic.format = AL_FORMAT_STEREO16; - jar_xm_set_max_loop_count(currentMusic.chipctx, 0); // infinite number of loops - currentMusic.totalSamplesLeft = jar_xm_get_remaining_samples(currentMusic.chipctx); - currentMusic.totalLengthSeconds = ((float)currentMusic.totalSamplesLeft) / ((float)currentMusic.sampleRate); + currentMusic[musicIndex].chipTune = true; + currentMusic[musicIndex].loop = true; + jar_xm_set_max_loop_count(currentMusic[musicIndex].chipctx, 0); // infinite number of loops + currentMusic[musicIndex].totalSamplesLeft = jar_xm_get_remaining_samples(currentMusic[musicIndex].chipctx); + currentMusic[musicIndex].totalLengthSeconds = ((float)currentMusic[musicIndex].totalSamplesLeft) / ((float)currentMusic[musicIndex].sampleRate); musicEnabled = true; - TraceLog(INFO, "[%s] XM number of samples: %i", fileName, currentMusic.totalSamplesLeft); - TraceLog(INFO, "[%s] XM track length: %11.6f sec", fileName, currentMusic.totalLengthSeconds); - - // Set up OpenAL - alGenSources(1, ¤tMusic.source); - alSourcef(currentMusic.source, AL_PITCH, 1); - alSourcef(currentMusic.source, AL_GAIN, 1); - alSource3f(currentMusic.source, AL_POSITION, 0, 0, 0); - alSource3f(currentMusic.source, AL_VELOCITY, 0, 0, 0); - alGenBuffers(2, currentMusic.buffers); - BufferMusicStream(currentMusic.buffers[0]); - BufferMusicStream(currentMusic.buffers[1]); - alSourceQueueBuffers(currentMusic.source, 2, currentMusic.buffers); - alSourcePlay(currentMusic.source); + TraceLog(INFO, "[%s] XM number of samples: %i", fileName, currentMusic[musicIndex].totalSamplesLeft); + TraceLog(INFO, "[%s] XM track length: %11.6f sec", fileName, currentMusic[musicIndex].totalLengthSeconds); - // NOTE: Regularly, we must check if a buffer has been processed and refill it: UpdateMusicStream() + currentMusic[musicIndex].ctx = InitAudioContext(48000, mixIndex, 2, true); } else TraceLog(WARNING, "[%s] XM file could not be opened", fileName); } else TraceLog(WARNING, "[%s] Music extension not recognized, it can't be loaded", fileName); } -// Stop music playing (close stream) -void StopMusicStream(void) +// Stop music playing for individual music index of currentMusic array (close stream) +void StopMusicStream(int index) { - if (musicEnabled) + if (index < MAX_MUSIC_STREAMS && currentMusic[index].ctx) { - alSourceStop(currentMusic.source); - EmptyMusicStream(); // Empty music buffers - alDeleteSources(1, ¤tMusic.source); - alDeleteBuffers(2, currentMusic.buffers); + CloseAudioContext(currentMusic[index].ctx); - if (currentMusic.chipTune) + if (currentMusic[index].chipTune) { - jar_xm_free_context(currentMusic.chipctx); + jar_xm_free_context(currentMusic[index].chipctx); } else { - stb_vorbis_close(currentMusic.stream); + stb_vorbis_close(currentMusic[index].stream); } + + if(!getMusicStreamCount()) musicEnabled = false; } +} - musicEnabled = false; +//get number of music channels active at this time, this does not mean they are playing +int getMusicStreamCount(void) +{ + int musicCount; + for(int musicIndex = 0; musicIndex < MAX_MUSIC_STREAMS; musicIndex++) // find empty music slot + if(currentMusic[musicIndex].stream != NULL || currentMusic[musicIndex].chipTune) musicCount++; + + return musicCount; } // Pause music playing -void PauseMusicStream(void) +void PauseMusicStream(int index) { // Pause music stream if music available! if (musicEnabled) { TraceLog(INFO, "Pausing music stream"); - alSourcePause(currentMusic.source); + UpdateAudioContext(currentMusic[index].ctx, NULL, 0); // pushing null data auto pauses stream musicEnabled = false; } } // Resume music playing -void ResumeMusicStream(void) +void ResumeMusicStream(int index) { // Resume music playing... if music available! ALenum state; - alGetSourcei(currentMusic.source, AL_SOURCE_STATE, &state); - - if (state == AL_PAUSED) - { - TraceLog(INFO, "Resuming music stream"); - alSourcePlay(currentMusic.source); - musicEnabled = true; + if(currentMusic[musicIndex].ctx){ + alGetSourcei(currentMusic[musicIndex].ctx->alSource, AL_SOURCE_STATE, &state); + if (state == AL_PAUSED) + { + TraceLog(INFO, "Resuming music stream"); + alSourcePlay(currentMusic[musicIndex].ctx->alSource); + musicEnabled = true; + } } } @@ -922,17 +896,24 @@ bool IsMusicPlaying(void) { bool playing = false; ALint state; - - alGetSourcei(currentMusic.source, AL_SOURCE_STATE, &state); - if (state == AL_PLAYING) playing = true; + + for(int musicIndex = 0; musicIndex < MAX_MUSIC_STREAMS; musicIndex++) + { + if(currentMusic[musicIndex].ctx){ + alGetSourcei(currentMusic[musicIndex].ctx->alSource, AL_SOURCE_STATE, &state); + if (state == AL_PLAYING) playing = true; + } + } return playing; } // Set volume for music -void SetMusicVolume(float volume) +void SetMusicVolume(int index, float volume) { - alSourcef(currentMusic.source, AL_GAIN, volume); + if(currentMusic[musicIndex].ctx){ + alSourcef(currentMusic[musicIndex].ctx->alSource, AL_GAIN, volume); + } } // Get current music time length (in seconds) diff --git a/src/audio.h b/src/audio.h index afd881b7..a1602bd9 100644 --- a/src/audio.h +++ b/src/audio.h @@ -102,13 +102,14 @@ void SetSoundPitch(Sound sound, float pitch); // Set pitch for void PlayMusicStream(char *fileName); // Start music playing (open stream) void UpdateMusicStream(void); // Updates buffers for music streaming -void StopMusicStream(void); // Stop music playing (close stream) -void PauseMusicStream(void); // Pause music playing -void ResumeMusicStream(void); // Resume playing paused music +void StopMusicStream(int index); // Stop music playing (close stream) +void PauseMusicStream(int index); // Pause music playing +void ResumeMusicStream(int index); // Resume playing paused music bool IsMusicPlaying(void); // Check if music is playing -void SetMusicVolume(float volume); // Set volume for music (1.0 is max level) +void SetMusicVolume(int index, float volume); // Set volume for music (1.0 is max level) float GetMusicTimeLength(void); // Get music time length (in seconds) float GetMusicTimePlayed(void); // Get current music time played (in seconds) +int getMusicStreamCount(void); #ifdef __cplusplus } diff --git a/src/raylib.h b/src/raylib.h index d464c8e9..0c49a085 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -893,13 +893,14 @@ void SetSoundPitch(Sound sound, float pitch); // Set pitch for void PlayMusicStream(char *fileName); // Start music playing (open stream) void UpdateMusicStream(void); // Updates buffers for music streaming -void StopMusicStream(void); // Stop music playing (close stream) -void PauseMusicStream(void); // Pause music playing -void ResumeMusicStream(void); // Resume playing paused music +void StopMusicStream(int index); // Stop music playing (close stream) +void PauseMusicStream(int index); // Pause music playing +void ResumeMusicStream(int index); // Resume playing paused music bool IsMusicPlaying(void); // Check if music is playing -void SetMusicVolume(float volume); // Set volume for music (1.0 is max level) +void SetMusicVolume(int index, float volume); // Set volume for music (1.0 is max level) float GetMusicTimeLength(void); // Get current music time length (in seconds) float GetMusicTimePlayed(void); // Get current music time played (in seconds) +int getMusicStreamCount(void); #ifdef __cplusplus } -- cgit v1.2.3 From ad3d270c429844462f3fd62fa12257760fac24b5 Mon Sep 17 00:00:00 2001 From: Joshua Reisenauer Date: Wed, 11 May 2016 18:14:59 -0700 Subject: added set pitch for music streams --- src/audio.c | 40 ++++++++++++++++++++++------------------ src/audio.h | 3 ++- src/raylib.h | 3 ++- 3 files changed, 26 insertions(+), 20 deletions(-) (limited to 'src') diff --git a/src/audio.c b/src/audio.c index 39be4eeb..f89be8bf 100644 --- a/src/audio.c +++ b/src/audio.c @@ -113,7 +113,7 @@ typedef enum { INFO = 0, ERROR, WARNING, DEBUG, OTHER } TraceLogType; //---------------------------------------------------------------------------------- static AudioContext_t* mixChannelsActive_g[MAX_AUDIO_CONTEXTS]; // What mix channels are currently active static bool musicEnabled = false; -static Music currentMusic[2]; // Current music loaded, up to two can play at the same time +static Music currentMusic[MAX_MUSIC_STREAMS]; // Current music loaded, up to two can play at the same time //---------------------------------------------------------------------------------- // Module specific Functions Declaration @@ -797,18 +797,18 @@ void PlayMusicStream(char *fileName) TraceLog(INFO, "[%s] Ogg channels: %i", fileName, info.channels); TraceLog(DEBUG, "[%s] Temp memory required: %i", fileName, info.temp_memory_required); + currentMusic[musicIndex].loop = true; // We loop by default + musicEnabled = true; + + currentMusic[musicIndex].totalSamplesLeft = stb_vorbis_stream_length_in_samples(currentMusic[musicIndex].stream) * info.channels; + currentMusic[musicIndex].totalLengthSeconds = stb_vorbis_stream_length_in_seconds(currentMusic[musicIndex].stream); + if (info.channels == 2){ currentMusic[musicIndex].ctx = InitAudioContext(info.sample_rate, mixIndex, 2, false); } else{ currentMusic[musicIndex].ctx = InitAudioContext(info.sample_rate, mixIndex, 1, false); } - - currentMusic[musicIndex].loop = true; // We loop by default - musicEnabled = true; - - currentMusic[musicIndex].totalSamplesLeft = stb_vorbis_stream_length_in_samples(currentMusic[musicIndex].stream) * currentMusic[musicIndex].channels; - currentMusic[musicIndex].totalLengthSeconds = stb_vorbis_stream_length_in_seconds(currentMusic[musicIndex].stream); } } else if (strcmp(GetExtension(fileName),"xm") == 0) @@ -820,7 +820,7 @@ void PlayMusicStream(char *fileName) currentMusic[musicIndex].loop = true; jar_xm_set_max_loop_count(currentMusic[musicIndex].chipctx, 0); // infinite number of loops currentMusic[musicIndex].totalSamplesLeft = jar_xm_get_remaining_samples(currentMusic[musicIndex].chipctx); - currentMusic[musicIndex].totalLengthSeconds = ((float)currentMusic[musicIndex].totalSamplesLeft) / ((float)currentMusic[musicIndex].sampleRate); + currentMusic[musicIndex].totalLengthSeconds = ((float)currentMusic[musicIndex].totalSamplesLeft) / 48000.f; musicEnabled = true; TraceLog(INFO, "[%s] XM number of samples: %i", fileName, currentMusic[musicIndex].totalSamplesLeft); @@ -891,18 +891,15 @@ void ResumeMusicStream(int index) } } -// Check if music is playing -bool IsMusicPlaying(void) +// Check if any music is playing +bool IsMusicPlaying(int index) { bool playing = false; ALint state; - for(int musicIndex = 0; musicIndex < MAX_MUSIC_STREAMS; musicIndex++) - { - if(currentMusic[musicIndex].ctx){ - alGetSourcei(currentMusic[musicIndex].ctx->alSource, AL_SOURCE_STATE, &state); - if (state == AL_PLAYING) playing = true; - } + if(index < MAX_MUSIC_STREAMS && currentMusic[index].ctx){ + alGetSourcei(currentMusic[index].ctx->alSource, AL_SOURCE_STATE, &state); + if (state == AL_PLAYING) playing = true; } return playing; @@ -911,8 +908,15 @@ bool IsMusicPlaying(void) // Set volume for music void SetMusicVolume(int index, float volume) { - if(currentMusic[musicIndex].ctx){ - alSourcef(currentMusic[musicIndex].ctx->alSource, AL_GAIN, volume); + if(index < MAX_MUSIC_STREAMS && currentMusic[index].ctx){ + alSourcef(currentMusic[index].ctx->alSource, AL_GAIN, volume); + } +} + +void SetMusicPitch(int index, float pitch) +{ + if(index < MAX_MUSIC_STREAMS && currentMusic[index].ctx){ + alSourcef(currentMusic[index].ctx->alSource, AL_PITCH, pitch); } } diff --git a/src/audio.h b/src/audio.h index a1602bd9..fef85b4f 100644 --- a/src/audio.h +++ b/src/audio.h @@ -105,11 +105,12 @@ void UpdateMusicStream(void); // Updates buffe void StopMusicStream(int index); // Stop music playing (close stream) void PauseMusicStream(int index); // Pause music playing void ResumeMusicStream(int index); // Resume playing paused music -bool IsMusicPlaying(void); // Check if music is playing +bool IsMusicPlaying(int index); // Check if music is playing void SetMusicVolume(int index, float volume); // Set volume for music (1.0 is max level) float GetMusicTimeLength(void); // Get music time length (in seconds) float GetMusicTimePlayed(void); // Get current music time played (in seconds) int getMusicStreamCount(void); +void SetMusicPitch(int index, float pitch); #ifdef __cplusplus } diff --git a/src/raylib.h b/src/raylib.h index 0c49a085..b9390d31 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -896,11 +896,12 @@ void UpdateMusicStream(void); // Updates buffe void StopMusicStream(int index); // Stop music playing (close stream) void PauseMusicStream(int index); // Pause music playing void ResumeMusicStream(int index); // Resume playing paused music -bool IsMusicPlaying(void); // Check if music is playing +bool IsMusicPlaying(int index); // Check if music is playing void SetMusicVolume(int index, float volume); // Set volume for music (1.0 is max level) float GetMusicTimeLength(void); // Get current music time length (in seconds) float GetMusicTimePlayed(void); // Get current music time played (in seconds) int getMusicStreamCount(void); +void SetMusicPitch(int index, float pitch); #ifdef __cplusplus } -- cgit v1.2.3 From 9737c58054d5d0cc636fca0c998b31a69d501970 Mon Sep 17 00:00:00 2001 From: Joshua Reisenauer Date: Wed, 11 May 2016 20:15:37 -0700 Subject: PlayMusicStream now uses index --- src/audio.c | 29 +++++++++++++++++++---------- src/audio.h | 2 +- src/raylib.h | 2 +- 3 files changed, 21 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/audio.c b/src/audio.c index f89be8bf..6a8d251e 100644 --- a/src/audio.c +++ b/src/audio.c @@ -763,19 +763,18 @@ void SetSoundPitch(Sound sound, float pitch) //---------------------------------------------------------------------------------- // Start music playing (open stream) -void PlayMusicStream(char *fileName) +// returns 0 on success +int PlayMusicStream(int musicIndex, char *fileName) { - int musicIndex; + int musicIndex = index; int mixIndex; - for(musicIndex = 0; musicIndex < MAX_MUSIC_STREAMS; musicIndex++) // find empty music slot - { - if(currentMusic[musicIndex].stream == NULL && !currentMusic[musicIndex].chipTune) break; - else if(musicIndex = MAX_MUSIC_STREAMS - 1) return; - } + + if(currentMusic[musicIndex].stream != NULL || currentMusic[musicIndex].chipTune) return 1; // error + for(mixIndex = 0; mixIndex < MAX_AUDIO_CONTEXTS; mixIndex++) // find empty mix channel slot { if(mixChannelsActive_g[mixIndex] == NULL) break; - else if(musicIndex = MAX_AUDIO_CONTEXTS - 1) return; + else if(musicIndex = MAX_AUDIO_CONTEXTS - 1) return 2; // error } @@ -787,6 +786,7 @@ void PlayMusicStream(char *fileName) if (currentMusic[musicIndex].stream == NULL) { TraceLog(WARNING, "[%s] OGG audio file could not be opened", fileName); + return 3; // error } else { @@ -828,9 +828,18 @@ void PlayMusicStream(char *fileName) currentMusic[musicIndex].ctx = InitAudioContext(48000, mixIndex, 2, true); } - else TraceLog(WARNING, "[%s] XM file could not be opened", fileName); + else + { + TraceLog(WARNING, "[%s] XM file could not be opened", fileName); + return 4; // error + } + } + else + { + TraceLog(WARNING, "[%s] Music extension not recognized, it can't be loaded", fileName); + return 5; // error } - else TraceLog(WARNING, "[%s] Music extension not recognized, it can't be loaded", fileName); + return 0; // normal return } // Stop music playing for individual music index of currentMusic array (close stream) diff --git a/src/audio.h b/src/audio.h index fef85b4f..d09c4acc 100644 --- a/src/audio.h +++ b/src/audio.h @@ -100,7 +100,7 @@ bool IsSoundPlaying(Sound sound); // Check if a so void SetSoundVolume(Sound sound, float volume); // Set volume for a sound (1.0 is max level) void SetSoundPitch(Sound sound, float pitch); // Set pitch for a sound (1.0 is base level) -void PlayMusicStream(char *fileName); // Start music playing (open stream) +int PlayMusicStream(int musicIndex, char *fileName); // Start music playing (open stream) void UpdateMusicStream(void); // Updates buffers for music streaming void StopMusicStream(int index); // Stop music playing (close stream) void PauseMusicStream(int index); // Pause music playing diff --git a/src/raylib.h b/src/raylib.h index a6507906..cb17aa78 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -894,7 +894,7 @@ bool IsSoundPlaying(Sound sound); // Check if a so void SetSoundVolume(Sound sound, float volume); // Set volume for a sound (1.0 is max level) void SetSoundPitch(Sound sound, float pitch); // Set pitch for a sound (1.0 is base level) -void PlayMusicStream(char *fileName); // Start music playing (open stream) +int PlayMusicStream(int musicIndex, char *fileName); // Start music playing (open stream) void UpdateMusicStream(void); // Updates buffers for music streaming void StopMusicStream(int index); // Stop music playing (close stream) void PauseMusicStream(int index); // Pause music playing -- cgit v1.2.3 From f0ada8c40d7887654d05c62f980a0a5473c1d8a7 Mon Sep 17 00:00:00 2001 From: Joshua Reisenauer Date: Wed, 11 May 2016 22:37:53 -0700 Subject: apply index to remaining functions --- src/audio.c | 108 +++++++++++++++++++++++++++++++---------------------------- src/audio.h | 6 ++-- src/raylib.h | 6 ++-- 3 files changed, 62 insertions(+), 58 deletions(-) (limited to 'src') diff --git a/src/audio.c b/src/audio.c index 6a8d251e..94729566 100644 --- a/src/audio.c +++ b/src/audio.c @@ -118,12 +118,12 @@ static Music currentMusic[MAX_MUSIC_STREAMS]; // Current //---------------------------------------------------------------------------------- // Module specific Functions Declaration //---------------------------------------------------------------------------------- -static Wave LoadWAV(const char *fileName); // Load WAV file -static Wave LoadOGG(char *fileName); // Load OGG file -static void UnloadWave(Wave wave); // Unload wave data +static Wave LoadWAV(const char *fileName); // Load WAV file +static Wave LoadOGG(char *fileName); // Load OGG file +static void UnloadWave(Wave wave); // Unload wave data -static bool BufferMusicStream(ALuint buffer); // Fill music buffers with data -static void EmptyMusicStream(void); // Empty music buffers +static bool BufferMusicStream(int index, ALuint buffer); // Fill music buffers with data +static void EmptyMusicStream(int index); // Empty music buffers static unsigned short FillAlBufferWithSilence(AudioContext_t *context, ALuint buffer);// fill buffer with zeros, returns number processed static void ResampleShortToFloat(short *shorts, float *floats, unsigned short len); // pass two arrays of the same legnth in @@ -766,7 +766,6 @@ void SetSoundPitch(Sound sound, float pitch) // returns 0 on success int PlayMusicStream(int musicIndex, char *fileName) { - int musicIndex = index; int mixIndex; if(currentMusic[musicIndex].stream != NULL || currentMusic[musicIndex].chipTune) return 1; // error @@ -930,36 +929,36 @@ void SetMusicPitch(int index, float pitch) } // Get current music time length (in seconds) -float GetMusicTimeLength(void) +float GetMusicTimeLength(int index) { float totalSeconds; - if (currentMusic.chipTune) + if (currentMusic[index].chipTune) { - totalSeconds = currentMusic.totalLengthSeconds; + totalSeconds = currentMusic[index].totalLengthSeconds; } else { - totalSeconds = stb_vorbis_stream_length_in_seconds(currentMusic.stream); + totalSeconds = stb_vorbis_stream_length_in_seconds(currentMusic[index].stream); } return totalSeconds; } // Get current music time played (in seconds) -float GetMusicTimePlayed(void) +float GetMusicTimePlayed(int index) { float secondsPlayed; - if (currentMusic.chipTune) + if (currentMusic[index].chipTune) { uint64_t samples; - jar_xm_get_position(currentMusic.chipctx, NULL, NULL, NULL, &samples); - secondsPlayed = (float)samples / (currentMusic.sampleRate * currentMusic.channels); // Not sure if this is the correct value + jar_xm_get_position(currentMusic[index].chipctx, NULL, NULL, NULL, &samples); + secondsPlayed = (float)samples / (48000 * currentMusic[index].ctx->channels); // Not sure if this is the correct value } else { - int totalSamples = stb_vorbis_stream_length_in_samples(currentMusic.stream) * currentMusic.channels; - int samplesPlayed = totalSamples - currentMusic.totalSamplesLeft; - secondsPlayed = (float)samplesPlayed / (currentMusic.sampleRate * currentMusic.channels); + int totalSamples = stb_vorbis_stream_length_in_samples(currentMusic[index].stream) * currentMusic[index].ctx->channels; + int samplesPlayed = totalSamples - currentMusic[index].totalSamplesLeft; + secondsPlayed = (float)samplesPlayed / (currentMusic[index].ctx->sampleRate * currentMusic[index].ctx->channels); } @@ -971,9 +970,10 @@ float GetMusicTimePlayed(void) //---------------------------------------------------------------------------------- // Fill music buffers with new data from music stream -static bool BufferMusicStream(ALuint buffer) +static bool BufferMusicStream(int index, ALuint buffer) { short pcm[MUSIC_BUFFER_SIZE_SHORT]; + float pcmf[MUSIC_BUFFER_SIZE_FLOAT]; int size = 0; // Total size of data steamed (in bytes) int streamedBytes = 0; // samples of data obtained, channels are not included in calculation @@ -981,93 +981,97 @@ static bool BufferMusicStream(ALuint buffer) if (musicEnabled) { - if (currentMusic.chipTune) // There is no end of stream for xmfiles, once the end is reached zeros are generated for non looped chiptunes. + if (currentMusic[index].chipTune) // There is no end of stream for xmfiles, once the end is reached zeros are generated for non looped chiptunes. { - int readlen = MUSIC_BUFFER_SIZE_SHORT / 2; - jar_xm_generate_samples_16bit(currentMusic.chipctx, pcm, readlen); // reads 2*readlen shorts and moves them to buffer+size memory location - size += readlen * currentMusic.channels; // Not sure if this is what it needs + int readlen = MUSIC_BUFFER_SIZE_FLOAT / 2; + jar_xm_generate_samples(currentMusic[index].chipctx, pcmf, readlen); // reads 2*readlen shorts and moves them to buffer+size memory location + size += readlen * currentMusic[index].ctx->channels; // Not sure if this is what it needs + + alBufferData(buffer, currentMusic[index].ctx->alFormat, pcmf, size*sizeof(float), 48000); + currentMusic[index].totalSamplesLeft -= size; + if(currentMusic[index].totalSamplesLeft <= 0) active = false; } else { while (size < MUSIC_BUFFER_SIZE_SHORT) { - streamedBytes = stb_vorbis_get_samples_short_interleaved(currentMusic.stream, currentMusic.channels, pcm + size, MUSIC_BUFFER_SIZE_SHORT - size); - if (streamedBytes > 0) size += (streamedBytes*currentMusic.channels); + streamedBytes = stb_vorbis_get_samples_short_interleaved(currentMusic[index].stream, currentMusic[index].ctx->channels, pcm + size, MUSIC_BUFFER_SIZE_SHORT - size); + if (streamedBytes > 0) size += (streamedBytes*currentMusic[index].ctx->channels); else break; } + + if (size > 0) + { + alBufferData(buffer, currentMusic[index].ctx->alFormat, pcm, size*sizeof(short), currentMusic[index].ctx->sampleRate); + currentMusic[index].totalSamplesLeft -= size; + + if(currentMusic[index].totalSamplesLeft <= 0) active = false; // end if no more samples left + } + else + { + active = false; + TraceLog(WARNING, "No more data obtained from stream"); + } } TraceLog(DEBUG, "Streaming music data to buffer. Bytes streamed: %i", size); } - if (size > 0) - { - alBufferData(buffer, currentMusic.format, pcm, size*sizeof(short), currentMusic.sampleRate); - currentMusic.totalSamplesLeft -= size; - - if(currentMusic.totalSamplesLeft <= 0) active = false; // end if no more samples left - } - else - { - active = false; - TraceLog(WARNING, "No more data obtained from stream"); - } - return active; } // Empty music buffers -static void EmptyMusicStream(void) +static void EmptyMusicStream(int index) { ALuint buffer = 0; int queued = 0; - alGetSourcei(currentMusic.source, AL_BUFFERS_QUEUED, &queued); + alGetSourcei(currentMusic[index].source, AL_BUFFERS_QUEUED, &queued); while (queued > 0) { - alSourceUnqueueBuffers(currentMusic.source, 1, &buffer); + alSourceUnqueueBuffers(currentMusic[index].source, 1, &buffer); queued--; } } // Update (re-fill) music buffers if data already processed -void UpdateMusicStream(void) +void UpdateMusicStream(int index) { ALuint buffer = 0; ALint processed = 0; bool active = true; - if (musicEnabled) + if (index < MAX_MUSIC_STREAMS && musicEnabled) { // Get the number of already processed buffers (if any) - alGetSourcei(currentMusic.source, AL_BUFFERS_PROCESSED, &processed); + alGetSourcei(currentMusic[index].source, AL_BUFFERS_PROCESSED, &processed); while (processed > 0) { // Recover processed buffer for refill - alSourceUnqueueBuffers(currentMusic.source, 1, &buffer); + alSourceUnqueueBuffers(currentMusic[index].source, 1, &buffer); // Refill buffer active = BufferMusicStream(buffer); // If no more data to stream, restart music (if loop) - if ((!active) && (currentMusic.loop)) + if ((!active) && (currentMusic[index].loop)) { - if(currentMusic.chipTune) + if(currentMusic[index].chipTune) { - currentMusic.totalSamplesLeft = currentMusic.totalLengthSeconds * currentMusic.sampleRate; + currentMusic[index].totalSamplesLeft = currentMusic[index].totalLengthSeconds * currentMusic[index].ctx->sampleRate; } else { - stb_vorbis_seek_start(currentMusic.stream); - currentMusic.totalSamplesLeft = stb_vorbis_stream_length_in_samples(currentMusic.stream)*currentMusic.channels; + stb_vorbis_seek_start(currentMusic[index].stream); + currentMusic[index].totalSamplesLeft = stb_vorbis_stream_length_in_samples(currentMusic[index].stream)*currentMusic[index].ctx->channels; } active = BufferMusicStream(buffer); } // Add refilled buffer to queue again... don't let the music stop! - alSourceQueueBuffers(currentMusic.source, 1, &buffer); + alSourceQueueBuffers(currentMusic[index].source, 1, &buffer); if (alGetError() != AL_NO_ERROR) TraceLog(WARNING, "Error buffering data..."); @@ -1075,9 +1079,9 @@ void UpdateMusicStream(void) } ALenum state; - alGetSourcei(currentMusic.source, AL_SOURCE_STATE, &state); + alGetSourcei(currentMusic[index].source, AL_SOURCE_STATE, &state); - if ((state != AL_PLAYING) && active) alSourcePlay(currentMusic.source); + if ((state != AL_PLAYING) && active) alSourcePlay(currentMusic[index].source); if (!active) StopMusicStream(); } diff --git a/src/audio.h b/src/audio.h index d09c4acc..63c1c136 100644 --- a/src/audio.h +++ b/src/audio.h @@ -101,14 +101,14 @@ void SetSoundVolume(Sound sound, float volume); // Set volume fo void SetSoundPitch(Sound sound, float pitch); // Set pitch for a sound (1.0 is base level) int PlayMusicStream(int musicIndex, char *fileName); // Start music playing (open stream) -void UpdateMusicStream(void); // Updates buffers for music streaming +void UpdateMusicStream(int index); // Updates buffers for music streaming void StopMusicStream(int index); // Stop music playing (close stream) void PauseMusicStream(int index); // Pause music playing void ResumeMusicStream(int index); // Resume playing paused music bool IsMusicPlaying(int index); // Check if music is playing void SetMusicVolume(int index, float volume); // Set volume for music (1.0 is max level) -float GetMusicTimeLength(void); // Get music time length (in seconds) -float GetMusicTimePlayed(void); // Get current music time played (in seconds) +float GetMusicTimeLength(int index); // Get music time length (in seconds) +float GetMusicTimePlayed(int index); // Get current music time played (in seconds) int getMusicStreamCount(void); void SetMusicPitch(int index, float pitch); diff --git a/src/raylib.h b/src/raylib.h index cb17aa78..05c945f7 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -895,14 +895,14 @@ void SetSoundVolume(Sound sound, float volume); // Set volume fo void SetSoundPitch(Sound sound, float pitch); // Set pitch for a sound (1.0 is base level) int PlayMusicStream(int musicIndex, char *fileName); // Start music playing (open stream) -void UpdateMusicStream(void); // Updates buffers for music streaming +void UpdateMusicStream(int index); // Updates buffers for music streaming void StopMusicStream(int index); // Stop music playing (close stream) void PauseMusicStream(int index); // Pause music playing void ResumeMusicStream(int index); // Resume playing paused music bool IsMusicPlaying(int index); // Check if music is playing void SetMusicVolume(int index, float volume); // Set volume for music (1.0 is max level) -float GetMusicTimeLength(void); // Get current music time length (in seconds) -float GetMusicTimePlayed(void); // Get current music time played (in seconds) +float GetMusicTimeLength(int index); // Get current music time length (in seconds) +float GetMusicTimePlayed(int index); // Get current music time played (in seconds) int getMusicStreamCount(void); void SetMusicPitch(int index, float pitch); -- cgit v1.2.3 From 83dbc076507b7025aa0a1315e409ff46f75c1e0b Mon Sep 17 00:00:00 2001 From: Joshua Reisenauer Date: Thu, 12 May 2016 16:02:23 -0700 Subject: buffering of music now uses update audio context --- src/audio.c | 94 ++++++++++++++++++++--------------------------------------- src/easings.h | 10 +++---- 2 files changed, 37 insertions(+), 67 deletions(-) (limited to 'src') diff --git a/src/audio.c b/src/audio.c index 94729566..cc964cd1 100644 --- a/src/audio.c +++ b/src/audio.c @@ -118,12 +118,12 @@ static Music currentMusic[MAX_MUSIC_STREAMS]; // Current //---------------------------------------------------------------------------------- // Module specific Functions Declaration //---------------------------------------------------------------------------------- -static Wave LoadWAV(const char *fileName); // Load WAV file -static Wave LoadOGG(char *fileName); // Load OGG file -static void UnloadWave(Wave wave); // Unload wave data +static Wave LoadWAV(const char *fileName); // Load WAV file +static Wave LoadOGG(char *fileName); // Load OGG file +static void UnloadWave(Wave wave); // Unload wave data -static bool BufferMusicStream(int index, ALuint buffer); // Fill music buffers with data -static void EmptyMusicStream(int index); // Empty music buffers +static bool BufferMusicStream(int index); // Fill music buffers with data +static void EmptyMusicStream(int index); // Empty music buffers static unsigned short FillAlBufferWithSilence(AudioContext_t *context, ALuint buffer);// fill buffer with zeros, returns number processed static void ResampleShortToFloat(short *shorts, float *floats, unsigned short len); // pass two arrays of the same legnth in @@ -970,7 +970,7 @@ float GetMusicTimePlayed(int index) //---------------------------------------------------------------------------------- // Fill music buffers with new data from music stream -static bool BufferMusicStream(int index, ALuint buffer) +static bool BufferMusicStream(int index) { short pcm[MUSIC_BUFFER_SIZE_SHORT]; float pcmf[MUSIC_BUFFER_SIZE_FLOAT]; @@ -985,33 +985,17 @@ static bool BufferMusicStream(int index, ALuint buffer) { int readlen = MUSIC_BUFFER_SIZE_FLOAT / 2; jar_xm_generate_samples(currentMusic[index].chipctx, pcmf, readlen); // reads 2*readlen shorts and moves them to buffer+size memory location + UpdateAudioContext(currentMusic[index].ctx, pcmf, MUSIC_BUFFER_SIZE_FLOAT); size += readlen * currentMusic[index].ctx->channels; // Not sure if this is what it needs - - alBufferData(buffer, currentMusic[index].ctx->alFormat, pcmf, size*sizeof(float), 48000); currentMusic[index].totalSamplesLeft -= size; if(currentMusic[index].totalSamplesLeft <= 0) active = false; } else { - while (size < MUSIC_BUFFER_SIZE_SHORT) - { - streamedBytes = stb_vorbis_get_samples_short_interleaved(currentMusic[index].stream, currentMusic[index].ctx->channels, pcm + size, MUSIC_BUFFER_SIZE_SHORT - size); - if (streamedBytes > 0) size += (streamedBytes*currentMusic[index].ctx->channels); - else break; - } - - if (size > 0) - { - alBufferData(buffer, currentMusic[index].ctx->alFormat, pcm, size*sizeof(short), currentMusic[index].ctx->sampleRate); - currentMusic[index].totalSamplesLeft -= size; - - if(currentMusic[index].totalSamplesLeft <= 0) active = false; // end if no more samples left - } - else - { - active = false; - TraceLog(WARNING, "No more data obtained from stream"); - } + streamedBytes = stb_vorbis_get_samples_short_interleaved(currentMusic[index].stream, currentMusic[index].ctx->channels, pcm, MUSIC_BUFFER_SIZE_SHORT); + UpdateAudioContext(currentMusic[index].ctx, pcm, MUSIC_BUFFER_SIZE_SHORT); + currentMusic[index].totalSamplesLeft -= MUSIC_BUFFER_SIZE_SHORT; + if(currentMusic[index].totalSamplesLeft <= 0) active = false; } TraceLog(DEBUG, "Streaming music data to buffer. Bytes streamed: %i", size); } @@ -1038,53 +1022,39 @@ static void EmptyMusicStream(int index) // Update (re-fill) music buffers if data already processed void UpdateMusicStream(int index) { - ALuint buffer = 0; - ALint processed = 0; + ALenum state; bool active = true; if (index < MAX_MUSIC_STREAMS && musicEnabled) { - // Get the number of already processed buffers (if any) - alGetSourcei(currentMusic[index].source, AL_BUFFERS_PROCESSED, &processed); - - while (processed > 0) + active = BufferMusicStream(index); + + if ((!active) && (currentMusic[index].loop)) { - // Recover processed buffer for refill - alSourceUnqueueBuffers(currentMusic[index].source, 1, &buffer); - - // Refill buffer - active = BufferMusicStream(buffer); - - // If no more data to stream, restart music (if loop) - if ((!active) && (currentMusic[index].loop)) + if(currentMusic[index].chipTune) { - if(currentMusic[index].chipTune) - { - currentMusic[index].totalSamplesLeft = currentMusic[index].totalLengthSeconds * currentMusic[index].ctx->sampleRate; - } - else - { - stb_vorbis_seek_start(currentMusic[index].stream); - currentMusic[index].totalSamplesLeft = stb_vorbis_stream_length_in_samples(currentMusic[index].stream)*currentMusic[index].ctx->channels; - } - active = BufferMusicStream(buffer); + currentMusic[index].totalSamplesLeft = currentMusic[index].totalLengthSeconds * currentMusic[index].ctx->sampleRate; } + else + { + stb_vorbis_seek_start(currentMusic[index].stream); + currentMusic[index].totalSamplesLeft = stb_vorbis_stream_length_in_samples(currentMusic[index].stream)*currentMusic[index].ctx->channels; + } + active = BufferMusicStream(index); + } + - // Add refilled buffer to queue again... don't let the music stop! - alSourceQueueBuffers(currentMusic[index].source, 1, &buffer); - - if (alGetError() != AL_NO_ERROR) TraceLog(WARNING, "Error buffering data..."); + if (alGetError() != AL_NO_ERROR) TraceLog(WARNING, "Error buffering data..."); - processed--; - } + processed--; + } - ALenum state; - alGetSourcei(currentMusic[index].source, AL_SOURCE_STATE, &state); + + alGetSourcei(currentMusic[index].source, AL_SOURCE_STATE, &state); - if ((state != AL_PLAYING) && active) alSourcePlay(currentMusic[index].source); + if ((state != AL_PLAYING) && active) alSourcePlay(currentMusic[index].source); - if (!active) StopMusicStream(); - } + if (!active) StopMusicStream(); } // Load WAV file into Wave structure diff --git a/src/easings.h b/src/easings.h index e1e5465a..a8178f4a 100644 --- a/src/easings.h +++ b/src/easings.h @@ -18,11 +18,11 @@ * float speed = 1.f; * float currentTime = 0.f; * float currentPos[2] = {0,0}; -* float newPos[2] = {1,1}; -* float tempPosition[2] = currentPos;//x,y positions -* while(currentPos[0] < newPos[0]) -* currentPos[0] = EaseSineIn(currentTime, tempPosition[0], tempPosition[0]-newPos[0], speed); -* currentPos[1] = EaseSineIn(currentTime, tempPosition[1], tempPosition[1]-newPos[0], speed); +* float finalPos[2] = {1,1}; +* float startPosition[2] = currentPos;//x,y positions +* while(currentPos[0] < finalPos[0]) +* currentPos[0] = EaseSineIn(currentTime, startPosition[0], startPosition[0]-finalPos[0], speed); +* currentPos[1] = EaseSineIn(currentTime, startPosition[1], startPosition[1]-finalPos[0], speed); * currentTime += diffTime(); * * A port of Robert Penner's easing equations to C (http://robertpenner.com/easing/) -- cgit v1.2.3 From 5107a2dc40330623f61205ee70f067d24afb0af8 Mon Sep 17 00:00:00 2001 From: Joshua Reisenauer Date: Thu, 12 May 2016 21:14:02 -0700 Subject: bug fixes --- src/audio.c | 69 +++++++++++++++++++++++++++++++------------------------------ 1 file changed, 35 insertions(+), 34 deletions(-) (limited to 'src') diff --git a/src/audio.c b/src/audio.c index cc964cd1..d19d3306 100644 --- a/src/audio.c +++ b/src/audio.c @@ -77,19 +77,6 @@ // Types and Structures Definition //---------------------------------------------------------------------------------- -// Music type (file streaming from memory) -// NOTE: Anything longer than ~10 seconds should be streamed... -typedef struct Music { - stb_vorbis *stream; - jar_xm_context_t *chipctx; // Stores jar_xm context - AudioContext_t *ctx; // audio context - - int totalSamplesLeft; - float totalLengthSeconds; - bool loop; - bool chipTune; // True if chiptune is loaded -} Music; - // Audio Context, used to create custom audio streams that are not bound to a sound file. There can be // no more than 4 concurrent audio contexts in use. This is due to each active context being tied to // a dedicated mix channel. All audio is 32bit floating point in stereo. @@ -104,6 +91,19 @@ typedef struct AudioContext_t { ALuint alBuffer[MAX_STREAM_BUFFERS]; // openAL sample buffer } AudioContext_t; +// Music type (file streaming from memory) +// NOTE: Anything longer than ~10 seconds should be streamed... +typedef struct Music { + stb_vorbis *stream; + jar_xm_context_t *chipctx; // Stores jar_xm context + AudioContext_t *ctx; // audio context + + int totalSamplesLeft; + float totalLengthSeconds; + bool loop; + bool chipTune; // True if chiptune is loaded +} Music; + #if defined(AUDIO_STANDALONE) typedef enum { INFO = 0, ERROR, WARNING, DEBUG, OTHER } TraceLogType; #endif @@ -168,7 +168,10 @@ void InitAudioDevice(void) // Close the audio device for the current context, and destroys the context void CloseAudioDevice(void) { - StopMusicStream(); // Stop music streaming and close current stream + for(int index=0; index= MAX_AUDIO_CONTEXTS) return NULL; if(!IsAudioDeviceReady()) InitAudioDevice(); - else StopMusicStream(); if(!mixChannelsActive_g[mixChannel]){ AudioContext_t *ac = (AudioContext_t*)malloc(sizeof(AudioContext_t)); @@ -776,7 +778,6 @@ int PlayMusicStream(int musicIndex, char *fileName) else if(musicIndex = MAX_AUDIO_CONTEXTS - 1) return 2; // error } - if (strcmp(GetExtension(fileName),"ogg") == 0) { // Open audio stream @@ -888,12 +889,12 @@ void ResumeMusicStream(int index) { // Resume music playing... if music available! ALenum state; - if(currentMusic[musicIndex].ctx){ - alGetSourcei(currentMusic[musicIndex].ctx->alSource, AL_SOURCE_STATE, &state); + if(currentMusic[index].ctx){ + alGetSourcei(currentMusic[index].ctx->alSource, AL_SOURCE_STATE, &state); if (state == AL_PAUSED) { TraceLog(INFO, "Resuming music stream"); - alSourcePlay(currentMusic[musicIndex].ctx->alSource); + alSourcePlay(currentMusic[index].ctx->alSource); musicEnabled = true; } } @@ -976,9 +977,8 @@ static bool BufferMusicStream(int index) float pcmf[MUSIC_BUFFER_SIZE_FLOAT]; int size = 0; // Total size of data steamed (in bytes) - int streamedBytes = 0; // samples of data obtained, channels are not included in calculation bool active = true; // We can get more data from stream (not finished) - + if (musicEnabled) { if (currentMusic[index].chipTune) // There is no end of stream for xmfiles, once the end is reached zeros are generated for non looped chiptunes. @@ -992,12 +992,12 @@ static bool BufferMusicStream(int index) } else { - streamedBytes = stb_vorbis_get_samples_short_interleaved(currentMusic[index].stream, currentMusic[index].ctx->channels, pcm, MUSIC_BUFFER_SIZE_SHORT); - UpdateAudioContext(currentMusic[index].ctx, pcm, MUSIC_BUFFER_SIZE_SHORT); - currentMusic[index].totalSamplesLeft -= MUSIC_BUFFER_SIZE_SHORT; + int streamedBytes = stb_vorbis_get_samples_short_interleaved(currentMusic[index].stream, currentMusic[index].ctx->channels, pcm, MUSIC_BUFFER_SIZE_SHORT); + UpdateAudioContext(currentMusic[index].ctx, pcm, streamedBytes); + currentMusic[index].totalSamplesLeft -= streamedBytes*currentMusic[index].ctx->channels; if(currentMusic[index].totalSamplesLeft <= 0) active = false; } - TraceLog(DEBUG, "Streaming music data to buffer. Bytes streamed: %i", size); + // TraceLog(DEBUG, "Streaming music data to buffer. Bytes streamed: %i", size); } return active; @@ -1009,11 +1009,11 @@ static void EmptyMusicStream(int index) ALuint buffer = 0; int queued = 0; - alGetSourcei(currentMusic[index].source, AL_BUFFERS_QUEUED, &queued); + alGetSourcei(currentMusic[index].ctx->alSource, AL_BUFFERS_QUEUED, &queued); while (queued > 0) { - alSourceUnqueueBuffers(currentMusic[index].source, 1, &buffer); + alSourceUnqueueBuffers(currentMusic[index].ctx->alSource, 1, &buffer); queued--; } @@ -1045,16 +1045,17 @@ void UpdateMusicStream(int index) if (alGetError() != AL_NO_ERROR) TraceLog(WARNING, "Error buffering data..."); + + alGetSourcei(currentMusic[index].ctx->alSource, AL_SOURCE_STATE, &state); - processed--; - } - - - alGetSourcei(currentMusic[index].source, AL_SOURCE_STATE, &state); + if ((state != AL_PLAYING) && active) alSourcePlay(currentMusic[index].ctx->alSource); - if ((state != AL_PLAYING) && active) alSourcePlay(currentMusic[index].source); + if (!active) StopMusicStream(index); + + } + else + return; - if (!active) StopMusicStream(); } // Load WAV file into Wave structure -- cgit v1.2.3 From ea4b5552c2b4ff8a907dd1fefc084317187af168 Mon Sep 17 00:00:00 2001 From: Joshua Reisenauer Date: Sat, 14 May 2016 00:25:40 -0700 Subject: corrected typos --- src/audio.c | 42 +++++++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/audio.c b/src/audio.c index d19d3306..c53d6318 100644 --- a/src/audio.c +++ b/src/audio.c @@ -85,7 +85,7 @@ typedef struct AudioContext_t { unsigned char channels; // 1=mono,2=stereo unsigned char mixChannel; // 0-3 or mixA-mixD, each mix channel can receive up to one dedicated audio stream bool floatingPoint; // if false then the short datatype is used instead - bool playing; + bool playing; // false if paused ALenum alFormat; // openAL format specifier ALuint alSource; // openAL source ALuint alBuffer[MAX_STREAM_BUFFERS]; // openAL sample buffer @@ -165,13 +165,14 @@ void InitAudioDevice(void) alListener3f(AL_ORIENTATION, 0, 0, -1); } -// Close the audio device for the current context, and destroys the context +// Close the audio device for all contexts void CloseAudioDevice(void) { for(int index=0; indexplaying = true; currentMusic[musicIndex].totalSamplesLeft = stb_vorbis_stream_length_in_samples(currentMusic[musicIndex].stream) * info.channels; currentMusic[musicIndex].totalLengthSeconds = stb_vorbis_stream_length_in_seconds(currentMusic[musicIndex].stream); @@ -822,6 +824,7 @@ int PlayMusicStream(int musicIndex, char *fileName) currentMusic[musicIndex].totalSamplesLeft = jar_xm_get_remaining_samples(currentMusic[musicIndex].chipctx); currentMusic[musicIndex].totalLengthSeconds = ((float)currentMusic[musicIndex].totalSamplesLeft) / 48000.f; musicEnabled = true; + currentMusic[musicIndex].ctx->playing = true; TraceLog(INFO, "[%s] XM number of samples: %i", fileName, currentMusic[musicIndex].totalSamplesLeft); TraceLog(INFO, "[%s] XM track length: %11.6f sec", fileName, currentMusic[musicIndex].totalLengthSeconds); @@ -859,16 +862,21 @@ void StopMusicStream(int index) } if(!getMusicStreamCount()) musicEnabled = false; + if(currentMusic[index].stream || currentMusic[index].chipctx) + { + currentMusic[index].stream = NULL; + currentMusic[index].chipctx = NULL; + } } } //get number of music channels active at this time, this does not mean they are playing int getMusicStreamCount(void) { - int musicCount; + int musicCount = 0; for(int musicIndex = 0; musicIndex < MAX_MUSIC_STREAMS; musicIndex++) // find empty music slot if(currentMusic[musicIndex].stream != NULL || currentMusic[musicIndex].chipTune) musicCount++; - + return musicCount; } @@ -876,11 +884,11 @@ int getMusicStreamCount(void) void PauseMusicStream(int index) { // Pause music stream if music available! - if (musicEnabled) + if (currentMusic[index].ctx && musicEnabled) { TraceLog(INFO, "Pausing music stream"); - UpdateAudioContext(currentMusic[index].ctx, NULL, 0); // pushing null data auto pauses stream - musicEnabled = false; + alSourcePause(currentMusic[index].ctx->alSource); + currentMusic[index].ctx->playing = false; } } @@ -895,7 +903,7 @@ void ResumeMusicStream(int index) { TraceLog(INFO, "Resuming music stream"); alSourcePlay(currentMusic[index].ctx->alSource); - musicEnabled = true; + currentMusic[index].ctx->playing = true; } } } @@ -981,13 +989,17 @@ static bool BufferMusicStream(int index) if (musicEnabled) { + if(!currentMusic[index].ctx->playing) + { + UpdateAudioContext(currentMusic[index].ctx, NULL, 0); + return false; + } + if (currentMusic[index].chipTune) // There is no end of stream for xmfiles, once the end is reached zeros are generated for non looped chiptunes. { - int readlen = MUSIC_BUFFER_SIZE_FLOAT / 2; - jar_xm_generate_samples(currentMusic[index].chipctx, pcmf, readlen); // reads 2*readlen shorts and moves them to buffer+size memory location + jar_xm_generate_samples(currentMusic[index].chipctx, pcmf, MUSIC_BUFFER_SIZE_FLOAT / 2); // reads 2*readlen shorts and moves them to buffer+size memory location UpdateAudioContext(currentMusic[index].ctx, pcmf, MUSIC_BUFFER_SIZE_FLOAT); - size += readlen * currentMusic[index].ctx->channels; // Not sure if this is what it needs - currentMusic[index].totalSamplesLeft -= size; + currentMusic[index].totalSamplesLeft -= MUSIC_BUFFER_SIZE_FLOAT; if(currentMusic[index].totalSamplesLeft <= 0) active = false; } else @@ -997,7 +1009,7 @@ static bool BufferMusicStream(int index) currentMusic[index].totalSamplesLeft -= streamedBytes*currentMusic[index].ctx->channels; if(currentMusic[index].totalSamplesLeft <= 0) active = false; } - // TraceLog(DEBUG, "Streaming music data to buffer. Bytes streamed: %i", size); + TraceLog(DEBUG, "Buffering index:%i, chiptune:%i", index, (int)currentMusic[index].chipTune); } return active; -- cgit v1.2.3 From 8c5d403dda7a7070dc58204bb10539b3646f183b Mon Sep 17 00:00:00 2001 From: Joshua Reisenauer Date: Sat, 14 May 2016 15:26:17 -0700 Subject: new function to check if music stream is ready _g naming convention for globals, new error exit numbers. --- src/audio.c | 43 ++++++++++++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/audio.c b/src/audio.c index c53d6318..0fd1bb13 100644 --- a/src/audio.c +++ b/src/audio.c @@ -112,7 +112,7 @@ typedef enum { INFO = 0, ERROR, WARNING, DEBUG, OTHER } TraceLogType; // Global Variables Definition //---------------------------------------------------------------------------------- static AudioContext_t* mixChannelsActive_g[MAX_AUDIO_CONTEXTS]; // What mix channels are currently active -static bool musicEnabled = false; +static bool musicEnabled_g = false; static Music currentMusic[MAX_MUSIC_STREAMS]; // Current music loaded, up to two can play at the same time //---------------------------------------------------------------------------------- @@ -126,8 +126,9 @@ static bool BufferMusicStream(int index); // Fill music buffers with da static void EmptyMusicStream(int index); // Empty music buffers static unsigned short FillAlBufferWithSilence(AudioContext_t *context, ALuint buffer);// fill buffer with zeros, returns number processed -static void ResampleShortToFloat(short *shorts, float *floats, unsigned short len); // pass two arrays of the same legnth in -static void ResampleByteToFloat(char *chars, float *floats, unsigned short len); // pass two arrays of same length in +static void ResampleShortToFloat(short *shorts, float *floats, unsigned short len); // pass two arrays of the same legnth in +static void ResampleByteToFloat(char *chars, float *floats, unsigned short len); // pass two arrays of same length in +static bool isMusicStreamReady(int index); // Checks if music buffer is ready to be refilled #if defined(AUDIO_STANDALONE) const char *GetExtension(const char *fileName); // Get the extension for a filename @@ -799,18 +800,21 @@ int PlayMusicStream(int musicIndex, char *fileName) TraceLog(DEBUG, "[%s] Temp memory required: %i", fileName, info.temp_memory_required); currentMusic[musicIndex].loop = true; // We loop by default - musicEnabled = true; - currentMusic[musicIndex].ctx->playing = true; + musicEnabled_g = true; + currentMusic[musicIndex].totalSamplesLeft = stb_vorbis_stream_length_in_samples(currentMusic[musicIndex].stream) * info.channels; currentMusic[musicIndex].totalLengthSeconds = stb_vorbis_stream_length_in_seconds(currentMusic[musicIndex].stream); if (info.channels == 2){ currentMusic[musicIndex].ctx = InitAudioContext(info.sample_rate, mixIndex, 2, false); + currentMusic[musicIndex].ctx->playing = true; } else{ currentMusic[musicIndex].ctx = InitAudioContext(info.sample_rate, mixIndex, 1, false); + currentMusic[musicIndex].ctx->playing = true; } + if(!currentMusic[musicIndex].ctx) return 4; // error } } else if (strcmp(GetExtension(fileName),"xm") == 0) @@ -823,24 +827,25 @@ int PlayMusicStream(int musicIndex, char *fileName) jar_xm_set_max_loop_count(currentMusic[musicIndex].chipctx, 0); // infinite number of loops currentMusic[musicIndex].totalSamplesLeft = jar_xm_get_remaining_samples(currentMusic[musicIndex].chipctx); currentMusic[musicIndex].totalLengthSeconds = ((float)currentMusic[musicIndex].totalSamplesLeft) / 48000.f; - musicEnabled = true; - currentMusic[musicIndex].ctx->playing = true; + musicEnabled_g = true; TraceLog(INFO, "[%s] XM number of samples: %i", fileName, currentMusic[musicIndex].totalSamplesLeft); TraceLog(INFO, "[%s] XM track length: %11.6f sec", fileName, currentMusic[musicIndex].totalLengthSeconds); currentMusic[musicIndex].ctx = InitAudioContext(48000, mixIndex, 2, true); + if(!currentMusic[musicIndex].ctx) return 5; // error + currentMusic[musicIndex].ctx->playing = true; } else { TraceLog(WARNING, "[%s] XM file could not be opened", fileName); - return 4; // error + return 6; // error } } else { TraceLog(WARNING, "[%s] Music extension not recognized, it can't be loaded", fileName); - return 5; // error + return 7; // error } return 0; // normal return } @@ -861,7 +866,7 @@ void StopMusicStream(int index) stb_vorbis_close(currentMusic[index].stream); } - if(!getMusicStreamCount()) musicEnabled = false; + if(!getMusicStreamCount()) musicEnabled_g = false; if(currentMusic[index].stream || currentMusic[index].chipctx) { currentMusic[index].stream = NULL; @@ -884,7 +889,7 @@ int getMusicStreamCount(void) void PauseMusicStream(int index) { // Pause music stream if music available! - if (currentMusic[index].ctx && musicEnabled) + if (currentMusic[index].ctx && musicEnabled_g) { TraceLog(INFO, "Pausing music stream"); alSourcePause(currentMusic[index].ctx->alSource); @@ -987,7 +992,7 @@ static bool BufferMusicStream(int index) int size = 0; // Total size of data steamed (in bytes) bool active = true; // We can get more data from stream (not finished) - if (musicEnabled) + if (musicEnabled_g) { if(!currentMusic[index].ctx->playing) { @@ -1031,13 +1036,25 @@ static void EmptyMusicStream(int index) } } +//determine if a music stream is ready to be written to +static bool isMusicStreamReady(int index) +{ + ALint processed = 0; + alGetSourcei(currentMusic[index].ctx->alSource, AL_BUFFERS_PROCESSED, &processed); + + if(processed) + return true; + + return false; +} + // Update (re-fill) music buffers if data already processed void UpdateMusicStream(int index) { ALenum state; bool active = true; - if (index < MAX_MUSIC_STREAMS && musicEnabled) + if (index < MAX_MUSIC_STREAMS && musicEnabled_g && isMusicStreamReady(index)) { active = BufferMusicStream(index); -- cgit v1.2.3 From d38d7a1bedadf7c4824c9bb1d4a9ecd3f0935b9c Mon Sep 17 00:00:00 2001 From: Joshua Reisenauer Date: Sat, 14 May 2016 16:30:32 -0700 Subject: clean up on buffering and preconditions --- src/audio.c | 89 ++++++++++++++++++++++++++++++++++--------------------------- 1 file changed, 50 insertions(+), 39 deletions(-) (limited to 'src') diff --git a/src/audio.c b/src/audio.c index 0fd1bb13..30e2343f 100644 --- a/src/audio.c +++ b/src/audio.c @@ -889,7 +889,7 @@ int getMusicStreamCount(void) void PauseMusicStream(int index) { // Pause music stream if music available! - if (currentMusic[index].ctx && musicEnabled_g) + if (index < MAX_MUSIC_STREAMS && currentMusic[index].ctx && musicEnabled_g) { TraceLog(INFO, "Pausing music stream"); alSourcePause(currentMusic[index].ctx->alSource); @@ -902,7 +902,7 @@ void ResumeMusicStream(int index) { // Resume music playing... if music available! ALenum state; - if(currentMusic[index].ctx){ + if(index < MAX_MUSIC_STREAMS && currentMusic[index].ctx){ alGetSourcei(currentMusic[index].ctx->alSource, AL_SOURCE_STATE, &state); if (state == AL_PAUSED) { @@ -962,17 +962,20 @@ float GetMusicTimeLength(int index) float GetMusicTimePlayed(int index) { float secondsPlayed; - if (currentMusic[index].chipTune) - { - uint64_t samples; - jar_xm_get_position(currentMusic[index].chipctx, NULL, NULL, NULL, &samples); - secondsPlayed = (float)samples / (48000 * currentMusic[index].ctx->channels); // Not sure if this is the correct value - } - else + if(index < MAX_MUSIC_STREAMS && currentMusic[index].ctx) { - int totalSamples = stb_vorbis_stream_length_in_samples(currentMusic[index].stream) * currentMusic[index].ctx->channels; - int samplesPlayed = totalSamples - currentMusic[index].totalSamplesLeft; - secondsPlayed = (float)samplesPlayed / (currentMusic[index].ctx->sampleRate * currentMusic[index].ctx->channels); + if (currentMusic[index].chipTune) + { + uint64_t samples; + jar_xm_get_position(currentMusic[index].chipctx, NULL, NULL, NULL, &samples); + secondsPlayed = (float)samples / (48000 * currentMusic[index].ctx->channels); // Not sure if this is the correct value + } + else + { + int totalSamples = stb_vorbis_stream_length_in_samples(currentMusic[index].stream) * currentMusic[index].ctx->channels; + int samplesPlayed = totalSamples - currentMusic[index].totalSamplesLeft; + secondsPlayed = (float)samplesPlayed / (currentMusic[index].ctx->sampleRate * currentMusic[index].ctx->channels); + } } @@ -989,33 +992,42 @@ static bool BufferMusicStream(int index) short pcm[MUSIC_BUFFER_SIZE_SHORT]; float pcmf[MUSIC_BUFFER_SIZE_FLOAT]; - int size = 0; // Total size of data steamed (in bytes) + int size = 0; // Total size of data steamed in L+R samples bool active = true; // We can get more data from stream (not finished) - if (musicEnabled_g) + + if(!currentMusic[index].ctx->playing && currentMusic[index].totalSamplesLeft > 0) { - if(!currentMusic[index].ctx->playing) - { - UpdateAudioContext(currentMusic[index].ctx, NULL, 0); - return false; - } - - if (currentMusic[index].chipTune) // There is no end of stream for xmfiles, once the end is reached zeros are generated for non looped chiptunes. - { - jar_xm_generate_samples(currentMusic[index].chipctx, pcmf, MUSIC_BUFFER_SIZE_FLOAT / 2); // reads 2*readlen shorts and moves them to buffer+size memory location - UpdateAudioContext(currentMusic[index].ctx, pcmf, MUSIC_BUFFER_SIZE_FLOAT); - currentMusic[index].totalSamplesLeft -= MUSIC_BUFFER_SIZE_FLOAT; - if(currentMusic[index].totalSamplesLeft <= 0) active = false; - } + UpdateAudioContext(currentMusic[index].ctx, NULL, 0); + return true; // it is still active, only it is paused + } + + + if (currentMusic[index].chipTune) // There is no end of stream for xmfiles, once the end is reached zeros are generated for non looped chiptunes. + { + if(currentMusic[index].totalSamplesLeft >= MUSIC_BUFFER_SIZE_FLOAT / 2) + size = MUSIC_BUFFER_SIZE_FLOAT / 2; else - { - int streamedBytes = stb_vorbis_get_samples_short_interleaved(currentMusic[index].stream, currentMusic[index].ctx->channels, pcm, MUSIC_BUFFER_SIZE_SHORT); - UpdateAudioContext(currentMusic[index].ctx, pcm, streamedBytes); - currentMusic[index].totalSamplesLeft -= streamedBytes*currentMusic[index].ctx->channels; - if(currentMusic[index].totalSamplesLeft <= 0) active = false; - } - TraceLog(DEBUG, "Buffering index:%i, chiptune:%i", index, (int)currentMusic[index].chipTune); + size = currentMusic[index].totalSamplesLeft / 2; + + jar_xm_generate_samples(currentMusic[index].chipctx, pcmf, size); // reads 2*readlen shorts and moves them to buffer+size memory location + UpdateAudioContext(currentMusic[index].ctx, pcmf, size * 2); + currentMusic[index].totalSamplesLeft -= size * 2; } + else + { + if(currentMusic[index].totalSamplesLeft >= MUSIC_BUFFER_SIZE_SHORT) + size = MUSIC_BUFFER_SIZE_SHORT; + else + size = currentMusic[index].totalSamplesLeft; + + int streamedBytes = stb_vorbis_get_samples_short_interleaved(currentMusic[index].stream, currentMusic[index].ctx->channels, pcm, size); + UpdateAudioContext(currentMusic[index].ctx, pcm, streamedBytes * currentMusic[index].ctx->channels); + currentMusic[index].totalSamplesLeft -= streamedBytes * currentMusic[index].ctx->channels; + } + + TraceLog(DEBUG, "Buffering index:%i, chiptune:%i", index, (int)currentMusic[index].chipTune); + if(currentMusic[index].totalSamplesLeft <= 0) active = false; return active; } @@ -1042,8 +1054,7 @@ static bool isMusicStreamReady(int index) ALint processed = 0; alGetSourcei(currentMusic[index].ctx->alSource, AL_BUFFERS_PROCESSED, &processed); - if(processed) - return true; + if(processed) return true; return false; } @@ -1054,11 +1065,11 @@ void UpdateMusicStream(int index) ALenum state; bool active = true; - if (index < MAX_MUSIC_STREAMS && musicEnabled_g && isMusicStreamReady(index)) + if (index < MAX_MUSIC_STREAMS && musicEnabled_g && currentMusic[index].ctx && isMusicStreamReady(index)) { active = BufferMusicStream(index); - if ((!active) && (currentMusic[index].loop)) + if (!active && currentMusic[index].loop && currentMusic[index].ctx->playing) { if(currentMusic[index].chipTune) { @@ -1067,7 +1078,7 @@ void UpdateMusicStream(int index) else { stb_vorbis_seek_start(currentMusic[index].stream); - currentMusic[index].totalSamplesLeft = stb_vorbis_stream_length_in_samples(currentMusic[index].stream)*currentMusic[index].ctx->channels; + currentMusic[index].totalSamplesLeft = stb_vorbis_stream_length_in_samples(currentMusic[index].stream) * currentMusic[index].ctx->channels; } active = BufferMusicStream(index); } -- cgit v1.2.3 From 86fbf4fd8f0cd5301259b2115125c45c9872c02a Mon Sep 17 00:00:00 2001 From: Joshua Reisenauer Date: Sun, 15 May 2016 02:09:57 -0700 Subject: logic bug fix --- src/audio.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/audio.c b/src/audio.c index 30e2343f..cc5ca1a2 100644 --- a/src/audio.c +++ b/src/audio.c @@ -996,10 +996,10 @@ static bool BufferMusicStream(int index) bool active = true; // We can get more data from stream (not finished) - if(!currentMusic[index].ctx->playing && currentMusic[index].totalSamplesLeft > 0) + if (!currentMusic[index].ctx->playing && currentMusic[index].totalSamplesLeft > 0) { UpdateAudioContext(currentMusic[index].ctx, NULL, 0); - return true; // it is still active, only it is paused + return true; // it is still active but it is paused } @@ -1071,7 +1071,7 @@ void UpdateMusicStream(int index) if (!active && currentMusic[index].loop && currentMusic[index].ctx->playing) { - if(currentMusic[index].chipTune) + if (currentMusic[index].chipTune) { currentMusic[index].totalSamplesLeft = currentMusic[index].totalLengthSeconds * currentMusic[index].ctx->sampleRate; } @@ -1080,7 +1080,7 @@ void UpdateMusicStream(int index) stb_vorbis_seek_start(currentMusic[index].stream); currentMusic[index].totalSamplesLeft = stb_vorbis_stream_length_in_samples(currentMusic[index].stream) * currentMusic[index].ctx->channels; } - active = BufferMusicStream(index); + active = true; } @@ -1088,7 +1088,7 @@ void UpdateMusicStream(int index) alGetSourcei(currentMusic[index].ctx->alSource, AL_SOURCE_STATE, &state); - if ((state != AL_PLAYING) && active) alSourcePlay(currentMusic[index].ctx->alSource); + if (state != AL_PLAYING && active && currentMusic[index].ctx->playing) alSourcePlay(currentMusic[index].ctx->alSource); if (!active) StopMusicStream(index); -- cgit v1.2.3 From 76ff4d220ee735b8b86bd4dae776665cf68e4fb4 Mon Sep 17 00:00:00 2001 From: Joshua Reisenauer Date: Sun, 15 May 2016 19:37:15 -0700 Subject: renamed everything so it is obvious what it does --- src/audio.c | 380 ++++++++++++++++++++++++++++------------------------------- src/audio.h | 15 +-- src/raylib.h | 15 +-- 3 files changed, 190 insertions(+), 220 deletions(-) (limited to 'src') diff --git a/src/audio.c b/src/audio.c index cc5ca1a2..584d3ad1 100644 --- a/src/audio.c +++ b/src/audio.c @@ -77,10 +77,10 @@ // Types and Structures Definition //---------------------------------------------------------------------------------- -// Audio Context, used to create custom audio streams that are not bound to a sound file. There can be -// no more than 4 concurrent audio contexts in use. This is due to each active context being tied to -// a dedicated mix channel. All audio is 32bit floating point in stereo. -typedef struct AudioContext_t { +// Used to create custom audio streams that are not bound to a specific file. There can be +// no more than 4 concurrent mixchannels in use. This is due to each active mixc being tied to +// a dedicated mix channel. +typedef struct MixChannel_t { unsigned short sampleRate; // default is 48000 unsigned char channels; // 1=mono,2=stereo unsigned char mixChannel; // 0-3 or mixA-mixD, each mix channel can receive up to one dedicated audio stream @@ -89,14 +89,14 @@ typedef struct AudioContext_t { ALenum alFormat; // openAL format specifier ALuint alSource; // openAL source ALuint alBuffer[MAX_STREAM_BUFFERS]; // openAL sample buffer -} AudioContext_t; +} MixChannel_t; // Music type (file streaming from memory) -// NOTE: Anything longer than ~10 seconds should be streamed... +// NOTE: Anything longer than ~10 seconds should be streamed into a mix channel... typedef struct Music { stb_vorbis *stream; - jar_xm_context_t *chipctx; // Stores jar_xm context - AudioContext_t *ctx; // audio context + jar_xm_context_t *chipctx; // Stores jar_xm mixc + MixChannel_t *mixc; // mix channel int totalSamplesLeft; float totalLengthSeconds; @@ -111,9 +111,9 @@ typedef enum { INFO = 0, ERROR, WARNING, DEBUG, OTHER } TraceLogType; //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- -static AudioContext_t* mixChannelsActive_g[MAX_AUDIO_CONTEXTS]; // What mix channels are currently active +static MixChannel_t* mixChannelsActive_g[MAX_AUDIO_CONTEXTS]; // What mix channels are currently active static bool musicEnabled_g = false; -static Music currentMusic[MAX_MUSIC_STREAMS]; // Current music loaded, up to two can play at the same time +static Music currentMusic[MAX_MUSIC_STREAMS]; // Current music loaded, up to two can play at the same time //---------------------------------------------------------------------------------- // Module specific Functions Declaration @@ -122,13 +122,17 @@ static Wave LoadWAV(const char *fileName); // Load WAV file static Wave LoadOGG(char *fileName); // Load OGG file static void UnloadWave(Wave wave); // Unload wave data -static bool BufferMusicStream(int index); // Fill music buffers with data -static void EmptyMusicStream(int index); // Empty music buffers +static bool BufferMusicStream(int index, int numBuffers); // Fill music buffers with data +static void EmptyMusicStream(int index); // Empty music buffers -static unsigned short FillAlBufferWithSilence(AudioContext_t *context, ALuint buffer);// fill buffer with zeros, returns number processed -static void ResampleShortToFloat(short *shorts, float *floats, unsigned short len); // pass two arrays of the same legnth in -static void ResampleByteToFloat(char *chars, float *floats, unsigned short len); // pass two arrays of same length in -static bool isMusicStreamReady(int index); // Checks if music buffer is ready to be refilled + +static MixChannel_t* InitMixChannel(unsigned short sampleRate, unsigned char mixChannel, unsigned char channels, bool floatingPoint); // For streaming into mix channels. +static void CloseMixChannel(MixChannel_t* mixc); // Frees mix channel +static unsigned short BufferMixChannel(MixChannel_t* mixc, void *data, int numberElements); // Pushes more audio data into mixc mix channel, if NULL is passed it pauses +static unsigned short FillAlBufferWithSilence(MixChannel_t *mixc, ALuint buffer); // Fill buffer with zeros, returns number processed +static void ResampleShortToFloat(short *shorts, float *floats, unsigned short len); // Pass two arrays of the same legnth in +static void ResampleByteToFloat(char *chars, float *floats, unsigned short len); // Pass two arrays of same length in +static int IsMusicStreamReadyForBuffering(int index); // Checks if music buffer is ready to be refilled #if defined(AUDIO_STANDALONE) const char *GetExtension(const char *fileName); // Get the extension for a filename @@ -139,7 +143,7 @@ void TraceLog(int msgType, const char *text, ...); // Outputs a trace log messa // Module Functions Definition - Audio Device initialization and Closing //---------------------------------------------------------------------------------- -// Initialize audio device and context +// Initialize audio device and mixc void InitAudioDevice(void) { // Open and initialize a device with default settings @@ -155,7 +159,7 @@ void InitAudioDevice(void) alcCloseDevice(device); - TraceLog(ERROR, "Could not setup audio context"); + TraceLog(ERROR, "Could not setup mix channel"); } TraceLog(INFO, "Audio device and context initialized successfully: %s", alcGetString(device, ALC_DEVICE_SPECIFIER)); @@ -171,14 +175,14 @@ void CloseAudioDevice(void) { for(int index=0; index= MAX_AUDIO_CONTEXTS) return NULL; if(!IsAudioDeviceReady()) InitAudioDevice(); if(!mixChannelsActive_g[mixChannel]){ - AudioContext_t *ac = (AudioContext_t*)malloc(sizeof(AudioContext_t)); - ac->sampleRate = sampleRate; - ac->channels = channels; - ac->mixChannel = mixChannel; - ac->floatingPoint = floatingPoint; - mixChannelsActive_g[mixChannel] = ac; + MixChannel_t *mixc = (MixChannel_t*)malloc(sizeof(MixChannel_t)); + mixc->sampleRate = sampleRate; + mixc->channels = channels; + mixc->mixChannel = mixChannel; + mixc->floatingPoint = floatingPoint; + mixChannelsActive_g[mixChannel] = mixc; // setup openAL format if(channels == 1) { if(floatingPoint) - ac->alFormat = AL_FORMAT_MONO_FLOAT32; + mixc->alFormat = AL_FORMAT_MONO_FLOAT32; else - ac->alFormat = AL_FORMAT_MONO16; + mixc->alFormat = AL_FORMAT_MONO16; } else if(channels == 2) { if(floatingPoint) - ac->alFormat = AL_FORMAT_STEREO_FLOAT32; + mixc->alFormat = AL_FORMAT_STEREO_FLOAT32; else - ac->alFormat = AL_FORMAT_STEREO16; + mixc->alFormat = AL_FORMAT_STEREO16; } // Create an audio source - alGenSources(1, &ac->alSource); - alSourcef(ac->alSource, AL_PITCH, 1); - alSourcef(ac->alSource, AL_GAIN, 1); - alSource3f(ac->alSource, AL_POSITION, 0, 0, 0); - alSource3f(ac->alSource, AL_VELOCITY, 0, 0, 0); + alGenSources(1, &mixc->alSource); + alSourcef(mixc->alSource, AL_PITCH, 1); + alSourcef(mixc->alSource, AL_GAIN, 1); + alSource3f(mixc->alSource, AL_POSITION, 0, 0, 0); + alSource3f(mixc->alSource, AL_VELOCITY, 0, 0, 0); // Create Buffer - alGenBuffers(MAX_STREAM_BUFFERS, ac->alBuffer); + alGenBuffers(MAX_STREAM_BUFFERS, mixc->alBuffer); //fill buffers int x; for(x=0;xalBuffer[x]); + FillAlBufferWithSilence(mixc, mixc->alBuffer[x]); - alSourceQueueBuffers(ac->alSource, MAX_STREAM_BUFFERS, ac->alBuffer); - alSourcePlay(ac->alSource); - ac->playing = true; + alSourceQueueBuffers(mixc->alSource, MAX_STREAM_BUFFERS, mixc->alBuffer); + mixc->playing = true; + alSourcePlay(mixc->alSource); - return ac; + return mixc; } return NULL; } -// Frees buffer in audio context -void CloseAudioContext(AudioContext ctx) +// Frees buffer in mix channel +static void CloseMixChannel(MixChannel_t* mixc) { - AudioContext_t *context = (AudioContext_t*)ctx; - if(context){ - alSourceStop(context->alSource); - context->playing = false; + if(mixc){ + alSourceStop(mixc->alSource); + mixc->playing = false; //flush out all queued buffers ALuint buffer = 0; int queued = 0; - alGetSourcei(context->alSource, AL_BUFFERS_QUEUED, &queued); + alGetSourcei(mixc->alSource, AL_BUFFERS_QUEUED, &queued); while (queued > 0) { - alSourceUnqueueBuffers(context->alSource, 1, &buffer); + alSourceUnqueueBuffers(mixc->alSource, 1, &buffer); queued--; } //delete source and buffers - alDeleteSources(1, &context->alSource); - alDeleteBuffers(MAX_STREAM_BUFFERS, context->alBuffer); - mixChannelsActive_g[context->mixChannel] = NULL; - free(context); - ctx = NULL; + alDeleteSources(1, &mixc->alSource); + alDeleteBuffers(MAX_STREAM_BUFFERS, mixc->alBuffer); + mixChannelsActive_g[mixc->mixChannel] = NULL; + free(mixc); + mixc = NULL; } } -// Pushes more audio data into context mix channel, if none are ever pushed then zeros are fed in. -// Call "UpdateAudioContext(ctx, NULL, 0)" if you want to pause the audio. +// Pushes more audio data into mixc mix channel, only one buffer per call +// Call "BufferMixChannel(mixc, NULL, 0)" if you want to pause the audio. // @Returns number of samples that where processed. -unsigned short UpdateAudioContext(AudioContext ctx, void *data, unsigned short numberElements) +static unsigned short BufferMixChannel(MixChannel_t* mixc, void *data, int numberElements) { - AudioContext_t *context = (AudioContext_t*)ctx; - - if(!context || (context->channels == 2 && numberElements % 2 != 0)) return 0; // when there is two channels there must be an even number of samples + if(!mixc || mixChannelsActive_g[mixc->mixChannel] != mixc) return 0; // when there is two channels there must be an even number of samples if (!data || !numberElements) { // pauses audio until data is given - alSourcePause(context->alSource); - context->playing = false; + if(mixc->playing){ + alSourcePause(mixc->alSource); + mixc->playing = false; + } return 0; } - else + else if(!mixc->playing) { // restart audio otherwise - ALint state; - alGetSourcei(context->alSource, AL_SOURCE_STATE, &state); - if (state != AL_PLAYING){ - alSourcePlay(context->alSource); - context->playing = true; - } + alSourcePlay(mixc->alSource); + mixc->playing = true; } - if (context && context->playing && mixChannelsActive_g[context->mixChannel] == context) + + ALuint buffer = 0; + + alSourceUnqueueBuffers(mixc->alSource, 1, &buffer); + if(!buffer) return 0; + if(mixc->floatingPoint) // process float buffers { - ALint processed = 0; - ALuint buffer = 0; - unsigned short numberProcessed = 0; - unsigned short numberRemaining = numberElements; - - - alGetSourcei(context->alSource, AL_BUFFERS_PROCESSED, &processed); // Get the number of already processed buffers (if any) - if(!processed) return 0; // nothing to process, queue is still full - - - while (processed > 0) - { - if(context->floatingPoint) // process float buffers - { - float *ptr = (float*)data; - alSourceUnqueueBuffers(context->alSource, 1, &buffer); - if(numberRemaining >= MUSIC_BUFFER_SIZE_FLOAT) - { - alBufferData(buffer, context->alFormat, &ptr[numberProcessed], MUSIC_BUFFER_SIZE_FLOAT*sizeof(float), context->sampleRate); - numberProcessed+=MUSIC_BUFFER_SIZE_FLOAT; - numberRemaining-=MUSIC_BUFFER_SIZE_FLOAT; - } - else - { - alBufferData(buffer, context->alFormat, &ptr[numberProcessed], numberRemaining*sizeof(float), context->sampleRate); - numberProcessed+=numberRemaining; - numberRemaining=0; - } - alSourceQueueBuffers(context->alSource, 1, &buffer); - processed--; - } - else if(!context->floatingPoint) // process short buffers - { - short *ptr = (short*)data; - alSourceUnqueueBuffers(context->alSource, 1, &buffer); - if(numberRemaining >= MUSIC_BUFFER_SIZE_SHORT) - { - alBufferData(buffer, context->alFormat, &ptr[numberProcessed], MUSIC_BUFFER_SIZE_FLOAT*sizeof(short), context->sampleRate); - numberProcessed+=MUSIC_BUFFER_SIZE_SHORT; - numberRemaining-=MUSIC_BUFFER_SIZE_SHORT; - } - else - { - alBufferData(buffer, context->alFormat, &ptr[numberProcessed], numberRemaining*sizeof(short), context->sampleRate); - numberProcessed+=numberRemaining; - numberRemaining=0; - } - alSourceQueueBuffers(context->alSource, 1, &buffer); - processed--; - } - else - break; - } - return numberProcessed; + float *ptr = (float*)data; + alBufferData(buffer, mixc->alFormat, ptr, numberElements*sizeof(float), mixc->sampleRate); + } + else // process short buffers + { + short *ptr = (short*)data; + alBufferData(buffer, mixc->alFormat, ptr, numberElements*sizeof(short), mixc->sampleRate); } - return 0; + alSourceQueueBuffers(mixc->alSource, 1, &buffer); + + return numberElements; } // fill buffer with zeros, returns number processed -static unsigned short FillAlBufferWithSilence(AudioContext_t *context, ALuint buffer) +static unsigned short FillAlBufferWithSilence(MixChannel_t *mixc, ALuint buffer) { - if(context->floatingPoint){ + if(mixc->floatingPoint){ float pcm[MUSIC_BUFFER_SIZE_FLOAT] = {0.f}; - alBufferData(buffer, context->alFormat, pcm, MUSIC_BUFFER_SIZE_FLOAT*sizeof(float), context->sampleRate); + alBufferData(buffer, mixc->alFormat, pcm, MUSIC_BUFFER_SIZE_FLOAT*sizeof(float), mixc->sampleRate); return MUSIC_BUFFER_SIZE_FLOAT; } else { short pcm[MUSIC_BUFFER_SIZE_SHORT] = {0}; - alBufferData(buffer, context->alFormat, pcm, MUSIC_BUFFER_SIZE_SHORT*sizeof(short), context->sampleRate); + alBufferData(buffer, mixc->alFormat, pcm, MUSIC_BUFFER_SIZE_SHORT*sizeof(short), mixc->sampleRate); return MUSIC_BUFFER_SIZE_SHORT; } } @@ -417,6 +376,28 @@ static void ResampleByteToFloat(char *chars, float *floats, unsigned short len) } } +// used to output raw audio streams, returns negative numbers on error +RawAudioContext InitRawAudioContext(int sampleRate, int channels, bool floatingPoint) +{ + int mixIndex; + for(mixIndex = 0; mixIndex < MAX_AUDIO_CONTEXTS; mixIndex++) // find empty mix channel slot + { + if(mixChannelsActive_g[mixIndex] == NULL) break; + else if(mixIndex = MAX_AUDIO_CONTEXTS - 1) return -1; // error + } + + if(InitMixChannel(sampleRate, mixIndex, channels, floatingPoint)) + return mixIndex; + else + return -2; // error +} + +void CloseRawAudioContext(RawAudioContext ctx) +{ + if(mixChannelsActive_g[ctx]) + CloseMixChannel(mixChannelsActive_g[ctx]); +} + //---------------------------------------------------------------------------------- @@ -807,14 +788,14 @@ int PlayMusicStream(int musicIndex, char *fileName) currentMusic[musicIndex].totalLengthSeconds = stb_vorbis_stream_length_in_seconds(currentMusic[musicIndex].stream); if (info.channels == 2){ - currentMusic[musicIndex].ctx = InitAudioContext(info.sample_rate, mixIndex, 2, false); - currentMusic[musicIndex].ctx->playing = true; + currentMusic[musicIndex].mixc = InitMixChannel(info.sample_rate, mixIndex, 2, false); + currentMusic[musicIndex].mixc->playing = true; } else{ - currentMusic[musicIndex].ctx = InitAudioContext(info.sample_rate, mixIndex, 1, false); - currentMusic[musicIndex].ctx->playing = true; + currentMusic[musicIndex].mixc = InitMixChannel(info.sample_rate, mixIndex, 1, false); + currentMusic[musicIndex].mixc->playing = true; } - if(!currentMusic[musicIndex].ctx) return 4; // error + if(!currentMusic[musicIndex].mixc) return 4; // error } } else if (strcmp(GetExtension(fileName),"xm") == 0) @@ -832,9 +813,9 @@ int PlayMusicStream(int musicIndex, char *fileName) TraceLog(INFO, "[%s] XM number of samples: %i", fileName, currentMusic[musicIndex].totalSamplesLeft); TraceLog(INFO, "[%s] XM track length: %11.6f sec", fileName, currentMusic[musicIndex].totalLengthSeconds); - currentMusic[musicIndex].ctx = InitAudioContext(48000, mixIndex, 2, true); - if(!currentMusic[musicIndex].ctx) return 5; // error - currentMusic[musicIndex].ctx->playing = true; + currentMusic[musicIndex].mixc = InitMixChannel(48000, mixIndex, 2, false); + if(!currentMusic[musicIndex].mixc) return 5; // error + currentMusic[musicIndex].mixc->playing = true; } else { @@ -853,9 +834,9 @@ int PlayMusicStream(int musicIndex, char *fileName) // Stop music playing for individual music index of currentMusic array (close stream) void StopMusicStream(int index) { - if (index < MAX_MUSIC_STREAMS && currentMusic[index].ctx) + if (index < MAX_MUSIC_STREAMS && currentMusic[index].mixc) { - CloseAudioContext(currentMusic[index].ctx); + CloseMixChannel(currentMusic[index].mixc); if (currentMusic[index].chipTune) { @@ -889,11 +870,11 @@ int getMusicStreamCount(void) void PauseMusicStream(int index) { // Pause music stream if music available! - if (index < MAX_MUSIC_STREAMS && currentMusic[index].ctx && musicEnabled_g) + if (index < MAX_MUSIC_STREAMS && currentMusic[index].mixc && musicEnabled_g) { TraceLog(INFO, "Pausing music stream"); - alSourcePause(currentMusic[index].ctx->alSource); - currentMusic[index].ctx->playing = false; + alSourcePause(currentMusic[index].mixc->alSource); + currentMusic[index].mixc->playing = false; } } @@ -902,13 +883,13 @@ void ResumeMusicStream(int index) { // Resume music playing... if music available! ALenum state; - if(index < MAX_MUSIC_STREAMS && currentMusic[index].ctx){ - alGetSourcei(currentMusic[index].ctx->alSource, AL_SOURCE_STATE, &state); + if(index < MAX_MUSIC_STREAMS && currentMusic[index].mixc){ + alGetSourcei(currentMusic[index].mixc->alSource, AL_SOURCE_STATE, &state); if (state == AL_PAUSED) { TraceLog(INFO, "Resuming music stream"); - alSourcePlay(currentMusic[index].ctx->alSource); - currentMusic[index].ctx->playing = true; + alSourcePlay(currentMusic[index].mixc->alSource); + currentMusic[index].mixc->playing = true; } } } @@ -919,8 +900,8 @@ bool IsMusicPlaying(int index) bool playing = false; ALint state; - if(index < MAX_MUSIC_STREAMS && currentMusic[index].ctx){ - alGetSourcei(currentMusic[index].ctx->alSource, AL_SOURCE_STATE, &state); + if(index < MAX_MUSIC_STREAMS && currentMusic[index].mixc){ + alGetSourcei(currentMusic[index].mixc->alSource, AL_SOURCE_STATE, &state); if (state == AL_PLAYING) playing = true; } @@ -930,15 +911,15 @@ bool IsMusicPlaying(int index) // Set volume for music void SetMusicVolume(int index, float volume) { - if(index < MAX_MUSIC_STREAMS && currentMusic[index].ctx){ - alSourcef(currentMusic[index].ctx->alSource, AL_GAIN, volume); + if(index < MAX_MUSIC_STREAMS && currentMusic[index].mixc){ + alSourcef(currentMusic[index].mixc->alSource, AL_GAIN, volume); } } void SetMusicPitch(int index, float pitch) { - if(index < MAX_MUSIC_STREAMS && currentMusic[index].ctx){ - alSourcef(currentMusic[index].ctx->alSource, AL_PITCH, pitch); + if(index < MAX_MUSIC_STREAMS && currentMusic[index].mixc){ + alSourcef(currentMusic[index].mixc->alSource, AL_PITCH, pitch); } } @@ -962,19 +943,19 @@ float GetMusicTimeLength(int index) float GetMusicTimePlayed(int index) { float secondsPlayed; - if(index < MAX_MUSIC_STREAMS && currentMusic[index].ctx) + if(index < MAX_MUSIC_STREAMS && currentMusic[index].mixc) { if (currentMusic[index].chipTune) { uint64_t samples; jar_xm_get_position(currentMusic[index].chipctx, NULL, NULL, NULL, &samples); - secondsPlayed = (float)samples / (48000 * currentMusic[index].ctx->channels); // Not sure if this is the correct value + secondsPlayed = (float)samples / (48000 * currentMusic[index].mixc->channels); // Not sure if this is the correct value } else { - int totalSamples = stb_vorbis_stream_length_in_samples(currentMusic[index].stream) * currentMusic[index].ctx->channels; + int totalSamples = stb_vorbis_stream_length_in_samples(currentMusic[index].stream) * currentMusic[index].mixc->channels; int samplesPlayed = totalSamples - currentMusic[index].totalSamplesLeft; - secondsPlayed = (float)samplesPlayed / (currentMusic[index].ctx->sampleRate * currentMusic[index].ctx->channels); + secondsPlayed = (float)samplesPlayed / (currentMusic[index].mixc->sampleRate * currentMusic[index].mixc->channels); } } @@ -987,32 +968,32 @@ float GetMusicTimePlayed(int index) //---------------------------------------------------------------------------------- // Fill music buffers with new data from music stream -static bool BufferMusicStream(int index) +static bool BufferMusicStream(int index, int numBuffers) { short pcm[MUSIC_BUFFER_SIZE_SHORT]; float pcmf[MUSIC_BUFFER_SIZE_FLOAT]; - int size = 0; // Total size of data steamed in L+R samples + int size = 0; // Total size of data steamed in L+R samples for xm floats, individual L or R for ogg shorts bool active = true; // We can get more data from stream (not finished) - - - if (!currentMusic[index].ctx->playing && currentMusic[index].totalSamplesLeft > 0) - { - UpdateAudioContext(currentMusic[index].ctx, NULL, 0); - return true; // it is still active but it is paused - } - if (currentMusic[index].chipTune) // There is no end of stream for xmfiles, once the end is reached zeros are generated for non looped chiptunes. { - if(currentMusic[index].totalSamplesLeft >= MUSIC_BUFFER_SIZE_FLOAT / 2) - size = MUSIC_BUFFER_SIZE_FLOAT / 2; + if(currentMusic[index].totalSamplesLeft >= MUSIC_BUFFER_SIZE_SHORT) + size = MUSIC_BUFFER_SIZE_SHORT / 2; else size = currentMusic[index].totalSamplesLeft / 2; - - jar_xm_generate_samples(currentMusic[index].chipctx, pcmf, size); // reads 2*readlen shorts and moves them to buffer+size memory location - UpdateAudioContext(currentMusic[index].ctx, pcmf, size * 2); - currentMusic[index].totalSamplesLeft -= size * 2; + + for(int x=0; xchannels, pcm, size); - UpdateAudioContext(currentMusic[index].ctx, pcm, streamedBytes * currentMusic[index].ctx->channels); - currentMusic[index].totalSamplesLeft -= streamedBytes * currentMusic[index].ctx->channels; + for(int x=0; xchannels, pcm, size); + BufferMixChannel(currentMusic[index].mixc, pcm, streamedBytes * currentMusic[index].mixc->channels); + currentMusic[index].totalSamplesLeft -= streamedBytes * currentMusic[index].mixc->channels; + if(currentMusic[index].totalSamplesLeft <= 0) + { + active = false; + break; + } + } } - - TraceLog(DEBUG, "Buffering index:%i, chiptune:%i", index, (int)currentMusic[index].chipTune); - if(currentMusic[index].totalSamplesLeft <= 0) active = false; return active; } @@ -1038,25 +1024,22 @@ static void EmptyMusicStream(int index) ALuint buffer = 0; int queued = 0; - alGetSourcei(currentMusic[index].ctx->alSource, AL_BUFFERS_QUEUED, &queued); + alGetSourcei(currentMusic[index].mixc->alSource, AL_BUFFERS_QUEUED, &queued); while (queued > 0) { - alSourceUnqueueBuffers(currentMusic[index].ctx->alSource, 1, &buffer); + alSourceUnqueueBuffers(currentMusic[index].mixc->alSource, 1, &buffer); queued--; } } //determine if a music stream is ready to be written to -static bool isMusicStreamReady(int index) +static int IsMusicStreamReadyForBuffering(int index) { ALint processed = 0; - alGetSourcei(currentMusic[index].ctx->alSource, AL_BUFFERS_PROCESSED, &processed); - - if(processed) return true; - - return false; + alGetSourcei(currentMusic[index].mixc->alSource, AL_BUFFERS_PROCESSED, &processed); + return processed; } // Update (re-fill) music buffers if data already processed @@ -1064,21 +1047,22 @@ void UpdateMusicStream(int index) { ALenum state; bool active = true; - - if (index < MAX_MUSIC_STREAMS && musicEnabled_g && currentMusic[index].ctx && isMusicStreamReady(index)) + int numBuffers = IsMusicStreamReadyForBuffering(index); + + if (currentMusic[index].mixc->playing && index < MAX_MUSIC_STREAMS && musicEnabled_g && currentMusic[index].mixc && numBuffers) { - active = BufferMusicStream(index); + active = BufferMusicStream(index, numBuffers); - if (!active && currentMusic[index].loop && currentMusic[index].ctx->playing) + if (!active && currentMusic[index].loop) { if (currentMusic[index].chipTune) { - currentMusic[index].totalSamplesLeft = currentMusic[index].totalLengthSeconds * currentMusic[index].ctx->sampleRate; + currentMusic[index].totalSamplesLeft = currentMusic[index].totalLengthSeconds * 48000; } else { stb_vorbis_seek_start(currentMusic[index].stream); - currentMusic[index].totalSamplesLeft = stb_vorbis_stream_length_in_samples(currentMusic[index].stream) * currentMusic[index].ctx->channels; + currentMusic[index].totalSamplesLeft = stb_vorbis_stream_length_in_samples(currentMusic[index].stream) * currentMusic[index].mixc->channels; } active = true; } @@ -1086,9 +1070,9 @@ void UpdateMusicStream(int index) if (alGetError() != AL_NO_ERROR) TraceLog(WARNING, "Error buffering data..."); - alGetSourcei(currentMusic[index].ctx->alSource, AL_SOURCE_STATE, &state); + alGetSourcei(currentMusic[index].mixc->alSource, AL_SOURCE_STATE, &state); - if (state != AL_PLAYING && active && currentMusic[index].ctx->playing) alSourcePlay(currentMusic[index].ctx->alSource); + if (state != AL_PLAYING && active) alSourcePlay(currentMusic[index].mixc->alSource); if (!active) StopMusicStream(index); diff --git a/src/audio.h b/src/audio.h index 63c1c136..d3276bf6 100644 --- a/src/audio.h +++ b/src/audio.h @@ -61,10 +61,7 @@ typedef struct Wave { short channels; } Wave; -// Audio Context, used to create custom audio streams that are not bound to a sound file. There can be -// no more than 4 concurrent audio contexts in use. This is due to each active context being tied to -// a dedicated mix channel. -typedef void* AudioContext; +typedef int RawAudioContext; #ifdef __cplusplus extern "C" { // Prevents name mangling of functions @@ -82,13 +79,6 @@ void InitAudioDevice(void); // Initialize au void CloseAudioDevice(void); // Close the audio device and context (and music stream) bool IsAudioDeviceReady(void); // True if call to InitAudioDevice() was successful and CloseAudioDevice() has not been called yet -// Audio contexts are for outputing custom audio waveforms, This will shut down any other sound sources currently playing -// The mixChannel is what mix channel you want to operate on, 0-3 are the ones available. Each mix channel can only be used one at a time. -// exmple usage is InitAudioContext(48000, 0, 2, true); // mixchannel 1, 48khz, stereo, floating point -AudioContext InitAudioContext(unsigned short sampleRate, unsigned char mixChannel, unsigned char channels, bool floatingPoint); -void CloseAudioContext(AudioContext ctx); // Frees audio context -unsigned short UpdateAudioContext(AudioContext ctx, void *data, unsigned short numberElements); // Pushes more audio data into context mix channel, if NULL is passed to data then zeros are played - Sound LoadSound(char *fileName); // Load sound to memory Sound LoadSoundFromWave(Wave wave); // Load sound to memory from wave data Sound LoadSoundFromRES(const char *rresName, int resId); // Load sound to memory from rRES file (raylib Resource) @@ -112,6 +102,9 @@ float GetMusicTimePlayed(int index); // Get current m int getMusicStreamCount(void); void SetMusicPitch(int index, float pitch); +RawAudioContext InitRawAudioContext(int sampleRate, int channels, bool floatingPoint); +void CloseRawAudioContext(RawAudioContext ctx); + #ifdef __cplusplus } #endif diff --git a/src/raylib.h b/src/raylib.h index ea9fbfcb..6efde710 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -451,10 +451,7 @@ typedef struct Wave { short channels; } Wave; -// Audio Context, used to create custom audio streams that are not bound to a sound file. There can be -// no more than 4 concurrent audio contexts in use. This is due to each active context being tied to -// a dedicated mix channel. -typedef void* AudioContext; +typedef int RawAudioContext; // Texture formats // NOTE: Support depends on OpenGL version and platform @@ -876,13 +873,6 @@ void InitAudioDevice(void); // Initialize au void CloseAudioDevice(void); // Close the audio device and context (and music stream) bool IsAudioDeviceReady(void); // True if call to InitAudioDevice() was successful and CloseAudioDevice() has not been called yet -// Audio contexts are for outputing custom audio waveforms, This will shut down any other sound sources currently playing -// The mixChannel is what mix channel you want to operate on, 0-3 are the ones available. Each mix channel can only be used one at a time. -// exmple usage is InitAudioContext(48000, 0, 2, true); // mixchannel 1, 48khz, stereo, floating point -AudioContext InitAudioContext(unsigned short sampleRate, unsigned char mixChannel, unsigned char channels, bool floatingPoint); -void CloseAudioContext(AudioContext ctx); // Frees audio context -unsigned short UpdateAudioContext(AudioContext ctx, void *data, unsigned short numberElements); // Pushes more audio data into context mix channel, if NULL is passed to data then zeros are played - Sound LoadSound(char *fileName); // Load sound to memory Sound LoadSoundFromWave(Wave wave); // Load sound to memory from wave data Sound LoadSoundFromRES(const char *rresName, int resId); // Load sound to memory from rRES file (raylib Resource) @@ -906,6 +896,9 @@ float GetMusicTimePlayed(int index); // Get current m int getMusicStreamCount(void); void SetMusicPitch(int index, float pitch); +RawAudioContext InitRawAudioContext(int sampleRate, int channels, bool floatingPoint); // used to output raw audio streams, returns negative numbers on error +void CloseRawAudioContext(RawAudioContext ctx); + #ifdef __cplusplus } #endif -- cgit v1.2.3 From 037edbaa1322a2174d91c7aeacc9170f5b6913cb Mon Sep 17 00:00:00 2001 From: raysan5 Date: Wed, 18 May 2016 13:22:14 +0200 Subject: Reorganize data for consistency --- src/models.c | 54 ++------ src/rlgl.c | 431 ++++++++++++++++++++++++++++++++++++----------------------- src/rlgl.h | 9 +- 3 files changed, 283 insertions(+), 211 deletions(-) (limited to 'src') diff --git a/src/models.c b/src/models.c index 066b919e..bb2d56c2 100644 --- a/src/models.c +++ b/src/models.c @@ -691,31 +691,14 @@ Model LoadCubicmap(Image cubicmap) return model; } -// Unload 3d model from memory +// Unload 3d model from memory (mesh and material) void UnloadModel(Model model) { - // Unload mesh data - if (model.mesh.vertices != NULL) free(model.mesh.vertices); - if (model.mesh.texcoords != NULL) free(model.mesh.texcoords); - if (model.mesh.normals != NULL) free(model.mesh.normals); - if (model.mesh.colors != NULL) free(model.mesh.colors); - if (model.mesh.tangents != NULL) free(model.mesh.tangents); - if (model.mesh.texcoords2 != NULL) free(model.mesh.texcoords2); - if (model.mesh.indices != NULL) free(model.mesh.indices); - - TraceLog(INFO, "Unloaded model data from RAM (CPU)"); - - rlDeleteBuffers(model.mesh.vboId[0]); // vertex - rlDeleteBuffers(model.mesh.vboId[1]); // texcoords - rlDeleteBuffers(model.mesh.vboId[2]); // normals - rlDeleteBuffers(model.mesh.vboId[3]); // colors - rlDeleteBuffers(model.mesh.vboId[4]); // tangents - rlDeleteBuffers(model.mesh.vboId[5]); // texcoords2 - rlDeleteBuffers(model.mesh.vboId[6]); // indices - - rlDeleteVertexArrays(model.mesh.vaoId); - + rlglUnloadMesh(&model.mesh); + UnloadMaterial(model.material); + + TraceLog(INFO, "Unloaded model data from RAM and VRAM"); } // Load material data (from file) @@ -1247,40 +1230,27 @@ void DrawModelEx(Model model, Vector3 position, Vector3 rotationAxis, float rota model.transform = MatrixMultiply(MatrixMultiply(matScale, matRotation), matTranslation); model.material.colDiffuse = tint; - rlglDrawEx(model.mesh, model.material, model.transform, false); + rlglDrawMesh(model.mesh, model.material, model.transform); } // Draw a model wires (with texture if set) void DrawModelWires(Model model, Vector3 position, float scale, Color tint) { - Vector3 vScale = { scale, scale, scale }; - Vector3 rotationAxis = { 0.0f, 0.0f, 0.0f }; - - // Calculate transformation matrix from function parameters - // Get transform matrix (rotation -> scale -> translation) - Matrix matRotation = MatrixRotate(rotationAxis, 0.0f); - Matrix matScale = MatrixScale(vScale.x, vScale.y, vScale.z); - Matrix matTranslation = MatrixTranslate(position.x, position.y, position.z); + rlEnableWireMode(); - model.transform = MatrixMultiply(MatrixMultiply(matScale, matRotation), matTranslation); - model.material.colDiffuse = tint; + DrawModel(model, position, scale, tint); - rlglDrawEx(model.mesh, model.material, model.transform, true); + rlDisableWireMode(); } // Draw a model wires (with texture if set) with extended parameters void DrawModelWiresEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint) { - // Calculate transformation matrix from function parameters - // Get transform matrix (rotation -> scale -> translation) - Matrix matRotation = MatrixRotate(rotationAxis, rotationAngle*DEG2RAD); - Matrix matScale = MatrixScale(scale.x, scale.y, scale.z); - Matrix matTranslation = MatrixTranslate(position.x, position.y, position.z); + rlEnableWireMode(); - model.transform = MatrixMultiply(MatrixMultiply(matScale, matRotation), matTranslation); - model.material.colDiffuse = tint; + DrawModelEx(model, position, rotationAxis, rotationAngle, scale, tint); - rlglDrawEx(model.mesh, model.material, model.transform, true); + rlDisableWireMode(); } // Draw a billboard diff --git a/src/rlgl.c b/src/rlgl.c index 0c0da221..8e725521 100644 --- a/src/rlgl.c +++ b/src/rlgl.c @@ -740,6 +740,24 @@ void rlDisableDepthTest(void) glDisable(GL_DEPTH_TEST); } +// Enable wire mode +void rlEnableWireMode(void) +{ +#if defined (GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_33) + // NOTE: glPolygonMode() not available on OpenGL ES + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); +#endif +} + +// Disable wire mode +void rlDisableWireMode(void) +{ +#if defined (GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_33) + // NOTE: glPolygonMode() not available on OpenGL ES + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); +#endif +} + // Unload texture from GPU memory void rlDeleteTextures(unsigned int id) { @@ -1033,175 +1051,15 @@ void rlglClose(void) void rlglDraw(void) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - UpdateDefaultBuffers(); - DrawDefaultBuffers(); -#endif -} - -// Draw a 3d mesh with material and transform -void rlglDrawEx(Mesh mesh, Material material, Matrix transform, bool wires) -{ -#if defined (GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_33) - // NOTE: glPolygonMode() not available on OpenGL ES - if (wires) glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); -#endif - -#if defined(GRAPHICS_API_OPENGL_11) - glEnable(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, material.texDiffuse.id); - - // NOTE: On OpenGL 1.1 we use Vertex Arrays to draw model - glEnableClientState(GL_VERTEX_ARRAY); // Enable vertex array - glEnableClientState(GL_TEXTURE_COORD_ARRAY); // Enable texture coords array - if (mesh.normals != NULL) glEnableClientState(GL_NORMAL_ARRAY); // Enable normals array - if (mesh.colors != NULL) glEnableClientState(GL_COLOR_ARRAY); // Enable colors array - - glVertexPointer(3, GL_FLOAT, 0, mesh.vertices); // Pointer to vertex coords array - glTexCoordPointer(2, GL_FLOAT, 0, mesh.texcoords); // Pointer to texture coords array - if (mesh.normals != NULL) glNormalPointer(GL_FLOAT, 0, mesh.normals); // Pointer to normals array - if (mesh.colors != NULL) glColorPointer(4, GL_UNSIGNED_BYTE, 0, mesh.colors); // Pointer to colors array - - rlPushMatrix(); - rlMultMatrixf(MatrixToFloat(transform)); - rlColor4ub(material.colDiffuse.r, material.colDiffuse.g, material.colDiffuse.b, material.colDiffuse.a); - - if (mesh.indices != NULL) glDrawElements(GL_TRIANGLES, mesh.triangleCount*3, GL_UNSIGNED_SHORT, mesh.indices); - else glDrawArrays(GL_TRIANGLES, 0, mesh.vertexCount); - rlPopMatrix(); - - glDisableClientState(GL_VERTEX_ARRAY); // Disable vertex array - glDisableClientState(GL_TEXTURE_COORD_ARRAY); // Disable texture coords array - if (mesh.normals != NULL) glDisableClientState(GL_NORMAL_ARRAY); // Disable normals array - if (mesh.colors != NULL) glDisableClientState(GL_NORMAL_ARRAY); // Disable colors array - - glDisable(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, 0); -#endif - -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - glUseProgram(material.shader.id); - - // At this point the modelview matrix just contains the view matrix (camera) - // That's because Begin3dMode() sets it an no model-drawing function modifies it, all use rlPushMatrix() and rlPopMatrix() - Matrix matView = modelview; // View matrix (camera) - Matrix matProjection = projection; // Projection matrix (perspective) - - // Calculate model-view matrix combining matModel and matView - Matrix matModelView = MatrixMultiply(transform, matView); // Transform to camera-space coordinates - - // Calculate model-view-projection matrix (MVP) - Matrix matMVP = MatrixMultiply(matModelView, matProjection); // Transform to screen-space coordinates - - // Send combined model-view-projection matrix to shader - glUniformMatrix4fv(material.shader.mvpLoc, 1, false, MatrixToFloat(matMVP)); - - // Apply color tinting (material.colDiffuse) - // NOTE: Just update one uniform on fragment shader - float vColor[4] = { (float)material.colDiffuse.r/255, (float)material.colDiffuse.g/255, (float)material.colDiffuse.b/255, (float)material.colDiffuse.a/255 }; - glUniform4fv(material.shader.tintColorLoc, 1, vColor); - - // Set shader textures (diffuse, normal, specular) - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, material.texDiffuse.id); - glUniform1i(material.shader.mapDiffuseLoc, 0); // Texture fits in active texture unit 0 - - if ((material.texNormal.id != 0) && (material.shader.mapNormalLoc != -1)) - { - glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_2D, material.texNormal.id); - glUniform1i(material.shader.mapNormalLoc, 1); // Texture fits in active texture unit 1 - } - - if ((material.texSpecular.id != 0) && (material.shader.mapSpecularLoc != -1)) - { - glActiveTexture(GL_TEXTURE2); - glBindTexture(GL_TEXTURE_2D, material.texSpecular.id); - glUniform1i(material.shader.mapSpecularLoc, 2); // Texture fits in active texture unit 2 - } - - if (vaoSupported) - { - glBindVertexArray(mesh.vaoId); - } - else - { - // Bind mesh VBO data: vertex position (shader-location = 0) - glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[0]); - glVertexAttribPointer(material.shader.vertexLoc, 3, GL_FLOAT, 0, 0, 0); - glEnableVertexAttribArray(material.shader.vertexLoc); - - // Bind mesh VBO data: vertex texcoords (shader-location = 1) - glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[1]); - glVertexAttribPointer(material.shader.texcoordLoc, 2, GL_FLOAT, 0, 0, 0); - glEnableVertexAttribArray(material.shader.texcoordLoc); - - // Bind mesh VBO data: vertex normals (shader-location = 2, if available) - if (material.shader.normalLoc != -1) - { - glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[2]); - glVertexAttribPointer(material.shader.normalLoc, 3, GL_FLOAT, 0, 0, 0); - glEnableVertexAttribArray(material.shader.normalLoc); - } - - // Bind mesh VBO data: vertex colors (shader-location = 3, if available) , tangents, texcoords2 (if available) - if (material.shader.colorLoc != -1) - { - glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[3]); - glVertexAttribPointer(material.shader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); - glEnableVertexAttribArray(material.shader.colorLoc); - } - - // Bind mesh VBO data: vertex tangents (shader-location = 4, if available) - if (material.shader.tangentLoc != -1) - { - glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[4]); - glVertexAttribPointer(material.shader.tangentLoc, 3, GL_FLOAT, 0, 0, 0); - glEnableVertexAttribArray(material.shader.tangentLoc); - } - - // Bind mesh VBO data: vertex texcoords2 (shader-location = 5, if available) - if (material.shader.texcoord2Loc != -1) - { - glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[5]); - glVertexAttribPointer(material.shader.texcoord2Loc, 2, GL_FLOAT, 0, 0, 0); - glEnableVertexAttribArray(material.shader.texcoord2Loc); - } - - if (mesh.indices != NULL) glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, quads.vboId[3]); - } - - // Draw call! - if (mesh.indices != NULL) glDrawElements(GL_TRIANGLES, mesh.triangleCount*3, GL_UNSIGNED_SHORT, 0); // Indexed vertices draw - else glDrawArrays(GL_TRIANGLES, 0, mesh.vertexCount); - - if (material.texNormal.id != 0) - { - glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_2D, 0); - } - - if (material.texSpecular.id != 0) - { - glActiveTexture(GL_TEXTURE2); - glBindTexture(GL_TEXTURE_2D, 0); - } - - glActiveTexture(GL_TEXTURE0); // Set shader active texture to default 0 - glBindTexture(GL_TEXTURE_2D, 0); // Unbind textures - - if (vaoSupported) glBindVertexArray(0); // Unbind VAO - else +/* + for (int i = 0; i < modelsCount; i++) { - glBindBuffer(GL_ARRAY_BUFFER, 0); // Unbind VBOs - if (mesh.indices != NULL) glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + rlglDrawMesh(models[i]->mesh, models[i]->material, models[i]->transform); } - - glUseProgram(0); // Unbind shader program -#endif - -#if defined (GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_33) - // NOTE: glPolygonMode() not available on OpenGL ES - if (wires) glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); +*/ + // NOTE: Default buffers always drawn at the end + UpdateDefaultBuffers(); + DrawDefaultBuffers(); #endif } @@ -1776,6 +1634,245 @@ void rlglLoadMesh(Mesh *mesh) #endif } +// Update vertex data on GPU (upload new data to one buffer) +void rlglUpdateMesh(Mesh mesh, int buffer, int numVertex) +{ + // Activate mesh VAO + if (vaoSupported) glBindVertexArray(mesh.vaoId); + + switch (buffer) + { + case 0: // Update vertices (vertex position) + { + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[0]); + if (numVertex >= mesh.vertexCount) glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*numVertex, mesh.vertices, GL_DYNAMIC_DRAW); + else glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*3*numVertex, mesh.vertices); + + } break; + case 1: // Update texcoords (vertex texture coordinates) + { + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[1]); + if (numVertex >= mesh.vertexCount) glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*numVertex, mesh.texcoords, GL_DYNAMIC_DRAW); + else glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*2*numVertex, mesh.texcoords); + + } break; + case 2: // Update normals (vertex normals) + { + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[0]); + if (numVertex >= mesh.vertexCount) glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*numVertex, mesh.normals, GL_DYNAMIC_DRAW); + else glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*3*numVertex, mesh.normals); + + } break; + case 3: // Update colors (vertex colors) + { + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[2]); + if (numVertex >= mesh.vertexCount) glBufferData(GL_ARRAY_BUFFER, sizeof(float)*4*numVertex, mesh.colors, GL_DYNAMIC_DRAW); + else glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(unsigned char)*4*numVertex, mesh.colors); + + } break; + case 4: // Update tangents (vertex tangents) + { + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[0]); + if (numVertex >= mesh.vertexCount) glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*numVertex, mesh.tangents, GL_DYNAMIC_DRAW); + else glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*3*numVertex, mesh.tangents); + } break; + case 5: // Update texcoords2 (vertex second texture coordinates) + { + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[1]); + if (numVertex >= mesh.vertexCount) glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*numVertex, mesh.texcoords2, GL_DYNAMIC_DRAW); + else glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*2*numVertex, mesh.texcoords2); + } break; + default: break; + } + + // Unbind the current VAO + if (vaoSupported) glBindVertexArray(0); + + // Another option would be using buffer mapping... + //mesh.vertices = glMapBuffer(GL_ARRAY_BUFFER, GL_READ_WRITE); + // Now we can modify vertices + //glUnmapBuffer(GL_ARRAY_BUFFER); +} + +// Draw a 3d mesh with material and transform +void rlglDrawMesh(Mesh mesh, Material material, Matrix transform) +{ +#if defined(GRAPHICS_API_OPENGL_11) + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, material.texDiffuse.id); + + // NOTE: On OpenGL 1.1 we use Vertex Arrays to draw model + glEnableClientState(GL_VERTEX_ARRAY); // Enable vertex array + glEnableClientState(GL_TEXTURE_COORD_ARRAY); // Enable texture coords array + if (mesh.normals != NULL) glEnableClientState(GL_NORMAL_ARRAY); // Enable normals array + if (mesh.colors != NULL) glEnableClientState(GL_COLOR_ARRAY); // Enable colors array + + glVertexPointer(3, GL_FLOAT, 0, mesh.vertices); // Pointer to vertex coords array + glTexCoordPointer(2, GL_FLOAT, 0, mesh.texcoords); // Pointer to texture coords array + if (mesh.normals != NULL) glNormalPointer(GL_FLOAT, 0, mesh.normals); // Pointer to normals array + if (mesh.colors != NULL) glColorPointer(4, GL_UNSIGNED_BYTE, 0, mesh.colors); // Pointer to colors array + + rlPushMatrix(); + rlMultMatrixf(MatrixToFloat(transform)); + rlColor4ub(material.colDiffuse.r, material.colDiffuse.g, material.colDiffuse.b, material.colDiffuse.a); + + if (mesh.indices != NULL) glDrawElements(GL_TRIANGLES, mesh.triangleCount*3, GL_UNSIGNED_SHORT, mesh.indices); + else glDrawArrays(GL_TRIANGLES, 0, mesh.vertexCount); + rlPopMatrix(); + + glDisableClientState(GL_VERTEX_ARRAY); // Disable vertex array + glDisableClientState(GL_TEXTURE_COORD_ARRAY); // Disable texture coords array + if (mesh.normals != NULL) glDisableClientState(GL_NORMAL_ARRAY); // Disable normals array + if (mesh.colors != NULL) glDisableClientState(GL_NORMAL_ARRAY); // Disable colors array + + glDisable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, 0); +#endif + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + glUseProgram(material.shader.id); + + // At this point the modelview matrix just contains the view matrix (camera) + // That's because Begin3dMode() sets it an no model-drawing function modifies it, all use rlPushMatrix() and rlPopMatrix() + Matrix matView = modelview; // View matrix (camera) + Matrix matProjection = projection; // Projection matrix (perspective) + + // Calculate model-view matrix combining matModel and matView + Matrix matModelView = MatrixMultiply(transform, matView); // Transform to camera-space coordinates + + // Calculate model-view-projection matrix (MVP) + Matrix matMVP = MatrixMultiply(matModelView, matProjection); // Transform to screen-space coordinates + + // Send combined model-view-projection matrix to shader + glUniformMatrix4fv(material.shader.mvpLoc, 1, false, MatrixToFloat(matMVP)); + + // Apply color tinting (material.colDiffuse) + // NOTE: Just update one uniform on fragment shader + float vColor[4] = { (float)material.colDiffuse.r/255, (float)material.colDiffuse.g/255, (float)material.colDiffuse.b/255, (float)material.colDiffuse.a/255 }; + glUniform4fv(material.shader.tintColorLoc, 1, vColor); + + // Set shader textures (diffuse, normal, specular) + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, material.texDiffuse.id); + glUniform1i(material.shader.mapDiffuseLoc, 0); // Texture fits in active texture unit 0 + + if ((material.texNormal.id != 0) && (material.shader.mapNormalLoc != -1)) + { + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, material.texNormal.id); + glUniform1i(material.shader.mapNormalLoc, 1); // Texture fits in active texture unit 1 + } + + if ((material.texSpecular.id != 0) && (material.shader.mapSpecularLoc != -1)) + { + glActiveTexture(GL_TEXTURE2); + glBindTexture(GL_TEXTURE_2D, material.texSpecular.id); + glUniform1i(material.shader.mapSpecularLoc, 2); // Texture fits in active texture unit 2 + } + + if (vaoSupported) + { + glBindVertexArray(mesh.vaoId); + } + else + { + // Bind mesh VBO data: vertex position (shader-location = 0) + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[0]); + glVertexAttribPointer(material.shader.vertexLoc, 3, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(material.shader.vertexLoc); + + // Bind mesh VBO data: vertex texcoords (shader-location = 1) + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[1]); + glVertexAttribPointer(material.shader.texcoordLoc, 2, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(material.shader.texcoordLoc); + + // Bind mesh VBO data: vertex normals (shader-location = 2, if available) + if (material.shader.normalLoc != -1) + { + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[2]); + glVertexAttribPointer(material.shader.normalLoc, 3, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(material.shader.normalLoc); + } + + // Bind mesh VBO data: vertex colors (shader-location = 3, if available) , tangents, texcoords2 (if available) + if (material.shader.colorLoc != -1) + { + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[3]); + glVertexAttribPointer(material.shader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); + glEnableVertexAttribArray(material.shader.colorLoc); + } + + // Bind mesh VBO data: vertex tangents (shader-location = 4, if available) + if (material.shader.tangentLoc != -1) + { + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[4]); + glVertexAttribPointer(material.shader.tangentLoc, 3, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(material.shader.tangentLoc); + } + + // Bind mesh VBO data: vertex texcoords2 (shader-location = 5, if available) + if (material.shader.texcoord2Loc != -1) + { + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[5]); + glVertexAttribPointer(material.shader.texcoord2Loc, 2, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(material.shader.texcoord2Loc); + } + + if (mesh.indices != NULL) glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, quads.vboId[3]); + } + + // Draw call! + if (mesh.indices != NULL) glDrawElements(GL_TRIANGLES, mesh.triangleCount*3, GL_UNSIGNED_SHORT, 0); // Indexed vertices draw + else glDrawArrays(GL_TRIANGLES, 0, mesh.vertexCount); + + if (material.texNormal.id != 0) + { + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, 0); + } + + if (material.texSpecular.id != 0) + { + glActiveTexture(GL_TEXTURE2); + glBindTexture(GL_TEXTURE_2D, 0); + } + + glActiveTexture(GL_TEXTURE0); // Set shader active texture to default 0 + glBindTexture(GL_TEXTURE_2D, 0); // Unbind textures + + if (vaoSupported) glBindVertexArray(0); // Unbind VAO + else + { + glBindBuffer(GL_ARRAY_BUFFER, 0); // Unbind VBOs + if (mesh.indices != NULL) glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + } + + glUseProgram(0); // Unbind shader program +#endif +} + +// Unload mesh data from CPU and GPU +void rlglUnloadMesh(Mesh *mesh) +{ + if (mesh->vertices != NULL) free(mesh->vertices); + if (mesh->texcoords != NULL) free(mesh->texcoords); + if (mesh->normals != NULL) free(mesh->normals); + if (mesh->colors != NULL) free(mesh->colors); + if (mesh->tangents != NULL) free(mesh->tangents); + if (mesh->texcoords2 != NULL) free(mesh->texcoords2); + if (mesh->indices != NULL) free(mesh->indices); + + rlDeleteBuffers(mesh->vboId[0]); // vertex + rlDeleteBuffers(mesh->vboId[1]); // texcoords + rlDeleteBuffers(mesh->vboId[2]); // normals + rlDeleteBuffers(mesh->vboId[3]); // colors + rlDeleteBuffers(mesh->vboId[4]); // tangents + rlDeleteBuffers(mesh->vboId[5]); // texcoords2 + rlDeleteBuffers(mesh->vboId[6]); // indices + + rlDeleteVertexArrays(mesh->vaoId); +} + // Read screen pixel data (color buffer) unsigned char *rlglReadScreenPixels(int width, int height) { diff --git a/src/rlgl.h b/src/rlgl.h index 7b88bc9e..a33e3a0a 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -256,6 +256,8 @@ void rlEnableRenderTexture(unsigned int id); // Enable render texture (fbo) void rlDisableRenderTexture(void); // Disable render texture (fbo), return to default framebuffer void rlEnableDepthTest(void); // Enable depth test void rlDisableDepthTest(void); // Disable depth test +void rlEnableWireMode(void); // Enable wire mode +void rlDisableWireMode(void); // Disable wire mode void rlDeleteTextures(unsigned int id); // Delete OpenGL texture from GPU void rlDeleteRenderTextures(RenderTexture2D target); // Delete render textures (fbo) from GPU void rlDeleteShader(unsigned int id); // Delete OpenGL shader program from GPU @@ -277,8 +279,11 @@ unsigned int rlglLoadTexture(void *data, int width, int height, int textureForma RenderTexture2D rlglLoadRenderTexture(int width, int height); // Load a texture to be used for rendering (fbo with color and depth attachments) void rlglUpdateTexture(unsigned int id, int width, int height, int format, void *data); // Update GPU texture with new data void rlglGenerateMipmaps(Texture2D texture); // Generate mipmap data for selected texture -void rlglLoadMesh(Mesh *mesh); // Upload vertex data into GPU and provided VAO/VBO ids -void rlglDrawEx(Mesh mesh, Material material, Matrix transform, bool wires); + +void rlglLoadMesh(Mesh *mesh); // Upload vertex data into GPU and provided VAO/VBO ids +void rlglUpdateMesh(Mesh mesh, int buffer, int numVertex); // Update vertex data on GPU (upload new data to one buffer) +void rlglDrawMesh(Mesh mesh, Material material, Matrix transform); // Draw a 3d mesh with material and transform +void rlglUnloadMesh(Mesh *mesh); // Unload mesh data from CPU and GPU Vector3 rlglUnproject(Vector3 source, Matrix proj, Matrix view); // Get world coordinates from screen coordinates -- cgit v1.2.3 From 8bbbe8cd76d93bc85810fd13a6cb6c4df18bd30d Mon Sep 17 00:00:00 2001 From: raysan5 Date: Thu, 19 May 2016 13:50:29 +0200 Subject: Corrected namings --- src/rlgl.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/rlgl.c b/src/rlgl.c index 8e725521..888a9313 100644 --- a/src/rlgl.c +++ b/src/rlgl.c @@ -2425,12 +2425,12 @@ static void LoadDefaultShaderLocations(Shader *shader) // vertex texcoord2 location = 5 // Get handles to GLSL input attibute locations - shader->vertexLoc = glGetAttribLocation(shader->id, "vertexPosition"); - shader->texcoordLoc = glGetAttribLocation(shader->id, "vertexTexCoord"); - shader->normalLoc = glGetAttribLocation(shader->id, "vertexNormal"); - shader->colorLoc = glGetAttribLocation(shader->id, "vertexColor"); - shader->tangentLoc = glGetAttribLocation(shader->id, "vertexTangent"); - shader->texcoord2Loc = glGetAttribLocation(shader->id, "vertexTexCoord2"); + shader->vertexLoc = glGetAttribLocation(shader->id, DEFAULT_ATTRIB_POSITION_NAME); + shader->texcoordLoc = glGetAttribLocation(shader->id, DEFAULT_ATTRIB_TEXCOORD_NAME); + shader->normalLoc = glGetAttribLocation(shader->id, DEFAULT_ATTRIB_NORMAL_NAME); + shader->colorLoc = glGetAttribLocation(shader->id, DEFAULT_ATTRIB_COLOR_NAME); + shader->tangentLoc = glGetAttribLocation(shader->id, DEFAULT_ATTRIB_TANGENT_NAME); + shader->texcoord2Loc = glGetAttribLocation(shader->id, DEFAULT_ATTRIB_TEXCOORD2_NAME); // Get handles to GLSL uniform locations (vertex shader) shader->mvpLoc = glGetUniformLocation(shader->id, "mvpMatrix"); @@ -2447,8 +2447,8 @@ static void UnloadDefaultShader(void) { glUseProgram(0); - //glDetachShader(defaultShaderProgram, vertexShader); - //glDetachShader(defaultShaderProgram, fragmentShader); + //glDetachShader(defaultShader, vertexShader); + //glDetachShader(defaultShader, fragmentShader); //glDeleteShader(vertexShader); // Already deleted on shader compilation //glDeleteShader(fragmentShader); // Already deleted on sahder compilation glDeleteProgram(defaultShader.id); -- cgit v1.2.3 From b10425492ac692d7c53001290e3d0b678df634b0 Mon Sep 17 00:00:00 2001 From: Joshua Reisenauer Date: Thu, 19 May 2016 15:22:12 -0700 Subject: name correction --- src/audio.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/audio.c b/src/audio.c index 584d3ad1..6d8f876c 100644 --- a/src/audio.c +++ b/src/audio.c @@ -59,9 +59,9 @@ //---------------------------------------------------------------------------------- // Defines and Macros //---------------------------------------------------------------------------------- -#define MAX_STREAM_BUFFERS 2 -#define MAX_AUDIO_CONTEXTS 4 // Number of open AL sources -#define MAX_MUSIC_STREAMS 2 +#define MAX_STREAM_BUFFERS 2 // Number of buffers for each alSource +#define MAX_MIX_CHANNELS 4 // Number of open AL sources +#define MAX_MUSIC_STREAMS 2 // Number of simultanious music sources #if defined(PLATFORM_RPI) || defined(PLATFORM_ANDROID) // NOTE: On RPI and Android should be lower to avoid frame-stalls @@ -96,12 +96,12 @@ typedef struct MixChannel_t { typedef struct Music { stb_vorbis *stream; jar_xm_context_t *chipctx; // Stores jar_xm mixc - MixChannel_t *mixc; // mix channel + MixChannel_t *mixc; // mix channel int totalSamplesLeft; float totalLengthSeconds; bool loop; - bool chipTune; // True if chiptune is loaded + bool chipTune; // True if chiptune is loaded } Music; #if defined(AUDIO_STANDALONE) @@ -111,7 +111,7 @@ typedef enum { INFO = 0, ERROR, WARNING, DEBUG, OTHER } TraceLogType; //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- -static MixChannel_t* mixChannelsActive_g[MAX_AUDIO_CONTEXTS]; // What mix channels are currently active +static MixChannel_t* mixChannelsActive_g[MAX_MIX_CHANNELS]; // What mix channels are currently active static bool musicEnabled_g = false; static Music currentMusic[MAX_MUSIC_STREAMS]; // Current music loaded, up to two can play at the same time @@ -123,7 +123,7 @@ static Wave LoadOGG(char *fileName); // Load OGG file static void UnloadWave(Wave wave); // Unload wave data static bool BufferMusicStream(int index, int numBuffers); // Fill music buffers with data -static void EmptyMusicStream(int index); // Empty music buffers +static void EmptyMusicStream(int index); // Empty music buffers static MixChannel_t* InitMixChannel(unsigned short sampleRate, unsigned char mixChannel, unsigned char channels, bool floatingPoint); // For streaming into mix channels. @@ -212,7 +212,7 @@ bool IsAudioDeviceReady(void) // exmple usage is InitMixChannel(48000, 0, 2, true); // mixchannel 1, 48khz, stereo, floating point static MixChannel_t* InitMixChannel(unsigned short sampleRate, unsigned char mixChannel, unsigned char channels, bool floatingPoint) { - if(mixChannel >= MAX_AUDIO_CONTEXTS) return NULL; + if(mixChannel >= MAX_MIX_CHANNELS) return NULL; if(!IsAudioDeviceReady()) InitAudioDevice(); if(!mixChannelsActive_g[mixChannel]){ @@ -380,10 +380,10 @@ static void ResampleByteToFloat(char *chars, float *floats, unsigned short len) RawAudioContext InitRawAudioContext(int sampleRate, int channels, bool floatingPoint) { int mixIndex; - for(mixIndex = 0; mixIndex < MAX_AUDIO_CONTEXTS; mixIndex++) // find empty mix channel slot + for(mixIndex = 0; mixIndex < MAX_MIX_CHANNELS; mixIndex++) // find empty mix channel slot { if(mixChannelsActive_g[mixIndex] == NULL) break; - else if(mixIndex = MAX_AUDIO_CONTEXTS - 1) return -1; // error + else if(mixIndex = MAX_MIX_CHANNELS - 1) return -1; // error } if(InitMixChannel(sampleRate, mixIndex, channels, floatingPoint)) @@ -755,10 +755,10 @@ int PlayMusicStream(int musicIndex, char *fileName) if(currentMusic[musicIndex].stream || currentMusic[musicIndex].chipctx) return 1; // error - for(mixIndex = 0; mixIndex < MAX_AUDIO_CONTEXTS; mixIndex++) // find empty mix channel slot + for(mixIndex = 0; mixIndex < MAX_MIX_CHANNELS; mixIndex++) // find empty mix channel slot { if(mixChannelsActive_g[mixIndex] == NULL) break; - else if(mixIndex = MAX_AUDIO_CONTEXTS - 1) return 2; // error + else if(mixIndex = MAX_MIX_CHANNELS - 1) return 2; // error } if (strcmp(GetExtension(fileName),"ogg") == 0) -- cgit v1.2.3 From 41c5f3a0178027e9b74e563ab102f603a53e35bf Mon Sep 17 00:00:00 2001 From: Joshua Reisenauer Date: Thu, 19 May 2016 20:44:09 -0700 Subject: Buffer for raw audio --- src/audio.c | 22 ++++++++++++++++++---- src/audio.h | 4 ++++ src/raylib.h | 6 +++++- 3 files changed, 27 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/audio.c b/src/audio.c index 6d8f876c..43e8be14 100644 --- a/src/audio.c +++ b/src/audio.c @@ -128,8 +128,8 @@ static void EmptyMusicStream(int index); // Empty music buffers static MixChannel_t* InitMixChannel(unsigned short sampleRate, unsigned char mixChannel, unsigned char channels, bool floatingPoint); // For streaming into mix channels. static void CloseMixChannel(MixChannel_t* mixc); // Frees mix channel -static unsigned short BufferMixChannel(MixChannel_t* mixc, void *data, int numberElements); // Pushes more audio data into mixc mix channel, if NULL is passed it pauses -static unsigned short FillAlBufferWithSilence(MixChannel_t *mixc, ALuint buffer); // Fill buffer with zeros, returns number processed +static int BufferMixChannel(MixChannel_t* mixc, void *data, int numberElements); // Pushes more audio data into mixc mix channel, if NULL is passed it pauses +static int FillAlBufferWithSilence(MixChannel_t *mixc, ALuint buffer); // Fill buffer with zeros, returns number processed static void ResampleShortToFloat(short *shorts, float *floats, unsigned short len); // Pass two arrays of the same legnth in static void ResampleByteToFloat(char *chars, float *floats, unsigned short len); // Pass two arrays of same length in static int IsMusicStreamReadyForBuffering(int index); // Checks if music buffer is ready to be refilled @@ -292,7 +292,7 @@ static void CloseMixChannel(MixChannel_t* mixc) // Pushes more audio data into mixc mix channel, only one buffer per call // Call "BufferMixChannel(mixc, NULL, 0)" if you want to pause the audio. // @Returns number of samples that where processed. -static unsigned short BufferMixChannel(MixChannel_t* mixc, void *data, int numberElements) +static int BufferMixChannel(MixChannel_t* mixc, void *data, int numberElements) { if(!mixc || mixChannelsActive_g[mixc->mixChannel] != mixc) return 0; // when there is two channels there must be an even number of samples @@ -331,7 +331,7 @@ static unsigned short BufferMixChannel(MixChannel_t* mixc, void *data, int numbe } // fill buffer with zeros, returns number processed -static unsigned short FillAlBufferWithSilence(MixChannel_t *mixc, ALuint buffer) +static int FillAlBufferWithSilence(MixChannel_t *mixc, ALuint buffer) { if(mixc->floatingPoint){ float pcm[MUSIC_BUFFER_SIZE_FLOAT] = {0.f}; @@ -377,6 +377,7 @@ static void ResampleByteToFloat(char *chars, float *floats, unsigned short len) } // used to output raw audio streams, returns negative numbers on error +// if floating point is false the data size is 16bit short, otherwise it is float 32bit RawAudioContext InitRawAudioContext(int sampleRate, int channels, bool floatingPoint) { int mixIndex; @@ -398,6 +399,19 @@ void CloseRawAudioContext(RawAudioContext ctx) CloseMixChannel(mixChannelsActive_g[ctx]); } +int BufferRawAudioContext(RawAudioContext ctx, void *data, int numberElements) +{ + int numBuffered = 0; + if(ctx >= 0) + { + MixChannel_t* mixc = mixChannelsActive_g[ctx]; + numBuffered = BufferMixChannel(mixc, data, numberElements); + } + return numBuffered; +} + + + //---------------------------------------------------------------------------------- diff --git a/src/audio.h b/src/audio.h index d3276bf6..1140a60a 100644 --- a/src/audio.h +++ b/src/audio.h @@ -102,8 +102,12 @@ float GetMusicTimePlayed(int index); // Get current m int getMusicStreamCount(void); void SetMusicPitch(int index, float pitch); +// used to output raw audio streams, returns negative numbers on error +// if floating point is false the data size is 16bit short, otherwise it is float 32bit RawAudioContext InitRawAudioContext(int sampleRate, int channels, bool floatingPoint); + void CloseRawAudioContext(RawAudioContext ctx); +int BufferRawAudioContext(RawAudioContext ctx, void *data, int numberElements); // returns number of elements buffered #ifdef __cplusplus } diff --git a/src/raylib.h b/src/raylib.h index 6efde710..986dc7bf 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -896,8 +896,12 @@ float GetMusicTimePlayed(int index); // Get current m int getMusicStreamCount(void); void SetMusicPitch(int index, float pitch); -RawAudioContext InitRawAudioContext(int sampleRate, int channels, bool floatingPoint); // used to output raw audio streams, returns negative numbers on error +// used to output raw audio streams, returns negative numbers on error +// if floating point is false the data size is 16bit short, otherwise it is float 32bit +RawAudioContext InitRawAudioContext(int sampleRate, int channels, bool floatingPoint); + void CloseRawAudioContext(RawAudioContext ctx); +int BufferRawAudioContext(RawAudioContext ctx, void *data, int numberElements); // returns number of elements buffered #ifdef __cplusplus } -- cgit v1.2.3 From 179f2f9e4fd9ad43e120f186484a78984a2ac063 Mon Sep 17 00:00:00 2001 From: Joshua Reisenauer Date: Thu, 19 May 2016 20:56:38 -0700 Subject: windows automated compile Only works when raylib is installed on windows system. --- src/windows_compile.bat | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 src/windows_compile.bat (limited to 'src') diff --git a/src/windows_compile.bat b/src/windows_compile.bat new file mode 100644 index 00000000..f1d0fb29 --- /dev/null +++ b/src/windows_compile.bat @@ -0,0 +1,2 @@ +set PATH=C:\raylib\MinGW\bin;%PATH% +mingw32-make \ No newline at end of file -- cgit v1.2.3 From 7d1d9ff143cd7c6c55d3fd891b43e143431ea15f Mon Sep 17 00:00:00 2001 From: raysan5 Date: Fri, 20 May 2016 09:36:02 +0200 Subject: Support DYNAMIC_DRAW mesh loading --- src/models.c | 10 +++++----- src/raylib.h | 2 +- src/rlgl.c | 17 ++++++++++------- src/rlgl.h | 2 +- 4 files changed, 17 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/models.c b/src/models.c index bb2d56c2..6d72d1e3 100644 --- a/src/models.c +++ b/src/models.c @@ -553,7 +553,7 @@ Model LoadModel(const char *fileName) if (model.mesh.vertexCount == 0) TraceLog(WARNING, "Model could not be loaded"); else { - rlglLoadMesh(&model.mesh); // Upload vertex data to GPU + rlglLoadMesh(&model.mesh, false); // Upload vertex data to GPU (static model) model.transform = MatrixIdentity(); model.material = LoadDefaultMaterial(); @@ -563,13 +563,13 @@ Model LoadModel(const char *fileName) } // Load a 3d model (from vertex data) -Model LoadModelEx(Mesh data) +Model LoadModelEx(Mesh data, bool dynamic) { Model model = { 0 }; model.mesh = data; - rlglLoadMesh(&model.mesh); // Upload vertex data to GPU + rlglLoadMesh(&model.mesh, dynamic); // Upload vertex data to GPU model.transform = MatrixIdentity(); model.material = LoadDefaultMaterial(); @@ -668,7 +668,7 @@ Model LoadHeightmap(Image heightmap, Vector3 size) model.mesh = GenMeshHeightmap(heightmap, size); - rlglLoadMesh(&model.mesh); + rlglLoadMesh(&model.mesh, false); // Upload vertex data to GPU (static model) model.transform = MatrixIdentity(); model.material = LoadDefaultMaterial(); @@ -683,7 +683,7 @@ Model LoadCubicmap(Image cubicmap) model.mesh = GenMeshCubicmap(cubicmap, (Vector3){ 1.0, 1.0, 1.5f }); - rlglLoadMesh(&model.mesh); + rlglLoadMesh(&model.mesh, false); // Upload vertex data to GPU (static model) model.transform = MatrixIdentity(); model.material = LoadDefaultMaterial(); diff --git a/src/raylib.h b/src/raylib.h index 986dc7bf..8a46ec35 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -803,7 +803,7 @@ void DrawGizmo(Vector3 position); // Model 3d Loading and Drawing Functions (Module: models) //------------------------------------------------------------------------------------ Model LoadModel(const char *fileName); // Load a 3d model (.OBJ) -Model LoadModelEx(Mesh data); // Load a 3d model (from mesh data) +Model LoadModelEx(Mesh data, bool dynamic); // Load a 3d model (from mesh data) Model LoadModelFromRES(const char *rresName, int resId); // Load a 3d model from rRES file (raylib Resource) Model LoadHeightmap(Image heightmap, Vector3 size); // Load a heightmap image as a 3d model Model LoadCubicmap(Image cubicmap); // Load a map image as a 3d model (cubes based) diff --git a/src/rlgl.c b/src/rlgl.c index 888a9313..cc2b8942 100644 --- a/src/rlgl.c +++ b/src/rlgl.c @@ -1500,7 +1500,7 @@ void rlglGenerateMipmaps(Texture2D texture) } // Upload vertex data into a VAO (if supported) and VBO -void rlglLoadMesh(Mesh *mesh) +void rlglLoadMesh(Mesh *mesh, bool dynamic) { mesh->vaoId = 0; // Vertex Array Object mesh->vboId[0] = 0; // Vertex positions VBO @@ -1510,6 +1510,9 @@ void rlglLoadMesh(Mesh *mesh) mesh->vboId[4] = 0; // Vertex tangents VBO mesh->vboId[5] = 0; // Vertex texcoords2 VBO mesh->vboId[6] = 0; // Vertex indices VBO + + int drawHint = GL_STATIC_DRAW; + if (dynamic) drawHint = GL_DYNAMIC_DRAW; #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) GLuint vaoId = 0; // Vertex Array Objects (VAO) @@ -1527,14 +1530,14 @@ void rlglLoadMesh(Mesh *mesh) // Enable vertex attributes: position (shader-location = 0) glGenBuffers(1, &vboId[0]); glBindBuffer(GL_ARRAY_BUFFER, vboId[0]); - glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*mesh->vertexCount, mesh->vertices, GL_STATIC_DRAW); + glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*mesh->vertexCount, mesh->vertices, drawHint); glVertexAttribPointer(0, 3, GL_FLOAT, 0, 0, 0); glEnableVertexAttribArray(0); // Enable vertex attributes: texcoords (shader-location = 1) glGenBuffers(1, &vboId[1]); glBindBuffer(GL_ARRAY_BUFFER, vboId[1]); - glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*mesh->vertexCount, mesh->texcoords, GL_STATIC_DRAW); + glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*mesh->vertexCount, mesh->texcoords, drawHint); glVertexAttribPointer(1, 2, GL_FLOAT, 0, 0, 0); glEnableVertexAttribArray(1); @@ -1543,7 +1546,7 @@ void rlglLoadMesh(Mesh *mesh) { glGenBuffers(1, &vboId[2]); glBindBuffer(GL_ARRAY_BUFFER, vboId[2]); - glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*mesh->vertexCount, mesh->normals, GL_STATIC_DRAW); + glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*mesh->vertexCount, mesh->normals, drawHint); glVertexAttribPointer(2, 3, GL_FLOAT, 0, 0, 0); glEnableVertexAttribArray(2); } @@ -1559,7 +1562,7 @@ void rlglLoadMesh(Mesh *mesh) { glGenBuffers(1, &vboId[3]); glBindBuffer(GL_ARRAY_BUFFER, vboId[3]); - glBufferData(GL_ARRAY_BUFFER, sizeof(unsigned char)*4*mesh->vertexCount, mesh->colors, GL_STATIC_DRAW); + glBufferData(GL_ARRAY_BUFFER, sizeof(unsigned char)*4*mesh->vertexCount, mesh->colors, drawHint); glVertexAttribPointer(3, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); glEnableVertexAttribArray(3); } @@ -1575,7 +1578,7 @@ void rlglLoadMesh(Mesh *mesh) { glGenBuffers(1, &vboId[4]); glBindBuffer(GL_ARRAY_BUFFER, vboId[4]); - glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*mesh->vertexCount, mesh->tangents, GL_STATIC_DRAW); + glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*mesh->vertexCount, mesh->tangents, drawHint); glVertexAttribPointer(4, 3, GL_FLOAT, 0, 0, 0); glEnableVertexAttribArray(4); } @@ -1591,7 +1594,7 @@ void rlglLoadMesh(Mesh *mesh) { glGenBuffers(1, &vboId[5]); glBindBuffer(GL_ARRAY_BUFFER, vboId[5]); - glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*mesh->vertexCount, mesh->texcoords2, GL_STATIC_DRAW); + glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*mesh->vertexCount, mesh->texcoords2, drawHint); glVertexAttribPointer(5, 2, GL_FLOAT, 0, 0, 0); glEnableVertexAttribArray(5); } diff --git a/src/rlgl.h b/src/rlgl.h index a33e3a0a..a557ffa2 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -280,7 +280,7 @@ RenderTexture2D rlglLoadRenderTexture(int width, int height); // Load a textur void rlglUpdateTexture(unsigned int id, int width, int height, int format, void *data); // Update GPU texture with new data void rlglGenerateMipmaps(Texture2D texture); // Generate mipmap data for selected texture -void rlglLoadMesh(Mesh *mesh); // Upload vertex data into GPU and provided VAO/VBO ids +void rlglLoadMesh(Mesh *mesh, bool dynamic); // Upload vertex data into GPU and provided VAO/VBO ids void rlglUpdateMesh(Mesh mesh, int buffer, int numVertex); // Update vertex data on GPU (upload new data to one buffer) void rlglDrawMesh(Mesh mesh, Material material, Matrix transform); // Draw a 3d mesh with material and transform void rlglUnloadMesh(Mesh *mesh); // Unload mesh data from CPU and GPU -- cgit v1.2.3 From 03cc031d00ab97ab46b61b66a6281a175c33541f Mon Sep 17 00:00:00 2001 From: raysan5 Date: Fri, 20 May 2016 09:40:48 +0200 Subject: Remove TODO comments (already done) --- src/raylib.h | 1 - src/shapes.c | 1 - 2 files changed, 2 deletions(-) (limited to 'src') diff --git a/src/raylib.h b/src/raylib.h index 8a46ec35..32cd54a6 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -423,7 +423,6 @@ typedef struct Material { } Material; // 3d Model type -// TODO: Replace shader/testure by material typedef struct Model { Mesh mesh; // Vertex data buffers (RAM and VRAM) Matrix transform; // Local transform matrix diff --git a/src/shapes.c b/src/shapes.c index 14d11315..5b66e5ef 100644 --- a/src/shapes.c +++ b/src/shapes.c @@ -446,7 +446,6 @@ bool CheckCollisionCircleRec(Vector2 center, float radius, Rectangle rec) } // Get collision rectangle for two rectangles collision -// TODO: Depending on rec1 and rec2 order, it fails -> Review! Rectangle GetCollisionRec(Rectangle rec1, Rectangle rec2) { Rectangle retRec = { 0, 0, 0, 0 }; -- cgit v1.2.3 From c9e30f77540e9693ddecffc353964eb71e854842 Mon Sep 17 00:00:00 2001 From: raysan5 Date: Fri, 20 May 2016 10:53:31 +0200 Subject: Review struct typedef to avoid pointers for users --- src/physac.c | 12 ++++++------ src/raylib.h | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/physac.c b/src/physac.c index ed707474..181488ac 100644 --- a/src/physac.c +++ b/src/physac.c @@ -49,7 +49,7 @@ //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- -static PhysicObject *physicObjects[MAX_PHYSIC_OBJECTS]; // Physic objects pool +static PhysicObject physicObjects[MAX_PHYSIC_OBJECTS]; // Physic objects pool static int physicObjectsCount; // Counts current enabled physic objects static Vector2 gravityForce; // Gravity force @@ -463,10 +463,10 @@ void ClosePhysics() } // Create a new physic object dinamically, initialize it and add to pool -PhysicObject *CreatePhysicObject(Vector2 position, float rotation, Vector2 scale) +PhysicObject CreatePhysicObject(Vector2 position, float rotation, Vector2 scale) { // Allocate dynamic memory - PhysicObject *obj = (PhysicObject *)malloc(sizeof(PhysicObject)); + PhysicObject obj = (PhysicObject)malloc(sizeof(PhysicObjectData)); // Initialize physic object values with generic values obj->id = physicObjectsCount; @@ -498,7 +498,7 @@ PhysicObject *CreatePhysicObject(Vector2 position, float rotation, Vector2 scale } // Destroy a specific physic object and take it out of the list -void DestroyPhysicObject(PhysicObject *pObj) +void DestroyPhysicObject(PhysicObject pObj) { // Free dynamic memory allocation free(physicObjects[pObj->id]); @@ -520,7 +520,7 @@ void DestroyPhysicObject(PhysicObject *pObj) } // Apply directional force to a physic object -void ApplyForce(PhysicObject *pObj, Vector2 force) +void ApplyForce(PhysicObject pObj, Vector2 force) { if (pObj->rigidbody.enabled) { @@ -571,7 +571,7 @@ Rectangle TransformToRectangle(Transform transform) } // Draw physic object information at screen position -void DrawPhysicObjectInfo(PhysicObject *pObj, Vector2 position, int fontSize) +void DrawPhysicObjectInfo(PhysicObject pObj, Vector2 position, int fontSize) { // Draw physic object ID DrawText(FormatText("PhysicObject ID: %i - Enabled: %i", pObj->id, pObj->enabled), position.x, position.y, fontSize, BLACK); diff --git a/src/raylib.h b/src/raylib.h index 32cd54a6..fc1914bb 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -535,13 +535,13 @@ typedef struct Collider { int radius; // Used for COLLIDER_CIRCLE } Collider; -typedef struct PhysicObject { +typedef struct PhysicObjectData { unsigned int id; Transform transform; Rigidbody rigidbody; Collider collider; bool enabled; -} PhysicObject; +} PhysicObjectData, *PhysicObject; #ifdef __cplusplus extern "C" { // Prevents name mangling of functions @@ -856,14 +856,14 @@ void InitPhysics(Vector2 gravity); void UpdatePhysics(); // Update physic objects, calculating physic behaviours and collisions detection void ClosePhysics(); // Unitialize all physic objects and empty the objects pool -PhysicObject *CreatePhysicObject(Vector2 position, float rotation, Vector2 scale); // Create a new physic object dinamically, initialize it and add to pool -void DestroyPhysicObject(PhysicObject *pObj); // Destroy a specific physic object and take it out of the list +PhysicObject CreatePhysicObject(Vector2 position, float rotation, Vector2 scale); // Create a new physic object dinamically, initialize it and add to pool +void DestroyPhysicObject(PhysicObject pObj); // Destroy a specific physic object and take it out of the list -void ApplyForce(PhysicObject *pObj, Vector2 force); // Apply directional force to a physic object +void ApplyForce(PhysicObject pObj, Vector2 force); // Apply directional force to a physic object void ApplyForceAtPosition(Vector2 position, float force, float radius); // Apply radial force to all physic objects in range Rectangle TransformToRectangle(Transform transform); // Convert Transform data type to Rectangle (position and scale) -void DrawPhysicObjectInfo(PhysicObject *pObj, Vector2 position, int fontSize); // Draw physic object information at screen position +void DrawPhysicObjectInfo(PhysicObject pObj, Vector2 position, int fontSize); // Draw physic object information at screen position //------------------------------------------------------------------------------------ // Audio Loading and Playing Functions (Module: audio) -- cgit v1.2.3 From af890cf21021e4a306bf3f6e4397496b95c1c93a Mon Sep 17 00:00:00 2001 From: raysan5 Date: Fri, 20 May 2016 10:53:58 +0200 Subject: Updated to avoid pointers --- src/physac.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/physac.h b/src/physac.h index 37544686..6cef480a 100644 --- a/src/physac.h +++ b/src/physac.h @@ -66,13 +66,13 @@ typedef struct Collider { int radius; // Used for COLLIDER_CIRCLE } Collider; -typedef struct PhysicObject { +typedef struct PhysicObjectData { unsigned int id; Transform transform; Rigidbody rigidbody; Collider collider; bool enabled; -} PhysicObject; +} PhysicObjectData, *PhysicObject; #ifdef __cplusplus extern "C" { // Prevents name mangling of functions @@ -85,14 +85,14 @@ void InitPhysics(Vector2 gravity); void UpdatePhysics(); // Update physic objects, calculating physic behaviours and collisions detection void ClosePhysics(); // Unitialize all physic objects and empty the objects pool -PhysicObject *CreatePhysicObject(Vector2 position, float rotation, Vector2 scale); // Create a new physic object dinamically, initialize it and add to pool -void DestroyPhysicObject(PhysicObject *pObj); // Destroy a specific physic object and take it out of the list +PhysicObject CreatePhysicObject(Vector2 position, float rotation, Vector2 scale); // Create a new physic object dinamically, initialize it and add to pool +void DestroyPhysicObject(PhysicObject pObj); // Destroy a specific physic object and take it out of the list -void ApplyForce(PhysicObject *pObj, Vector2 force); // Apply directional force to a physic object +void ApplyForce(PhysicObject pObj, Vector2 force); // Apply directional force to a physic object void ApplyForceAtPosition(Vector2 position, float force, float radius); // Apply radial force to all physic objects in range Rectangle TransformToRectangle(Transform transform); // Convert Transform data type to Rectangle (position and scale) -void DrawPhysicObjectInfo(PhysicObject *pObj, Vector2 position, int fontSize); // Draw physic object information at screen position +void DrawPhysicObjectInfo(PhysicObject pObj, Vector2 position, int fontSize); // Draw physic object information at screen position #ifdef __cplusplus } -- cgit v1.2.3 From dcf5f45f687f2a534286aecd5e6471a0440b0c21 Mon Sep 17 00:00:00 2001 From: raysan5 Date: Fri, 20 May 2016 12:28:07 +0200 Subject: Add lighting system -IN PROGRESS- Improved materials --- src/models.c | 12 +++++ src/raylib.h | 30 ++++++++++- src/rlgl.c | 167 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- src/rlgl.h | 25 +++++++++ 4 files changed, 228 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/models.c b/src/models.c index 6d72d1e3..44e30390 100644 --- a/src/models.c +++ b/src/models.c @@ -732,6 +732,18 @@ Material LoadDefaultMaterial(void) return material; } +// Load standard material (uses standard models shader) +// NOTE: Standard shader supports multiple maps and lights +Material LoadStandardMaterial(void) +{ + Material material = LoadDefaultMaterial(); + + //material.shader = GetStandardShader(); + + return material; +} + +// Unload material from memory void UnloadMaterial(Material material) { rlDeleteTextures(material.texDiffuse.id); diff --git a/src/raylib.h b/src/raylib.h index fc1914bb..d98a0797 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -422,13 +422,38 @@ typedef struct Material { float normalDepth; // Normal map depth } Material; -// 3d Model type +// Model type typedef struct Model { Mesh mesh; // Vertex data buffers (RAM and VRAM) Matrix transform; // Local transform matrix Material material; // Shader and textures data } Model; +// Light type +// TODO: Review contained data to support different light types and features +typedef struct LightData { + int id; + int type; // LIGHT_POINT, LIGHT_DIRECTIONAL, LIGHT_SPOT + bool enabled; + + Vector3 position; + Vector3 direction; // Used on LIGHT_DIRECTIONAL and LIGHT_SPOT (cone direction) + float attenuation; // Lost of light intensity with distance (use radius?) + + Color diffuse; // Use Vector3 diffuse (including intensities)? + float intensity; + + Color specular; + //float specFactor; // Specular intensity ? + + //Color ambient; // Required? + + float coneAngle; // SpotLight +} LightData, *Light; + +// Light types +typedef enum { LIGHT_POINT, LIGHT_DIRECTIONAL, LIGHT_SPOT } LightType; + // Ray type (useful for raycast) typedef struct Ray { Vector3 position; @@ -849,6 +874,9 @@ void SetShaderValueMatrix(Shader shader, int uniformLoc, Matrix mat); // S void SetBlendMode(int mode); // Set blending mode (alpha, additive, multiplied) +Light CreateLight(int type, Vector3 position, Color diffuse); // Create a new light, initialize it and add to pool +void DestroyLight(Light light); // Destroy a light and take it out of the list + //---------------------------------------------------------------------------------- // Physics System Functions (Module: physac) //---------------------------------------------------------------------------------- diff --git a/src/rlgl.c b/src/rlgl.c index cc2b8942..e971d747 100644 --- a/src/rlgl.c +++ b/src/rlgl.c @@ -71,6 +71,8 @@ #define MAX_DRAWS_BY_TEXTURE 256 // Draws are organized by texture changes #define TEMP_VERTEX_BUFFER_SIZE 4096 // Temporal Vertex Buffer (required for vertex-transformations) // NOTE: Every vertex are 3 floats (12 bytes) + +#define MAX_LIGHTS 8 // Max lights supported by standard shader #ifndef GL_SHADING_LANGUAGE_VERSION #define GL_SHADING_LANGUAGE_VERSION 0x8B8C @@ -199,6 +201,10 @@ static bool texCompETC1Supported = false; // ETC1 texture compression support static bool texCompETC2Supported = false; // ETC2/EAC texture compression support static bool texCompPVRTSupported = false; // PVR texture compression support static bool texCompASTCSupported = false; // ASTC texture compression support + +// Lighting data +static Light lights[MAX_LIGHTS]; // Lights pool +static int lightsCount; // Counts current enabled physic objects #endif // Compressed textures support flags @@ -227,6 +233,7 @@ static void LoadCompressedTexture(unsigned char *data, int width, int height, in static unsigned int LoadShaderProgram(char *vShaderStr, char *fShaderStr); // Load custom shader strings and return program id static Shader LoadDefaultShader(void); // Load default shader (just vertex positioning and texture coloring) +static Shader LoadStandardShader(void); // Load standard shader (support materials and lighting) static void LoadDefaultShaderLocations(Shader *shader); // Bind default shader locations (attributes and uniforms) static void UnloadDefaultShader(void); // Unload default shader @@ -235,6 +242,8 @@ static void UpdateDefaultBuffers(void); // Update default internal buffers ( static void DrawDefaultBuffers(void); // Draw default internal buffers vertex data static void UnloadDefaultBuffers(void); // Unload default internal buffers vertex data from CPU and GPU +static void SetShaderLights(Shader shader); // Sets shader uniform values for lights array + static char *ReadTextFile(const char *fileName); #endif @@ -1749,11 +1758,19 @@ void rlglDrawMesh(Mesh mesh, Material material, Matrix transform) // Send combined model-view-projection matrix to shader glUniformMatrix4fv(material.shader.mvpLoc, 1, false, MatrixToFloat(matMVP)); - // Apply color tinting (material.colDiffuse) - // NOTE: Just update one uniform on fragment shader - float vColor[4] = { (float)material.colDiffuse.r/255, (float)material.colDiffuse.g/255, (float)material.colDiffuse.b/255, (float)material.colDiffuse.a/255 }; - glUniform4fv(material.shader.tintColorLoc, 1, vColor); - + // Setup shader uniforms for material related data + // TODO: Check if using standard shader to get location points + + // Upload to shader material.colDiffuse + float vColorDiffuse[4] = { (float)material.colDiffuse.r/255, (float)material.colDiffuse.g/255, (float)material.colDiffuse.b/255, (float)material.colDiffuse.a/255 }; + glUniform4fv(material.shader.tintColorLoc, 1, vColorDiffuse); + + // TODO: Upload to shader material.colAmbient + // glUniform4f(???, (float)material.colAmbient.r/255, (float)material.colAmbient.g/255, (float)material.colAmbient.b/255, (float)material.colAmbient.a/255); + + // TODO: Upload to shader material.colSpecular + // glUniform4f(???, (float)material.colSpecular.r/255, (float)material.colSpecular.g/255, (float)material.colSpecular.b/255, (float)material.colSpecular.a/255); + // Set shader textures (diffuse, normal, specular) glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, material.texDiffuse.id); @@ -1764,6 +1781,9 @@ void rlglDrawMesh(Mesh mesh, Material material, Matrix transform) glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, material.texNormal.id); glUniform1i(material.shader.mapNormalLoc, 1); // Texture fits in active texture unit 1 + + // TODO: Upload to shader normalDepth + //glUniform1f(???, material.normalDepth); } if ((material.texSpecular.id != 0) && (material.shader.mapSpecularLoc != -1)) @@ -1771,7 +1791,13 @@ void rlglDrawMesh(Mesh mesh, Material material, Matrix transform) glActiveTexture(GL_TEXTURE2); glBindTexture(GL_TEXTURE_2D, material.texSpecular.id); glUniform1i(material.shader.mapSpecularLoc, 2); // Texture fits in active texture unit 2 + + // TODO: Upload to shader glossiness + //glUniform1f(???, material.glossiness); } + + // Setup shader uniforms for lights + SetShaderLights(material.shader); if (vaoSupported) { @@ -2198,6 +2224,55 @@ void SetBlendMode(int mode) } } +// Create a new light, initialize it and add to pool +// TODO: Review creation parameters (only generic ones) +Light CreateLight(int type, Vector3 position, Color diffuse) +{ + // Allocate dynamic memory + Light light = (Light)malloc(sizeof(LightData)); + + // Initialize light values with generic values + light->id = lightsCount; + light->type = type; + light->enabled = true; + + light->position = position; + light->direction = (Vector3){ 0.0f, 0.0f, 0.0f }; + light->intensity = 1.0f; + light->diffuse = diffuse; + light->specular = WHITE; + + // Add new light to the array + lights[lightsCount] = light; + + // Increase enabled lights count + lightsCount++; + + return light; +} + +// Destroy a light and take it out of the list +void DestroyLight(Light light) +{ + // Free dynamic memory allocation + free(lights[light->id]); + + // Remove *obj from the pointers array + for (int i = light->id; i < lightsCount; i++) + { + // Resort all the following pointers of the array + if ((i + 1) < lightsCount) + { + lights[i] = lights[i + 1]; + lights[i]->id = lights[i + 1]->id; + } + else free(lights[i]); + } + + // Decrease enabled physic objects count + lightsCount--; +} + //---------------------------------------------------------------------------------- // Module specific Functions Definition //---------------------------------------------------------------------------------- @@ -2415,6 +2490,32 @@ static Shader LoadDefaultShader(void) return shader; } +// Load standard shader +// NOTE: This shader supports: +// - Up to 3 different maps: diffuse, normal, specular +// - Material properties: colDiffuse, colAmbient, colSpecular, glossiness, normalDepth +// - Up to 8 lights: Point, Directional or Spot +static Shader LoadStandardShader(void) +{ + Shader shader; + + char *vShaderStr; + char *fShaderStr; + + // TODO: Implement standard uber-shader, supporting all features (GLSL 100 / GLSL 330) + + // NOTE: Shader could be quite extensive so it could be implemented in external files (standard.vs/standard.fs) + + shader.id = LoadShaderProgram(vShaderStr, fShaderStr); + + if (shader.id != 0) TraceLog(INFO, "[SHDR ID %i] Standard shader loaded successfully", shader.id); + else TraceLog(WARNING, "[SHDR ID %i] Standard shader could not be loaded", shader.id); + + if (shader.id != 0) LoadDefaultShaderLocations(&shader); // TODO: Review locations fetching + + return shader; +} + // Get location handlers to for shader attributes and uniforms // NOTE: If any location is not found, loc point becomes -1 static void LoadDefaultShaderLocations(Shader *shader) @@ -2900,6 +3001,62 @@ static void UnloadDefaultBuffers(void) free(quads.indices); } +// Sets shader uniform values for lights array +// NOTE: It would be far easier with shader UBOs but are not supported on OpenGL ES 2.0f +// TODO: Review memcpy() and parameters pass +static void SetShaderLights(Shader shader) +{ + /* + // NOTE: Standard Shader must include the following data: + + // Shader Light struct + struct Light { + vec3 position; + vec3 direction; + + vec3 diffuse; + float intensity; + } + + const int maxLights = 8; + uniform int lightsCount; // Number of lights + uniform Light lights[maxLights]; + */ + + int locPoint; + char locName[32] = "lights[x].position\0"; + + glUseProgram(shader.id); + + locPoint = glGetUniformLocation(shader.id, "lightsCount"); + glUniform1i(locPoint, lightsCount); + + for (int i = 0; i < lightsCount; i++) + { + locName[7] = '0' + i; + + memcpy(&locName[10], "position\0", strlen("position\0")); + locPoint = glGetUniformLocation(shader.id, locName); + glUniform3f(locPoint, lights[i]->position.x, lights[i]->position.y, lights[i]->position.z); + + memcpy(&locName[10], "direction\0", strlen("direction\0")); + locPoint = glGetUniformLocation(shader.id, locName); + glUniform3f(locPoint, lights[i]->direction.x, lights[i]->direction.y, lights[i]->direction.z); + + memcpy(&locName[10], "diffuse\0", strlen("diffuse\0")); + locPoint = glGetUniformLocation(shader.id, locName); + glUniform4f(locPoint, (float)lights[i]->diffuse.r/255, (float)lights[i]->diffuse.g/255, (float)lights[i]->diffuse.b/255, (float)lights[i]->diffuse.a/255 ); + + memcpy(&locName[10], "intensity\0", strlen("intensity\0")); + locPoint = glGetUniformLocation(shader.id, locName); + glUniform1f(locPoint, lights[i]->intensity); + + // TODO: Pass to the shader any other required data from LightData struct + } + + glUseProgram(0); +} + // Read text data from file // NOTE: text chars array should be freed manually static char *ReadTextFile(const char *fileName) diff --git a/src/rlgl.h b/src/rlgl.h index a557ffa2..39941b33 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -209,6 +209,28 @@ typedef enum { OPENGL_11 = 1, OPENGL_33, OPENGL_ES_20 } GlVersion; float glossiness; float normalDepth; } Material; + + // Light type + // TODO: Review contained data to support different light types and features + typedef struct LightData { + int id; + int type; // LIGHT_POINT, LIGHT_DIRECTIONAL, LIGHT_SPOT + bool enabled; + + Vector3 position; + Vector3 direction; // Used on LIGHT_DIRECTIONAL and LIGHT_SPOT (cone direction) + float attenuation; // Lost of light intensity with distance (use radius?) + + Color diffuse; // Use Vector3 diffuse (including intensities)? + float intensity; + + Color specular; + //float specFactor; // Specular intensity ? + + //Color ambient; // Required? + + float coneAngle; // SpotLight + } LightData, *Light; // Color blending modes (pre-defined) typedef enum { BLEND_ALPHA = 0, BLEND_ADDITIVE, BLEND_MULTIPLIED } BlendMode; @@ -311,6 +333,9 @@ void SetShaderValuei(Shader shader, int uniformLoc, int *value, int size); // S void SetShaderValueMatrix(Shader shader, int uniformLoc, Matrix mat); // Set shader uniform value (matrix 4x4) void SetBlendMode(int mode); // Set blending mode (alpha, additive, multiplied) + +Light CreateLight(int type, Vector3 position, Color diffuse); // Create a new light, initialize it and add to pool +void DestroyLight(Light light); // Destroy a light and take it out of the list #endif #ifdef __cplusplus -- cgit v1.2.3 From 90c62c4cc0bc79ea51ae114467757a8d80c38fa6 Mon Sep 17 00:00:00 2001 From: victorfisac Date: Fri, 20 May 2016 14:07:50 +0200 Subject: Fix small warning Material glossiness is a float type value... --- src/models.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/models.c b/src/models.c index 44e30390..0e59242b 100644 --- a/src/models.c +++ b/src/models.c @@ -2068,7 +2068,7 @@ static Material LoadMTL(const char *fileName) { if (buffer[1] == 's') // Ns int Shininess (specular exponent). Ranges from 0 to 1000. { - sscanf(buffer, "Ns %i", &material.glossiness); + sscanf(buffer, "Ns %f", &material.glossiness); } else if (buffer[1] == 'i') // Ni int Refraction index. { -- cgit v1.2.3 From 3fa6fdacf2be438cfc81d3ae5ef2b58801aecce6 Mon Sep 17 00:00:00 2001 From: victorfisac Date: Fri, 20 May 2016 14:24:53 +0200 Subject: Improved MTL loading shininess value --- src/models.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/models.c b/src/models.c index 0e59242b..414f6716 100644 --- a/src/models.c +++ b/src/models.c @@ -2068,7 +2068,11 @@ static Material LoadMTL(const char *fileName) { if (buffer[1] == 's') // Ns int Shininess (specular exponent). Ranges from 0 to 1000. { - sscanf(buffer, "Ns %f", &material.glossiness); + int shininess = 0; + sscanf(buffer, "Ns %i", &shininess); + + // Normalize shininess value to material glossiness attribute + material.glossiness = (float)shininess/1000; } else if (buffer[1] == 'i') // Ni int Refraction index. { -- cgit v1.2.3 From 6dac1efefef137ae487fa9f4c782b80538f784ba Mon Sep 17 00:00:00 2001 From: raysan5 Date: Fri, 20 May 2016 15:01:36 +0200 Subject: Comented buggy code to avoid problems... ...on model drawing --- src/rlgl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/rlgl.c b/src/rlgl.c index e971d747..c5048aff 100644 --- a/src/rlgl.c +++ b/src/rlgl.c @@ -1797,7 +1797,7 @@ void rlglDrawMesh(Mesh mesh, Material material, Matrix transform) } // Setup shader uniforms for lights - SetShaderLights(material.shader); + //SetShaderLights(material.shader); if (vaoSupported) { -- cgit v1.2.3 From 30c8058fca39e4d6566b2fc06ae2498c48c00717 Mon Sep 17 00:00:00 2001 From: victorfisac Date: Fri, 20 May 2016 17:18:07 +0200 Subject: Add standard lighting (1/3) - Ambient and lambert lighting added. - Ambient and diffuse colors linked to standard shader. - Single light linked to standard shader. - LoadStandardMaterial() and depending functions added. --- src/models.c | 6 +-- src/raylib.h | 9 ++-- src/rlgl.c | 149 +++++++++++++++++++++++++++++++++++++++++++++++++---------- 3 files changed, 131 insertions(+), 33 deletions(-) (limited to 'src') diff --git a/src/models.c b/src/models.c index 414f6716..2629dffd 100644 --- a/src/models.c +++ b/src/models.c @@ -732,13 +732,13 @@ Material LoadDefaultMaterial(void) return material; } -// Load standard material (uses standard models shader) +// Load standard material (uses material attributes and lighting shader) // NOTE: Standard shader supports multiple maps and lights Material LoadStandardMaterial(void) { Material material = LoadDefaultMaterial(); - //material.shader = GetStandardShader(); + material.shader = GetStandardShader(); return material; } @@ -1240,7 +1240,7 @@ void DrawModelEx(Model model, Vector3 position, Vector3 rotationAxis, float rota //Matrix matModel = MatrixMultiply(model.transform, matTransform); // Transform to world-space coordinates model.transform = MatrixMultiply(MatrixMultiply(matScale, matRotation), matTranslation); - model.material.colDiffuse = tint; + // model.material.colDiffuse = tint; rlglDrawMesh(model.mesh, model.material, model.transform); } diff --git a/src/raylib.h b/src/raylib.h index d98a0797..48534fd6 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -398,7 +398,7 @@ typedef struct Shader { // Uniform locations int mvpLoc; // ModelView-Projection matrix uniform location point (vertex shader) - int tintColorLoc; // Color uniform location point (fragment shader) + int tintColorLoc; // Diffuse color uniform location point (fragment shader) // Texture map locations int mapDiffuseLoc; // Diffuse map texture uniform location point (fragment shader) @@ -444,11 +444,8 @@ typedef struct LightData { float intensity; Color specular; - //float specFactor; // Specular intensity ? - - //Color ambient; // Required? - float coneAngle; // SpotLight + float coneAngle; // SpotLight } LightData, *Light; // Light types @@ -836,6 +833,7 @@ void SetModelTexture(Model *model, Texture2D texture); // Link a textur Material LoadMaterial(const char *fileName); // Load material data (from file) Material LoadDefaultMaterial(void); // Load default material (uses default models shader) +Material LoadStandardMaterial(void); // Load standard material (uses material attributes and lighting shader) void UnloadMaterial(Material material); // Unload material textures from VRAM void DrawModel(Model model, Vector3 position, float scale, Color tint); // Draw a model (with texture if set) @@ -865,6 +863,7 @@ void UnloadShader(Shader shader); // Unload a void SetDefaultShader(void); // Set default shader to be used in batch draw void SetCustomShader(Shader shader); // Set custom shader to be used in batch draw Shader GetDefaultShader(void); // Get default shader +Shader GetStandardShader(void); // Get default shader Texture2D GetDefaultTexture(void); // Get default texture int GetShaderLocation(Shader shader, const char *uniformName); // Get shader uniform location diff --git a/src/rlgl.c b/src/rlgl.c index c5048aff..e2195e4d 100644 --- a/src/rlgl.c +++ b/src/rlgl.c @@ -191,6 +191,7 @@ static bool useTempBuffer = false; // Shader Programs static Shader defaultShader; +static Shader standardShader; static Shader currentShader; // By default, defaultShader // Flags for supported extensions @@ -236,6 +237,7 @@ static Shader LoadDefaultShader(void); // Load default shader (just vertex static Shader LoadStandardShader(void); // Load standard shader (support materials and lighting) static void LoadDefaultShaderLocations(Shader *shader); // Bind default shader locations (attributes and uniforms) static void UnloadDefaultShader(void); // Unload default shader +static void UnloadStandardShader(void); // Unload standard shader static void LoadDefaultBuffers(void); // Load default internal buffers (lines, triangles, quads) static void UpdateDefaultBuffers(void); // Update default internal buffers (VAOs/VBOs) with vertex data @@ -1018,6 +1020,7 @@ void rlglInit(void) // Init default Shader (customized for GL 3.3 and ES2) defaultShader = LoadDefaultShader(); + standardShader = LoadStandardShader(); currentShader = defaultShader; LoadDefaultBuffers(); // Initialize default vertex arrays buffers (lines, triangles, quads) @@ -1046,6 +1049,7 @@ void rlglClose(void) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) UnloadDefaultShader(); + UnloadStandardShader(); UnloadDefaultBuffers(); // Delete default white texture @@ -1757,19 +1761,30 @@ void rlglDrawMesh(Mesh mesh, Material material, Matrix transform) // Send combined model-view-projection matrix to shader glUniformMatrix4fv(material.shader.mvpLoc, 1, false, MatrixToFloat(matMVP)); - - // Setup shader uniforms for material related data - // TODO: Check if using standard shader to get location points // Upload to shader material.colDiffuse float vColorDiffuse[4] = { (float)material.colDiffuse.r/255, (float)material.colDiffuse.g/255, (float)material.colDiffuse.b/255, (float)material.colDiffuse.a/255 }; glUniform4fv(material.shader.tintColorLoc, 1, vColorDiffuse); + + // Check if using standard shader to get location points + // NOTE: standard shader specific locations are got at render time to keep Shader struct as simple as possible (with just default shader locations) + if(material.shader.id == standardShader.id) + { + // Send model transformations matrix to shader + glUniformMatrix4fv(glGetUniformLocation(material.shader.id, "modelMatrix"), 1, false, MatrixToFloat(transform)); + + // Setup shader uniforms for lights + SetShaderLights(material.shader); + + // Upload to shader material.colAmbient + glUniform4f(glGetUniformLocation(material.shader.id, "colAmbient"), (float)material.colAmbient.r/255, (float)material.colAmbient.g/255, (float)material.colAmbient.b/255, (float)material.colAmbient.a/255); + + // Upload to shader material.colSpecular + glUniform4f(glGetUniformLocation(material.shader.id, "colSpecular"), (float)material.colSpecular.r/255, (float)material.colSpecular.g/255, (float)material.colSpecular.b/255, (float)material.colSpecular.a/255); - // TODO: Upload to shader material.colAmbient - // glUniform4f(???, (float)material.colAmbient.r/255, (float)material.colAmbient.g/255, (float)material.colAmbient.b/255, (float)material.colAmbient.a/255); - - // TODO: Upload to shader material.colSpecular - // glUniform4f(???, (float)material.colSpecular.r/255, (float)material.colSpecular.g/255, (float)material.colSpecular.b/255, (float)material.colSpecular.a/255); + // TODO: Upload to shader glossiness + //glUniform1f(???, material.glossiness); + } // Set shader textures (diffuse, normal, specular) glActiveTexture(GL_TEXTURE0); @@ -1791,13 +1806,7 @@ void rlglDrawMesh(Mesh mesh, Material material, Matrix transform) glActiveTexture(GL_TEXTURE2); glBindTexture(GL_TEXTURE_2D, material.texSpecular.id); glUniform1i(material.shader.mapSpecularLoc, 2); // Texture fits in active texture unit 2 - - // TODO: Upload to shader glossiness - //glUniform1f(???, material.glossiness); } - - // Setup shader uniforms for lights - //SetShaderLights(material.shader); if (vaoSupported) { @@ -2148,6 +2157,17 @@ Shader GetDefaultShader(void) #endif } +// Get default shader +Shader GetStandardShader(void) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + return standardShader; +#else + Shader shader = { 0 }; + return shader; +#endif +} + // Get shader uniform location int GetShaderLocation(Shader shader, const char *uniformName) { @@ -2499,13 +2519,75 @@ static Shader LoadStandardShader(void) { Shader shader; - char *vShaderStr; - char *fShaderStr; - - // TODO: Implement standard uber-shader, supporting all features (GLSL 100 / GLSL 330) - - // NOTE: Shader could be quite extensive so it could be implemented in external files (standard.vs/standard.fs) - + // Vertex shader directly defined, no external file required +#if defined(GRAPHICS_API_OPENGL_33) + char vShaderStr[] = "#version 330 \n" + "in vec3 vertexPosition; \n" + "in vec3 vertexNormal; \n" + "in vec2 vertexTexCoord; \n" + "in vec4 vertexColor; \n" + "out vec2 fragTexCoord; \n" + "out vec4 fragColor; \n" + "out vec3 fragNormal; \n" +#elif defined(GRAPHICS_API_OPENGL_ES2) + char vShaderStr[] = "#version 100 \n" + "attribute vec3 vertexPosition; \n" + "attribute vec3 vertexNormal; \n" + "attribute vec2 vertexTexCoord; \n" + "attribute vec4 vertexColor; \n" + "varying vec2 fragTexCoord; \n" + "varying vec4 fragColor; \n" + "varying vec3 fragNormal; \n" +#endif + "uniform mat4 mvpMatrix; \n" + "uniform mat4 modelMatrix; \n" + "void main() \n" + "{ \n" + " fragTexCoord = vertexTexCoord; \n" + " fragColor = vertexColor; \n" + " mat3 normalMatrix = transpose(inverse(mat3(modelMatrix))); \n" + " fragNormal = normalize(normalMatrix*vertexNormal); \n" + " gl_Position = mvpMatrix*vec4(vertexPosition, 1.0); \n" + "} \n"; + + // TODO: add specular calculation, multi-lights structs and light type calculations (directional, point, spot) + // Fragment shader directly defined, no external file required +#if defined(GRAPHICS_API_OPENGL_33) + char fShaderStr[] = "#version 330 \n" + "in vec2 fragTexCoord; \n" + "in vec4 fragColor; \n" + "in vec3 fragNormal; \n" + "out vec4 finalColor; \n" +#elif defined(GRAPHICS_API_OPENGL_ES2) + char fShaderStr[] = "#version 100 \n" + "precision mediump float; \n" // precision required for OpenGL ES2 (WebGL) + "varying vec2 fragTexCoord; \n" + "varying vec4 fragColor; \n" + "varying vec3 fragNormal; \n" +#endif + "uniform sampler2D texture0; \n" + "uniform vec4 fragTintColor; \n" + "uniform vec4 colAmbient; \n" + "uniform vec4 colSpecular; \n" + "uniform vec3 lightDir; \n" + "vec3 LambertLighting(in vec3 n, in vec3 l) \n" + "{ \n" + " return clamp(dot(n, l), 0, 1)*fragTintColor.rgb; \n" + "} \n" + + "void main() \n" + "{ \n" + " vec3 n = normalize(fragNormal); \n" + " vec3 l = normalize(lightDir); \n" +#if defined(GRAPHICS_API_OPENGL_33) + " vec4 texelColor = texture(texture0, fragTexCoord); \n" + " finalColor = vec4(texelColor.rgb*(colAmbient.rgb + LambertLighting(n, l)) - colSpecular.rgb + colSpecular.rgb, texelColor.a*fragTintColor.a); \n" // Stupid specular color operation to avoid shader location errors +#elif defined(GRAPHICS_API_OPENGL_ES2) + " vec4 texelColor = texture2D(texture0, fragTexCoord); \n" // NOTE: texture2D() is deprecated on OpenGL 3.3 and ES 3.0 + " gl_FragColor = texelColor*fragTintColor*fragColor; \n" +#endif + "} \n"; + shader.id = LoadShaderProgram(vShaderStr, fShaderStr); if (shader.id != 0) TraceLog(INFO, "[SHDR ID %i] Standard shader loaded successfully", shader.id); @@ -2554,10 +2636,23 @@ static void UnloadDefaultShader(void) //glDetachShader(defaultShader, vertexShader); //glDetachShader(defaultShader, fragmentShader); //glDeleteShader(vertexShader); // Already deleted on shader compilation - //glDeleteShader(fragmentShader); // Already deleted on sahder compilation + //glDeleteShader(fragmentShader); // Already deleted on shader compilation glDeleteProgram(defaultShader.id); } +// Unload standard shader +static void UnloadStandardShader(void) +{ + glUseProgram(0); + + //glDetachShader(defaultShader, vertexShader); + //glDetachShader(defaultShader, fragmentShader); + //glDeleteShader(vertexShader); // Already deleted on shader compilation + //glDeleteShader(fragmentShader); // Already deleted on shader compilation + glDeleteProgram(standardShader.id); +} + + // Load default internal buffers (lines, triangles, quads) static void LoadDefaultBuffers(void) { @@ -3006,6 +3101,9 @@ static void UnloadDefaultBuffers(void) // TODO: Review memcpy() and parameters pass static void SetShaderLights(Shader shader) { + // Note: currently working with one light (index 0) + // TODO: add multi-lights feature (http://www.learnopengl.com/#!Lighting/Multiple-lights) + /* // NOTE: Standard Shader must include the following data: @@ -3023,7 +3121,7 @@ static void SetShaderLights(Shader shader) uniform Light lights[maxLights]; */ - int locPoint; + /*int locPoint; char locName[32] = "lights[x].position\0"; glUseProgram(shader.id); @@ -3052,9 +3150,10 @@ static void SetShaderLights(Shader shader) glUniform1f(locPoint, lights[i]->intensity); // TODO: Pass to the shader any other required data from LightData struct - } + }*/ - glUseProgram(0); + int locPoint = GetShaderLocation(shader, "lightDir"); + glUniform3f(locPoint, lights[0]->position.x, lights[0]->position.y, lights[0]->position.z); } // Read text data from file -- cgit v1.2.3 From cf71e1242e593de5b77d6ff219569e8c0d2c7fd9 Mon Sep 17 00:00:00 2001 From: victorfisac Date: Sat, 21 May 2016 18:08:09 +0200 Subject: Fix some audio module compile warnings --- src/audio.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/audio.c b/src/audio.c index 43e8be14..0c61c0fa 100644 --- a/src/audio.c +++ b/src/audio.c @@ -384,7 +384,7 @@ RawAudioContext InitRawAudioContext(int sampleRate, int channels, bool floatingP for(mixIndex = 0; mixIndex < MAX_MIX_CHANNELS; mixIndex++) // find empty mix channel slot { if(mixChannelsActive_g[mixIndex] == NULL) break; - else if(mixIndex = MAX_MIX_CHANNELS - 1) return -1; // error + else if(mixIndex == MAX_MIX_CHANNELS - 1) return -1; // error } if(InitMixChannel(sampleRate, mixIndex, channels, floatingPoint)) @@ -772,7 +772,7 @@ int PlayMusicStream(int musicIndex, char *fileName) for(mixIndex = 0; mixIndex < MAX_MIX_CHANNELS; mixIndex++) // find empty mix channel slot { if(mixChannelsActive_g[mixIndex] == NULL) break; - else if(mixIndex = MAX_MIX_CHANNELS - 1) return 2; // error + else if(mixIndex == MAX_MIX_CHANNELS - 1) return 2; // error } if (strcmp(GetExtension(fileName),"ogg") == 0) @@ -956,7 +956,7 @@ float GetMusicTimeLength(int index) // Get current music time played (in seconds) float GetMusicTimePlayed(int index) { - float secondsPlayed; + float secondsPlayed = 0.0f; if(index < MAX_MUSIC_STREAMS && currentMusic[index].mixc) { if (currentMusic[index].chipTune) @@ -972,7 +972,6 @@ float GetMusicTimePlayed(int index) secondsPlayed = (float)samplesPlayed / (currentMusic[index].mixc->sampleRate * currentMusic[index].mixc->channels); } } - return secondsPlayed; } -- cgit v1.2.3 From 30941c0dd1f7904b5a0b50c05ec17265f8d69baa Mon Sep 17 00:00:00 2001 From: victorfisac Date: Sat, 21 May 2016 18:10:06 +0200 Subject: Add Draw3DLine function and fixed MLT glossiness import value In standard shader, material glossiness is a value from 0 to 1000 like in MLT files. So, it doesn't need to be normalized. --- src/models.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/models.c b/src/models.c index 2629dffd..aef79626 100644 --- a/src/models.c +++ b/src/models.c @@ -65,6 +65,16 @@ static Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize); // Module Functions Definition //---------------------------------------------------------------------------------- +// Draw a line in 3D world space +void Draw3DLine(Vector3 startPos, Vector3 endPos, Color color) +{ + rlBegin(RL_LINES); + rlColor4ub(color.r, color.g, color.b, color.a); + rlVertex3f(startPos.x, startPos.y, startPos.z); + rlVertex3f(endPos.x, endPos.y, endPos.z); + rlEnd(); +} + // Draw cube // NOTE: Cube position is the center position void DrawCube(Vector3 position, float width, float height, float length, Color color) @@ -2071,8 +2081,7 @@ static Material LoadMTL(const char *fileName) int shininess = 0; sscanf(buffer, "Ns %i", &shininess); - // Normalize shininess value to material glossiness attribute - material.glossiness = (float)shininess/1000; + material.glossiness = (float)shininess; } else if (buffer[1] == 'i') // Ni int Refraction index. { -- cgit v1.2.3 From c320a21f2b0e96f7605624e84048ccab9700b516 Mon Sep 17 00:00:00 2001 From: victorfisac Date: Sat, 21 May 2016 18:16:39 +0200 Subject: Add standard lighting (2/3) - 3 light types added (point, directional, spot). - DrawLights() function added using line shapes. - Standard lighting example added. - Removed useless struct variables from material and light. - Fixed light attributes dynamic locations errors. - Standard vertex and fragment shaders temporally added until rewrite it as char pointers in rlgl. TODO: - Add normal and specular maps calculations in standard shader. - Add control structs to handle which attributes needs to be calculated (textures, specular...). - Adapt standard shader to version 110. - Rewrite standard shader as char pointers in rlgl. --- src/raylib.h | 15 ++--- src/rlgl.c | 213 +++++++++++++++++++++++++---------------------------------- src/rlgl.h | 32 ++++----- 3 files changed, 111 insertions(+), 149 deletions(-) (limited to 'src') diff --git a/src/raylib.h b/src/raylib.h index 48534fd6..9cd02fd8 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -418,7 +418,7 @@ typedef struct Material { Color colAmbient; // Ambient color Color colSpecular; // Specular color - float glossiness; // Glossiness level + float glossiness; // Glossiness level (Ranges from 0 to 1000) float normalDepth; // Normal map depth } Material; @@ -430,22 +430,19 @@ typedef struct Model { } Model; // Light type -// TODO: Review contained data to support different light types and features typedef struct LightData { int id; int type; // LIGHT_POINT, LIGHT_DIRECTIONAL, LIGHT_SPOT bool enabled; Vector3 position; - Vector3 direction; // Used on LIGHT_DIRECTIONAL and LIGHT_SPOT (cone direction) - float attenuation; // Lost of light intensity with distance (use radius?) + Vector3 target; // Used on LIGHT_DIRECTIONAL and LIGHT_SPOT (cone direction target) + float attenuation; // Lost of light intensity with distance (world distance) - Color diffuse; // Use Vector3 diffuse (including intensities)? + Color diffuse; // Use Vector3 diffuse float intensity; - Color specular; - - float coneAngle; // SpotLight + float coneAngle; // Spot light max angle } LightData, *Light; // Light types @@ -805,6 +802,7 @@ const char *SubText(const char *text, int position, int length); //------------------------------------------------------------------------------------ // Basic 3d Shapes Drawing Functions (Module: models) //------------------------------------------------------------------------------------ +void Draw3DLine(Vector3 startPos, Vector3 endPos, Color color); // Draw a line in 3D world space void DrawCube(Vector3 position, float width, float height, float lenght, Color color); // Draw cube void DrawCubeV(Vector3 position, Vector3 size, Color color); // Draw cube (Vector version) void DrawCubeWires(Vector3 position, float width, float height, float lenght, Color color); // Draw cube wires @@ -874,6 +872,7 @@ void SetShaderValueMatrix(Shader shader, int uniformLoc, Matrix mat); // S void SetBlendMode(int mode); // Set blending mode (alpha, additive, multiplied) Light CreateLight(int type, Vector3 position, Color diffuse); // Create a new light, initialize it and add to pool +void DrawLights(void); // Draw all created lights in 3D world void DestroyLight(Light light); // Destroy a light and take it out of the list //---------------------------------------------------------------------------------- diff --git a/src/rlgl.c b/src/rlgl.c index e2195e4d..55677f3e 100644 --- a/src/rlgl.c +++ b/src/rlgl.c @@ -1773,6 +1773,9 @@ void rlglDrawMesh(Mesh mesh, Material material, Matrix transform) // Send model transformations matrix to shader glUniformMatrix4fv(glGetUniformLocation(material.shader.id, "modelMatrix"), 1, false, MatrixToFloat(transform)); + // Send view transformation matrix to shader. View matrix 8, 9 and 10 are view direction vector axis values (target - position) + glUniform3f(glGetUniformLocation(material.shader.id, "viewDir"), matView.m8, matView.m9, matView.m10); + // Setup shader uniforms for lights SetShaderLights(material.shader); @@ -1782,8 +1785,8 @@ void rlglDrawMesh(Mesh mesh, Material material, Matrix transform) // Upload to shader material.colSpecular glUniform4f(glGetUniformLocation(material.shader.id, "colSpecular"), (float)material.colSpecular.r/255, (float)material.colSpecular.g/255, (float)material.colSpecular.b/255, (float)material.colSpecular.a/255); - // TODO: Upload to shader glossiness - //glUniform1f(???, material.glossiness); + // Upload to shader glossiness + glUniform1f(glGetUniformLocation(material.shader.id, "glossiness"), material.glossiness); } // Set shader textures (diffuse, normal, specular) @@ -2245,7 +2248,6 @@ void SetBlendMode(int mode) } // Create a new light, initialize it and add to pool -// TODO: Review creation parameters (only generic ones) Light CreateLight(int type, Vector3 position, Color diffuse) { // Allocate dynamic memory @@ -2257,10 +2259,9 @@ Light CreateLight(int type, Vector3 position, Color diffuse) light->enabled = true; light->position = position; - light->direction = (Vector3){ 0.0f, 0.0f, 0.0f }; + light->target = (Vector3){ 0.0f, 0.0f, 0.0f }; light->intensity = 1.0f; light->diffuse = diffuse; - light->specular = WHITE; // Add new light to the array lights[lightsCount] = light; @@ -2271,6 +2272,31 @@ Light CreateLight(int type, Vector3 position, Color diffuse) return light; } +// Draw all created lights in 3D world +void DrawLights(void) +{ + for (int i = 0; i < lightsCount; i++) + { + switch (lights[i]->type) + { + case LIGHT_POINT: DrawSphereWires(lights[i]->position, 0.3f*lights[i]->intensity, 4, 8, (lights[i]->enabled ? lights[i]->diffuse : BLACK)); break; + case LIGHT_DIRECTIONAL: + { + Draw3DLine(lights[i]->position, lights[i]->target, (lights[i]->enabled ? lights[i]->diffuse : BLACK)); + DrawSphereWires(lights[i]->position, 0.3f*lights[i]->intensity, 4, 8, (lights[i]->enabled ? lights[i]->diffuse : BLACK)); + DrawCubeWires(lights[i]->target, 0.3f, 0.3f, 0.3f, (lights[i]->enabled ? lights[i]->diffuse : BLACK)); + } + case LIGHT_SPOT: + { + Draw3DLine(lights[i]->position, lights[i]->target, (lights[i]->enabled ? lights[i]->diffuse : BLACK)); + DrawCylinderWires(lights[i]->position, 0.0f, 0.3f*lights[i]->coneAngle/50, 0.6f, 5, (lights[i]->enabled ? lights[i]->diffuse : BLACK)); + DrawCubeWires(lights[i]->target, 0.3f, 0.3f, 0.3f, (lights[i]->enabled ? lights[i]->diffuse : BLACK)); + } break; + default: break; + } + } +} + // Destroy a light and take it out of the list void DestroyLight(Light light) { @@ -2488,15 +2514,15 @@ static Shader LoadDefaultShader(void) "varying vec4 fragColor; \n" #endif "uniform sampler2D texture0; \n" - "uniform vec4 fragTintColor; \n" + "uniform vec4 colDiffuse; \n" "void main() \n" "{ \n" #if defined(GRAPHICS_API_OPENGL_33) " vec4 texelColor = texture(texture0, fragTexCoord); \n" - " finalColor = texelColor*fragTintColor*fragColor; \n" + " finalColor = texelColor*colDiffuse*fragColor; \n" #elif defined(GRAPHICS_API_OPENGL_ES2) " vec4 texelColor = texture2D(texture0, fragTexCoord); \n" // NOTE: texture2D() is deprecated on OpenGL 3.3 and ES 3.0 - " gl_FragColor = texelColor*fragTintColor*fragColor; \n" + " gl_FragColor = texelColor*colDiffuse*fragColor; \n" #endif "} \n"; @@ -2513,87 +2539,17 @@ static Shader LoadDefaultShader(void) // Load standard shader // NOTE: This shader supports: // - Up to 3 different maps: diffuse, normal, specular -// - Material properties: colDiffuse, colAmbient, colSpecular, glossiness, normalDepth +// - Material properties: colAmbient, colDiffuse, colSpecular, glossiness, normalDepth // - Up to 8 lights: Point, Directional or Spot static Shader LoadStandardShader(void) { - Shader shader; - - // Vertex shader directly defined, no external file required -#if defined(GRAPHICS_API_OPENGL_33) - char vShaderStr[] = "#version 330 \n" - "in vec3 vertexPosition; \n" - "in vec3 vertexNormal; \n" - "in vec2 vertexTexCoord; \n" - "in vec4 vertexColor; \n" - "out vec2 fragTexCoord; \n" - "out vec4 fragColor; \n" - "out vec3 fragNormal; \n" -#elif defined(GRAPHICS_API_OPENGL_ES2) - char vShaderStr[] = "#version 100 \n" - "attribute vec3 vertexPosition; \n" - "attribute vec3 vertexNormal; \n" - "attribute vec2 vertexTexCoord; \n" - "attribute vec4 vertexColor; \n" - "varying vec2 fragTexCoord; \n" - "varying vec4 fragColor; \n" - "varying vec3 fragNormal; \n" -#endif - "uniform mat4 mvpMatrix; \n" - "uniform mat4 modelMatrix; \n" - "void main() \n" - "{ \n" - " fragTexCoord = vertexTexCoord; \n" - " fragColor = vertexColor; \n" - " mat3 normalMatrix = transpose(inverse(mat3(modelMatrix))); \n" - " fragNormal = normalize(normalMatrix*vertexNormal); \n" - " gl_Position = mvpMatrix*vec4(vertexPosition, 1.0); \n" - "} \n"; - - // TODO: add specular calculation, multi-lights structs and light type calculations (directional, point, spot) - // Fragment shader directly defined, no external file required -#if defined(GRAPHICS_API_OPENGL_33) - char fShaderStr[] = "#version 330 \n" - "in vec2 fragTexCoord; \n" - "in vec4 fragColor; \n" - "in vec3 fragNormal; \n" - "out vec4 finalColor; \n" -#elif defined(GRAPHICS_API_OPENGL_ES2) - char fShaderStr[] = "#version 100 \n" - "precision mediump float; \n" // precision required for OpenGL ES2 (WebGL) - "varying vec2 fragTexCoord; \n" - "varying vec4 fragColor; \n" - "varying vec3 fragNormal; \n" -#endif - "uniform sampler2D texture0; \n" - "uniform vec4 fragTintColor; \n" - "uniform vec4 colAmbient; \n" - "uniform vec4 colSpecular; \n" - "uniform vec3 lightDir; \n" - "vec3 LambertLighting(in vec3 n, in vec3 l) \n" - "{ \n" - " return clamp(dot(n, l), 0, 1)*fragTintColor.rgb; \n" - "} \n" - - "void main() \n" - "{ \n" - " vec3 n = normalize(fragNormal); \n" - " vec3 l = normalize(lightDir); \n" -#if defined(GRAPHICS_API_OPENGL_33) - " vec4 texelColor = texture(texture0, fragTexCoord); \n" - " finalColor = vec4(texelColor.rgb*(colAmbient.rgb + LambertLighting(n, l)) - colSpecular.rgb + colSpecular.rgb, texelColor.a*fragTintColor.a); \n" // Stupid specular color operation to avoid shader location errors -#elif defined(GRAPHICS_API_OPENGL_ES2) - " vec4 texelColor = texture2D(texture0, fragTexCoord); \n" // NOTE: texture2D() is deprecated on OpenGL 3.3 and ES 3.0 - " gl_FragColor = texelColor*fragTintColor*fragColor; \n" -#endif - "} \n"; - - shader.id = LoadShaderProgram(vShaderStr, fShaderStr); + // Load standard shader (TODO: rewrite as char pointers) + Shader shader = LoadShader("resources/shaders/standard.vs", "resources/shaders/standard.fs"); if (shader.id != 0) TraceLog(INFO, "[SHDR ID %i] Standard shader loaded successfully", shader.id); else TraceLog(WARNING, "[SHDR ID %i] Standard shader could not be loaded", shader.id); - if (shader.id != 0) LoadDefaultShaderLocations(&shader); // TODO: Review locations fetching + if (shader.id != 0) LoadDefaultShaderLocations(&shader); return shader; } @@ -2622,7 +2578,7 @@ static void LoadDefaultShaderLocations(Shader *shader) shader->mvpLoc = glGetUniformLocation(shader->id, "mvpMatrix"); // Get handles to GLSL uniform locations (fragment shader) - shader->tintColorLoc = glGetUniformLocation(shader->id, "fragTintColor"); + shader->tintColorLoc = glGetUniformLocation(shader->id, "colDiffuse"); shader->mapDiffuseLoc = glGetUniformLocation(shader->id, "texture0"); shader->mapNormalLoc = glGetUniformLocation(shader->id, "texture1"); shader->mapSpecularLoc = glGetUniformLocation(shader->id, "texture2"); @@ -3098,62 +3054,75 @@ static void UnloadDefaultBuffers(void) // Sets shader uniform values for lights array // NOTE: It would be far easier with shader UBOs but are not supported on OpenGL ES 2.0f -// TODO: Review memcpy() and parameters pass static void SetShaderLights(Shader shader) { - // Note: currently working with one light (index 0) - // TODO: add multi-lights feature (http://www.learnopengl.com/#!Lighting/Multiple-lights) - - /* - // NOTE: Standard Shader must include the following data: - - // Shader Light struct - struct Light { - vec3 position; - vec3 direction; - - vec3 diffuse; - float intensity; - } - - const int maxLights = 8; - uniform int lightsCount; // Number of lights - uniform Light lights[maxLights]; - */ + int locPoint = glGetUniformLocation(shader.id, "lightsCount"); + glUniform1i(locPoint, lightsCount); - /*int locPoint; char locName[32] = "lights[x].position\0"; - - glUseProgram(shader.id); - - locPoint = glGetUniformLocation(shader.id, "lightsCount"); - glUniform1i(locPoint, lightsCount); for (int i = 0; i < lightsCount; i++) { locName[7] = '0' + i; - memcpy(&locName[10], "position\0", strlen("position\0")); - locPoint = glGetUniformLocation(shader.id, locName); - glUniform3f(locPoint, lights[i]->position.x, lights[i]->position.y, lights[i]->position.z); + memcpy(&locName[10], "enabled\0", strlen("enabled\0") + 1); + locPoint = GetShaderLocation(shader, locName); + glUniform1i(locPoint, lights[i]->enabled); - memcpy(&locName[10], "direction\0", strlen("direction\0")); - locPoint = glGetUniformLocation(shader.id, locName); - glUniform3f(locPoint, lights[i]->direction.x, lights[i]->direction.y, lights[i]->direction.z); - - memcpy(&locName[10], "diffuse\0", strlen("diffuse\0")); + memcpy(&locName[10], "type\0", strlen("type\0") + 1); + locPoint = GetShaderLocation(shader, locName); + glUniform1i(locPoint, lights[i]->type); + + memcpy(&locName[10], "diffuse\0", strlen("diffuse\0") + 2); locPoint = glGetUniformLocation(shader.id, locName); - glUniform4f(locPoint, (float)lights[i]->diffuse.r/255, (float)lights[i]->diffuse.g/255, (float)lights[i]->diffuse.b/255, (float)lights[i]->diffuse.a/255 ); + glUniform4f(locPoint, (float)lights[i]->diffuse.r/255, (float)lights[i]->diffuse.g/255, (float)lights[i]->diffuse.b/255, (float)lights[i]->diffuse.a/255); memcpy(&locName[10], "intensity\0", strlen("intensity\0")); locPoint = glGetUniformLocation(shader.id, locName); glUniform1f(locPoint, lights[i]->intensity); + switch(lights[i]->type) + { + case LIGHT_POINT: + { + memcpy(&locName[10], "position\0", strlen("position\0") + 1); + locPoint = GetShaderLocation(shader, locName); + glUniform3f(locPoint, lights[i]->position.x, lights[i]->position.y, lights[i]->position.z); + + memcpy(&locName[10], "attenuation\0", strlen("attenuation\0")); + locPoint = GetShaderLocation(shader, locName); + glUniform1f(locPoint, lights[i]->attenuation); + } break; + case LIGHT_DIRECTIONAL: + { + memcpy(&locName[10], "direction\0", strlen("direction\0") + 2); + locPoint = GetShaderLocation(shader, locName); + Vector3 direction = { lights[i]->target.x - lights[i]->position.x, lights[i]->target.y - lights[i]->position.y, lights[i]->target.z - lights[i]->position.z }; + VectorNormalize(&direction); + glUniform3f(locPoint, direction.x, direction.y, direction.z); + } break; + case LIGHT_SPOT: + { + memcpy(&locName[10], "position\0", strlen("position\0") + 1); + locPoint = GetShaderLocation(shader, locName); + glUniform3f(locPoint, lights[i]->position.x, lights[i]->position.y, lights[i]->position.z); + + memcpy(&locName[10], "direction\0", strlen("direction\0") + 2); + locPoint = GetShaderLocation(shader, locName); + + Vector3 direction = { lights[i]->target.x - lights[i]->position.x, lights[i]->target.y - lights[i]->position.y, lights[i]->target.z - lights[i]->position.z }; + VectorNormalize(&direction); + glUniform3f(locPoint, direction.x, direction.y, direction.z); + + memcpy(&locName[10], "coneAngle\0", strlen("coneAngle\0")); + locPoint = GetShaderLocation(shader, locName); + glUniform1f(locPoint, lights[i]->coneAngle); + } break; + default: break; + } + // TODO: Pass to the shader any other required data from LightData struct - }*/ - - int locPoint = GetShaderLocation(shader, "lightDir"); - glUniform3f(locPoint, lights[0]->position.x, lights[0]->position.y, lights[0]->position.z); + } } // Read text data from file diff --git a/src/rlgl.h b/src/rlgl.h index 39941b33..0765a8a7 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -196,40 +196,34 @@ typedef enum { OPENGL_11 = 1, OPENGL_33, OPENGL_ES_20 } GlVersion; // Material type typedef struct Material { - Shader shader; + Shader shader; // Standard shader (supports 3 map types: diffuse, normal, specular) - Texture2D texDiffuse; // Diffuse texture - Texture2D texNormal; // Normal texture - Texture2D texSpecular; // Specular texture + Texture2D texDiffuse; // Diffuse texture + Texture2D texNormal; // Normal texture + Texture2D texSpecular; // Specular texture - Color colDiffuse; - Color colAmbient; - Color colSpecular; + Color colDiffuse; // Diffuse color + Color colAmbient; // Ambient color + Color colSpecular; // Specular color - float glossiness; - float normalDepth; + float glossiness; // Glossiness level (Ranges from 0 to 1000) + float normalDepth; // Normal map depth } Material; // Light type - // TODO: Review contained data to support different light types and features typedef struct LightData { int id; int type; // LIGHT_POINT, LIGHT_DIRECTIONAL, LIGHT_SPOT bool enabled; Vector3 position; - Vector3 direction; // Used on LIGHT_DIRECTIONAL and LIGHT_SPOT (cone direction) - float attenuation; // Lost of light intensity with distance (use radius?) + Vector3 target; // Used on LIGHT_DIRECTIONAL and LIGHT_SPOT (cone direction target) + float attenuation; // Lost of light intensity with distance (world distance) - Color diffuse; // Use Vector3 diffuse (including intensities)? + Color diffuse; // Use Vector3 diffuse float intensity; - Color specular; - //float specFactor; // Specular intensity ? - - //Color ambient; // Required? - - float coneAngle; // SpotLight + float coneAngle; // Spot light max angle } LightData, *Light; // Color blending modes (pre-defined) -- cgit v1.2.3 From dcd6942ed1ab703625f5c7072cbcfd823c681db7 Mon Sep 17 00:00:00 2001 From: victorfisac Date: Sat, 21 May 2016 18:22:15 +0200 Subject: Fix small bug and spacing --- src/models.c | 46 +++++++++++++++++++++++----------------------- src/rlgl.c | 10 +++++----- 2 files changed, 28 insertions(+), 28 deletions(-) (limited to 'src') diff --git a/src/models.c b/src/models.c index aef79626..07dee720 100644 --- a/src/models.c +++ b/src/models.c @@ -302,9 +302,9 @@ void DrawSphereEx(Vector3 centerPos, float radius, int rings, int slices, Color rlBegin(RL_TRIANGLES); rlColor4ub(color.r, color.g, color.b, color.a); - for(int i = 0; i < (rings + 2); i++) + for (int i = 0; i < (rings + 2); i++) { - for(int j = 0; j < slices; j++) + for (int j = 0; j < slices; j++) { rlVertex3f(cos(DEG2RAD*(270+(180/(rings + 1))*i)) * sin(DEG2RAD*(j*360/slices)), sin(DEG2RAD*(270+(180/(rings + 1))*i)), @@ -341,9 +341,9 @@ void DrawSphereWires(Vector3 centerPos, float radius, int rings, int slices, Col rlBegin(RL_LINES); rlColor4ub(color.r, color.g, color.b, color.a); - for(int i = 0; i < (rings + 2); i++) + for (int i = 0; i < (rings + 2); i++) { - for(int j = 0; j < slices; j++) + for (int j = 0; j < slices; j++) { rlVertex3f(cos(DEG2RAD*(270+(180/(rings + 1))*i)) * sin(DEG2RAD*(j*360/slices)), sin(DEG2RAD*(270+(180/(rings + 1))*i)), @@ -386,7 +386,7 @@ void DrawCylinder(Vector3 position, float radiusTop, float radiusBottom, float h if (radiusTop > 0) { // Draw Body ------------------------------------------------------------------------------------- - for(int i = 0; i < 360; i += 360/sides) + for (int i = 0; i < 360; i += 360/sides) { rlVertex3f(sin(DEG2RAD*i) * radiusBottom, 0, cos(DEG2RAD*i) * radiusBottom); //Bottom Left rlVertex3f(sin(DEG2RAD*(i+360/sides)) * radiusBottom, 0, cos(DEG2RAD*(i+360/sides)) * radiusBottom); //Bottom Right @@ -398,7 +398,7 @@ void DrawCylinder(Vector3 position, float radiusTop, float radiusBottom, float h } // Draw Cap -------------------------------------------------------------------------------------- - for(int i = 0; i < 360; i += 360/sides) + for (int i = 0; i < 360; i += 360/sides) { rlVertex3f(0, height, 0); rlVertex3f(sin(DEG2RAD*i) * radiusTop, height, cos(DEG2RAD*i) * radiusTop); @@ -408,7 +408,7 @@ void DrawCylinder(Vector3 position, float radiusTop, float radiusBottom, float h else { // Draw Cone ------------------------------------------------------------------------------------- - for(int i = 0; i < 360; i += 360/sides) + for (int i = 0; i < 360; i += 360/sides) { rlVertex3f(0, height, 0); rlVertex3f(sin(DEG2RAD*i) * radiusBottom, 0, cos(DEG2RAD*i) * radiusBottom); @@ -417,7 +417,7 @@ void DrawCylinder(Vector3 position, float radiusTop, float radiusBottom, float h } // Draw Base ----------------------------------------------------------------------------------------- - for(int i = 0; i < 360; i += 360/sides) + for (int i = 0; i < 360; i += 360/sides) { rlVertex3f(0, 0, 0); rlVertex3f(sin(DEG2RAD*(i+360/sides)) * radiusBottom, 0, cos(DEG2RAD*(i+360/sides)) * radiusBottom); @@ -431,7 +431,7 @@ void DrawCylinder(Vector3 position, float radiusTop, float radiusBottom, float h // NOTE: It could be also used for pyramid and cone void DrawCylinderWires(Vector3 position, float radiusTop, float radiusBottom, float height, int sides, Color color) { - if(sides < 3) sides = 3; + if (sides < 3) sides = 3; rlPushMatrix(); rlTranslatef(position.x, position.y, position.z); @@ -439,7 +439,7 @@ void DrawCylinderWires(Vector3 position, float radiusTop, float radiusBottom, fl rlBegin(RL_LINES); rlColor4ub(color.r, color.g, color.b, color.a); - for(int i = 0; i < 360; i += 360/sides) + for (int i = 0; i < 360; i += 360/sides) { rlVertex3f(sin(DEG2RAD*i) * radiusBottom, 0, cos(DEG2RAD*i) * radiusBottom); rlVertex3f(sin(DEG2RAD*(i+360/sides)) * radiusBottom, 0, cos(DEG2RAD*(i+360/sides)) * radiusBottom); @@ -500,7 +500,7 @@ void DrawGrid(int slices, float spacing) int halfSlices = slices / 2; rlBegin(RL_LINES); - for(int i = -halfSlices; i <= halfSlices; i++) + for (int i = -halfSlices; i <= halfSlices; i++) { if (i == 0) { @@ -798,9 +798,9 @@ static Mesh GenMeshHeightmap(Image heightmap, Vector3 size) Vector3 scaleFactor = { size.x/mapX, size.y/255.0f, size.z/mapZ }; - for(int z = 0; z < mapZ-1; z++) + for (int z = 0; z < mapZ-1; z++) { - for(int x = 0; x < mapX-1; x++) + for (int x = 0; x < mapX-1; x++) { // Fill vertices array with data //---------------------------------------------------------- @@ -1417,7 +1417,7 @@ bool CheckCollisionRaySphere(Ray ray, Vector3 spherePosition, float sphereRadius float vector = VectorDotProduct(raySpherePos, ray.direction); float d = sphereRadius*sphereRadius - (distance*distance - vector*vector); - if(d >= 0.0f) collision = true; + if (d >= 0.0f) collision = true; return collision; } @@ -1432,14 +1432,14 @@ bool CheckCollisionRaySphereEx(Ray ray, Vector3 spherePosition, float sphereRadi float vector = VectorDotProduct(raySpherePos, ray.direction); float d = sphereRadius*sphereRadius - (distance*distance - vector*vector); - if(d >= 0.0f) collision = true; + if (d >= 0.0f) collision = true; // Calculate collision point Vector3 offset = ray.direction; float collisionDistance = 0; // Check if ray origin is inside the sphere to calculate the correct collision point - if(distance < sphereRadius) collisionDistance = vector + sqrt(d); + if (distance < sphereRadius) collisionDistance = vector + sqrt(d); else collisionDistance = vector - sqrt(d); VectorScale(&offset, collisionDistance); @@ -1777,11 +1777,11 @@ static Mesh LoadOBJ(const char *fileName) // First reading pass: Get numVertex, numNormals, numTexCoords, numTriangles // NOTE: vertex, texcoords and normals could be optimized (to be used indexed on faces definition) // NOTE: faces MUST be defined as TRIANGLES (3 vertex per face) - while(!feof(objFile)) + while (!feof(objFile)) { fscanf(objFile, "%c", &dataType); - switch(dataType) + switch (dataType) { case '#': // Comments case 'o': // Object name (One OBJ file can contain multible named meshes) @@ -1842,11 +1842,11 @@ static Mesh LoadOBJ(const char *fileName) // Second reading pass: Get vertex data to fill intermediate arrays // NOTE: This second pass is required in case of multiple meshes defined in same OBJ // TODO: Consider that different meshes can have different vertex data available (position, texcoords, normals) - while(!feof(objFile)) + while (!feof(objFile)) { fscanf(objFile, "%c", &dataType); - switch(dataType) + switch (dataType) { case '#': case 'o': case 'g': case 's': case 'm': case 'u': case 'f': fgets(comments, 200, objFile); break; case 'v': @@ -1903,11 +1903,11 @@ static Mesh LoadOBJ(const char *fileName) if (numNormals == 0) TraceLog(INFO, "[%s] No normals data on OBJ, normals will be generated from faces data", fileName); // Third reading pass: Get faces (triangles) data and fill VertexArray - while(!feof(objFile)) + while (!feof(objFile)) { fscanf(objFile, "%c", &dataType); - switch(dataType) + switch (dataType) { case '#': case 'o': case 'g': case 's': case 'm': case 'u': case 'v': fgets(comments, 200, objFile); break; case 'f': @@ -2023,7 +2023,7 @@ static Material LoadMTL(const char *fileName) return material; } - while(!feof(mtlFile)) + while (!feof(mtlFile)) { fgets(buffer, MAX_BUFFER_SIZE, mtlFile); diff --git a/src/rlgl.c b/src/rlgl.c index 55677f3e..85c0cae2 100644 --- a/src/rlgl.c +++ b/src/rlgl.c @@ -1397,7 +1397,7 @@ RenderTexture2D rlglLoadRenderTexture(int width, int height) { TraceLog(WARNING, "Framebuffer object could not be created..."); - switch(status) + switch (status) { case GL_FRAMEBUFFER_UNSUPPORTED: TraceLog(WARNING, "Framebuffer is unsupported"); break; case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: TraceLog(WARNING, "Framebuffer incomplete attachment"); break; @@ -1768,7 +1768,7 @@ void rlglDrawMesh(Mesh mesh, Material material, Matrix transform) // Check if using standard shader to get location points // NOTE: standard shader specific locations are got at render time to keep Shader struct as simple as possible (with just default shader locations) - if(material.shader.id == standardShader.id) + if (material.shader.id == standardShader.id) { // Send model transformations matrix to shader glUniformMatrix4fv(glGetUniformLocation(material.shader.id, "modelMatrix"), 1, false, MatrixToFloat(transform)); @@ -2285,7 +2285,7 @@ void DrawLights(void) Draw3DLine(lights[i]->position, lights[i]->target, (lights[i]->enabled ? lights[i]->diffuse : BLACK)); DrawSphereWires(lights[i]->position, 0.3f*lights[i]->intensity, 4, 8, (lights[i]->enabled ? lights[i]->diffuse : BLACK)); DrawCubeWires(lights[i]->target, 0.3f, 0.3f, 0.3f, (lights[i]->enabled ? lights[i]->diffuse : BLACK)); - } + } break; case LIGHT_SPOT: { Draw3DLine(lights[i]->position, lights[i]->target, (lights[i]->enabled ? lights[i]->diffuse : BLACK)); @@ -3081,7 +3081,7 @@ static void SetShaderLights(Shader shader) locPoint = glGetUniformLocation(shader.id, locName); glUniform1f(locPoint, lights[i]->intensity); - switch(lights[i]->type) + switch (lights[i]->type) { case LIGHT_POINT: { @@ -3295,7 +3295,7 @@ static void TraceLog(int msgType, const char *text, ...) va_list args; va_start(args, text); - switch(msgType) + switch (msgType) { case INFO: fprintf(stdout, "INFO: "); break; case ERROR: fprintf(stdout, "ERROR: "); break; -- cgit v1.2.3