aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJoshua Reisenauer <kd7tck@msn.com>2016-05-22 15:03:10 -0700
committerJoshua Reisenauer <kd7tck@msn.com>2016-05-22 15:03:10 -0700
commitf232f34981c9ccc757e486c116db6d22d3042694 (patch)
tree26adf20c7a1f71cfc986df22c0f5e15520485ec1 /src
parentcd7f25830bcf1f1bdc2efcde8ec70759b8eac3fc (diff)
parent9811a376902b0f568f31d2e8aace28144fbf33ff (diff)
downloadraylib-f232f34981c9ccc757e486c116db6d22d3042694.tar.gz
raylib-f232f34981c9ccc757e486c116db6d22d3042694.zip
Merge remote-tracking branch 'refs/remotes/raysan5/develop' into develop
Diffstat (limited to 'src')
-rw-r--r--src/audio.c692
-rw-r--r--src/audio.h39
-rw-r--r--src/easings.h10
-rw-r--r--src/models.c139
-rw-r--r--src/physac.c12
-rw-r--r--src/physac.h12
-rw-r--r--src/raylib.h86
-rw-r--r--src/rlgl.c703
-rw-r--r--src/rlgl.h46
-rw-r--r--src/shapes.c1
-rw-r--r--src/windows_compile.bat2
11 files changed, 1058 insertions, 684 deletions
diff --git a/src/audio.c b/src/audio.c
index fbf53df6..0c61c0fa 100644
--- a/src/audio.c
+++ b/src/audio.c
@@ -59,8 +59,9 @@
//----------------------------------------------------------------------------------
// Defines and Macros
//----------------------------------------------------------------------------------
-#define MAX_STREAM_BUFFERS 2
-#define MAX_AUDIO_CONTEXTS 4 // Number of open AL sources
+#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
@@ -76,37 +77,32 @@
// 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
-
- ALuint buffers[MAX_STREAM_BUFFERS];
- ALuint source;
- ALenum format;
-
- int channels;
- int sampleRate;
- 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.
-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
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
-} AudioContext_t;
+} MixChannel_t;
+
+// Music type (file streaming from memory)
+// 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 mixc
+ MixChannel_t *mixc; // mix channel
+
+ 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;
@@ -115,23 +111,28 @@ 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 Music currentMusic; // Current music loaded
- // NOTE: Only one music file playing at a time
+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
+
//----------------------------------------------------------------------------------
// 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, 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 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 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
#if defined(AUDIO_STANDALONE)
const char *GetExtension(const char *fileName); // Get the extension for a filename
@@ -142,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
@@ -158,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));
@@ -169,15 +170,19 @@ 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)
{
- StopMusicStream(); // Stop music streaming and close current stream
+ for(int index=0; index<MAX_MUSIC_STREAMS; index++)
+ {
+ if(currentMusic[index].mixc) StopMusicStream(index); // Stop music streaming and close current stream
+ }
+
ALCdevice *device;
ALCcontext *context = alcGetCurrentContext();
- if (context == NULL) TraceLog(WARNING, "Could not get current audio context for closing");
+ if (context == NULL) TraceLog(WARNING, "Could not get current mix channel for closing");
device = alcGetContextsDevice(context);
@@ -202,187 +207,141 @@ bool IsAudioDeviceReady(void)
// Module Functions Definition - Custom audio output
//----------------------------------------------------------------------------------
-// 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)
+// For streaming into mix channels.
+// The mixChannel is what audio muxing 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 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();
- else StopMusicStream();
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;x<MAX_STREAM_BUFFERS;x++)
- FillAlBufferWithSilence(ac, ac->alBuffer[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 int 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 int 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,42 @@ 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;
+ 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
+ }
+
+ if(InitMixChannel(sampleRate, mixIndex, channels, floatingPoint))
+ return mixIndex;
+ else
+ return -2; // error
+}
+
+void CloseRawAudioContext(RawAudioContext ctx)
+{
+ if(mixChannelsActive_g[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;
+}
+
+
+
//----------------------------------------------------------------------------------
@@ -767,207 +762,216 @@ 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 mixIndex;
+
+ if(currentMusic[musicIndex].stream || currentMusic[musicIndex].chipctx) return 1; // error
+
+ 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
+ }
+
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);
+ return 3; // error
}
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;
-
- currentMusic.loop = true; // We loop by default
- musicEnabled = true;
-
- // Create an audio source
- alGenSources(1, &currentMusic.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[musicIndex].loop = true; // We loop by default
+ musicEnabled_g = true;
+
- 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) * info.channels;
+ currentMusic[musicIndex].totalLengthSeconds = stb_vorbis_stream_length_in_seconds(currentMusic[musicIndex].stream);
+
+ if (info.channels == 2){
+ currentMusic[musicIndex].mixc = InitMixChannel(info.sample_rate, mixIndex, 2, false);
+ currentMusic[musicIndex].mixc->playing = true;
+ }
+ else{
+ currentMusic[musicIndex].mixc = InitMixChannel(info.sample_rate, mixIndex, 1, false);
+ currentMusic[musicIndex].mixc->playing = true;
+ }
+ if(!currentMusic[musicIndex].mixc) return 4; // error
}
}
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(&currentMusic.chipctx, currentMusic.sampleRate, fileName))
+ if(!jar_xm_create_context_from_file(&currentMusic[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);
- musicEnabled = true;
+ 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) / 48000.f;
+ musicEnabled_g = true;
- TraceLog(INFO, "[%s] XM number of samples: %i", fileName, currentMusic.totalSamplesLeft);
- TraceLog(INFO, "[%s] XM track length: %11.6f sec", fileName, currentMusic.totalLengthSeconds);
+ 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);
- // Set up OpenAL
- alGenSources(1, &currentMusic.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);
-
- // NOTE: Regularly, we must check if a buffer has been processed and refill it: UpdateMusicStream()
+ currentMusic[musicIndex].mixc = InitMixChannel(48000, mixIndex, 2, false);
+ if(!currentMusic[musicIndex].mixc) return 5; // error
+ currentMusic[musicIndex].mixc->playing = true;
}
- else TraceLog(WARNING, "[%s] XM file could not be opened", fileName);
+ else
+ {
+ TraceLog(WARNING, "[%s] XM file could not be opened", fileName);
+ return 6; // error
+ }
+ }
+ else
+ {
+ TraceLog(WARNING, "[%s] Music extension not recognized, it can't be loaded", fileName);
+ return 7; // error
}
- else TraceLog(WARNING, "[%s] Music extension not recognized, it can't be loaded", fileName);
+ return 0; // normal return
}
-// 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].mixc)
{
- alSourceStop(currentMusic.source);
- EmptyMusicStream(); // Empty music buffers
- alDeleteSources(1, &currentMusic.source);
- alDeleteBuffers(2, currentMusic.buffers);
+ CloseMixChannel(currentMusic[index].mixc);
- 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_g = false;
+ if(currentMusic[index].stream || currentMusic[index].chipctx)
+ {
+ currentMusic[index].stream = NULL;
+ currentMusic[index].chipctx = NULL;
}
}
+}
- musicEnabled = false;
+//get number of music channels active at this time, this does not mean they are playing
+int getMusicStreamCount(void)
+{
+ 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;
}
// Pause music playing
-void PauseMusicStream(void)
+void PauseMusicStream(int index)
{
// Pause music stream if music available!
- if (musicEnabled)
+ if (index < MAX_MUSIC_STREAMS && currentMusic[index].mixc && musicEnabled_g)
{
TraceLog(INFO, "Pausing music stream");
- alSourcePause(currentMusic.source);
- musicEnabled = false;
+ alSourcePause(currentMusic[index].mixc->alSource);
+ currentMusic[index].mixc->playing = 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(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].mixc->alSource);
+ currentMusic[index].mixc->playing = true;
+ }
}
}
-// Check if music is playing
-bool IsMusicPlaying(void)
+// Check if any music is playing
+bool IsMusicPlaying(int index)
{
bool playing = false;
ALint state;
-
- alGetSourcei(currentMusic.source, AL_SOURCE_STATE, &state);
- if (state == AL_PLAYING) playing = true;
+
+ if(index < MAX_MUSIC_STREAMS && currentMusic[index].mixc){
+ alGetSourcei(currentMusic[index].mixc->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(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].mixc){
+ alSourcef(currentMusic[index].mixc->alSource, AL_PITCH, 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)
- {
- 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
- }
- else
+ float secondsPlayed = 0.0f;
+ if(index < MAX_MUSIC_STREAMS && currentMusic[index].mixc)
{
- int totalSamples = stb_vorbis_stream_length_in_samples(currentMusic.stream) * currentMusic.channels;
- int samplesPlayed = totalSamples - currentMusic.totalSamplesLeft;
- secondsPlayed = (float)samplesPlayed / (currentMusic.sampleRate * currentMusic.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].mixc->channels); // Not sure if this is the correct value
+ }
+ else
+ {
+ 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].mixc->sampleRate * currentMusic[index].mixc->channels);
+ }
}
-
return secondsPlayed;
}
@@ -977,116 +981,118 @@ float GetMusicTimePlayed(void)
//----------------------------------------------------------------------------------
// Fill music buffers with new data from music stream
-static bool BufferMusicStream(ALuint buffer)
+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 bytes)
- int streamedBytes = 0; // samples of data obtained, channels are not included in calculation
+ 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 (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.
{
- if (currentMusic.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
- }
+ if(currentMusic[index].totalSamplesLeft >= MUSIC_BUFFER_SIZE_SHORT)
+ size = MUSIC_BUFFER_SIZE_SHORT / 2;
else
+ size = currentMusic[index].totalSamplesLeft / 2;
+
+ for(int x=0; x<numBuffers; x++)
{
- while (size < MUSIC_BUFFER_SIZE_SHORT)
+ jar_xm_generate_samples_16bit(currentMusic[index].chipctx, pcm, size); // reads 2*readlen shorts and moves them to buffer+size memory location
+ BufferMixChannel(currentMusic[index].mixc, pcm, size * 2);
+ currentMusic[index].totalSamplesLeft -= size * 2;
+ if(currentMusic[index].totalSamplesLeft <= 0)
{
- 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);
- else break;
+ active = false;
+ break;
}
}
- 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");
+ if(currentMusic[index].totalSamplesLeft >= MUSIC_BUFFER_SIZE_SHORT)
+ size = MUSIC_BUFFER_SIZE_SHORT;
+ else
+ size = currentMusic[index].totalSamplesLeft;
+
+ for(int x=0; x<numBuffers; x++)
+ {
+ int streamedBytes = stb_vorbis_get_samples_short_interleaved(currentMusic[index].stream, currentMusic[index].mixc->channels, 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;
+ }
+ }
}
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].mixc->alSource, AL_BUFFERS_QUEUED, &queued);
while (queued > 0)
{
- alSourceUnqueueBuffers(currentMusic.source, 1, &buffer);
+ alSourceUnqueueBuffers(currentMusic[index].mixc->alSource, 1, &buffer);
queued--;
}
}
-// Update (re-fill) music buffers if data already processed
-void UpdateMusicStream(void)
+//determine if a music stream is ready to be written to
+static int IsMusicStreamReadyForBuffering(int index)
{
- ALuint buffer = 0;
ALint processed = 0;
- bool active = true;
+ alGetSourcei(currentMusic[index].mixc->alSource, AL_BUFFERS_PROCESSED, &processed);
+ return processed;
+}
- if (musicEnabled)
+// Update (re-fill) music buffers if data already processed
+void UpdateMusicStream(int index)
+{
+ ALenum state;
+ bool active = true;
+ int numBuffers = IsMusicStreamReadyForBuffering(index);
+
+ if (currentMusic[index].mixc->playing && index < MAX_MUSIC_STREAMS && musicEnabled_g && currentMusic[index].mixc && numBuffers)
{
- // Get the number of already processed buffers (if any)
- alGetSourcei(currentMusic.source, AL_BUFFERS_PROCESSED, &processed);
-
- while (processed > 0)
+ active = BufferMusicStream(index, numBuffers);
+
+ if (!active && currentMusic[index].loop)
{
- // Recover processed buffer for refill
- alSourceUnqueueBuffers(currentMusic.source, 1, &buffer);
-
- // Refill buffer
- active = BufferMusicStream(buffer);
-
- // If no more data to stream, restart music (if loop)
- if ((!active) && (currentMusic.loop))
+ if (currentMusic[index].chipTune)
{
- if(currentMusic.chipTune)
- {
- currentMusic.totalSamplesLeft = currentMusic.totalLengthSeconds * currentMusic.sampleRate;
- }
- else
- {
- stb_vorbis_seek_start(currentMusic.stream);
- currentMusic.totalSamplesLeft = stb_vorbis_stream_length_in_samples(currentMusic.stream)*currentMusic.channels;
- }
- active = BufferMusicStream(buffer);
+ currentMusic[index].totalSamplesLeft = currentMusic[index].totalLengthSeconds * 48000;
}
-
- // Add refilled buffer to queue again... don't let the music stop!
- alSourceQueueBuffers(currentMusic.source, 1, &buffer);
-
- if (alGetError() != AL_NO_ERROR) TraceLog(WARNING, "Error buffering data...");
-
- processed--;
+ else
+ {
+ stb_vorbis_seek_start(currentMusic[index].stream);
+ currentMusic[index].totalSamplesLeft = stb_vorbis_stream_length_in_samples(currentMusic[index].stream) * currentMusic[index].mixc->channels;
+ }
+ active = true;
}
+
- ALenum state;
- alGetSourcei(currentMusic.source, AL_SOURCE_STATE, &state);
+ if (alGetError() != AL_NO_ERROR) TraceLog(WARNING, "Error buffering data...");
+
+ alGetSourcei(currentMusic[index].mixc->alSource, AL_SOURCE_STATE, &state);
- if ((state != AL_PLAYING) && active) alSourcePlay(currentMusic.source);
+ if (state != AL_PLAYING && active) alSourcePlay(currentMusic[index].mixc->alSource);
- if (!active) StopMusicStream();
+ if (!active) StopMusicStream(index);
+
}
+ else
+ return;
+
}
// Load WAV file into Wave structure
diff --git a/src/audio.h b/src/audio.h
index afd881b7..1140a60a 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)
@@ -100,15 +90,24 @@ 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)
-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
-bool IsMusicPlaying(void); // Check if music is playing
-void SetMusicVolume(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 PlayMusicStream(int musicIndex, char *fileName); // Start music playing (open stream)
+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(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);
+
+// 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/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/)
diff --git a/src/models.c b/src/models.c
index 066b919e..07dee720 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)
@@ -292,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)),
@@ -331,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)),
@@ -376,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
@@ -388,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);
@@ -398,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);
@@ -407,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);
@@ -421,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);
@@ -429,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);
@@ -490,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)
{
@@ -553,7 +563,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 +573,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 +678,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 +693,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();
@@ -691,31 +701,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)
@@ -749,6 +742,18 @@ Material LoadDefaultMaterial(void)
return material;
}
+// 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();
+
+ return material;
+}
+
+// Unload material from memory
void UnloadMaterial(Material material)
{
rlDeleteTextures(material.texDiffuse.id);
@@ -793,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
//----------------------------------------------------------
@@ -1245,42 +1250,29 @@ 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;
- 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
@@ -1425,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;
}
@@ -1440,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);
@@ -1785,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)
@@ -1850,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':
@@ -1911,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':
@@ -2031,7 +2023,7 @@ static Material LoadMTL(const char *fileName)
return material;
}
- while(!feof(mtlFile))
+ while (!feof(mtlFile))
{
fgets(buffer, MAX_BUFFER_SIZE, mtlFile);
@@ -2086,7 +2078,10 @@ 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);
+ int shininess = 0;
+ sscanf(buffer, "Ns %i", &shininess);
+
+ material.glossiness = (float)shininess;
}
else if (buffer[1] == 'i') // Ni int Refraction index.
{
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/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
}
diff --git a/src/raylib.h b/src/raylib.h
index 911fd8b5..9cd02fd8 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)
@@ -418,18 +418,36 @@ 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;
-// 3d Model type
-// TODO: Replace shader/testure by material
+// 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
+typedef struct LightData {
+ int id;
+ int type; // LIGHT_POINT, LIGHT_DIRECTIONAL, LIGHT_SPOT
+ bool enabled;
+
+ Vector3 position;
+ 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
+ float intensity;
+
+ float coneAngle; // Spot light max angle
+} LightData, *Light;
+
+// Light types
+typedef enum { LIGHT_POINT, LIGHT_DIRECTIONAL, LIGHT_SPOT } LightType;
+
// Ray type (useful for raycast)
typedef struct Ray {
Vector3 position;
@@ -451,10 +469,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
@@ -539,13 +554,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
@@ -787,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
@@ -806,7 +822,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)
@@ -815,6 +831,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)
@@ -844,6 +861,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
@@ -853,6 +871,10 @@ 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
+
//----------------------------------------------------------------------------------
// Physics System Functions (Module: physac)
//----------------------------------------------------------------------------------
@@ -860,14 +882,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)
@@ -876,13 +898,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)
@@ -894,15 +909,24 @@ 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)
-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
-bool IsMusicPlaying(void); // Check if music is playing
-void SetMusicVolume(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 PlayMusicStream(int musicIndex, char *fileName); // Start music playing (open stream)
+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(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);
+
+// 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/rlgl.c b/src/rlgl.c
index 0c0da221..85c0cae2 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
@@ -189,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
@@ -199,6 +202,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,14 +234,18 @@ 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
+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
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
@@ -740,6 +751,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)
{
@@ -991,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)
@@ -1019,6 +1049,7 @@ void rlglClose(void)
{
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
UnloadDefaultShader();
+ UnloadStandardShader();
UnloadDefaultBuffers();
// Delete default white texture
@@ -1033,175 +1064,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)
+/*
+ for (int i = 0; i < modelsCount; i++)
{
- glActiveTexture(GL_TEXTURE2);
- glBindTexture(GL_TEXTURE_2D, 0);
+ rlglDrawMesh(models[i]->mesh, models[i]->material, models[i]->transform);
}
-
- 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
-
-#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
}
@@ -1526,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;
@@ -1642,7 +1513,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
@@ -1652,6 +1523,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)
@@ -1669,14 +1543,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);
@@ -1685,7 +1559,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);
}
@@ -1701,7 +1575,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);
}
@@ -1717,7 +1591,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);
}
@@ -1733,7 +1607,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);
}
@@ -1776,6 +1650,270 @@ 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));
+
+ // 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));
+
+ // 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);
+
+ // 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);
+
+ // Upload to shader glossiness
+ glUniform1f(glGetUniformLocation(material.shader.id, "glossiness"), material.glossiness);
+ }
+
+ // 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
+
+ // TODO: Upload to shader normalDepth
+ //glUniform1f(???, material.normalDepth);
+ }
+
+ 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)
{
@@ -2022,6 +2160,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)
{
@@ -2098,6 +2247,78 @@ void SetBlendMode(int mode)
}
}
+// Create a new light, initialize it and add to pool
+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->target = (Vector3){ 0.0f, 0.0f, 0.0f };
+ light->intensity = 1.0f;
+ light->diffuse = diffuse;
+
+ // Add new light to the array
+ lights[lightsCount] = light;
+
+ // Increase enabled lights count
+ lightsCount++;
+
+ 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));
+ } break;
+ 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)
+{
+ // 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
//----------------------------------------------------------------------------------
@@ -2293,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";
@@ -2315,6 +2536,24 @@ static Shader LoadDefaultShader(void)
return shader;
}
+// Load standard shader
+// NOTE: This shader supports:
+// - Up to 3 different maps: diffuse, normal, specular
+// - Material properties: colAmbient, colDiffuse, colSpecular, glossiness, normalDepth
+// - Up to 8 lights: Point, Directional or Spot
+static Shader LoadStandardShader(void)
+{
+ // 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);
+
+ 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)
@@ -2328,18 +2567,18 @@ 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");
// 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");
@@ -2350,13 +2589,26 @@ 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
+ //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)
{
@@ -2800,6 +3052,79 @@ 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
+static void SetShaderLights(Shader shader)
+{
+ int locPoint = glGetUniformLocation(shader.id, "lightsCount");
+ glUniform1i(locPoint, lightsCount);
+
+ char locName[32] = "lights[x].position\0";
+
+ for (int i = 0; i < lightsCount; i++)
+ {
+ locName[7] = '0' + i;
+
+ memcpy(&locName[10], "enabled\0", strlen("enabled\0") + 1);
+ locPoint = GetShaderLocation(shader, locName);
+ glUniform1i(locPoint, lights[i]->enabled);
+
+ 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);
+
+ 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
+ }
+}
+
// Read text data from file
// NOTE: text chars array should be freed manually
static char *ReadTextFile(const char *fileName)
@@ -2970,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;
diff --git a/src/rlgl.h b/src/rlgl.h
index 7b88bc9e..0765a8a7 100644
--- a/src/rlgl.h
+++ b/src/rlgl.h
@@ -196,19 +196,35 @@ 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
+ typedef struct LightData {
+ int id;
+ int type; // LIGHT_POINT, LIGHT_DIRECTIONAL, LIGHT_SPOT
+ bool enabled;
+
+ Vector3 position;
+ 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
+ float intensity;
+
+ float coneAngle; // Spot light max angle
+ } LightData, *Light;
// Color blending modes (pre-defined)
typedef enum { BLEND_ALPHA = 0, BLEND_ADDITIVE, BLEND_MULTIPLIED } BlendMode;
@@ -256,6 +272,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 +295,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, 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
Vector3 rlglUnproject(Vector3 source, Matrix proj, Matrix view); // Get world coordinates from screen coordinates
@@ -306,6 +327,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
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 };
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