raylib 1.1

View CHANGELOG for a detailed list of changes
This commit is contained in:
raysan5 2014-04-19 16:36:49 +02:00
parent 650a8f7f15
commit f06a15ac8b
17 changed files with 1573 additions and 1114 deletions

View File

@ -1,11 +1,44 @@
changelog
---------
Current Release: raylib 1.0.6 (March 2014)
Current Release: raylib 1.1.0 (April 2014)
NOTE: Only versions marked as 'Release' are available on release folder, updates are only available as source.
NOTE: Current Release includes all previous updates.
-----------------------------------------------
Release: raylib 1.1.0 (19 April 2014)
-----------------------------------------------
NOTE:
This version supposed a complete internal redesign of the library to support OpenGL 3.3+ and OpenGL ES 2.0.
New module [rlgl] has been added to 'translate' immediate mode style functions (i.e. rlVertex3f()) to GL 1.1, 3.3+ or ES2.
Another new module [raymath] has also been added with lot of useful 3D math vector-matrix-quaternion functions.
[rlgl] New module, abstracts OpenGL rendering (multiple versions support)
[raymath] New module, useful 3D math vector-matrix-quaternion functions
[core] Adapt all OpenGL code (initialization, drawing) to use [rlgl]
[shapes] Rewrite all shapes drawing functions to use [rlgl]
[textures] Adapt texture GPU loading to use [rlgl]
[textures] Added support for DDS images (compressed and uncompressed)
[textures] CreateTexture() - Redesigned to add mipmap automatic generation
[textures] DrawTexturePro() - Redesigned and corrected bugs
[models] Rewrite all 3d-shapes drawing functions to use [rlgl]
[models] Adapt model loading and drawing to use [rlgl]
[models] Model struct updated to include texture id
[models] SetModelTexture() - Added, link a texture to a model
[models] DrawModelEx() - Redesigned with extended parameters
[audio] Added music streaming support (OGG files)
[audio] Added support for OGG files as Sound
[audio] PlayMusicStream() - Added, open a new music stream and play it
[audio] StopMusicStream() - Added, stop music stream playing and close stream
[audio] PauseMusicStream() - Added, pause music stream playing
[audio] MusicIsPlaying() - Added, to check if music is playing
[audio] SetMusicVolume() - Added, set volume for music
[audio] GetMusicTimeLength() - Added, get current music time length (in seconds)
[audio] GetMusicTimePlayed() - Added, get current music time played (in seconds)
[utils] Added log tracing functionality - TraceLog(), TraceLogOpen(), TraceLogClose()
[*] Log tracing messages all around the code
-----------------------------------------------
Release: raylib 1.0.6 (16 March 2014)
-----------------------------------------------

View File

@ -10,7 +10,7 @@ The following help is highly appreciated:
- Translators / Localizators - Can you translate raylib to another language?
- Documentation / Tutorials / Example writters - Can you write some tutorial / example?
- Web Development - Can you help with the web? Can you setup a forum?
- Porting to Linux and OSX - Can you compile and test raylib on another OS?
- Porting to Linux, OSX... - Can you compile and test raylib on another OS?
- Testers of current features and multiple systems - Can you find some bug on raylib?
If you can not help on any of the above points but you still want to contribute in some way... please, consider helping
@ -34,4 +34,4 @@ contact
* Facebook: [http://www.facebook.com/raylibgames](http://www.facebook.com/raylibgames)
[raysan5]: mailto:raysan@raysanweb.com "Ramon Santamaria - Ray San"
[raysan5]: mailto:raysan5@gmail.com "Ramon Santamaria - Ray San"

View File

@ -23,29 +23,51 @@ a simple PONG and some of them even a BREAKOUT!
But WinBGI was not the clearer and most organized lib. There were a lot of things I found useless and
confusing and some function names were not clear enough for most of the students; not to mention points
like no transparencies support or no hardware acceleration.
like no transparencies support or no hardware acceleration.
So, I decided to create my own lib, hardware accelerated, clear function names, quite organized, well structured,
plain C coding and, the most important, primarily intended to LEARN videogames programming.
I've coded quite a lot in C# and XNA and I really love it (in fact, my students learn C# with XNA after C),
I've coded quite a lot in C# and XNA and I really love it (in fact, my students learn C# after C),
so, I decided to use C# language notation and XNA naming conventions. That way, students can jump from
raylib to XNA (or MonoGame) extremely easily.
raylib to XNA, MonoGame or similar libs extremely easily.
raylib started as a weekend project and after three months of hard work, here it is the first version.
raylib started as a weekend project and after three months of hard work, first version was published.
Enjoy it.
notes on raylib 1.1
-------------------
On April 2014, after 6 month of first raylib release, raybil 1.1 has been released. This new version presents a
complete internal redesign of the library to support OpenGL 1.1, OpenGL 3.3+ and OpenGL ES 2.0.
A new module named [rlgl] (https://github.com/raysan5/raylib/blob/master/src/rlgl.h) has been added to the library. This new module translate raylib-OpenGL-style
immediate mode functions (i.e. rlVertex3f(), rlBegin(), ...) to different versions of OpenGL (1.1, 3.3+, ES2), selectable by one define.
[rlgl] (https://github.com/raysan5/raylib/blob/master/src/rlgl.h) also comes with a second new module named [raymath] (https://github.com/raysan5/raylib/blob/master/src/raymath.h), which includes
a bunch of useful functions for 3d-math with vectors, matrices and quaternions.
Some other big changes of this new version have been the support for OGG files loading and stream playing, and the
support of DDS texture files (compressed and uncompressed) along with mipmaps support.
Lots of code changes and lot of testing have concluded in this amazing new raylib 1.1.
Enjoy it.
features
--------
* Written in plain C code (C99)
* Uses C# PascalCase/camelCase notation
* Hardware accelerated using OpenGL 1.1
* Transparencies support (RGBA Colors)
* Custom color palette for better use on white background
* Basic 3D Support (camera, basic models, OBJ models, etc)
* Powerful Text module with SpriteFonts support
* Written in plain C code (C99)
* Uses C# PascalCase/camelCase notation
* Hardware accelerated with OpenGL (1.1, 3.3+ or ES2)
* Unique OpenGL abstraction layer [rlgl]
* Powerful fonts module with SpriteFonts support
* Multiple textures support, including DDS and mipmaps generation
* Basic 3d support for Shapes, Models, Heightmaps and Billboards
* Powerful math module for Vector and Matrix operations [raymath]
* Audio loading and playing with streaming support
* Custom color palette for fancy visuals on raywhite background
raylib uses on its core module the outstanding [GLFW3] (http://www.glfw.org/) library. The best option by far I found for
window/context and input management (clean, focused, great license, well documented, modern, ...).
@ -75,19 +97,30 @@ raylib could be build with the following command lines (Using GCC compiler):
gcc -c core.c -std=c99 -Wall
gcc -c shapes.c -std=c99 -Wall
gcc -c textures.c -std=c99 -Wall
gcc -c stb_image.c -std=c99 -Wall
gcc -c text.c -std=c99 -Wall
gcc -c models.c -std=c99 -Wall
gcc -c vector3.c -std=c99 -Wall
gcc -c raymath.c -std=c99 -Wall
gcc -c rlgl.c -std=c99 -Wall
gcc -c audio.c -std=c99 -Wall
gcc -c utils.c -std=c99 -Wall
ar rcs raylib.a core.o shapes.o textures.o stb_image.o text.o models.o vector3.o utils.o audio.o
gcc -c stb_image.c -std=c99 -Wall
gcc -c stb_vorbis.c -std=c99 -Wall
ar rcs libraylib.a core.o shapes.o textures.o stb_image.o text.o models.o raymath.o rlgl.o utils.o stb_vorbis.o audio.o
To compile examples, make sure raylib.h is placed in include path and libraries raylib (libraylib.a) and glfw3 (libglfw3.a)
are placed in the libraries path. It's also recommended to link with file icon.o for fancy raylib icon usage.
To compile examples, make sure raylib.h is placed in the include path and the following libraries are placed in the libraries path:
libraylib.a - raylib
libglfw3.a - GLFW3 (static version)
libglew32.a - GLEW, OpenGL extension loading, only required if using OpenGL 3.3+ or ES2
libopenal32.a - OpenAL, audio device management
It's also recommended to link with file icon.o for fancy raylib icon usage. Linking command:
cd raylib/examples
gcc -o test_code.exe test_code.c icon.o -lraylib -lglfw3 -lopengl32 -lgdi32 -std=c99 -Wl,--subsystem,windows
gcc -o test_code.exe test_code.c icon.o -lraylib -lglfw3 -lglew32 -lopenal32 -lopengl32 -lgdi32 -std=c99 -Wl,--subsystem,windows
If you have any doubt, [let me know][raysan5].
contact
-------
@ -109,4 +142,4 @@ The following people have contributed in some way to make raylib project a reali
- [Elendow](http://www.elendow.com)
[raysan5]: mailto:raysan@raysanweb.com "Ramon Santamaria - Ray San"
[raysan5]: mailto:raysan5@gmail.com "Ramon Santamaria - Ray San"

View File

@ -1,7 +1,7 @@
roadmap
-------
First version of raylib is quite complete and functional but there is still a lot of things I would like to improve.
Current version of raylib is quite complete and functional but there is still a lot of things I would like to improve.
Here it is a list of features I would like to add and functions to improve.
Around the source code there are some TODO points with pending revisions/bugs and here it is a list of features I would like to add.
@ -10,13 +10,13 @@ raylib v1.x
- [DONE] Review Billboard Drawing functions
- [DONE] Review Heightmap Loading and Drawing functions - Load Heightmap directly as a Model
- Lighting support (only 3d mode) - CreateLight()
- Lighting support (only 3d mode)
- [DONE] Simple Collision Detection functions
- Default scene Camera controls (zoom, pan, rotate)
- Basic Procedural Texture / Image generation (Gradient, Checked, Spot, Noise, Cellular)
- Software mipmapping generation and POT conversion (custom implementation)
- Comments / Functions translation (?)
- Basic Procedural Image Generation (Gradient, Checked, Spot, Noise, Cellular)
- [DONE] Software mipmapping generation and POT conversion (custom implementation)
- TTF fonts support
Any feature missing? Do you have a request? [Let me know!][raysan5]
[raysan5]: mailto:raysan@raysanweb.com "Ramon Santamaria - Ray San"
[raysan5]: mailto:raysan5@gmail.com "Ramon Santamaria - Ray San"

View File

@ -6,7 +6,7 @@
*
* Uses external lib:
* OpenAL - Audio device management lib
* TODO: stb_vorbis - Ogg audio files loading
* stb_vorbis - Ogg audio files loading
*
* Copyright (c) 2013 Ramon Santamaria (Ray San - raysan@raysanweb.com)
*
@ -32,50 +32,45 @@
#include <AL/al.h> // OpenAL basic header
#include <AL/alc.h> // OpenAL context header (like OpenGL, OpenAL requires a context to work)
#include <stdlib.h> // To use exit() function
#include <stdlib.h> // Declares malloc() and free() for memory management
#include <string.h> // Required for strcmp()
#include <stdio.h> // Used for .WAV loading
#include "utils.h" // rRES data decompression utility function
//#include "stb_vorbis.h" // OGG loading functions
#include "stb_vorbis.h" // OGG loading functions
//----------------------------------------------------------------------------------
// Defines and Macros
//----------------------------------------------------------------------------------
// Nop...
#define MUSIC_STREAM_BUFFERS 2
#define MUSIC_BUFFER_SIZE 4096*8 //4096*32
//----------------------------------------------------------------------------------
// Types and Structures Definition
//----------------------------------------------------------------------------------
// Sound source type (all file loaded in memory)
/*
struct Sound {
unsigned int source;
unsigned int buffer;
};
// Music type (file streamming from memory)
// NOTE: Anything longer than ~10 seconds should be Music...
struct Music {
stb_vorbis* stream;
stb_vorbis_info info;
// Music type (file streaming from memory)
// NOTE: Anything longer than ~10 seconds should be streamed...
typedef struct Music {
stb_vorbis *stream;
ALuint id;
ALuint buffers[2];
ALuint buffers[MUSIC_STREAM_BUFFERS];
ALuint source;
ALenum format;
int bufferSize;
int channels;
int sampleRate;
int totalSamplesLeft;
bool loop;
};
*/
} Music;
// Wave file data
typedef struct Wave {
unsigned char *data; // Buffer data pointer
void *data; // Buffer data pointer
unsigned int dataSize; // Data size in bytes
unsigned int sampleRate;
unsigned int dataSize;
short bitsPerSample;
short channels;
} Wave;
@ -83,22 +78,23 @@ typedef struct Wave {
//----------------------------------------------------------------------------------
// Global Variables Definition
//----------------------------------------------------------------------------------
static bool musicIsPlaying;
static Music *currentMusic;
bool musicEnabled = false;
static Music currentMusic; // Current music loaded
// NOTE: Only one music file playing at a time
//----------------------------------------------------------------------------------
// Module specific Functions Declaration
//----------------------------------------------------------------------------------
static Wave LoadWAV(char *fileName);
static void UnloadWAV(Wave wave);
//static Ogg LoadOGG(char *fileName);
static bool MusicStream(Music music, ALuint buffer);
static Wave LoadWAV(const char *fileName);
static Wave LoadOGG(char *fileName);
static void UnloadWave(Wave wave);
extern bool MusicStreamUpdate();
extern void PlayCurrentMusic();
static bool BufferMusicStream(ALuint buffer); // Fill music buffers with data
static void EmptyMusicStream(); // Empty music buffers
extern void UpdateMusicStream(); // Updates buffers (refill) for music streaming
//----------------------------------------------------------------------------------
// Module Functions Definition - Window and OpenGL Context Functions
// Module Functions Definition - Audio Device initialization and Closing
//----------------------------------------------------------------------------------
// Initialize audio device and context
@ -126,13 +122,13 @@ void InitAudioDevice()
alListener3f(AL_POSITION, 0, 0, 0);
alListener3f(AL_VELOCITY, 0, 0, 0);
alListener3f(AL_ORIENTATION, 0, 0, -1);
musicIsPlaying = false;
}
// Close the audio device for the current context, and destroys the context
void CloseAudioDevice()
{
StopMusicStream(); // Stop music streaming and close current stream
ALCdevice *device;
ALCcontext *context = alcGetCurrentContext();
@ -145,61 +141,71 @@ void CloseAudioDevice()
alcCloseDevice(device);
}
//----------------------------------------------------------------------------------
// Module Functions Definition - Sounds loading and playing (.WAV)
//----------------------------------------------------------------------------------
// Load sound to memory
Sound LoadSound(char *fileName)
{
Sound sound;
Wave wave;
// NOTE: The entire file is loaded to memory to play it all at once (no-streaming)
// WAV file loading
// NOTE: Buffer space is allocated inside LoadWAV, Wave must be freed
Wave wave = LoadWAV(fileName);
// Audio file loading
// NOTE: Buffer space is allocated inside function, Wave must be freed
ALenum format = 0;
// The OpenAL format is worked out by looking at the number of channels and the bits per sample
if (wave.channels == 1)
if (strcmp(GetExtension(fileName),"wav") == 0) wave = LoadWAV(fileName);
else if (strcmp(GetExtension(fileName),"ogg") == 0) wave = LoadOGG(fileName);
else TraceLog(WARNING, "[%s] Sound extension not recognized, it can't be loaded", fileName);
if (wave.data != NULL)
{
if (wave.bitsPerSample == 8 ) format = AL_FORMAT_MONO8;
else if (wave.bitsPerSample == 16) format = AL_FORMAT_MONO16;
}
else if (wave.channels == 2)
{
if (wave.bitsPerSample == 8 ) format = AL_FORMAT_STEREO8;
else if (wave.bitsPerSample == 16) format = AL_FORMAT_STEREO16;
ALenum format = 0;
// The OpenAL format is worked out by looking at the number of channels and the bits per sample
if (wave.channels == 1)
{
if (wave.bitsPerSample == 8 ) format = AL_FORMAT_MONO8;
else if (wave.bitsPerSample == 16) format = AL_FORMAT_MONO16;
}
else if (wave.channels == 2)
{
if (wave.bitsPerSample == 8 ) format = AL_FORMAT_STEREO8;
else if (wave.bitsPerSample == 16) format = AL_FORMAT_STEREO16;
}
// Create an audio source
ALuint source;
alGenSources(1, &source); // Generate pointer to audio source
alSourcef(source, AL_PITCH, 1);
alSourcef(source, AL_GAIN, 1);
alSource3f(source, AL_POSITION, 0, 0, 0);
alSource3f(source, AL_VELOCITY, 0, 0, 0);
alSourcei(source, AL_LOOPING, AL_FALSE);
// Convert loaded data to OpenAL buffer
//----------------------------------------
ALuint buffer;
alGenBuffers(1, &buffer); // Generate pointer to buffer
// Upload sound data to buffer
alBufferData(buffer, format, wave.data, wave.dataSize, wave.sampleRate);
// Attach sound buffer to source
alSourcei(source, AL_BUFFER, buffer);
// Unallocate WAV data
UnloadWave(wave);
TraceLog(INFO, "[%s] Sound file loaded successfully", fileName);
TraceLog(INFO, "[%s] Sample rate: %i - Channels: %i", fileName, wave.sampleRate, wave.channels);
sound.source = source;
sound.buffer = buffer;
}
// Create an audio source
ALuint source;
alGenSources(1, &source); // Generate pointer to audio source
alSourcef(source, AL_PITCH, 1);
alSourcef(source, AL_GAIN, 1);
alSource3f(source, AL_POSITION, 0, 0, 0);
alSource3f(source, AL_VELOCITY, 0, 0, 0);
alSourcei(source, AL_LOOPING, AL_FALSE);
// Convert loaded data to OpenAL buffer
//----------------------------------------
ALuint buffer;
alGenBuffers(1, &buffer); // Generate pointer to buffer
// Upload sound data to buffer
alBufferData(buffer, format, (void*)wave.data, wave.dataSize, wave.sampleRate);
// Attach sound buffer to source
alSourcei(source, AL_BUFFER, buffer);
// Unallocate WAV data
UnloadWAV(wave);
TraceLog(INFO, "[%s] Sound file loaded successfully", fileName);
TraceLog(INFO, "[%s] Sample rate: %i - Channels: %i", fileName, wave.sampleRate, wave.channels);
sound.source = source;
sound.buffer = buffer;
return sound;
}
@ -314,7 +320,7 @@ Sound LoadSoundFromRES(const char *rresName, int resId)
alSourcei(source, AL_BUFFER, buffer);
// Unallocate WAV data
UnloadWAV(wave);
UnloadWave(wave);
TraceLog(INFO, "[%s] Sound loaded successfully from resource, sample rate: %i", rresName, (int)sampleRate);
@ -381,22 +387,6 @@ void PlaySound(Sound sound)
//alGetSourcef(sound.source, AL_SEC_OFFSET, &result); // AL_SAMPLE_OFFSET
}
// Play a sound with extended options
// TODO: This function should be reviewed...
void PlaySoundEx(Sound sound, float timePosition, bool loop)
{
// TODO: Review
// Change the current position (e.g. skip some part of the sound)
// NOTE: Only work when the entire file is in a single buffer
//alSourcei(sound.source, AL_BYTE_OFFSET, int(position * sampleRate));
alSourcePlay(sound.source); // Play the sound
if (loop) alSourcei(sound.source, AL_LOOPING, AL_TRUE);
else alSourcei(sound.source, AL_LOOPING, AL_FALSE);
}
// Pause a sound
void PauseSound(Sound sound)
{
@ -421,30 +411,250 @@ bool SoundIsPlaying(Sound sound)
return playing;
}
// Check if music is playing
bool MusicIsPlaying(Music music)
{
ALenum state;
alGetSourcei(music.source, AL_SOURCE_STATE, &state);
return (state == AL_PLAYING);
}
// Set volume for a sound
void SetVolume(Sound sound, float volume)
void SetSoundVolume(Sound sound, float volume)
{
alSourcef(sound.source, AL_GAIN, volume);
}
// Set pitch for a sound
void SetPitch(Sound sound, float pitch)
void SetSoundPitch(Sound sound, float pitch)
{
alSourcef(sound.source, AL_PITCH, pitch);
}
//----------------------------------------------------------------------------------
// Module Functions Definition - Music loading and stream playing (.OGG)
//----------------------------------------------------------------------------------
// Start music playing (open stream)
void PlayMusicStream(char *fileName)
{
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);
if (currentMusic.stream == NULL) TraceLog(WARNING, "[%s] Could not open ogg audio file", fileName);
else
{
// Get file info
stb_vorbis_info info = stb_vorbis_get_info(currentMusic.stream);
currentMusic.channels = info.channels;
currentMusic.sampleRate = info.sample_rate;
TraceLog(INFO, "[%s] Ogg sample rate: %i", fileName, info.sample_rate);
TraceLog(INFO, "[%s] Ogg channels: %i", fileName, info.channels);
TraceLog(INFO, "[%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: MusicStreamUpdate()
currentMusic.totalSamplesLeft = stb_vorbis_stream_length_in_samples(currentMusic.stream) * currentMusic.channels;
}
}
else TraceLog(WARNING, "[%s] Music extension not recognized, it can't be loaded", fileName);
}
// Stop music playing (close stream)
void StopMusicStream()
{
if (musicEnabled)
{
alSourceStop(currentMusic.source);
EmptyMusicStream(); // Empty music buffers
alDeleteSources(1, &currentMusic.source);
alDeleteBuffers(2, currentMusic.buffers);
stb_vorbis_close(currentMusic.stream);
}
musicEnabled = false;
}
// Pause music playing
void PauseMusicStream()
{
// TODO: Record music is paused or check if music available!
alSourcePause(currentMusic.source);
}
// Check if music is playing
bool MusicIsPlaying()
{
ALenum state;
alGetSourcei(currentMusic.source, AL_SOURCE_STATE, &state);
return (state == AL_PLAYING);
}
// Set volume for music
void SetMusicVolume(float volume)
{
alSourcef(currentMusic.source, AL_GAIN, volume);
}
// Get current music time length (in seconds)
float GetMusicTimeLength()
{
float totalSeconds = stb_vorbis_stream_length_in_seconds(currentMusic.stream);
return totalSeconds;
}
// Get current music time played (in seconds)
float GetMusicTimePlayed()
{
int totalSamples = stb_vorbis_stream_length_in_samples(currentMusic.stream) * currentMusic.channels;
int samplesPlayed = totalSamples - currentMusic.totalSamplesLeft;
float secondsPlayed = (float)samplesPlayed / (currentMusic.sampleRate * currentMusic.channels);
return secondsPlayed;
}
//----------------------------------------------------------------------------------
// Module specific Functions Definition
//----------------------------------------------------------------------------------
// Fill music buffers with new data from music stream
static bool BufferMusicStream(ALuint buffer)
{
short pcm[MUSIC_BUFFER_SIZE];
int size = 0; // Total size of data steamed (in bytes)
int streamedBytes = 0; // Bytes of data obtained in one samples get
bool active = true; // We can get more data from stream (not finished)
if (musicEnabled)
{
while (size < MUSIC_BUFFER_SIZE)
{
streamedBytes = stb_vorbis_get_samples_short_interleaved(currentMusic.stream, currentMusic.channels, pcm + size, MUSIC_BUFFER_SIZE - size);
if (streamedBytes > 0) size += (streamedBytes*currentMusic.channels);
else 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;
}
else
{
active = false;
TraceLog(WARNING, "No more data obtained from stream");
}
return active;
}
// Empty music buffers
static void EmptyMusicStream()
{
ALuint buffer = 0;
int queued = 0;
alGetSourcei(currentMusic.source, AL_BUFFERS_QUEUED, &queued);
while(queued > 0)
{
alSourceUnqueueBuffers(currentMusic.source, 1, &buffer);
queued--;
}
}
// Update (re-fill) music buffers if data already processed
extern void UpdateMusicStream()
{
ALuint buffer = 0;
ALint processed = 0;
bool active = true;
if (musicEnabled)
{
// Get the number of already processed buffers (if any)
alGetSourcei(currentMusic.source, AL_BUFFERS_PROCESSED, &processed);
while (processed > 0)
{
// 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.loop)
{
stb_vorbis_seek_start(currentMusic.stream);
currentMusic.totalSamplesLeft = stb_vorbis_stream_length_in_samples(currentMusic.stream) * currentMusic.channels;
active = BufferMusicStream(buffer);
}
}
// Add refilled buffer to queue again... don't let the music stop!
alSourceQueueBuffers(currentMusic.source, 1, &buffer);
if(alGetError() != AL_NO_ERROR) TraceLog(WARNING, "Ogg playing, error buffering data...");
processed--;
}
ALenum state;
alGetSourcei(currentMusic.source, AL_SOURCE_STATE, &state);
if ((state != AL_PLAYING) && active) alSourcePlay(currentMusic.source);
if (!active) StopMusicStream();
}
}
// Load WAV file into Wave structure
static Wave LoadWAV(char *fileName)
static Wave LoadWAV(const char *fileName)
{
// Basic WAV headers structs
typedef struct {
@ -543,199 +753,51 @@ static Wave LoadWAV(char *fileName)
return wave;
}
// Unload WAV file data
static void UnloadWAV(Wave wave)
// Load OGG file into Wave structure
static Wave LoadOGG(char *fileName)
{
Wave wave;
stb_vorbis *oggFile = stb_vorbis_open_filename(fileName, NULL, NULL);
stb_vorbis_info info = stb_vorbis_get_info(oggFile);
wave.sampleRate = info.sample_rate;
wave.bitsPerSample = 16;
wave.channels = info.channels;
TraceLog(DEBUG, "[%s] Ogg sample rate: %i", fileName, info.sample_rate);
TraceLog(DEBUG, "[%s] Ogg channels: %i", fileName, info.channels);
int totalSamplesLength = (stb_vorbis_stream_length_in_samples(oggFile) * info.channels);
wave.dataSize = totalSamplesLength*sizeof(short); // Size must be in bytes
TraceLog(DEBUG, "[%s] Samples length: %i", fileName, totalSamplesLength);
float totalSeconds = stb_vorbis_stream_length_in_seconds(oggFile);
TraceLog(DEBUG, "[%s] Total seconds: %f", fileName, totalSeconds);
if (totalSeconds > 10) TraceLog(WARNING, "[%s] Ogg audio lenght is larger than 10 seconds (%f), that's a big file in memory, consider music streaming", fileName, totalSeconds);
int totalSamples = totalSeconds*info.sample_rate*info.channels;
TraceLog(DEBUG, "[%s] Total samples calculated: %i", fileName, totalSamples);
//short *data
wave.data = malloc(sizeof(short)*totalSamplesLength);
int samplesObtained = stb_vorbis_get_samples_short_interleaved(oggFile, info.channels, wave.data, totalSamplesLength);
TraceLog(DEBUG, "[%s] Samples obtained: %i", fileName, samplesObtained);
stb_vorbis_close(oggFile);
return wave;
}
// Unload Wave data
static void UnloadWave(Wave wave)
{
free(wave.data);
}
// TODO: Ogg data loading
Music LoadMusic(char *fileName)
{
Music music;
// Open audio stream
music.stream = stb_vorbis_open_filename(fileName, NULL, NULL);
if (music.stream == NULL) TraceLog(WARNING, "Could not open ogg audio file");
else
{
// Get file info
music.info = stb_vorbis_get_info(music.stream);
printf("Ogg sample rate: %i\n", music.info.sample_rate);
printf("Ogg channels: %i\n", music.info.channels);
printf("Temp memory required: %i\n", music.info.temp_memory_required);
if (music.info.channels == 2) music.format = AL_FORMAT_STEREO16;
else music.format = AL_FORMAT_MONO16;
music.bufferSize = 4096*8;
music.loop = true; // We loop by default
// Create an audio source
alGenSources(1, &music.source); // Generate pointer to audio source
alSourcef(music.source, AL_PITCH, 1);
alSourcef(music.source, AL_GAIN, 1);
alSource3f(music.source, AL_POSITION, 0, 0, 0);
alSource3f(music.source, AL_VELOCITY, 0, 0, 0);
alSourcei(music.source, AL_LOOPING, AL_TRUE); // We loop by default
// Convert loaded data to OpenAL buffers
alGenBuffers(2, music.buffers);
/*
if (!MusicStream(music, music.buffers[0])) exit(1);
if (!MusicStream(music, music.buffers[1])) exit(1);
alSourceQueueBuffers(music.source, 2, music.buffers);
PlayMusic(music);
*/
music.totalSamplesLeft = stb_vorbis_stream_length_in_samples(music.stream) * music.info.channels;
currentMusic = &music;
}
return music;
}
void UnloadMusic(Music music)
{
StopMusic(music);
alDeleteSources(1, &music.source);
alDeleteBuffers(2, music.buffers);
stb_vorbis_close(music.stream);
}
void PlayMusic(Music music)
{
//if (MusicIsPlaying(music)) return true;
if (!MusicStream(music, music.buffers[0])) TraceLog(WARNING, "MusicStream returned 0");
if (!MusicStream(music, music.buffers[1])) TraceLog(WARNING, "MusicStream returned 0");
alSourceQueueBuffers(music.source, 2, music.buffers);
alSourcePlay(music.source);
TraceLog(INFO, "Playing music");
}
extern void PlayCurrentMusic()
{
if (!MusicStream(*currentMusic, currentMusic->buffers[0])) TraceLog(WARNING, "MusicStream returned 0");
if (!MusicStream(*currentMusic, currentMusic->buffers[1])) TraceLog(WARNING, "MusicStream returned 0");
alSourceQueueBuffers(currentMusic->source, 2, currentMusic->buffers);
alSourcePlay(currentMusic->source);
}
// Stop reproducing music
void StopMusic(Music music)
{
alSourceStop(music.source);
musicIsPlaying = false;
}
static bool MusicStream(Music music, ALuint buffer)
{
//Uncomment this to avoid VLAs
//#define BUFFER_SIZE 4096*32
#ifndef BUFFER_SIZE//VLAs ftw
#define BUFFER_SIZE (music.bufferSize)
#endif
ALshort pcm[BUFFER_SIZE];
int size = 0;
int result = 0;
while (size < BUFFER_SIZE)
{
result = stb_vorbis_get_samples_short_interleaved(music.stream, music.info.channels, pcm+size, BUFFER_SIZE-size);
if (result > 0) size += (result*music.info.channels);
else break;
}
if (size == 0) return false;
alBufferData(buffer, music.format, pcm, size*sizeof(ALshort), music.info.sample_rate);
music.totalSamplesLeft -= size;
#undef BUFFER_SIZE
return true;
}
/*
extern bool MusicStreamUpdate()
{
ALint processed = 0;
alGetSourcei(currentMusic->source, AL_BUFFERS_PROCESSED, &processed);
while (processed--)
{
ALuint buffer = 0;
alSourceUnqueueBuffers(currentMusic->source, 1, &buffer);
if (!MusicStream(*currentMusic, buffer))
{
bool shouldExit = true;
if (currentMusic->loop)
{
stb_vorbis_seek_start(currentMusic->stream);
currentMusic->totalSamplesLeft = stb_vorbis_stream_length_in_samples(currentMusic->stream) * currentMusic->info.channels;
shouldExit = !MusicStream(*currentMusic, buffer);
}
if (shouldExit) return false;
}
alSourceQueueBuffers(currentMusic->source, 1, &buffer);
}
return true;
}
*/
extern bool MusicStreamUpdate()
{
int processed;
bool active = true;
alGetSourcei(currentMusic->source, AL_BUFFERS_PROCESSED, &processed);
printf("Data processed: %i\n", processed);
while (processed--)
{
ALuint buffer = 0;
alSourceUnqueueBuffers(currentMusic->source, 1, &buffer);
active = MusicStream(*currentMusic, buffer);
alSourceQueueBuffers(currentMusic->source, 1, &buffer);
}
return active;
}
void MusicStreamEmpty()
{
int queued;
alGetSourcei(currentMusic->source, AL_BUFFERS_QUEUED, &queued);
while(queued--)
{
ALuint buffer;
alSourceUnqueueBuffers(currentMusic->source, 1, &buffer);
}
}

View File

@ -89,11 +89,10 @@ static Color background = { 0, 0, 0, 0 }; // Screen background color
//----------------------------------------------------------------------------------
// Other Modules Functions Declaration (required by core)
//----------------------------------------------------------------------------------
extern void LoadDefaultFont(); // [Module: text] Loads default font on InitWindow()
extern void UnloadDefaultFont(); // [Module: text] Unloads default font from GPU memory
extern void LoadDefaultFont(); // [Module: text] Loads default font on InitWindow()
extern void UnloadDefaultFont(); // [Module: text] Unloads default font from GPU memory
extern bool MusicStreamUpdate(); // [Module: audio] Updates buffers for music streamming
extern void PlayCurrentMusic(); // [Module: audio] Plays current music stream
extern void UpdateMusicStream(); // [Module: audio] Updates buffers for music streaming
//----------------------------------------------------------------------------------
// Module specific Functions Declaration
@ -103,7 +102,7 @@ static void KeyCallback(GLFWwindow* window, int key, int scancode, int action, i
static void ScrollCallback(GLFWwindow* window, double xoffset, double yoffset); // GLFW3 Srolling Callback, runs on mouse wheel
static void CursorEnterCallback(GLFWwindow* window, int enter); // GLFW3 Cursor Enter Callback, cursor enters client area
static void WindowSizeCallback(GLFWwindow* window, int width, int height); // GLFW3 WindowSize Callback, runs when window is resized
static void TakeScreenshot(); // Takes a bitmap (BMP) screenshot and saves it in the same folder as executable
static void TakeScreenshot(); // Takes a screenshot and saves it in the same folder as executable
//----------------------------------------------------------------------------------
// Module Functions Definition - Window and OpenGL Context Functions
@ -304,9 +303,7 @@ void EndDrawing()
glfwSwapBuffers(window); // Swap back and front buffers
glfwPollEvents(); // Register keyboard/mouse events
//MusicStreamUpdate();
//if (!MusicIsPlaying())
//PlayCurrentMusic();
UpdateMusicStream(); // NOTE: Function checks if music is enabled
currentTime = glfwGetTime();
drawTime = currentTime - previousTime;
@ -748,4 +745,6 @@ static void TakeScreenshot()
free(imgData);
shotNum++;
TraceLog(INFO, "[%s] Screenshot taken!", buffer);
}

View File

@ -25,9 +25,9 @@
#include "raylib.h"
#include <GL/gl.h> // OpenGL functions
#include <stdio.h> // Standard input/output functions, used to read model files data
#include <stdlib.h> // Declares malloc() and free() for memory management
#include <string.h> // Required for strcmp()
#include <math.h> // Used for sin, cos, tan
#include "raymath.h" // Required for data type Matrix and Matrix functions
@ -52,6 +52,7 @@
// Module specific Functions Declaration
//----------------------------------------------------------------------------------
static float GetHeightValue(Color pixel);
static VertexData LoadOBJ(const char *fileName);
//----------------------------------------------------------------------------------
// Module Functions Definition
@ -67,9 +68,9 @@ void DrawCube(Vector3 position, float width, float height, float lenght, Color c
rlPushMatrix();
// NOTE: Be careful! Function order matters (scale, translate, rotate)
//rlScalef(2.0f, 2.0f, 2.0f);
// NOTE: Be careful! Function order matters (rotate -> scale -> translate)
//rlTranslatef(0.0f, 0.0f, 0.0f);
//rlScalef(2.0f, 2.0f, 2.0f);
//rlRotatef(45, 0, 1, 0);
rlBegin(RL_TRIANGLES);
@ -215,9 +216,9 @@ void DrawCubeTexture(Texture2D texture, Vector3 position, float width, float hei
float y = position.y;
float z = position.z;
rlEnableTexture(texture.glId);
rlEnableTexture(texture.id);
rlPushMatrix();
//rlPushMatrix();
// NOTE: Be careful! Function order matters (scale, translate, rotate)
//rlScalef(2.0f, 2.0f, 2.0f);
//rlTranslatef(2.0f, 0.0f, 0.0f);
@ -262,7 +263,7 @@ void DrawCubeTexture(Texture2D texture, Vector3 position, float width, float hei
rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x-width/2, y+height/2, z+lenght/2); // Top Right Of The Texture and Quad
rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x-width/2, y+height/2, z-lenght/2); // Top Left Of The Texture and Quad
rlEnd();
rlPopMatrix();
//rlPopMatrix();
rlDisableTexture();
}
@ -278,13 +279,13 @@ void DrawSphereEx(Vector3 centerPos, float radius, int rings, int slices, Color
{
rlPushMatrix();
rlTranslatef(centerPos.x, centerPos.y, centerPos.z);
//rlRotatef(rotation, 0, 1, 0);
rlScalef(radius, radius, radius);
//rlRotatef(rotation, 0, 1, 0);
rlBegin(RL_TRIANGLES);
rlColor4ub(color.r, color.g, color.b, color.a);
for(int i = 0; i < 2 * rings + 1; i ++)
for(int i = 0; i < 2 * rings + 1; i++)
{
for(int j = 0; j < slices; j++)
{
@ -317,14 +318,14 @@ void DrawSphereEx(Vector3 centerPos, float radius, int rings, int slices, Color
void DrawSphereWires(Vector3 centerPos, float radius, int rings, int slices, Color color)
{
rlPushMatrix();
rlTranslatef(centerPos.x, centerPos.y, centerPos.z);
//rlRotatef(rotation, 0, 1, 0);
//rlTranslatef(centerPos.x, centerPos.y, centerPos.z);
rlScalef(radius, radius, radius);
//rlRotatef(rotation, 0, 1, 0);
rlBegin(RL_LINES);
rlColor4ub(color.r, color.g, color.b, color.a);
for(int i = 0; i < 2 * rings + 1; i ++)
for(int i = 0; i < 2 * rings + 1; i++)
{
for(int j = 0; j < slices; j++)
{
@ -447,12 +448,12 @@ void DrawPlane(Vector3 centerPos, Vector2 size, Vector3 rotation, Color color)
// NOTE: Plane is always created on XZ ground and then rotated
rlPushMatrix();
rlTranslatef(centerPos.x, centerPos.y, centerPos.z);
rlScalef(size.x, 1.0f, size.y);
// TODO: Review multiples rotations Gimbal-Lock... use matrix or quaternions...
rlRotatef(rotation.x, 1, 0, 0);
rlRotatef(rotation.y, 0, 1, 0);
rlRotatef(rotation.z, 0, 0, 1);
rlScalef(size.x, 1.0f, size.y);
rlBegin(RL_QUADS);
rlColor4ub(color.r, color.g, color.b, color.a);
@ -568,14 +569,13 @@ void DrawGizmo(Vector3 position)
rlPopMatrix();
}
void DrawGizmoEx(Vector3 position, Vector3 rot, float scale, bool orbits)
{
static float rotation = 0;
void DrawGizmoEx(Vector3 position, Vector3 rotation, float scale)
{
// NOTE: RGB = XYZ
rlPushMatrix();
rlTranslatef(position.x, position.y, position.z);
rlRotatef(rotation, 0, 1, 0);
rlScalef(scale, scale, scale);
rlRotatef(rotation.y, 0, 1, 0);
rlBegin(RL_LINES);
// X Axis
@ -612,43 +612,327 @@ void DrawGizmoEx(Vector3 position, Vector3 rot, float scale, bool orbits)
rlColor4ub(0, 0, 200, 255); rlVertex3f(position.x - .1, position.y, position.z - .9);
// Extra
if(orbits)
int n = 3;
// X Axis
for (int i=0; i < 360; i += 6)
{
int n = 3;
// X Axis
for (int i=0; i < 360; i += 6)
{
rlColor4ub(200, 0, 0, 255); rlVertex3f(0, position.x + sin(DEG2RAD*i) * scale/n, position.y + cos(DEG2RAD*i) * scale/n);
rlColor4ub(200, 0, 0, 255); rlVertex3f(0, position.x + sin(DEG2RAD*(i+6)) * scale/n, position.y + cos(DEG2RAD*(i+6)) * scale/n);
}
// Y Axis
for (int i=0; i < 360; i += 6)
{
rlColor4ub(0, 200, 0, 255); rlVertex3f(position.x + sin(DEG2RAD*i) * scale/n, 0, position.y + cos(DEG2RAD*i) * scale/n);
rlColor4ub(0, 200, 0, 255); rlVertex3f(position.x + sin(DEG2RAD*(i+6)) * scale/n, 0, position.y + cos(DEG2RAD*(i+6)) * scale/n);
}
// Z Axis
for (int i=0; i < 360; i += 6)
{
rlColor4ub(0, 0, 200, 255); rlVertex3f(position.x + sin(DEG2RAD*i) * scale/n, position.y + cos(DEG2RAD*i) * scale/n, 0);
rlColor4ub(0, 0, 200, 255); rlVertex3f(position.x + sin(DEG2RAD*(i+6)) * scale/n, position.y + cos(DEG2RAD*(i+6)) * scale/n, 0);
}
rlColor4ub(200, 0, 0, 255); rlVertex3f(0, position.x + sin(DEG2RAD*i) * scale/n, position.y + cos(DEG2RAD*i) * scale/n);
rlColor4ub(200, 0, 0, 255); rlVertex3f(0, position.x + sin(DEG2RAD*(i+6)) * scale/n, position.y + cos(DEG2RAD*(i+6)) * scale/n);
}
// Y Axis
for (int i=0; i < 360; i += 6)
{
rlColor4ub(0, 200, 0, 255); rlVertex3f(position.x + sin(DEG2RAD*i) * scale/n, 0, position.y + cos(DEG2RAD*i) * scale/n);
rlColor4ub(0, 200, 0, 255); rlVertex3f(position.x + sin(DEG2RAD*(i+6)) * scale/n, 0, position.y + cos(DEG2RAD*(i+6)) * scale/n);
}
// Z Axis
for (int i=0; i < 360; i += 6)
{
rlColor4ub(0, 0, 200, 255); rlVertex3f(position.x + sin(DEG2RAD*i) * scale/n, position.y + cos(DEG2RAD*i) * scale/n, 0);
rlColor4ub(0, 0, 200, 255); rlVertex3f(position.x + sin(DEG2RAD*(i+6)) * scale/n, position.y + cos(DEG2RAD*(i+6)) * scale/n, 0);
}
rlEnd();
rlPopMatrix();
rotation += 0.1f;
}
// Load a 3d model (.OBJ)
// TODO: Add comments explaining this function process
// Load a 3d model
Model LoadModel(const char *fileName)
{
VertexData vData;
if (strcmp(GetExtension(fileName),"obj") == 0) vData = LoadOBJ(fileName);
else TraceLog(WARNING, "[%s] Model extension not recognized, it can't be loaded", fileName);
Model model;
model.mesh = vData; // Model mesh is vertex data
model.textureId = 0;
#if defined(USE_OPENGL_33) || defined(USE_OPENGL_ES2)
model.vaoId = rlglLoadModel(vData); // Use loaded data to generate VAO
model.textureId = 1; // Default whiteTexture
// Now that vertex data is uploaded to GPU, we can free arrays
//free(vData.vertices);
//free(vData.texcoords);
//free(vData.normals);
#endif
return model;
}
// Load a heightmap image as a 3d model
Model LoadHeightmap(Image heightmap, float maxHeight)
{
VertexData vData;
int mapX = heightmap.width;
int mapZ = heightmap.height;
// NOTE: One vertex per pixel
// TODO: Consider resolution when generating model data?
int numTriangles = (mapX-1)*(mapZ-1)*2; // One quad every four pixels
vData.vertexCount = numTriangles*3;
vData.vertices = (float *)malloc(vData.vertexCount * 3 * sizeof(float));
vData.normals = (float *)malloc(vData.vertexCount * 3 * sizeof(float));
vData.texcoords = (float *)malloc(vData.vertexCount * 2 * sizeof(float));
int vCounter = 0; // Used to count vertices float by float
int tcCounter = 0; // Used to count texcoords float by float
int nCounter = 0; // Used to count normals float by float
int trisCounter = 0;
float scaleFactor = maxHeight/255; // TODO: Review scaleFactor calculation
for(int z = 0; z < mapZ-1; z++)
{
for(int x = 0; x < mapX-1; x++)
{
// Fill vertices array with data
//----------------------------------------------------------
// one triangle - 3 vertex
vData.vertices[vCounter] = x;
vData.vertices[vCounter + 1] = GetHeightValue(heightmap.pixels[x + z*mapX])*scaleFactor;
vData.vertices[vCounter + 2] = z;
vData.vertices[vCounter + 3] = x;
vData.vertices[vCounter + 4] = GetHeightValue(heightmap.pixels[x + (z+1)*mapX])*scaleFactor;
vData.vertices[vCounter + 5] = z+1;
vData.vertices[vCounter + 6] = x+1;
vData.vertices[vCounter + 7] = GetHeightValue(heightmap.pixels[(x+1) + z*mapX])*scaleFactor;
vData.vertices[vCounter + 8] = z;
// another triangle - 3 vertex
vData.vertices[vCounter + 9] = vData.vertices[vCounter + 6];
vData.vertices[vCounter + 10] = vData.vertices[vCounter + 7];
vData.vertices[vCounter + 11] = vData.vertices[vCounter + 8];
vData.vertices[vCounter + 12] = vData.vertices[vCounter + 3];
vData.vertices[vCounter + 13] = vData.vertices[vCounter + 4];
vData.vertices[vCounter + 14] = vData.vertices[vCounter + 5];
vData.vertices[vCounter + 15] = x+1;
vData.vertices[vCounter + 16] = GetHeightValue(heightmap.pixels[(x+1) + (z+1)*mapX])*scaleFactor;
vData.vertices[vCounter + 17] = z+1;
vCounter += 18; // 6 vertex, 18 floats
// Fill texcoords array with data
//--------------------------------------------------------------
vData.texcoords[tcCounter] = (float)x / (mapX-1);
vData.texcoords[tcCounter + 1] = (float)z / (mapZ-1);
vData.texcoords[tcCounter + 2] = (float)x / (mapX-1);
vData.texcoords[tcCounter + 3] = (float)(z+1) / (mapZ-1);
vData.texcoords[tcCounter + 4] = (float)(x+1) / (mapX-1);
vData.texcoords[tcCounter + 5] = (float)z / (mapZ-1);
vData.texcoords[tcCounter + 6] = vData.texcoords[tcCounter + 4];
vData.texcoords[tcCounter + 7] = vData.texcoords[tcCounter + 5];
vData.texcoords[tcCounter + 8] = vData.texcoords[tcCounter + 2];
vData.texcoords[tcCounter + 9] = vData.texcoords[tcCounter + 1];
vData.texcoords[tcCounter + 10] = (float)(x+1) / (mapX-1);
vData.texcoords[tcCounter + 11] = (float)(z+1) / (mapZ-1);
tcCounter += 12; // 6 texcoords, 12 floats
// Fill normals array with data
//--------------------------------------------------------------
// NOTE: Current Model implementation doe not use normals!
for (int i = 0; i < 18; i += 3)
{
vData.normals[nCounter + i] = 0.0f;
vData.normals[nCounter + i + 1] = 1.0f;
vData.normals[nCounter + i + 2] = 0.0f;
}
// TODO: Calculate normals in an efficient way
nCounter += 18; // 6 vertex, 18 floats
trisCounter += 2;
}
}
// NOTE: At this point we have all vertex, texcoord, normal data for the model in vData struct
Model model;
model.mesh = vData; // Model mesh is vertex data
model.textureId = 0;
#if defined(USE_OPENGL_33) || defined(USE_OPENGL_ES2)
model.vaoId = rlglLoadModel(vData); // Use loaded data to generate VAO
model.textureId = 1; // Default whiteTexture
// Now that vertex data is uploaded to GPU, we can free arrays
//free(vData.vertices);
//free(vData.texcoords);
//free(vData.normals);
#endif
return model;
}
// Unload 3d model from memory
void UnloadModel(Model model)
{
free(model.mesh.vertices);
free(model.mesh.texcoords);
free(model.mesh.normals);
#if defined(USE_OPENGL_33) || defined(USE_OPENGL_ES2)
rlDeleteVertexArrays(model.vaoId);
#endif
}
void SetModelTexture(Model *model, Texture2D texture)
{
if (texture.id <= 0) model->textureId = 1; // Default white texture (use mesh color)
else model->textureId = texture.id;
}
// Draw a model (with texture if set)
void DrawModel(Model model, Vector3 position, float scale, Color tint)
{
Vector3 vScale = { scale, scale, scale };
Vector3 rotation = { 0, 0, 0 };
rlglDrawModel(model, position, rotation, vScale, tint, false);
}
// Draw a model with extended parameters
void DrawModelEx(Model model, Vector3 position, Vector3 rotation, Vector3 scale, Color tint)
{
rlglDrawModel(model, position, rotation, scale, tint, false);
}
// Draw a model wires (with texture if set)
void DrawModelWires(Model model, Vector3 position, float scale, Color color)
{
Vector3 vScale = { scale, scale, scale };
Vector3 rotation = { 0, 0, 0 };
rlglDrawModel(model, position, rotation, vScale, color, true);
}
// Draw a billboard
void DrawBillboard(Camera camera, Texture2D texture, Vector3 center, float size, Color tint)
{
// NOTE: Billboard size will maintain texture aspect ratio, size will be billboard width
Vector2 sizeRatio = { size, size * (float)texture.height/texture.width };
Matrix viewMatrix = MatrixLookAt(camera.position, camera.target, camera.up);
MatrixTranspose(&viewMatrix);
Vector3 right = { viewMatrix.m0, viewMatrix.m4, viewMatrix.m8 };
Vector3 up = { viewMatrix.m1, viewMatrix.m5, viewMatrix.m9 };
/*
d-------c
| |
| * |
| |
a-------b
*/
VectorScale(&right, sizeRatio.x/2);
VectorScale(&up, sizeRatio.y/2);
Vector3 p1 = VectorAdd(right, up);
Vector3 p2 = VectorSubtract(right, up);
Vector3 a = VectorSubtract(center, p2);
Vector3 b = VectorAdd(center, p1);
Vector3 c = VectorAdd(center, p2);
Vector3 d = VectorSubtract(center, p1);
rlEnableTexture(texture.id);
rlBegin(RL_QUADS);
rlColor4ub(tint.r, tint.g, tint.b, tint.a);
rlNormal3f(0.0f, 1.0f, 0.0f);
rlTexCoord2f(0.0f, 0.0f); rlVertex3f(a.x, a.y, a.z);
rlTexCoord2f(1.0f, 0.0f); rlVertex3f(b.x, b.y, b.z);
rlTexCoord2f(1.0f, 1.0f); rlVertex3f(c.x, c.y, c.z);
rlTexCoord2f(0.0f, 1.0f); rlVertex3f(d.x, d.y, d.z);
rlEnd();
rlDisableTexture();
}
// Draw a billboard (part of a texture defined by a rectangle)
void DrawBillboardRec(Camera camera, Texture2D texture, Rectangle sourceRec, Vector3 center, float size, Color tint)
{
// NOTE: Billboard size will maintain sourceRec aspect ratio, size will represent billboard width
Vector2 sizeRatio = { size, size * (float)sourceRec.height/sourceRec.width };
Matrix viewMatrix = MatrixLookAt(camera.position, camera.target, camera.up);
MatrixTranspose(&viewMatrix);
Vector3 right = { viewMatrix.m0, viewMatrix.m4, viewMatrix.m8 };
Vector3 up = { viewMatrix.m1, viewMatrix.m5, viewMatrix.m9 };
/*
d-------c
| |
| * |
| |
a-------b
*/
VectorScale(&right, sizeRatio.x/2);
VectorScale(&up, sizeRatio.y/2);
Vector3 p1 = VectorAdd(right, up);
Vector3 p2 = VectorSubtract(right, up);
Vector3 a = VectorSubtract(center, p2);
Vector3 b = VectorAdd(center, p1);
Vector3 c = VectorAdd(center, p2);
Vector3 d = VectorSubtract(center, p1);
rlEnableTexture(texture.id);
rlBegin(RL_QUADS);
rlColor4ub(tint.r, tint.g, tint.b, tint.a);
// Bottom-left corner for texture and quad
rlTexCoord2f((float)sourceRec.x / texture.width, (float)sourceRec.y / texture.height);
rlVertex3f(a.x, a.y, a.z);
// Bottom-right corner for texture and quad
rlTexCoord2f((float)(sourceRec.x + sourceRec.width) / texture.width, (float)sourceRec.y / texture.height);
rlVertex3f(b.x, b.y, b.z);
// Top-right corner for texture and quad
rlTexCoord2f((float)(sourceRec.x + sourceRec.width) / texture.width, (float)(sourceRec.y + sourceRec.height) / texture.height);
rlVertex3f(c.x, c.y, c.z);
// Top-left corner for texture and quad
rlTexCoord2f((float)sourceRec.x / texture.width, (float)(sourceRec.y + sourceRec.height) / texture.height);
rlVertex3f(d.x, d.y, d.z);
rlEnd();
rlDisableTexture();
}
// Get current vertex y altitude (proportional to pixel colors in grayscale)
static float GetHeightValue(Color pixel)
{
return (((float)pixel.r + (float)pixel.g + (float)pixel.b)/3);
}
// Load OBJ mesh data
static VertexData LoadOBJ(const char *fileName)
{
VertexData vData;
char dataType;
char comments[200];
@ -661,6 +945,8 @@ Model LoadModel(const char *fileName)
objFile = fopen(fileName, "rt");
// First pass over all file to get numVertex, numNormals, numTexCoords, numTriangles
// NOTE: vertex, texcoords and normals could be optimized (to be used indexed on faces definition)
while(!feof(objFile))
{
fscanf(objFile, "%c", &dataType);
@ -671,7 +957,14 @@ Model LoadModel(const char *fileName)
{
fgets(comments, 200, objFile);
} break;
case 'v':
case 'o': // New object
{
// TODO: Read multiple objects, we need to know numMeshes + verticesPerMesh
// NOTE: One OBJ file can contain multible meshes defined, one after every 'o'
} break;
case 'v':
{
fscanf(objFile, "%c", &dataType);
@ -753,15 +1046,18 @@ Model LoadModel(const char *fileName)
}
}
Vector3 midVertices[numVertex];
Vector3 midNormals[numNormals];
Vector2 midTexCoords[numTexCoords];
// Once we know the number of vertices to store, we create required arrays
Vector3 *midVertices = (Vector3 *)malloc(numVertex*sizeof(Vector3));
Vector3 *midNormals = (Vector3 *)malloc(numNormals*sizeof(Vector3));
Vector2 *midTexCoords = (Vector2 *)malloc(numTexCoords*sizeof(Vector2));
vData.numVertices = numTriangles*3;
vData.vertexCount = numTriangles*3;
vData.vertices = (float *)malloc(vData.numVertices * 3 * sizeof(float));
vData.texcoords = (float *)malloc(vData.numVertices * 2 * sizeof(float));
vData.normals = (float *)malloc(vData.numVertices * 3 * sizeof(float));
// Additional arrays to store vertex data as floats
vData.vertices = (float *)malloc(vData.vertexCount * 3 * sizeof(float));
vData.texcoords = (float *)malloc(vData.vertexCount * 2 * sizeof(float));
vData.normals = (float *)malloc(vData.vertexCount * 3 * sizeof(float));
vData.colors = (float *)malloc(vData.vertexCount * 4 * sizeof(float));
int countVertex = 0;
int countNormals = 0;
@ -771,8 +1067,9 @@ Model LoadModel(const char *fileName)
int tcCounter = 0; // Used to count texcoords float by float
int nCounter = 0; // Used to count normals float by float
rewind(objFile);
rewind(objFile); // Return to the beginning of the file, to read again
// Reading again file to get vertex data
while(!feof(objFile))
{
fscanf(objFile, "%c", &dataType);
@ -872,274 +1169,16 @@ Model LoadModel(const char *fileName)
fclose(objFile);
// NOTE: At this point we have all vertex, texcoord, normal data for the model in vData struct
Model model;
#ifdef USE_OPENGL_11
model.data = vData; // model data is vertex data
#else
model.vaoId = rlglLoadModel(vData); // Use loaded data to generate VAO
// NOTE: We set all vertex colors to white
for (int i = 0; i < (4*vData.vertexCount); i++) vData.colors[i] = 1.0f;
// Now that vertex data is uploaded to GPU, we can free arrays
free(vData.vertices);
free(vData.texcoords);
free(vData.normals);
#endif
return model;
}
// Load a heightmap image as a 3d model
Model LoadHeightmap(Image heightmap, float maxHeight)
{
VertexData vData;
int mapX = heightmap.width;
int mapZ = heightmap.height;
// NOTE: One vertex per pixel
// TODO: Consider resolution when generating model data?
int numTriangles = (mapX-1)*(mapZ-1)*2; // One quad every four pixels
vData.numVertices = numTriangles*3;
vData.vertices = (float *)malloc(vData.numVertices * 3 * sizeof(float));
vData.normals = (float *)malloc(vData.numVertices * 3 * sizeof(float));
vData.texcoords = (float *)malloc(vData.numVertices * 2 * sizeof(float));
int vCounter = 0; // Used to count vertices float by float
int tcCounter = 0; // Used to count texcoords float by float
int nCounter = 0; // Used to count normals float by float
int trisCounter = 0;
float scaleFactor = maxHeight/255; // TODO: Review scaleFactor calculation
for(int z = 0; z < mapZ-1; z++)
{
for(int x = 0; x < mapX-1; x++)
{
// Fill vertices array with data
//----------------------------------------------------------
// one triangle - 3 vertex
vData.vertices[vCounter] = x;
vData.vertices[vCounter + 1] = GetHeightValue(heightmap.pixels[x + z*mapX])*scaleFactor;
vData.vertices[vCounter + 2] = z;
vData.vertices[vCounter + 3] = x;
vData.vertices[vCounter + 4] = GetHeightValue(heightmap.pixels[x + (z+1)*mapX])*scaleFactor;
vData.vertices[vCounter + 5] = z+1;
vData.vertices[vCounter + 6] = x+1;
vData.vertices[vCounter + 7] = GetHeightValue(heightmap.pixels[(x+1) + z*mapX])*scaleFactor;
vData.vertices[vCounter + 8] = z;
// another triangle - 3 vertex
vData.vertices[vCounter + 9] = vData.vertices[vCounter + 6];
vData.vertices[vCounter + 10] = vData.vertices[vCounter + 7];
vData.vertices[vCounter + 11] = vData.vertices[vCounter + 8];
vData.vertices[vCounter + 12] = vData.vertices[vCounter + 3];
vData.vertices[vCounter + 13] = vData.vertices[vCounter + 4];
vData.vertices[vCounter + 14] = vData.vertices[vCounter + 5];
vData.vertices[vCounter + 15] = x+1;
vData.vertices[vCounter + 16] = GetHeightValue(heightmap.pixels[(x+1) + (z+1)*mapX])*scaleFactor;
vData.vertices[vCounter + 17] = z+1;
vCounter += 18; // 6 vertex, 18 floats
// Fill texcoords array with data
//--------------------------------------------------------------
vData.texcoords[tcCounter] = (float)x / (mapX-1);
vData.texcoords[tcCounter + 1] = (float)z / (mapZ-1);
vData.texcoords[tcCounter + 2] = (float)x / (mapX-1);
vData.texcoords[tcCounter + 3] = (float)(z+1) / (mapZ-1);
vData.texcoords[tcCounter + 4] = (float)(x+1) / (mapX-1);
vData.texcoords[tcCounter + 5] = (float)z / (mapZ-1);
vData.texcoords[tcCounter + 6] = vData.texcoords[tcCounter + 4];
vData.texcoords[tcCounter + 7] = vData.texcoords[tcCounter + 5];
vData.texcoords[tcCounter + 8] = vData.texcoords[tcCounter + 2];
vData.texcoords[tcCounter + 9] = vData.texcoords[tcCounter + 1];
vData.texcoords[tcCounter + 10] = (float)(x+1) / (mapX-1);
vData.texcoords[tcCounter + 11] = (float)(z+1) / (mapZ-1);
tcCounter += 12; // 6 texcoords, 12 floats
// Fill normals array with data
//--------------------------------------------------------------
// TODO: Review normals calculation
for (int i = 0; i < 18; i += 3)
{
vData.normals[nCounter + i] = 0.0f;
vData.normals[nCounter + i + 1] = 1.0f;
vData.normals[nCounter + i + 2] = 0.0f;
}
nCounter += 18; // 6 vertex, 18 floats
trisCounter += 2;
}
}
// Now we can free temp mid* arrays
free(midVertices);
free(midNormals);
free(midTexCoords);
// NOTE: At this point we have all vertex, texcoord, normal data for the model in vData struct
Model model;
#ifdef USE_OPENGL_11
model.data = vData; // model data is vertex data
#else
model.vaoId = rlglLoadModel(vData); // Use loaded data to generate VAO
// Now that vertex data is uploaded to GPU, we can free arrays
free(vData.vertices);
free(vData.texcoords);
free(vData.normals);
#endif
return model;
}
// Unload 3d model from memory
void UnloadModel(Model model)
{
#ifdef USE_OPENGL_11
free(model.data.vertices);
free(model.data.texcoords);
free(model.data.normals);
#endif
#if defined(USE_OPENGL_33) || defined(USE_OPENGL_ES2)
rlDeleteVertexArrays(model.vaoId);
#endif
}
// Draw a model
void DrawModel(Model model, Vector3 position, float scale, Color color)
{
rlglDrawModel(model, position, scale, false);
}
// Draw a textured model
void DrawModelEx(Model model, Texture2D texture, Vector3 position, float scale, Color tint)
{
rlEnableTexture(texture.glId);
TraceLog(INFO, "[%s] Model loaded successfully in RAM (CPU)", fileName);
DrawModel(model, position, scale, tint);
rlDisableTexture();
}
// Draw a model wires
void DrawModelWires(Model model, Vector3 position, float scale, Color color)
{
rlglDrawModel(model, position, scale, true);
}
// Draw a billboard
void DrawBillboard(Camera camera, Texture2D texture, Vector3 center, float size, Color tint)
{
// NOTE: Billboard size will maintain texture aspect ratio, size will be billboard width
Vector2 sizeRatio = { size, size * (float)texture.height/texture.width };
Matrix viewMatrix = MatrixLookAt(camera.position, camera.target, camera.up);
MatrixTranspose(&viewMatrix);
Vector3 right = { viewMatrix.m0, viewMatrix.m4, viewMatrix.m8 };
Vector3 up = { viewMatrix.m1, viewMatrix.m5, viewMatrix.m9 };
/*
d-------c
| |
| * |
| |
a-------b
*/
VectorScale(&right, sizeRatio.x/2);
VectorScale(&up, sizeRatio.y/2);
Vector3 p1 = VectorAdd(right, up);
Vector3 p2 = VectorSubtract(right, up);
Vector3 a = VectorSubtract(center, p2);
Vector3 b = VectorAdd(center, p1);
Vector3 c = VectorAdd(center, p2);
Vector3 d = VectorSubtract(center, p1);
rlEnableTexture(texture.glId);
rlBegin(RL_QUADS);
rlColor4ub(tint.r, tint.g, tint.b, tint.a);
rlNormal3f(0.0f, 1.0f, 0.0f);
rlTexCoord2f(0.0f, 0.0f); rlVertex3f(a.x, a.y, a.z);
rlTexCoord2f(1.0f, 0.0f); rlVertex3f(b.x, b.y, b.z);
rlTexCoord2f(1.0f, 1.0f); rlVertex3f(c.x, c.y, c.z);
rlTexCoord2f(0.0f, 1.0f); rlVertex3f(d.x, d.y, d.z);
rlEnd();
rlDisableTexture();
}
// Draw a billboard (part of a texture defined by a rectangle)
void DrawBillboardRec(Camera camera, Texture2D texture, Rectangle sourceRec, Vector3 center, float size, Color tint)
{
// NOTE: Billboard size will maintain sourceRec aspect ratio, size will represent billboard width
Vector2 sizeRatio = { size, size * (float)sourceRec.height/sourceRec.width };
Matrix viewMatrix = MatrixLookAt(camera.position, camera.target, camera.up);
MatrixTranspose(&viewMatrix);
Vector3 right = { viewMatrix.m0, viewMatrix.m4, viewMatrix.m8 };
Vector3 up = { viewMatrix.m1, viewMatrix.m5, viewMatrix.m9 };
/*
d-------c
| |
| * |
| |
a-------b
*/
VectorScale(&right, sizeRatio.x/2);
VectorScale(&up, sizeRatio.y/2);
Vector3 p1 = VectorAdd(right, up);
Vector3 p2 = VectorSubtract(right, up);
Vector3 a = VectorSubtract(center, p2);
Vector3 b = VectorAdd(center, p1);
Vector3 c = VectorAdd(center, p2);
Vector3 d = VectorSubtract(center, p1);
rlEnableTexture(texture.glId);
rlBegin(RL_QUADS);
rlColor4ub(tint.r, tint.g, tint.b, tint.a);
// Bottom-left corner for texture and quad
rlTexCoord2f((float)sourceRec.x / texture.width, (float)sourceRec.y / texture.height);
rlVertex3f(a.x, a.y, a.z);
// Bottom-right corner for texture and quad
rlTexCoord2f((float)(sourceRec.x + sourceRec.width) / texture.width, (float)sourceRec.y / texture.height);
rlVertex3f(b.x, b.y, b.z);
// Top-right corner for texture and quad
rlTexCoord2f((float)(sourceRec.x + sourceRec.width) / texture.width, (float)(sourceRec.y + sourceRec.height) / texture.height);
rlVertex3f(c.x, c.y, c.z);
// Top-left corner for texture and quad
rlTexCoord2f((float)sourceRec.x / texture.width, (float)(sourceRec.y + sourceRec.height) / texture.height);
rlVertex3f(d.x, d.y, d.z);
rlEnd();
rlDisableTexture();
}
// Get current vertex y altitude (proportional to pixel colors in grayscale)
static float GetHeightValue(Color pixel)
{
return (((float)pixel.r + (float)pixel.g + (float)pixel.b)/3);
return vData;
}

View File

@ -10,13 +10,17 @@
* Hardware accelerated with OpenGL (1.1, 3.3+ or ES2)
* Unique OpenGL abstraction layer [rlgl]
* Powerful fonts module with SpriteFonts support
* Multiple textures support, including DDS and mipmaps generation
* Basic 3d support for Shapes, Models, Heightmaps and Billboards
* Powerful math module for Vector and Matrix operations [raymath]
* Audio loading and playing
* Audio loading and playing with streaming support
*
* Used external libs:
* GLFW3 (www.glfw.org) for window/context management and input
* GLEW for OpenGL extensions loading (3.3+ and ES2)
* stb_image (Sean Barret) for images loading (JPEG, PNG, BMP, TGA, PSD, GIF, HDR, PIC)
* stb_image_write (Sean Barret) for image writting (PNG)
* stb_vorbis (Sean Barret) for ogg audio loading
* OpenAL Soft for audio device/context management
* tinfl for data decompression (DEFLATE algorithm)
*
@ -25,9 +29,9 @@
* 32bit Textures - All loaded images are converted automatically to RGBA textures
* SpriteFonts - All loaded sprite-font images are converted to RGBA and POT textures
* One custom default font is loaded automatically when InitWindow()
* If using OpenGL 3.3+, one default shader is loaded automatically (internally defined)
* If using OpenGL 3.3+ or ES2, one default shader is loaded automatically (internally defined)
*
* -- LICENSE (raylib v1.1, March 2014) --
* -- LICENSE (raylib v1.1, April 2014) --
*
* raylib is licensed under an unmodified zlib/libpng license, which is an OSI-certified,
* BSD-like license that allows static linking with closed source software:
@ -52,9 +56,7 @@
**********************************************************************************************/
#ifndef RAYLIB_H
#define RAYLIB_H
#include "stb_vorbis.h"
#define RAYLIB_H
//----------------------------------------------------------------------------------
// Some basic Defines
@ -195,11 +197,22 @@ typedef struct Image {
// Texture2D type, bpp always RGBA (32bit)
// NOTE: Data stored in GPU memory
typedef struct Texture2D {
unsigned int glId;
unsigned int id; // OpenGL id
int width;
int height;
} Texture2D;
// Character type (one font glyph)
// NOTE: Defined in module: text
typedef struct Character Character;
// SpriteFont type, includes texture and charSet array data
typedef struct SpriteFont {
Texture2D texture;
int numChars;
Character *charSet;
} SpriteFont;
// Camera type, defines a camera position/orientation in 3d space
typedef struct Camera {
Vector3 position;
@ -207,18 +220,23 @@ typedef struct Camera {
Vector3 up;
} Camera;
typedef struct Character Character;
// SpriteFont type
typedef struct SpriteFont {
Texture2D texture;
int numChars;
Character *charSet;
} SpriteFont;
// Vertex data definning a mesh
typedef struct {
int vertexCount;
float *vertices; // 3 components per vertex
float *texcoords; // 2 components per vertex
float *normals; // 3 components per vertex
float *colors; // 4 components per vertex
} VertexData;
// 3d Model type
// NOTE: If using OpenGL 1.1 loaded in CPU; if OpenGL 3.3+ loaded in GPU
typedef struct Model Model; // Defined in module: rlgl
// NOTE: If using OpenGL 1.1 loaded in CPU (mesh); if OpenGL 3.3+ loaded in GPU (vaoId)
typedef struct Model {
VertexData mesh;
unsigned int vaoId;
unsigned int textureId;
//Matrix transform;
} Model;
// Sound source type
typedef struct Sound {
@ -226,23 +244,6 @@ typedef struct Sound {
unsigned int buffer;
} Sound;
typedef struct OggStream OggStream;
// Music type (streamming)
typedef struct Music {
stb_vorbis *stream;
stb_vorbis_info info;
unsigned int source;
unsigned int buffers[2];
int format;
int bufferSize;
int totalSamplesLeft;
bool loop;
} Music;
#ifdef __cplusplus
extern "C" { // Prevents name mangling of functions
#endif
@ -342,7 +343,7 @@ Image LoadImage(const char *fileName);
Image LoadImageFromRES(const char *rresName, int resId); // Load an image from rRES file (raylib Resource)
Texture2D LoadTexture(const char *fileName); // Load an image as texture into GPU memory
Texture2D LoadTextureFromRES(const char *rresName, int resId); // Load an image as texture from rRES file (raylib Resource)
Texture2D CreateTexture(Image image); // Create a Texture2D from Image data
Texture2D CreateTexture(Image image, bool genMipmaps); // Create a Texture2D from Image data (and generate mipmaps)
void UnloadImage(Image image); // Unload image from CPU memory (RAM)
void UnloadTexture(Texture2D texture); // Unload texture from GPU memory
@ -359,6 +360,7 @@ void DrawTexturePro(Texture2D texture, Rectangle sourceRec, Rectangle destRec, V
SpriteFont GetDefaultFont(); // Get the default SpriteFont
SpriteFont LoadSpriteFont(const char *fileName); // Load a SpriteFont image into GPU memory
void UnloadSpriteFont(SpriteFont spriteFont); // Unload SpriteFont from GPU memory
void DrawText(const char *text, int posX, int posY, int fontSize, Color color); // Draw text (using default font)
void DrawTextEx(SpriteFont spriteFont, const char* text, Vector2 position, // Draw text using SpriteFont and additional parameters
int fontSize, int spacing, Color tint);
@ -384,7 +386,7 @@ void DrawPlane(Vector3 centerPos, Vector2 size, Vector3 rotation, Color color);
void DrawPlaneEx(Vector3 centerPos, Vector2 size, Vector3 rotation, int slicesX, int slicesZ, Color color); // Draw a plane with divisions
void DrawGrid(int slices, float spacing); // Draw a grid (centered at (0, 0, 0))
void DrawGizmo(Vector3 position); // Draw simple gizmo
void DrawGizmoEx(Vector3 position, Vector3 rot, float scale, bool orbits); // Draw gizmo with extended parameters
void DrawGizmoEx(Vector3 position, Vector3 rotation, float scale); // Draw gizmo with extended parameters
//DrawTorus(), DrawTeapot() are useless...
//------------------------------------------------------------------------------------
@ -394,9 +396,12 @@ Model LoadModel(const char *fileName);
//Model LoadModelFromRES(const char *rresName, int resId); // TODO: Load a 3d model from rRES file (raylib Resource)
Model LoadHeightmap(Image heightmap, float maxHeight); // Load a heightmap image as a 3d model
void UnloadModel(Model model); // Unload 3d model from memory
void DrawModel(Model model, Vector3 position, float scale, Color color); // Draw a model
void DrawModelEx(Model model, Texture2D texture, Vector3 position, float scale, Color tint); // Draw a textured model
void DrawModelWires(Model model, Vector3 position, float scale, Color color); // Draw a model wires
void SetModelTexture(Model *model, Texture2D texture); // Link a texture to a model
void DrawModel(Model model, Vector3 position, float scale, Color tint); // Draw a model (with texture if set)
void DrawModelEx(Model model, Vector3 position, Vector3 rotation, Vector3 scale, Color tint); // Draw a model with extended parameters
void DrawModelWires(Model model, Vector3 position, float scale, Color color); // Draw a model wires (with texture if set)
void DrawBillboard(Camera camera, Texture2D texture, Vector3 center, float size, Color tint); // Draw a billboard texture
void DrawBillboardRec(Camera camera, Texture2D texture, Rectangle sourceRec, Vector3 center, float size, Color tint); // Draw a billboard texture defined by sourceRec
@ -404,22 +409,25 @@ void DrawBillboardRec(Camera camera, Texture2D texture, Rectangle sourceRec, Vec
// Audio Loading and Playing Functions (Module: audio)
//------------------------------------------------------------------------------------
void InitAudioDevice(); // Initialize audio device and context
void CloseAudioDevice(); // Close the audio device and context
void CloseAudioDevice(); // Close the audio device and context (and music stream)
Sound LoadSound(char *fileName); // Load sound to memory
Sound LoadSoundFromRES(const char *rresName, int resId); // Load sound to memory from rRES file (raylib Resource)
void UnloadSound(Sound sound); // Unload sound
Music LoadMusic(char *fileName);
void UnloadMusic(Music music);
void PlaySound(Sound sound); // Play a sound
void PauseSound(Sound sound); // Pause a sound
void StopSound(Sound sound); // Stop playing a sound
bool SoundIsPlaying(Sound sound); // Check if a sound is currently playing
void SetVolume(Sound sound, float volume); // Set volume for a sound (1.0 is base level)
void SetPitch(Sound sound, float pitch); // Set pitch for a sound (1.0 is base level)
void PlayMusic(Music music);
void StopMusic(Music music);
bool MusicIsPlaying();
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 StopMusicStream(); // Stop music playing (close stream)
void PauseMusicStream(); // Pause music playing
bool MusicIsPlaying(); // Check if music is playing
void SetMusicVolume(float volume); // Set volume for music (1.0 is max level)
float GetMusicTimeLength(); // Get current music time length (in seconds)
float GetMusicTimePlayed(); // Get current music time played (in seconds)
#ifdef __cplusplus
}

View File

@ -435,7 +435,7 @@ Matrix MatrixSubstract(Matrix left, Matrix right)
}
// Returns translation matrix
// TODO: REVIEW
// TODO: Review this function
Matrix MatrixTranslate(float x, float y, float z)
{
/*
@ -478,6 +478,7 @@ Matrix MatrixTranslate(float x, float y, float z)
}
// Returns rotation matrix
// TODO: Review this function
Matrix MatrixRotate(float angleX, float angleY, float angleZ)
{
Matrix result;
@ -492,6 +493,7 @@ Matrix MatrixRotate(float angleX, float angleY, float angleZ)
}
// Create rotation matrix from axis and angle
// TODO: Test this function
Matrix MatrixFromAxisAngle(Vector3 axis, float angle)
{
Matrix result;
@ -545,7 +547,8 @@ Matrix MatrixFromAxisAngle(Vector3 axis, float angle)
return result;
};
// Create rotation matrix from axis and angle
// Create rotation matrix from axis and angle (version 2)
// TODO: Test this function
Matrix MatrixFromAxisAngle2(Vector3 axis, float angle)
{
Matrix result;
@ -661,6 +664,21 @@ Matrix MatrixScale(float x, float y, float z)
return result;
}
// Returns transformation matrix for a given translation, rotation and scale
// NOTE: Transformation order is rotation -> scale -> translation
Matrix MatrixTransform(Vector3 translation, Vector3 rotation, Vector3 scale)
{
Matrix result = MatrixIdentity();
Matrix mRotation = MatrixRotate(rotation.x, rotation.y, rotation.z);
Matrix mScale = MatrixScale(scale.x, scale.y, scale.z);
Matrix mTranslate = MatrixTranslate(translation.x, translation.y, translation.z);
result = MatrixMultiply(MatrixMultiply(mRotation, mScale), mTranslate);
return result;
}
// Returns two matrix multiplication
// NOTE: When multiplying matrices... the order matters!
Matrix MatrixMultiply(Matrix left, Matrix right)

View File

@ -115,6 +115,7 @@ Matrix MatrixRotateX(float angle); // Returns x-rotation ma
Matrix MatrixRotateY(float angle); // Returns y-rotation matrix (angle in radians)
Matrix MatrixRotateZ(float angle); // Returns z-rotation matrix (angle in radians)
Matrix MatrixScale(float x, float y, float z); // Returns scaling matrix
Matrix MatrixTransform(Vector3 translation, Vector3 rotation, Vector3 scale); // Returns transformation matrix for a given translation, rotation and scale
Matrix MatrixMultiply(Matrix left, Matrix right); // Returns two matrix multiplication
Matrix MatrixFrustum(double left, double right, double bottom, double top, double near, double far); // Returns perspective projection matrix
Matrix MatrixPerspective(double fovy, double aspect, double near, double far); // Returns perspective projection matrix

View File

@ -31,10 +31,24 @@
#include <stdio.h> // Standard input / output lib
#include <stdlib.h> // Declares malloc() and free() for memory management, rand()
// TODO: Security check in case multiple USE_OPENGL_* defined
// Security check in case no USE_OPENGL_* defined
#if !defined(USE_OPENGL_11) && !defined(USE_OPENGL_33) && !defined(USE_OPENGL_ES2)
#define USE_OPENGL_11
#endif
// Security check in case multiple USE_OPENGL_* defined
#ifdef USE_OPENGL_11
#ifdef USE_OPENGL_33
#undef USE_OPENGL_33
#endif
#ifdef USE_OPENGL_ES2
#undef USE_OPENGL_ES2
#endif
#endif
#ifdef USE_OPENGL_11
#include <GL/gl.h> // Extensions loading lib
#include <GL/gl.h> // Basic OpenGL include
#endif
#ifdef USE_OPENGL_33
@ -42,7 +56,7 @@
#include <GL/glew.h> // Extensions loading lib
#endif
//#include "glad.h" // Extensions loading lib? --> REVIEW
//#include "glad.h" // Other extensions loading lib? --> REVIEW
#define USE_VBO_DOUBLE_BUFFERS // Enable VBO double buffers usage --> REVIEW!
@ -56,18 +70,18 @@
//----------------------------------------------------------------------------------
// Types and Structures Definition
//----------------------------------------------------------------------------------
typedef struct {
int numQuads;
int texId;
} QuadsByTexture;
// Vertex buffer (position + color arrays)
// NOTE: Used for lines and triangles VAOs
typedef struct {
int vCounter;
int cCounter;
float *vertices; // 3 components per vertex
float *colors; // 4 components per vertex
} VertexPositionColorBuffer;
/*
// Vertex buffer (position + texcoords + color arrays)
// NOTE: Not used
typedef struct {
int vCounter;
int tcCounter;
@ -76,8 +90,9 @@ typedef struct {
float *texcoords; // 2 components per vertex
float *colors; // 4 components per vertex
} VertexPositionColorTextureBuffer;
*/
/*
// Vertex buffer (position + texcoords + normals arrays)
// NOTE: Not used
typedef struct {
int vCounter;
int tcCounter;
@ -86,7 +101,9 @@ typedef struct {
float *texcoords; // 2 components per vertex
float *normals; // 3 components per vertex
} VertexPositionTextureNormalBuffer;
*/
// Vertex buffer (position + texcoords + colors + indices arrays)
// NOTE: Used for quads VAO
typedef struct {
int vCounter;
int tcCounter;
@ -97,12 +114,22 @@ typedef struct {
unsigned int *indices; // 6 indices per quad
} VertexPositionColorTextureIndexBuffer;
// Draw call type
// NOTE: Used to track required draw-calls, organized by texture
typedef struct {
GLuint texId;
int firstVertex; // Actually, when using glDrawElements, this parameter is useless..
int vCount;
GLuint textureId;
int vertexCount;
} DrawCall;
// pixel type (same as Color type)
// NOTE: Used exclusively in mipmap generation functions
typedef struct {
unsigned char r;
unsigned char g;
unsigned char b;
unsigned char a;
} pixel;
//----------------------------------------------------------------------------------
// Global Variables Definition
//----------------------------------------------------------------------------------
@ -140,7 +167,6 @@ static GLuint quadsBuffer[4];
#ifdef USE_VBO_DOUBLE_BUFFERS
// Double buffering
// TODO: REVIEW -> Not getting any performance improvement... why?
static GLuint vaoQuadsB;
static GLuint quadsBufferB[4];
static bool useBufferB = false;
@ -172,6 +198,11 @@ static GLuint LoadShaders(char *vertexFileName, char *fragmentFileName);
static char *TextFileRead(char *fn);
#endif
#ifdef USE_OPENGL_11
static int GenerateMipmaps(unsigned char *data, int baseWidth, int baseHeight);
static pixel *GenNextMipmap(pixel *srcData, int srcWidth, int srcHeight);
#endif
//----------------------------------------------------------------------------------
// Module Functions Definition - Matrix operations
//----------------------------------------------------------------------------------
@ -216,7 +247,7 @@ void rlMatrixMode(int mode)
{
if (mode == RL_PROJECTION) currentMatrix = &projection;
else if (mode == RL_MODELVIEW) currentMatrix = &modelview;
//else if (mode == GL_TEXTURE) TODO: NEVER USED!
//else if (mode == RL_TEXTURE) // Not supported
currentMatrixMode = mode;
}
@ -257,6 +288,7 @@ void rlLoadIdentity()
void rlTranslatef(float x, float y, float z)
{
Matrix mat = MatrixTranslate(x, y, z);
MatrixTranspose(&mat);
*currentMatrix = MatrixMultiply(*currentMatrix, mat);
}
@ -264,13 +296,15 @@ void rlTranslatef(float x, float y, float z)
// Multiply the current matrix by a rotation matrix
void rlRotatef(float angleDeg, float x, float y, float z)
{
// TODO: Rotation matrix --> REVIEW!
// TODO: Support rotation in multiple axes
Matrix rot = MatrixIdentity();
if (x == 1) rot = MatrixRotateX(angleDeg*DEG2RAD);
else if (y == 1) rot = MatrixRotateY(angleDeg*DEG2RAD);
else if (z == 1) rot = MatrixRotateZ(angleDeg*DEG2RAD);
MatrixTranspose(&rot);
*currentMatrix = MatrixMultiply(*currentMatrix, rot);
}
@ -278,6 +312,7 @@ void rlRotatef(float angleDeg, float x, float y, float z)
void rlScalef(float x, float y, float z)
{
Matrix mat = MatrixScale(x, y, z);
MatrixTranspose(&mat);
*currentMatrix = MatrixMultiply(*currentMatrix, mat);
}
@ -356,12 +391,12 @@ void rlEnd()
{
if (useTempBuffer)
{
// IT WORKS!!! --> Refactor...
Matrix mat = *currentMatrix;
MatrixTranspose(&mat);
// NOTE: In this case, *currentMatrix is already transposed because transposing has been applied
// independently to translation-scale-rotation matrices -> t(M1 x M2) = t(M2) x t(M1)
// This way, rlTranslatef(), rlRotatef()... behaviour is the same than OpenGL 1.1
// Apply transformation matrix to all temp vertices
for (int i = 0; i < tempBufferCount; i++) VectorTransform(&tempBuffer[i], mat);
for (int i = 0; i < tempBufferCount; i++) VectorTransform(&tempBuffer[i], *currentMatrix);
// Deactivate tempBuffer usage to allow rlVertex3f do its job
useTempBuffer = false;
@ -373,7 +408,7 @@ void rlEnd()
tempBufferCount = 0;
}
// Make sure vertexCounter is the same for vertices-texcoords-normals-colors
// Make sure vertexCount is the same for vertices-texcoords-normals-colors
// NOTE: In OpenGL 1.1, one glColor call can be made for all the subsequent glVertex calls.
switch (currentDrawMode)
{
@ -490,7 +525,7 @@ void rlVertex3f(float x, float y, float z)
quads.vCounter++;
draws[drawsCounter - 1].vCount++;
draws[drawsCounter - 1].vertexCount++;
} break;
default: break;
@ -596,13 +631,12 @@ void rlEnableTexture(unsigned int id)
#endif
#if defined(USE_OPENGL_33) || defined(USE_OPENGL_ES2)
if (draws[drawsCounter - 1].texId != id)
if (draws[drawsCounter - 1].textureId != id)
{
if (draws[drawsCounter - 1].vCount > 0) drawsCounter++;
if (draws[drawsCounter - 1].vertexCount > 0) drawsCounter++;
draws[drawsCounter - 1].texId = id;
draws[drawsCounter - 1].firstVertex = draws[drawsCounter - 2].vCount;
draws[drawsCounter - 1].vCount = 0;
draws[drawsCounter - 1].textureId = id;
draws[drawsCounter - 1].vertexCount = 0;
}
#endif
}
@ -708,9 +742,7 @@ void rlglInit()
projectionMatrixLoc = glGetUniformLocation(shaderProgram, "projectionMatrix");
// Get handles to GLSL uniform vars locations (fragment-shader)
textureLoc = glGetUniformLocation(shaderProgram, "texture0");
TraceLog(INFO, "Default shader loaded");
textureLoc = glGetUniformLocation(shaderProgram, "texture0");
InitializeBuffers(); // Init vertex arrays
InitializeVAOs(); // Init VBO and VAO
@ -723,9 +755,9 @@ void rlglInit()
// Create default white texture for plain colors (required by shader)
unsigned char pixels[4] = { 255, 255, 255, 255 }; // 1 pixel RGBA (4 bytes)
whiteTexture = rlglLoadTexture(1, 1, pixels);
whiteTexture = rlglLoadTexture(pixels, 1, 1, false);
if (whiteTexture != 0) TraceLog(INFO, "Base white texture successfully created, id: %i", whiteTexture);
if (whiteTexture != 0) TraceLog(INFO, "[ID %i] Base white texture created successfully", whiteTexture);
else TraceLog(WARNING, "Base white texture could not be created");
// Init draw calls tracking system
@ -733,13 +765,12 @@ void rlglInit()
for (int i = 0; i < MAX_DRAWS_BY_TEXTURE; i++)
{
draws[i].texId = 0;
draws[i].firstVertex = 0;
draws[i].vCount = 0;
draws[i].textureId = 0;
draws[i].vertexCount = 0;
}
drawsCounter = 1;
draws[drawsCounter - 1].texId = whiteTexture;
draws[drawsCounter - 1].textureId = whiteTexture;
}
// Vertex Buffer Object deinitialization (memory free)
@ -789,6 +820,8 @@ void rlglClose()
// Free GPU texture
glDeleteTextures(1, &whiteTexture);
free(draws);
}
void rlglDraw()
@ -823,7 +856,7 @@ void rlglDraw()
if (quads.vCounter > 0)
{
int numQuads = 0;
int quadsCount = 0;
int numIndicesToProcess = 0;
int indicesOffset = 0;
@ -836,21 +869,21 @@ void rlglDraw()
glBindVertexArray(vaoQuads);
}
//TraceLog(INFO, "Draws required per frame: %i", drawsCounter);
//TraceLog(DEBUG, "Draws required per frame: %i", drawsCounter);
for (int i = 0; i < drawsCounter; i++)
{
numQuads = draws[i].vCount/4;
numIndicesToProcess = numQuads*6; // Get number of Quads * 6 index by Quad
quadsCount = draws[i].vertexCount/4;
numIndicesToProcess = quadsCount*6; // Get number of Quads * 6 index by Quad
//TraceLog(INFO, "Quads to render: %i - Vertex Count: %i", numQuads, draws[i].vCount);
//TraceLog(DEBUG, "Quads to render: %i - Vertex Count: %i", quadsCount, draws[i].vertexCount);
glBindTexture(GL_TEXTURE_2D, draws[i].texId);
glBindTexture(GL_TEXTURE_2D, draws[i].textureId);
// NOTE: The final parameter tells the GPU the offset in bytes from the start of the index buffer to the location of the first index to process
glDrawElements(GL_TRIANGLES, numIndicesToProcess, GL_UNSIGNED_INT, (GLvoid*) (sizeof(GLuint) * indicesOffset));
indicesOffset += draws[i].vCount/4*6;
indicesOffset += draws[i].vertexCount/4*6;
}
}
@ -859,9 +892,8 @@ void rlglDraw()
// Reset draws counter
drawsCounter = 1;
draws[0].texId = whiteTexture;
draws[0].firstVertex = 0;
draws[0].vCount = 0;
draws[0].textureId = whiteTexture;
draws[0].vertexCount = 0;
// Reset vertex counters for next frame
lines.vCounter = 0;
@ -883,52 +915,71 @@ void rlglDraw()
#endif // End for OpenGL 3.3+ and ES2 only functions
// Draw a 3d model
void rlglDrawModel(Model model, Vector3 position, float scale, bool wires)
void rlglDrawModel(Model model, Vector3 position, Vector3 rotation, Vector3 scale, Color color, bool wires)
{
if (wires) glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
#ifdef USE_OPENGL_11
// NOTE: For models we use Vertex Arrays (OpenGL 1.1)
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, model.textureId);
// 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
glEnableClientState(GL_NORMAL_ARRAY); // Enable normals array
glVertexPointer(3, GL_FLOAT, 0, model.data.vertices); // Pointer to vertex coords array
glTexCoordPointer(2, GL_FLOAT, 0, model.data.texcoords); // Pointer to texture coords array
glNormalPointer(GL_FLOAT, 0, model.data.normals); // Pointer to normals array
glVertexPointer(3, GL_FLOAT, 0, model.mesh.vertices); // Pointer to vertex coords array
glTexCoordPointer(2, GL_FLOAT, 0, model.mesh.texcoords); // Pointer to texture coords array
glNormalPointer(GL_FLOAT, 0, model.mesh.normals); // Pointer to normals array
//glColorPointer(4, GL_UNSIGNED_BYTE, 0, model.colors); // Pointer to colors array (NOT USED)
//TraceLog(DEBUG, "Drawing model.mesh, VertexCount: %i", model.mesh.vertexCount);
rlPushMatrix();
rlTranslatef(position.x, position.y, position.z);
//rlRotatef(rotation * GetFrameTime(), 0, 1, 0);
rlScalef(scale, scale, scale);
rlScalef(scale.x, scale.y, scale.z);
//rlRotatef(rotation, 0, 1, 0);
rlColor4ub(1.0f, 1.0f, 1.0f, 1.0f);
// TODO: If rotate in multiple axis, get rotation matrix and use rlMultMatrix()
glDrawArrays(GL_TRIANGLES, 0, model.data.numVertices);
rlColor4ub(color.r, color.g, color.b, color.a);
glDrawArrays(GL_TRIANGLES, 0, model.mesh.vertexCount);
rlPopMatrix();
glDisableClientState(GL_VERTEX_ARRAY); // Disable vertex array
glDisableClientState(GL_TEXTURE_COORD_ARRAY); // Disable texture coords array
glDisableClientState(GL_NORMAL_ARRAY); // Disable normals array
glDisable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, 0);
#endif
#if defined(USE_OPENGL_33) || defined(USE_OPENGL_ES2)
glUseProgram(shaderProgram); // Use our shader
Matrix modelview2 = MatrixMultiply(model.transform, modelview);
// Get transform matrix (rotation -> scale -> translation)
Matrix transform = MatrixTransform(position, rotation, scale);
Matrix modelviewworld = MatrixMultiply(transform, modelview);
// NOTE: Drawing in OpenGL 3.3+, transform is passed to shader
glUniformMatrix4fv(projectionMatrixLoc, 1, false, GetMatrixVector(projection));
glUniformMatrix4fv(modelviewMatrixLoc, 1, false, GetMatrixVector(modelview2));
glUniformMatrix4fv(modelviewMatrixLoc, 1, false, GetMatrixVector(modelviewworld));
glUniform1i(textureLoc, 0);
//TraceLog(DEBUG, "ShaderProgram: %i, VAO ID: %i, VertexCount: %i", shaderProgram, model.vaoId, model.mesh.vertexCount);
glBindVertexArray(model.vaoId);
//glBindTexture(GL_TEXTURE_2D, model.textureId);
// TODO: Update vertex color
glBindBuffer(GL_ARRAY_BUFFER, linesBuffer[1]);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*4*model.mesh.vertexCount, model.mesh.colors);
glBindTexture(GL_TEXTURE_2D, model.textureId);
glDrawArrays(GL_TRIANGLES, 0, model.numVertices);
glDrawArrays(GL_TRIANGLES, 0, model.mesh.vertexCount);
//glBindTexture(GL_TEXTURE_2D, 0); // Unbind textures
glBindTexture(GL_TEXTURE_2D, 0); // Unbind textures
glBindVertexArray(0); // Unbind VAO
#endif
@ -982,8 +1033,7 @@ void rlglInitGraphicsDevice(int fbWidth, int fbHeight)
}
// Convert image data to OpenGL texture (returns OpenGL valid Id)
// NOTE: Image is not unloaded, it should be done manually...
unsigned int rlglLoadTexture(int width, int height, unsigned char *data)
unsigned int rlglLoadTexture(unsigned char *data, int width, int height, bool genMipmaps)
{
glBindTexture(GL_TEXTURE_2D,0); // Free any old binding
@ -996,27 +1046,82 @@ unsigned int rlglLoadTexture(int width, int height, unsigned char *data)
// NOTE: glTexParameteri does NOT affect texture uploading, just the way it's used!
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // Set texture to repead on x-axis
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); // Set texture to repead on y-axis
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // Filter for pixel-perfect drawing, alternative: GL_LINEAR
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); // Filter for pixel-perfect drawing, alternative: GL_LINEAR
#if defined(USE_OPENGL_33) || defined(USE_OPENGL_ES2)
// Trilinear filtering
//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); // Activate use of mipmaps (must be available)
//glGenerateMipmap(GL_TEXTURE_2D); // OpenGL 3.3!
#endif
// NOTE: Not using mipmappings (texture for 2D drawing)
// At this point we have the image converted to texture and uploaded to GPU
bool texIsPOT = false;
// Check if width and height are power-of-two (POT)
if (((width > 0) && ((width & (width - 1)) == 0)) && ((height > 0) && ((height & (height - 1)) == 0))) texIsPOT = true;
if (!texIsPOT)
{
TraceLog(WARNING, "[ID %i] Texture is not power-of-two, mipmaps can not be generated", id);
genMipmaps = false;
}
// If mipmaps are being used, we configure mag-min filters accordingly
if (genMipmaps)
{
// Trilinear filtering with mipmaps
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); // Activate use of mipmaps (must be available)
}
else
{
// Not using mipmappings
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // Filter for pixel-perfect drawing, alternative: GL_LINEAR
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); // Filter for pixel-perfect drawing, alternative: GL_LINEAR
}
#ifdef USE_OPENGL_11
if (genMipmaps)
{
TraceLog(WARNING, "[ID %i] Mipmaps generated manually on CPU side", id);
// Compute required mipmaps
// NOTE: data size is reallocated to fit mipmaps data
int mipmapCount = GenerateMipmaps(data, width, height);
int offset = 0;
int size = 0;
int mipWidth = width;
int mipHeight = height;
// Load the mipmaps
for (int level = 0; level < mipmapCount; level++)
{
glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA8, mipWidth, mipHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, data + offset);
size = mipWidth*mipHeight*4;
offset += size;
mipWidth /= 2;
mipHeight /= 2;
}
}
else glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
#endif
#if defined(USE_OPENGL_33) || defined(USE_OPENGL_ES2)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
if (genMipmaps)
{
glGenerateMipmap(GL_TEXTURE_2D); // Generate mipmaps automatically
TraceLog(INFO, "[ID %i] Mipmaps generated automatically for new texture", id);
}
#endif
// At this point we have the image converted to texture and uploaded to GPU
// Unbind current texture
glBindTexture(GL_TEXTURE_2D, 0);
TraceLog(INFO, "New texture created, id: %i (%i x %i)", id, width, height);
TraceLog(INFO, "[ID %i] New texture created (%i x %i)", id, width, height);
return id;
}
@ -1024,51 +1129,42 @@ unsigned int rlglLoadTexture(int width, int height, unsigned char *data)
#ifdef USE_OPENGL_33
#define FOURCC_DXT1 0x31545844 // Equivalent to "DXT1" in ASCII
#define FOURCC_DXT3 0x33545844 // Equivalent to "DXT3" in ASCII
#define FOURCC_DXT5 0x35545844 // Equivalent to "DXT5" in ASCII
// Convert image data to OpenGL texture (returns OpenGL valid Id)
// NOTE: Expected compressed data from DDS file
unsigned int rlglLoadCompressedTexture(unsigned char *data, int width, int height, int mipmapCount, int format)
// NOTE: Expected compressed image data and POT image
unsigned int rlglLoadCompressedTexture(unsigned char *data, int width, int height, int mipmapCount, int compFormat)
{
// Create one OpenGL texture
GLuint id;
int compFormat = 0;
glGenTextures(1, &id);
TraceLog(DEBUG, "Compressed texture width: %i", width);
TraceLog(DEBUG, "Compressed texture height: %i", height);
TraceLog(DEBUG, "Compressed texture mipmap levels: %i", mipmapCount);
TraceLog(DEBUG, "Compressed texture format: 0x%x", format);
switch(format)
{
case FOURCC_DXT1: compFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; break;
case FOURCC_DXT3: compFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; break;
case FOURCC_DXT5: compFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; break;
default: compFormat = -1; break;
}
if (compFormat == -1)
TraceLog(DEBUG, "Compressed texture format: 0x%x", compFormat);
if (compFormat == 0)
{
TraceLog(WARNING, "Texture compressed format not recognized");
TraceLog(WARNING, "[ID %i] Texture compressed format not recognized", id);
id = 0;
}
else
{
glGenTextures(1, &id);
// Bind the texture
glBindTexture(GL_TEXTURE_2D, id);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
unsigned int blockSize = (compFormat == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) ? 8 : 16;
unsigned int offset = 0;
int blockSize = 0;
int offset = 0;
if (compFormat == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) blockSize = 8;
else blockSize = 16;
// Load the mipmaps
for (int level = 0; level < mipmapCount && (width || height); level++)
{
unsigned int size = ((width+3)/4)*((height+3)/4)*blockSize;
// NOTE: size specifies the number of bytes of image data (S3TC/DXTC)
unsigned int size = ((width + 3)/4)*((height + 3)/4)*blockSize;
glCompressedTexImage2D(GL_TEXTURE_2D, level, compFormat, width, height, 0, size, data + offset);
@ -1100,20 +1196,28 @@ unsigned int rlglLoadModel(VertexData mesh)
// Enable vertex attributes
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*mesh.numVertices, mesh.vertices, GL_STATIC_DRAW);
glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*mesh.vertexCount, mesh.vertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(vertexLoc);
glVertexAttribPointer(vertexLoc, 3, GL_FLOAT, 0, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer[1]);
glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*mesh.numVertices, mesh.texcoords, GL_STATIC_DRAW);
glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*mesh.vertexCount, mesh.texcoords, GL_STATIC_DRAW);
glEnableVertexAttribArray(texcoordLoc);
glVertexAttribPointer(texcoordLoc, 2, GL_FLOAT, 0, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer[2]);
glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*mesh.numVertices, mesh.normals, GL_STATIC_DRAW);
//glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer[2]);
//glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*mesh.vertexCount, mesh.normals, GL_STATIC_DRAW);
//glEnableVertexAttribArray(normalLoc);
//glVertexAttribPointer(normalLoc, 3, GL_FLOAT, 0, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer[2]);
glBufferData(GL_ARRAY_BUFFER, sizeof(float)*4*mesh.vertexCount, mesh.colors, GL_STATIC_DRAW);
glEnableVertexAttribArray(colorLoc);
glVertexAttribPointer(colorLoc, 4, GL_FLOAT, 0, 0, 0);
if (vaoModel > 0) TraceLog(INFO, "[ID %i] Model uploaded successfully to VRAM (GPU)", vaoModel);
else TraceLog(WARNING, "Model could not be uploaded to VRAM (GPU)");
return vaoModel;
}
#endif
@ -1208,6 +1312,9 @@ static GLuint LoadDefaultShaders()
glCompileShader(vertexShader);
glCompileShader(fragmentShader);
TraceLog(INFO, "[ID %i] Default vertex shader compiled succesfully", vertexShader);
TraceLog(INFO, "[ID %i] Default fragment shader compiled succesfully", fragmentShader);
program = glCreateProgram();
@ -1218,6 +1325,8 @@ static GLuint LoadDefaultShaders()
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
TraceLog(INFO, "[ID %i] Default shader program loaded succesfully", program);
return program;
}
@ -1245,6 +1354,9 @@ static GLuint LoadShaders(char *vertexFileName, char *fragmentFileName)
glCompileShader(vertexShader);
glCompileShader(fragmentShader);
TraceLog(INFO, "[ID %i] Vertex shader compiled succesfully", vertexShader);
TraceLog(INFO, "[ID %i] Fragment shader compiled succesfully", fragmentShader);
program = glCreateProgram();
@ -1255,6 +1367,8 @@ static GLuint LoadShaders(char *vertexFileName, char *fragmentFileName)
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
TraceLog(INFO, "[ID %i] Shader program loaded succesfully", program);
return program;
}
@ -1364,7 +1478,8 @@ static void InitializeVAOs()
glBufferData(GL_ARRAY_BUFFER, sizeof(float)*4*2*MAX_LINES_BATCH, lines.colors, GL_DYNAMIC_DRAW);
glEnableVertexAttribArray(colorLoc);
glVertexAttribPointer(colorLoc, 4, GL_FLOAT, 0, 0, 0);
TraceLog(INFO, "[ID %i] Lines VAO successfully initialized", vaoLines);
//--------------------------------------------------------------
// Initialize Triangles VAO
@ -1384,7 +1499,8 @@ static void InitializeVAOs()
glBufferData(GL_ARRAY_BUFFER, sizeof(float)*4*3*MAX_TRIANGLES_BATCH, triangles.colors, GL_DYNAMIC_DRAW);
glEnableVertexAttribArray(colorLoc);
glVertexAttribPointer(colorLoc, 4, GL_FLOAT, 0, 0, 0);
TraceLog(INFO, "[ID %i] Triangles VAO successfully initialized", vaoTriangles);
//--------------------------------------------------------------
// Initialize Quads VAO (Buffer A)
@ -1414,6 +1530,8 @@ static void InitializeVAOs()
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, quadsBuffer[3]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(int)*6*MAX_QUADS_BATCH, quads.indices, GL_STATIC_DRAW);
TraceLog(INFO, "[ID %i] Quads VAO successfully initialized", vaoQuads);
#ifdef USE_VBO_DOUBLE_BUFFERS
// Initialize Quads VAO (Buffer B)
glGenVertexArrays(1, &vaoQuadsB);
@ -1442,11 +1560,9 @@ static void InitializeVAOs()
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, quadsBufferB[3]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(int)*6*MAX_QUADS_BATCH, quads.indices, GL_STATIC_DRAW);
TraceLog(INFO, "Using VBO double buffering");
TraceLog(INFO, "[ID %i] Second Quads VAO successfully initilized (double buffering)", vaoQuadsB);
#endif
TraceLog(INFO, "Vertex buffers successfully initialized (lines, triangles, quads)\n");
// Unbind the current VAO
glBindVertexArray(0);
}
@ -1540,6 +1656,135 @@ static void UpdateBuffers()
glBindVertexArray(0);
}
#endif //defined(USE_OPENGL_33) || defined(USE_OPENGL_ES2)
#ifdef USE_OPENGL_11
// Mipmaps data is generated after image data
static int GenerateMipmaps(unsigned char *data, int baseWidth, int baseHeight)
{
int mipmapCount = 1; // Required mipmap levels count (including base level)
int width = baseWidth;
int height = baseHeight;
int size = baseWidth*baseHeight*4; // Size in bytes (will include mipmaps...)
// Count mipmap levels required
while ((width != 1) && (height != 1))
{
if (width != 1) width /= 2;
if (height != 1) height /= 2;
TraceLog(DEBUG, "Next mipmap size: %i x %i", width, height);
mipmapCount++;
size += (width*height*4); // Add mipmap size (in bytes)
}
TraceLog(DEBUG, "Total mipmaps required: %i", mipmapCount);
TraceLog(DEBUG, "Total size of data required: %i", size);
unsigned char *temp = realloc(data, size);
if (temp != NULL) data = temp;
else TraceLog(WARNING, "Mipmaps required memory could not be allocated");
width = baseWidth;
height = baseHeight;
size = (width*height*4);
// Generate mipmaps
// NOTE: Every mipmap data is stored after data
pixel *image = (pixel *)malloc(width*height*sizeof(pixel));
pixel *mipmap = NULL;
int offset = 0;
int j = 0;
for (int i = 0; i < size; i += 4)
{
image[j].r = data[i];
image[j].g = data[i + 1];
image[j].b = data[i + 2];
image[j].a = data[i + 3];
j++;
}
TraceLog(DEBUG, "Mipmap base (%i, %i)", width, height);
for (int mip = 1; mip < mipmapCount; mip++)
{
mipmap = GenNextMipmap(image, width, height);
offset += (width*height*4); // Size of last mipmap
j = 0;
width /= 2;
height /= 2;
size = (width*height*4); // Mipmap size to store after offset
// Add mipmap to data
for (int i = 0; i < size; i += 4)
{
data[offset + i] = mipmap[j].r;
data[offset + i + 1] = mipmap[j].g;
data[offset + i + 2] = mipmap[j].b;
data[offset + i + 3] = mipmap[j].a;
j++;
}
free(image);
image = mipmap;
mipmap = NULL;
}
free(mipmap); // free mipmap data
return mipmapCount;
}
// Manual mipmap generation (basic scaling algorithm)
static pixel *GenNextMipmap(pixel *srcData, int srcWidth, int srcHeight)
{
int x2, y2;
pixel prow, pcol;
int width = srcWidth / 2;
int height = srcHeight / 2;
pixel *mipmap = (pixel *)malloc(width*height*sizeof(pixel));
// Scaling algorithm works perfectly (box-filter)
for (int y = 0; y < height; y++)
{
y2 = 2 * y;
for (int x = 0; x < width; x++)
{
x2 = 2 * x;
prow.r = (srcData[y2*srcWidth + x2].r + srcData[y2*srcWidth + x2 + 1].r)/2;
prow.g = (srcData[y2*srcWidth + x2].g + srcData[y2*srcWidth + x2 + 1].g)/2;
prow.b = (srcData[y2*srcWidth + x2].b + srcData[y2*srcWidth + x2 + 1].b)/2;
prow.a = (srcData[y2*srcWidth + x2].a + srcData[y2*srcWidth + x2 + 1].a)/2;
pcol.r = (srcData[(y2+1)*srcWidth + x2].r + srcData[(y2+1)*srcWidth + x2 + 1].r)/2;
pcol.g = (srcData[(y2+1)*srcWidth + x2].g + srcData[(y2+1)*srcWidth + x2 + 1].g)/2;
pcol.b = (srcData[(y2+1)*srcWidth + x2].b + srcData[(y2+1)*srcWidth + x2 + 1].b)/2;
pcol.a = (srcData[(y2+1)*srcWidth + x2].a + srcData[(y2+1)*srcWidth + x2 + 1].a)/2;
mipmap[y*width + x].r = (prow.r + pcol.r)/2;
mipmap[y*width + x].g = (prow.g + pcol.g)/2;
mipmap[y*width + x].b = (prow.b + pcol.b)/2;
mipmap[y*width + x].a = (prow.a + pcol.a)/2;
}
}
TraceLog(DEBUG, "Mipmap generated successfully (%i, %i)", width, height);
return mipmap;
}
#endif
#ifdef RLGL_STANDALONE
@ -1555,10 +1800,10 @@ void TraceLog(int msgType, const char *text, ...)
switch(msgType)
{
case 0: fprintf(stdout, "INFO: "); break;
case 1: fprintf(stdout, "ERROR: "); break;
case 2: fprintf(stdout, "WARNING: "); break;
case 3: fprintf(logstream, "DEBUG: "); break;
case INFO: fprintf(stdout, "INFO: "); break;
case ERROR: fprintf(stdout, "ERROR: "); break;
case WARNING: fprintf(stdout, "WARNING: "); break;
case DEBUG: fprintf(stdout, "DEBUG: "); break;
default: break;
}
@ -1567,6 +1812,6 @@ void TraceLog(int msgType, const char *text, ...)
va_end(args);
if (msgType == 1) exit(1);
if (msgType == ERROR) exit(1);
}
#endif

View File

@ -36,19 +36,19 @@
#include "utils.h" // Required for function TraceLog()
#endif
#include "raymath.h" // Required for data type Matrix and Matrix functions
#include "raymath.h" // Required for data type Matrix and Matrix functions
// Select desired OpenGL version
//#define USE_OPENGL_11
#define USE_OPENGL_33
#define USE_OPENGL_11
//#define USE_OPENGL_33
//#define USE_OPENGL_ES2
//----------------------------------------------------------------------------------
// Defines and Macros
//----------------------------------------------------------------------------------
#define MAX_LINES_BATCH 8192 // 1024
#define MAX_TRIANGLES_BATCH 2048
#define MAX_QUADS_BATCH 8192
#define MAX_LINES_BATCH 8192 // NOTE: Be careful with limits!
#define MAX_TRIANGLES_BATCH 4096 // NOTE: Be careful with limits!
#define MAX_QUADS_BATCH 8192 // NOTE: Be careful with limits!
//----------------------------------------------------------------------------------
// Types and Structures Definition
@ -60,26 +60,20 @@ typedef enum { RL_PROJECTION, RL_MODELVIEW, RL_TEXTURE } MatrixMode;
typedef enum { RL_LINES, RL_TRIANGLES, RL_QUADS } DrawMode;
#ifdef RLGL_STANDALONE
typedef struct Model Model;
#endif
typedef struct {
int vertexCount;
float *vertices; // 3 components per vertex
float *texcoords; // 2 components per vertex
float *normals; // 3 components per vertex
float *colors;
} VertexData;
typedef struct {
int numVertices;
float *vertices; // 3 components per vertex
float *texcoords; // 2 components per vertex
float *normals; // 3 components per vertex
} VertexData;
#ifdef USE_OPENGL_11
struct Model {
VertexData data;
};
#else
struct Model {
unsigned int vaoId;
Matrix transform;
int numVertices;
};
typedef struct Model {
VertexData mesh;
unsigned int vaoId;
unsigned int textureId;
//Matrix transform;
} Model;
#endif
#ifdef __cplusplus
@ -90,8 +84,8 @@ extern "C" { // Prevents name mangling of functions
// Functions Declaration - Matrix operations
//------------------------------------------------------------------------------------
void rlMatrixMode(int mode); // Choose the current matrix to be transformed
void rlPushMatrix(); // TODO: REVIEW: Required? - Push the current matrix to stack
void rlPopMatrix(); // TODO: REVIEW: Required? - Pop lattest inserted matrix from stack
void rlPushMatrix(); // Push the current matrix to stack
void rlPopMatrix(); // Pop lattest inserted matrix from stack
void rlLoadIdentity(); // Reset current matrix to identity matrix
void rlTranslatef(float x, float y, float z); // Multiply the current matrix by a translation matrix
void rlRotatef(float angleDeg, float x, float y, float z); // Multiply the current matrix by a rotation matrix
@ -132,13 +126,14 @@ void rlClearScreenBuffers(); // Clear used screen buffers (color
void rlglInit(); // Initialize rlgl (shaders, VAO, VBO...)
void rlglClose(); // De-init rlgl
void rlglDraw(); // Draw VAOs
unsigned int rlglLoadModel(VertexData data);
unsigned int rlglLoadModel(VertexData mesh);
unsigned int rlglLoadCompressedTexture(unsigned char *data, int width, int height, int mipmapCount, int format);
#endif
void rlglDrawModel(Model model, Vector3 position, float scale, bool wires); // Draw model
void rlglDrawModel(Model model, Vector3 position, Vector3 rotation, Vector3 scale, Color color, bool wires);
void rlglInitGraphicsDevice(int fbWidth, int fbHeight); // Initialize Graphics Device (OpenGL stuff)
unsigned int rlglLoadTexture(int width, int height, unsigned char *pixels); // Load in GPU OpenGL texture
unsigned int rlglLoadTexture(unsigned char *data, int width, int height, bool genMipmaps); // Load in GPU OpenGL texture
byte *rlglReadScreenPixels(int width, int height); // Read screen pixel data (color buffer)
#if defined(USE_OPENGL_33) || defined(USE_OPENGL_ES2)

View File

@ -112,6 +112,7 @@ void DrawCircleGradient(int centerX, int centerY, float radius, Color color1, Co
rlVertex2i(centerX, centerY);
rlColor4ub(color2.r, color2.g, color2.b, color2.a);
rlVertex2f(centerX + sin(DEG2RAD*i) * radius, centerY + cos(DEG2RAD*i) * radius);
rlColor4ub(color2.r, color2.g, color2.b, color2.a);
rlVertex2f(centerX + sin(DEG2RAD*(i+2)) * radius, centerY + cos(DEG2RAD*(i+2)) * radius);
}
rlEnd();
@ -149,17 +150,10 @@ void DrawCircleLines(int centerX, int centerY, float radius, Color color)
// Draw a color-filled rectangle
void DrawRectangle(int posX, int posY, int width, int height, Color color)
{
rlBegin(RL_QUADS);
rlColor4ub(color.r, color.g, color.b, color.a);
rlTexCoord2f(0.0f, 0.0f);
rlVertex2i(posX, posY);
rlTexCoord2f(0.0f, 1.0f);
rlVertex2i(posX, posY + height);
rlTexCoord2f(1.0f, 1.0f);
rlVertex2i(posX + width, posY + height);
rlTexCoord2f(1.0f, 0.0f);
rlVertex2i(posX + width, posY);
rlEnd();
Vector2 position = { (float)posX, (float)posY };
Vector2 size = { (float)width, (float)height };
DrawRectangleV(position, size, color);
}
// Draw a color-filled rectangle
@ -172,26 +166,29 @@ void DrawRectangleRec(Rectangle rec, Color color)
// NOTE: Gradient goes from bottom (color1) to top (color2)
void DrawRectangleGradient(int posX, int posY, int width, int height, Color color1, Color color2)
{
rlBegin(RL_QUADS);
rlColor4ub(color1.r, color1.g, color1.b, color1.a);
rlVertex2i(posX, posY);
rlColor4ub(color1.r, color1.g, color1.b, color1.a);
rlVertex2i(posX, posY + height);
rlColor4ub(color2.r, color2.g, color2.b, color2.a);
rlVertex2i(posX + width, posY + height);
rlColor4ub(color2.r, color2.g, color2.b, color2.a);
rlVertex2i(posX + width, posY);
rlBegin(RL_TRIANGLES);
rlColor4ub(color1.r, color1.g, color1.b, color1.a); rlVertex2i(posX, posY);
rlColor4ub(color2.r, color2.g, color2.b, color2.a); rlVertex2i(posX, posY + height);
rlColor4ub(color2.r, color2.g, color2.b, color2.a); rlVertex2i(posX + width, posY + height);
rlColor4ub(color1.r, color1.g, color1.b, color1.a); rlVertex2i(posX, posY);
rlColor4ub(color2.r, color2.g, color2.b, color2.a); rlVertex2i(posX + width, posY + height);
rlColor4ub(color1.r, color1.g, color1.b, color1.a); rlVertex2i(posX + width, posY);
rlEnd();
}
// Draw a color-filled rectangle (Vector version)
void DrawRectangleV(Vector2 position, Vector2 size, Color color)
{
rlBegin(RL_QUADS);
rlBegin(RL_TRIANGLES);
rlColor4ub(color.r, color.g, color.b, color.a);
rlVertex2i(position.x, position.y);
rlVertex2i(position.x, position.y + size.y);
rlVertex2i(position.x + size.x, position.y + size.y);
rlVertex2i(position.x, position.y);
rlVertex2i(position.x + size.x, position.y + size.y);
rlVertex2i(position.x + size.x, position.y);
rlEnd();
}

View File

@ -35,6 +35,8 @@
#include "rlgl.h" // raylib OpenGL abstraction layer to OpenGL 1.1, 3.3+ or ES2
#include "utils.h" // Required for function GetExtendion()
//----------------------------------------------------------------------------------
// Defines and Macros
//----------------------------------------------------------------------------------
@ -58,15 +60,6 @@ typedef struct Character {
int h;
} Character;
// SpriteFont type, includes texture and charSet array data
/*
struct SpriteFont {
Texture2D texture;
int numChars;
Character *charSet;
};
*/
//----------------------------------------------------------------------------------
// Global variables
//----------------------------------------------------------------------------------
@ -85,7 +78,6 @@ static bool PixelIsMagenta(Color p); // Check if a pixel is magen
static int ParseImageData(Color *imgDataPixel, int imgWidth, int imgHeight, Character **charSet); // Parse image pixel data to obtain character set measures
static int GetNextPOT(int num); // Calculate next power-of-two value for a given value
static SpriteFont LoadRBMF(const char *fileName); // Load a rBMF font file (raylib BitMap Font)
static const char *GetExtension(const char *fileName);
//----------------------------------------------------------------------------------
// Module Functions Definition
@ -153,8 +145,7 @@ extern void LoadDefaultFont()
if (counter > 256) counter = 0; // Security check...
}
defaultFont.texture = CreateTexture(image); // Convert loaded image to OpenGL texture
defaultFont.texture = CreateTexture(image, false); // Convert loaded image to OpenGL texture
UnloadImage(image);
// Reconstruct charSet using charsWidth[], charsHeight, charsDivisor, numChars
@ -192,7 +183,7 @@ extern void LoadDefaultFont()
extern void UnloadDefaultFont()
{
rlDeleteTextures(defaultFont.texture.glId);
rlDeleteTextures(defaultFont.texture.id);
free(defaultFont.charSet);
}
@ -277,8 +268,7 @@ SpriteFont LoadSpriteFont(const char* fileName)
image.width = potWidth;
image.height = potHeight;
spriteFont.texture = CreateTexture(image); // Convert loaded image to OpenGL texture
spriteFont.texture = CreateTexture(image, false); // Convert loaded image to OpenGL texture
UnloadImage(image);
}
@ -288,7 +278,7 @@ SpriteFont LoadSpriteFont(const char* fileName)
// Unload SpriteFont from GPU memory
void UnloadSpriteFont(SpriteFont spriteFont)
{
rlDeleteTextures(spriteFont.texture.glId);
rlDeleteTextures(spriteFont.texture.id);
free(spriteFont.charSet);
}
@ -322,7 +312,7 @@ void DrawTextEx(SpriteFont spriteFont, const char* text, Vector2 position, int f
if (fontSize <= spriteFont.charSet[0].h) scaleFactor = 1.0f;
else scaleFactor = (float)fontSize / spriteFont.charSet[0].h;
rlEnableTexture(spriteFont.texture.glId);
rlEnableTexture(spriteFont.texture.id);
rlBegin(RL_QUADS);
for(int i = 0; i < length; i++)
@ -513,11 +503,11 @@ static int GetNextPOT(int num)
if (num != 0)
{
num--;
num |= (num >> 1); // Or first 2 bits
num |= (num >> 1); // Or first 2 bits
num |= (num >> 2); // Or next 2 bits
num |= (num >> 4); // Or next 4 bits
num |= (num >> 8); // Or next 8 bits
num |= (num >> 16); // Or next 16 bits
num |= (num >> 16); // Or next 16 bits
num++;
}
@ -596,8 +586,7 @@ static SpriteFont LoadRBMF(const char *fileName)
TraceLog(INFO, "[%s] Image reconstructed correctly, now converting it to texture", fileName);
spriteFont.texture = CreateTexture(image);
spriteFont.texture = CreateTexture(image, false);
UnloadImage(image); // Unload image data
TraceLog(INFO, "[%s] Starting charSet reconstruction", fileName);
@ -641,10 +630,12 @@ static SpriteFont LoadRBMF(const char *fileName)
return spriteFont;
}
// Get the extension for a filename
static const char *GetExtension(const char *fileName)
// Generate a sprite font from TTF data (font size required)
static SpriteFont GenerateFromTTF(const char *fileName, int fontSize)
{
const char *dot = strrchr(fileName, '.');
if(!dot || dot == fileName) return "";
return dot + 1;
SpriteFont font;
// TODO: Load TTF and generate bitmap font and chars data
return font;
}

View File

@ -46,12 +46,14 @@
typedef unsigned char byte;
typedef struct {
unsigned char *data;
int width;
int height;
int mipmaps;
int format;
} ImageDDS;
unsigned char *data; // Image raw data
int width; // Image base width
int height; // Image base height
//int bpp; // bytes per pixel
//int components; // num color components
int mipmaps; // Mipmap levels, 1 by default
int compFormat; // Compressed data format, 0 if no compression
} ImageEx;
//----------------------------------------------------------------------------------
// Global Variables Definition
@ -66,8 +68,7 @@ typedef struct {
//----------------------------------------------------------------------------------
// Module specific Functions Declaration
//----------------------------------------------------------------------------------
static const char *GetExtension(const char *fileName);
static ImageDDS LoadDDS(const char *fileName);
static ImageEx LoadDDS(const char *fileName);
//----------------------------------------------------------------------------------
// Module Functions Definition
@ -78,6 +79,11 @@ Image LoadImage(const char *fileName)
{
Image image;
// Initial values
image.pixels = NULL;
image.width = 0;
image.height = 0;
if ((strcmp(GetExtension(fileName),"png") == 0) ||
(strcmp(GetExtension(fileName),"bmp") == 0) ||
(strcmp(GetExtension(fileName),"tga") == 0) ||
@ -115,6 +121,35 @@ Image LoadImage(const char *fileName)
TraceLog(INFO, "[%s] Image loaded successfully", fileName);
}
else if (strcmp(GetExtension(fileName),"dds") == 0)
{
// NOTE: DDS uncompressed images can also be loaded (discarding mipmaps...)
ImageEx imageDDS = LoadDDS(fileName);
if (imageDDS.compFormat == 0)
{
image.pixels = (Color *)malloc(imageDDS.width * imageDDS.height * sizeof(Color));
image.width = imageDDS.width;
image.height = imageDDS.height;
int pix = 0;
for (int i = 0; i < (image.width * image.height * 4); i += 4)
{
image.pixels[pix].r = imageDDS.data[i];
image.pixels[pix].g = imageDDS.data[i+1];
image.pixels[pix].b = imageDDS.data[i+2];
image.pixels[pix].a = imageDDS.data[i+3];
pix++;
}
free(imageDDS.data);
TraceLog(INFO, "[%s] Image loaded successfully", fileName);
}
else TraceLog(WARNING, "[%s] Compressed image data could not be loaded", fileName);
}
else TraceLog(WARNING, "[%s] Image extension not recognized, it can't be loaded", fileName);
// ALTERNATIVE: We can load pixel data directly into Color struct pixels array,
@ -127,7 +162,8 @@ Image LoadImage(const char *fileName)
// Load an image from rRES file (raylib Resource)
Image LoadImageFromRES(const char *rresName, int resId)
{
// NOTE: rresName could be directly a char array with all the data!!! ---> TODO!
// TODO: rresName could be directly a char array with all the data! --> support it! :P
Image image;
bool found = false;
@ -172,8 +208,8 @@ Image LoadImageFromRES(const char *rresName, int resId)
if (infoHeader.type == 0) // IMAGE data type
{
// TODO: Check data compression type
// NOTE: We suppose compression type 2 (DEFLATE - default)
short imgWidth, imgHeight;
char colorFormat, mipmaps;
@ -220,11 +256,11 @@ Image LoadImageFromRES(const char *rresName, int resId)
// Depending on type, skip the right amount of parameters
switch (infoHeader.type)
{
case 0: fseek(rresFile, 6, SEEK_CUR); break; // IMAGE: Jump 6 bytes of parameters
case 1: fseek(rresFile, 6, SEEK_CUR); break; // SOUND: Jump 6 bytes of parameters
case 2: fseek(rresFile, 5, SEEK_CUR); break; // MODEL: Jump 5 bytes of parameters (TODO: Review)
case 3: break; // TEXT: No parameters
case 4: break; // RAW: No parameters
case 0: fseek(rresFile, 6, SEEK_CUR); break; // IMAGE: Jump 6 bytes of parameters
case 1: fseek(rresFile, 6, SEEK_CUR); break; // SOUND: Jump 6 bytes of parameters
case 2: fseek(rresFile, 5, SEEK_CUR); break; // MODEL: Jump 5 bytes of parameters (TODO: Review)
case 3: break; // TEXT: No parameters
case 4: break; // RAW: No parameters
default: break;
}
@ -249,19 +285,26 @@ Texture2D LoadTexture(const char *fileName)
if (strcmp(GetExtension(fileName),"dds") == 0)
{
#ifdef USE_OPENGL_11
TraceLog(WARNING, "[%s] DDS file loading requires OpenGL 3.2+ or ES 2.0", fileName);
#else
ImageDDS image = LoadDDS(fileName);
ImageEx image = LoadDDS(fileName);
if (image.compFormat == 0)
{
texture.id = rlglLoadTexture(image.data, image.width, image.height, false);
}
else
{
#ifdef USE_OPENGL_33
texture.id = rlglLoadCompressedTexture(image.data, image.width, image.height, image.mipmaps, image.compFormat);
#endif
}
texture.glId = rlglLoadCompressedTexture(image.data, image.width, image.height, image.mipmaps, image.format);
texture.width = image.width;
texture.height = image.height;
if (texture.glId == 0) TraceLog(WARNING, "Compressed texture could not be loaded");
else TraceLog(INFO, "Compressed texture loaded succesfully");
#endif
if (texture.id == 0) TraceLog(WARNING, "[%s] DDS texture could not be loaded", fileName);
else TraceLog(INFO, "[%s] DDS texture loaded succesfully", fileName);
free(image.data);
}
else
{
@ -269,7 +312,7 @@ Texture2D LoadTexture(const char *fileName)
if (image.pixels != NULL)
{
texture = CreateTexture(image);
texture = CreateTexture(image, false);
UnloadImage(image);
}
}
@ -283,7 +326,8 @@ Texture2D LoadTextureFromRES(const char *rresName, int resId)
Texture2D texture;
Image image = LoadImageFromRES(rresName, resId);
texture = CreateTexture(image);
texture = CreateTexture(image, false);
UnloadImage(image);
return texture;
}
@ -297,7 +341,7 @@ void UnloadImage(Image image)
// Unload texture from GPU memory
void UnloadTexture(Texture2D texture)
{
rlDeleteTextures(texture.glId);
rlDeleteTextures(texture.id);
}
// Draw a Texture2D
@ -315,76 +359,32 @@ void DrawTextureV(Texture2D texture, Vector2 position, Color tint)
// Draw a Texture2D with extended parameters
void DrawTextureEx(Texture2D texture, Vector2 position, float rotation, float scale, Color tint)
{
rlEnableTexture(texture.glId);
Rectangle sourceRec = { 0, 0, texture.width, texture.height };
Rectangle destRec = { (int)position.x, (int)position.y, texture.width*scale, texture.height*scale };
Vector2 origin = { 0, 0 };
// NOTE: Rotation is applied before translation and scaling, even being called in inverse order...
// NOTE: Rotation point is upper-left corner
rlPushMatrix();
//rlTranslatef(position.x, position.y, 0.0);
rlRotatef(rotation, 0, 0, 1);
rlScalef(scale, scale, 1.0f);
rlBegin(RL_QUADS);
rlColor4ub(tint.r, tint.g, tint.b, tint.a);
rlNormal3f(0.0f, 0.0f, 1.0f); // Normal vector pointing towards viewer
rlTexCoord2f(0.0f, 0.0f);
rlVertex2f(position.x, position.y); // Bottom-left corner for texture and quad
rlTexCoord2f(0.0f, 1.0f);
rlVertex2f(position.x, position.y + texture.height); // Bottom-right corner for texture and quad
rlTexCoord2f(1.0f, 1.0f);
rlVertex2f(position.x + texture.width, position.y + texture.height); // Top-right corner for texture and quad
rlTexCoord2f(1.0f, 0.0f);
rlVertex2f(position.x + texture.width, position.y); // Top-left corner for texture and quad
rlEnd();
rlPopMatrix();
rlDisableTexture();
DrawTexturePro(texture, sourceRec, destRec, origin, rotation, tint);
}
// Draw a part of a texture (defined by a rectangle)
void DrawTextureRec(Texture2D texture, Rectangle sourceRec, Vector2 position, Color tint)
{
rlEnableTexture(texture.glId);
Rectangle destRec = { (int)position.x, (int)position.y, sourceRec.width, sourceRec.height };
Vector2 origin = { 0, 0 };
rlBegin(RL_QUADS);
rlColor4ub(tint.r, tint.g, tint.b, tint.a);
rlNormal3f(0.0f, 0.0f, 1.0f); // Normal vector pointing towards viewer
// Bottom-left corner for texture and quad
rlTexCoord2f((float)sourceRec.x / texture.width, (float)sourceRec.y / texture.height);
rlVertex2f(position.x, position.y);
// Bottom-right corner for texture and quad
rlTexCoord2f((float)sourceRec.x / texture.width, (float)(sourceRec.y + sourceRec.height) / texture.height);
rlVertex2f(position.x, position.y + sourceRec.height);
// Top-right corner for texture and quad
rlTexCoord2f((float)(sourceRec.x + sourceRec.width) / texture.width, (float)(sourceRec.y + sourceRec.height) / texture.height);
rlVertex2f(position.x + sourceRec.width, position.y + sourceRec.height);
// Top-left corner for texture and quad
rlTexCoord2f((float)(sourceRec.x + sourceRec.width) / texture.width, (float)sourceRec.y / texture.height);
rlVertex2f(position.x + sourceRec.width, position.y);
rlEnd();
rlDisableTexture();
DrawTexturePro(texture, sourceRec, destRec, origin, 0, tint);
}
// Draw a part of a texture (defined by a rectangle) with 'pro' parameters
// TODO: Test this function...
// NOTE: origin is relative to destination rectangle size
void DrawTexturePro(Texture2D texture, Rectangle sourceRec, Rectangle destRec, Vector2 origin, float rotation, Color tint)
{
rlEnableTexture(texture.glId);
rlEnableTexture(texture.id);
// NOTE: First we translate texture to origin to apply rotation and translation from there
rlPushMatrix();
rlTranslatef(-origin.x, -origin.y, 0);
rlTranslatef(destRec.x, destRec.y, 0);
rlRotatef(rotation, 0, 0, 1);
rlTranslatef(destRec.x + origin.x, destRec.y + origin.y, 0);
rlTranslatef(-origin.x, -origin.y, 0);
rlBegin(RL_QUADS);
rlColor4ub(tint.r, tint.g, tint.b, tint.a);
@ -395,65 +395,84 @@ void DrawTexturePro(Texture2D texture, Rectangle sourceRec, Rectangle destRec, V
rlVertex2f(0.0f, 0.0f);
// Bottom-right corner for texture and quad
rlTexCoord2f((float)(sourceRec.x + sourceRec.width) / texture.width, (float)sourceRec.y / texture.height);
rlVertex2f(destRec.width, 0.0f);
rlTexCoord2f((float)sourceRec.x / texture.width, (float)(sourceRec.y + sourceRec.height) / texture.height);
rlVertex2f(0.0f, destRec.height);
// Top-right corner for texture and quad
rlTexCoord2f((float)(sourceRec.x + sourceRec.width) / texture.width, (float)(sourceRec.y + sourceRec.height) / texture.height);
rlVertex2f(destRec.width, destRec.height);
// Top-left corner for texture and quad
rlTexCoord2f((float)sourceRec.x / texture.width, (float)(sourceRec.y + sourceRec.height) / texture.height);
rlVertex2f(0.0f, destRec.height);
rlTexCoord2f((float)(sourceRec.x + sourceRec.width) / texture.width, (float)sourceRec.y / texture.height);
rlVertex2f(destRec.width, 0.0f);
rlEnd();
rlPopMatrix();
rlDisableTexture();
}
Texture2D CreateTexture(Image image)
// Create a texture from an image
// NOTE: image is not unloaded, iot must be done manually
Texture2D CreateTexture(Image image, bool genMipmaps)
{
Texture2D texture;
unsigned char *img = malloc(image.width * image.height * 4);
// Init texture to default values
texture.id = 0;
texture.width = 0;
texture.height = 0;
int j = 0;
for (int i = 0; i < image.width * image.height * 4; i += 4)
if (image.pixels != NULL)
{
img[i] = image.pixels[j].r;
img[i+1] = image.pixels[j].g;
img[i+2] = image.pixels[j].b;
img[i+3] = image.pixels[j].a;
unsigned char *imgData = malloc(image.width * image.height * 4);
j++;
int j = 0;
for (int i = 0; i < image.width * image.height * 4; i += 4)
{
imgData[i] = image.pixels[j].r;
imgData[i+1] = image.pixels[j].g;
imgData[i+2] = image.pixels[j].b;
imgData[i+3] = image.pixels[j].a;
j++;
}
// NOTE: rlglLoadTexture() can generate mipmaps (POT image required)
texture.id = rlglLoadTexture(imgData, image.width, image.height, genMipmaps);
texture.width = image.width;
texture.height = image.height;
TraceLog(INFO, "[ID %i] Texture created succesfully", texture.id);
free(imgData);
}
texture.glId = rlglLoadTexture(image.width, image.height, img);
texture.width = image.width;
texture.height = image.height;
TraceLog(INFO, "Texture created succesfully");
free(img);
else TraceLog(WARNING, "Texture could not be created, image data is not valid");
return texture;
}
// Get the extension for a filename
static const char *GetExtension(const char *fileName)
{
const char *dot = strrchr(fileName, '.');
if(!dot || dot == fileName) return "";
return (dot + 1);
}
// Loading DDS image compressed data
ImageDDS LoadDDS(const char *fileName)
// Loading DDS image data (compressed or uncompressed)
// NOTE: Compressed data loading not supported on OpenGL 1.1
ImageEx LoadDDS(const char *fileName)
{
// TODO: Review and expand DDS file loading to support uncompressed formats and new formats
#define FOURCC_DXT1 0x31545844 // Equivalent to "DXT1" in ASCII
#define FOURCC_DXT3 0x33545844 // Equivalent to "DXT3" in ASCII
#define FOURCC_DXT5 0x35545844 // Equivalent to "DXT5" in ASCII
#ifndef GL_COMPRESSED_RGBA_S3TC_DXT1_EXT
#define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1
#endif
#ifndef GL_COMPRESSED_RGBA_S3TC_DXT3_EXT
#define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2
#endif
#ifndef GL_COMPRESSED_RGBA_S3TC_DXT5_EXT
#define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3
#endif
// DDS Pixel Format
typedef struct {
unsigned int size;
@ -484,7 +503,7 @@ ImageDDS LoadDDS(const char *fileName)
unsigned int reserved2;
} ddsHeader;
ImageDDS image;
ImageEx image;
ddsHeader header;
FILE *ddsFile = fopen(fileName, "rb");
@ -510,36 +529,84 @@ ImageDDS LoadDDS(const char *fileName)
// Get the surface descriptor
fread(&header, sizeof(ddsHeader), 1, ddsFile);
int height = header.height;
int width = header.width;
int linearSize = header.pitchOrLinearSize;
int mipMapCount = header.mipMapCount;
int fourCC = header.ddspf.fourCC;
TraceLog(DEBUG, "[%s] DDS file header size: %i", fileName, sizeof(ddsHeader));
TraceLog(DEBUG, "[%s] DDS file pixel format size: %i", fileName, header.ddspf.size);
TraceLog(DEBUG, "[%s] DDS file pixel format flags: 0x%x", fileName, header.ddspf.flags);
TraceLog(DEBUG, "[%s] DDS file format: 0x%x", fileName, fourCC);
TraceLog(DEBUG, "[%s] DDS file format: 0x%x", fileName, header.ddspf.fourCC);
int bufsize;
image.width = header.width;
image.height = header.height;
image.mipmaps = 1;
image.compFormat = 0;
// Calculate data size, including all mipmaps
bufsize = mipMapCount > 1 ? linearSize * 2 : linearSize;
if (header.ddspf.flags == 0x40 && header.ddspf.rgbBitCount == 24) // DDS_RGB, no compressed
{
image.data = (unsigned char *)malloc(header.width * header.height * 4);
unsigned char *buffer = (unsigned char *)malloc(header.width * header.height * 3);
image.data = (unsigned char*)malloc(bufsize * sizeof(unsigned char));
fread(buffer, image.width*image.height*3, 1, ddsFile);
unsigned char *src = buffer;
unsigned char *dest = image.data;
for(int y = 0; y < image.height; y++)
{
for(int x = 0; x < image.width; x++)
{
*dest++ = *src++;
*dest++ = *src++;
*dest++ = *src++;
*dest++ = 255;
}
}
free(buffer);
}
else if (header.ddspf.flags == 0x41 && header.ddspf.rgbBitCount == 32) // DDS_RGBA, no compressed
{
image.data = (unsigned char *)malloc(header.width * header.height * 4);
fread(image.data, 1, bufsize, ddsFile);
fread(image.data, image.width*image.height*4, 1, ddsFile);
// Close file pointer
fclose(ddsFile);
//int components = (fourCC == FOURCC_DXT1) ? 3 : 4; // Not required
image.mipmaps = 1;
image.compFormat = 0;
}
else if ((header.ddspf.flags == 0x04) && (header.ddspf.fourCC > 0))
{
#ifdef USE_OPENGL_11
TraceLog(WARNING, "[%s] DDS image uses compression, not supported by current OpenGL version", fileName);
TraceLog(WARNING, "[%s] DDS compressed files require OpenGL 3.2+ or ES 2.0", fileName);
fclose(ddsFile);
#else
int bufsize;
// Calculate data size, including all mipmaps
if (header.mipMapCount > 1) bufsize = header.pitchOrLinearSize * 2;
else bufsize = header.pitchOrLinearSize;
image.data = (unsigned char*)malloc(bufsize * sizeof(unsigned char));
fread(image.data, 1, bufsize, ddsFile);
// Close file pointer
fclose(ddsFile);
image.mipmaps = header.mipMapCount;
image.compFormat = 0;
switch(header.ddspf.fourCC)
{
case FOURCC_DXT1: image.compFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; break;
case FOURCC_DXT3: image.compFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; break;
case FOURCC_DXT5: image.compFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; break;
default: break;
}
image.width = width;
image.height = height;
image.mipmaps = mipMapCount;
image.format = fourCC;
// NOTE: Image num color components not required... for now...
//if (fourCC == FOURCC_DXT1) image.components = 3;
//else image.components = 4;
#endif
}
}
}

View File

@ -4,8 +4,9 @@
*
* Utils Functions Definitions
*
* Uses external lib:
* Uses external libs:
* tinfl - zlib DEFLATE algorithm decompression lib
* stb_image_write - PNG writting functions
*
* Copyright (c) 2013 Ramon Santamaria (Ray San - raysan@raysanweb.com)
*
@ -28,14 +29,14 @@
#include "utils.h"
#include <stdlib.h> // malloc(), free()
#include <stdio.h> // printf(), fprintf()
#include <stdarg.h> // Used for functions with variable number of parameters (TraceLog())
//#include <string.h> // String management functions: strlen(), strrchr(), strcmp()
#include <stdlib.h> // malloc(), free()
#include <stdio.h> // printf(), fprintf()
#include <stdarg.h> // Used for functions with variable number of parameters (TraceLog())
//#include <string.h> // String management functions: strlen(), strrchr(), strcmp()
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.h" // Create PNG file
#include "tinfl.c"
//----------------------------------------------------------------------------------
@ -140,84 +141,39 @@ void WritePNG(const char *fileName, unsigned char *imgData, int width, int heigh
// NOTE: If a file has been init, output log is written there
void TraceLog(int msgType, const char *text, ...)
{
// TODO: This function requires some refactoring...
// NOTE: If trace log file has been set, stdout is being redirected to a file
va_list args;
int traceDebugMsgs = 1;
#ifdef DO_NOT_TRACE_DEBUG_MSGS
traceDebugMsgs = 0;
#endif
// NOTE: If trace log file not set, output redirected to stdout
if (logstream == NULL) logstream = stdout;
if (logstream != NULL)
switch(msgType)
{
switch(msgType)
{
case 0: fprintf(logstream, "INFO: "); break;
case 1: fprintf(logstream, "ERROR: "); break;
case 2: fprintf(logstream, "WARNING: "); break;
case 3: if (traceDebugMsgs) fprintf(logstream, "DEBUG: "); break;
default: break;
}
if (msgType == 3)
{
if (traceDebugMsgs)
{
va_start(args, text);
vfprintf(logstream, text, args);
va_end(args);
fprintf(logstream, "\n");
}
}
else
{
va_start(args, text);
vfprintf(logstream, text, args);
va_end(args);
fprintf(logstream, "\n");
}
}
else
{
switch(msgType)
{
case 0: fprintf(stdout, "INFO: "); break;
case 1: fprintf(stdout, "ERROR: "); break;
case 2: fprintf(stdout, "WARNING: "); break;
case 3: if (traceDebugMsgs) fprintf(stdout, "DEBUG: "); break;
default: break;
}
if (msgType == 3)
{
if (traceDebugMsgs)
{
va_start(args, text);
vfprintf(stdout, text, args);
va_end(args);
fprintf(stdout, "\n");
}
}
else
{
va_start(args, text);
vfprintf(stdout, text, args);
va_end(args);
fprintf(stdout, "\n");
}
case INFO: fprintf(logstream, "INFO: "); break;
case ERROR: fprintf(logstream, "ERROR: "); break;
case WARNING: fprintf(logstream, "WARNING: "); break;
case DEBUG: if (traceDebugMsgs) fprintf(logstream, "DEBUG: "); break;
default: break;
}
if (msgType == 1) exit(1); // If ERROR message, exit program
if ((msgType != DEBUG) || ((msgType == DEBUG) && (traceDebugMsgs)))
{
va_start(args, text);
vfprintf(logstream, text, args);
va_end(args);
fprintf(logstream, "\n");
}
if (msgType == ERROR) exit(1); // If ERROR message, exit program
}
// Inits a trace log file
void InitTraceLogFile(const char *logFileName)
// Open a trace log file (if desired)
void TraceLogOpen(const char *logFileName)
{
// stdout redirected to stream file
FILE *logstream = fopen(logFileName, "w");
@ -225,9 +181,25 @@ void InitTraceLogFile(const char *logFileName)
if (logstream == NULL) TraceLog(WARNING, "Unable to open log file");
}
// Closes the trace log file
void CloseTraceLogFile()
// Close the trace log file
void TraceLogClose()
{
if (logstream != NULL) fclose(logstream);
}
// Keep track of memory allocated
// NOTE: mallocType defines the type of data allocated
void RecordMalloc(int mallocType, int mallocSize, const char *msg)
{
// TODO: Investigate how to record memory allocation data...
// Maybe creating my own malloc function...
}
// Get the extension for a filename
const char *GetExtension(const char *fileName)
{
const char *dot = strrchr(fileName, '.');
if(!dot || dot == fileName) return "";
return (dot + 1);
}

View File

@ -3,9 +3,6 @@
* raylib.utils
*
* Some utility functions: rRES files data decompression
*
* Uses external lib:
* tinfl - zlib DEFLATE algorithm decompression lib
*
* Copyright (c) 2013 Ramon Santamaria (Ray San - raysan@raysanweb.com)
*
@ -32,12 +29,12 @@
//----------------------------------------------------------------------------------
// Some basic Defines
//----------------------------------------------------------------------------------
//#define DO_NOT_TRACE_DEBUG_MSGS // Use this define to avoid DEBUG tracing
#define DO_NOT_TRACE_DEBUG_MSGS // Use this define to avoid DEBUG tracing
//----------------------------------------------------------------------------------
// Types and Structures Definition
//----------------------------------------------------------------------------------
typedef enum { IMAGE, SOUND, MODEL, TEXT, RAW } DataType;
typedef enum { IMAGE = 0, SOUND, MODEL, TEXT, RAW } DataType;
typedef enum { INFO = 0, ERROR, WARNING, DEBUG, OTHER } TraceLogType;
@ -68,8 +65,10 @@ void WriteBitmap(const char *fileName, unsigned char *imgData, int width, int he
void WritePNG(const char *fileName, unsigned char *imgData, int width, int height);
void TraceLog(int msgType, const char *text, ...); // Outputs a trace log message
void InitTraceLogFile(const char *logFileName); // Inits a trace log file
void CloseTraceLogFile(); // Closes the trace log file
void TraceLogOpen(const char *logFileName); // Open a trace log file (if desired)
void TraceLogClose(); // Close the trace log file
const char *GetExtension(const char *fileName);
#ifdef __cplusplus
}