aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRay <raysan5@gmail.com>2016-11-24 17:26:07 +0100
committerGitHub <noreply@github.com>2016-11-24 17:26:07 +0100
commit17f09cb03484a408cdd50a3d2e4d6604bb1f4c70 (patch)
tree51e909c419de9625f1beaaca44696857a600e8d5 /src
parent05f68c22d5c8d8f7c4254ae47700318e21709887 (diff)
parenta81dfabf863c512044b246e23aaf43489d2fa1ac (diff)
downloadraylib-17f09cb03484a408cdd50a3d2e4d6604bb1f4c70.tar.gz
raylib-17f09cb03484a408cdd50a3d2e4d6604bb1f4c70.zip
Merge pull request #198 from raysan5/develop
Develop branch integration
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt32
-rw-r--r--src/Makefile28
-rw-r--r--src/audio.c31
-rw-r--r--src/audio.h55
-rw-r--r--src/core.c43
-rw-r--r--src/external/stb_image.h4
-rw-r--r--src/models.c42
-rw-r--r--src/physac.h2509
-rw-r--r--src/raylib.h55
-rw-r--r--src/resourcesbin107204 -> 107212 bytes
-rw-r--r--src/rlgl.c63
-rw-r--r--src/rlgl.h36
-rw-r--r--src/rlua.h276
-rw-r--r--src/shapes.c10
-rw-r--r--src/text.c22
-rw-r--r--src/textures.c18
-rw-r--r--src/utils.c14
-rw-r--r--src/utils.h6
18 files changed, 2311 insertions, 933 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
deleted file mode 100644
index c094ad96..00000000
--- a/src/CMakeLists.txt
+++ /dev/null
@@ -1,32 +0,0 @@
-cmake_minimum_required (VERSION 3.0)
-project (raylib)
-SET(PLATFORM_TO_USE "PLATFORM_DESKTOP" CACHE STRING "Platform to compile for")
-SET_PROPERTY(CACHE PLATFORM_TO_USE PROPERTY STRINGS PLATFORM_DESKTOP PLATFORM_RPI PLATFORM_WEB)
-
-set(CMAKE_C_FLAGS "-O1 -Wall -std=gnu99 -fgnu89-inline -Wno-missing-braces")
-
-IF(${PLATFORM_TO_USE} MATCHES "PLATFORM_DESKTOP")
-
- add_definitions(-DPLATFORM_DESKTOP, -DGRAPHICS_API_OPENGL_33)
- include_directories("." "external/" "external/openal_soft/include" "external/glfw3/include")
-
-ENDIF()
-
-IF(${PLATFORM_TO_USE} MATCHES "PLATFORM_RPI")
-
- add_definitions(-DPLATFORM_RPI, -GRAPHICS_API_OPENGL_ES2)
- include_directories("." "external/" "/opt/vc/include" "/opt/vc/include/interface/vmcs_host/linux" "/opt/vc/include/interface/vcos/pthreads")
-
-ENDIF()
-
-IF(${PLATFORM_TO_USE} MATCHES "PLATFORM_WEB")
-
- add_definitions(-DPLATFORM_WEB, -GRAPHICS_API_OPENGL_ES2)
- include_directories("." "external/" "external/openal_soft/include" "external/glfw3/include")
-
-ENDIF()
-
-
-file(GLOB SOURCES "*.c" "external/*.c")
-add_library(raylib STATIC ${SOURCES})
-install(TARGETS raylib DESTINATION ../lib/) \ No newline at end of file
diff --git a/src/Makefile b/src/Makefile
index ee3f0c12..2e263189 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -38,6 +38,14 @@ PLATFORM ?= PLATFORM_DESKTOP
# define YES if you want shared/dynamic version of library instead of static (default)
SHARED ?= NO
+# define NO to use OpenAL Soft as static library (or shared by default)
+SHARED_OPENAL ?= YES
+
+# on PLATFORM_WEB force OpenAL Soft shared library
+ifeq ($(PLATFORM),PLATFORM_WEB)
+ SHARED_OPENAL ?= YES
+endif
+
# determine if the file has root access (only for installing raylib)
# "whoami" prints the name of the user that calls him (so, if it is the root
# user, "whoami" prints "root").
@@ -99,11 +107,20 @@ CFLAGS = -O1 -Wall -std=gnu99 -fgnu89-inline -Wno-missing-braces
ifeq ($(SHARED),YES)
CFLAGS += -fPIC
SHAREDFLAG = BUILDING_DLL
- SHAREDLIBS = -Lexternal/glfw3/lib/win32 -Lexternal/openal_soft/lib/win32 -lglfw3 -lopenal32 -lgdi32
+ SHAREDLIBS = -Lexternal/glfw3/lib/win32 -Lexternal/openal_soft/lib/win32 -lglfw3 -lgdi32
else
SHAREDFLAG = BUILDING_STATIC
endif
+# if static OpenAL Soft required, define the corresponding flags
+ifeq ($(SHARED_OPENAL),NO)
+ SHAREDLIBS += -lopenal32 -lwinmm
+ SHAREDOPENALFLAG = AL_LIBTYPE_STATIC
+else
+ SHAREDLIBS += -lopenal32dll
+ SHAREDOPENALFLAG = SHARED_OPENAL
+endif
+
#CFLAGSEXTRA = -Wextra -Wmissing-prototypes -Wstrict-prototypes
# define any directories containing required header files
@@ -165,12 +182,17 @@ else
endif
ifeq ($(PLATFORM_OS),WINDOWS)
$(CC) -shared -o $(OUTPUT_PATH)/raylib.dll $(OBJS) $(SHAREDLIBS) -Wl,--out-implib,$(OUTPUT_PATH)/libraylibdll.a
- @echo "raylib dynamic library (raylib.dll) and MSVC required import library (libraylibdll.a) generated!"
+ @echo "raylib dynamic library (raylib.dll) and import library (libraylibdll.a) generated!"
endif
else
# compile raylib static library for desktop platforms.
ar rcs $(OUTPUT_PATH)/libraylib.a $(OBJS)
@echo "libraylib.a generated (static library)!"
+ ifeq ($(SHARED_OPENAL),NO)
+ @echo "expected OpenAL Soft static library linking"
+ else
+ @echo "expected OpenAL Soft shared library linking"
+ endif
endif
endif
@@ -202,7 +224,7 @@ models.o : models.c raylib.h rlgl.h raymath.h
# compile audio module
audio.o : audio.c raylib.h
- $(CC) -c $< $(CFLAGS) $(INCLUDES) -D$(PLATFORM) -D$(SHAREDFLAG)
+ $(CC) -c $< $(CFLAGS) $(INCLUDES) -D$(PLATFORM) -D$(SHAREDFLAG) -D$(SHAREDOPENALFLAG)
# compile stb_vorbis library
external/stb_vorbis.o: external/stb_vorbis.c external/stb_vorbis.h
diff --git a/src/audio.c b/src/audio.c
index 3684e10a..49aca4b0 100644
--- a/src/audio.c
+++ b/src/audio.c
@@ -2,18 +2,22 @@
*
* raylib.audio
*
-* Basic functions to manage Audio:
+* This module provides basic functionality to work with audio:
* Manage audio device (init/close)
-* Load and Unload audio files
+* Load and Unload audio files (WAV, OGG, FLAC, XM, MOD)
* Play/Stop/Pause/Resume loaded audio
* Manage mixing channels
* Manage raw audio context
*
-* Uses external lib:
-* OpenAL Soft - Audio device management lib (http://kcat.strangesoft.net/openal.html)
-* stb_vorbis - Ogg audio files loading (http://www.nothings.org/stb_vorbis/)
-* jar_xm - XM module file loading
-* jar_mod - MOD audio file loading
+* External libs:
+* OpenAL Soft - Audio device management (http://kcat.strangesoft.net/openal.html)
+* stb_vorbis - OGG audio files loading (http://www.nothings.org/stb_vorbis/)
+* jar_xm - XM module file loading
+* jar_mod - MOD audio file loading
+* dr_flac - FLAC audio file loading
+*
+* Module Configuration Flags:
+* AUDIO_STANDALONE - Use this module as standalone library (independently of raylib)
*
* Many thanks to Joshua Reisenauer (github: @kd7tck) for the following additions:
* XM audio module support (jar_xm)
@@ -21,6 +25,7 @@
* Mixing channels support
* Raw audio context support
*
+*
* Copyright (c) 2014-2016 Ramon Santamaria (@raysan5)
*
* This software is provided "as-is", without any express or implied warranty. In no event
@@ -44,8 +49,11 @@
#if defined(AUDIO_STANDALONE)
#include "audio.h"
+ #include <stdarg.h> // Required for: va_list, va_start(), vfprintf(), va_end()
#else
#include "raylib.h"
+ #include "utils.h" // Required for: DecompressData()
+ // NOTE: Includes Android fopen() function map
#endif
#include "AL/al.h" // OpenAL basic header
@@ -63,13 +71,6 @@
#define AL_FORMAT_STEREO_FLOAT32 0x10011
#endif
-#if defined(AUDIO_STANDALONE)
- #include <stdarg.h> // Required for: va_list, va_start(), vfprintf(), va_end()
-#else
- #include "utils.h" // Required for: DecompressData()
- // NOTE: Includes Android fopen() function map
-#endif
-
//#define STB_VORBIS_HEADER_ONLY
#include "external/stb_vorbis.h" // OGG loading functions
@@ -117,7 +118,7 @@ typedef struct MusicData {
bool loop; // Repeat music after finish (loop)
unsigned int totalSamples; // Total number of samples
unsigned int samplesLeft; // Number of samples left to end
-} MusicData, *Music;
+} MusicData;
#if defined(AUDIO_STANDALONE)
typedef enum { INFO = 0, ERROR, WARNING, DEBUG, OTHER } TraceLogType;
diff --git a/src/audio.h b/src/audio.h
index 923492ca..2b3c5933 100644
--- a/src/audio.h
+++ b/src/audio.h
@@ -2,18 +2,19 @@
*
* raylib.audio
*
-* Basic functions to manage Audio:
+* This module provides basic functionality to work with audio:
* Manage audio device (init/close)
-* Load and Unload audio files
+* Load and Unload audio files (WAV, OGG, FLAC, XM, MOD)
* Play/Stop/Pause/Resume loaded audio
* Manage mixing channels
* Manage raw audio context
*
-* Uses external lib:
-* OpenAL Soft - Audio device management lib (http://kcat.strangesoft.net/openal.html)
-* stb_vorbis - Ogg audio files loading (http://www.nothings.org/stb_vorbis/)
-* jar_xm - XM module file loading
-* jar_mod - MOD audio file loading
+* External libs:
+* OpenAL Soft - Audio device management (http://kcat.strangesoft.net/openal.html)
+* stb_vorbis - OGG audio files loading (http://www.nothings.org/stb_vorbis/)
+* jar_xm - XM module file loading
+* jar_mod - MOD audio file loading
+* dr_flac - FLAC audio file loading
*
* Many thanks to Joshua Reisenauer (github: @kd7tck) for the following additions:
* XM audio module support (jar_xm)
@@ -21,6 +22,7 @@
* Mixing channels support
* Raw audio context support
*
+*
* Copyright (c) 2014-2016 Ramon Santamaria (@raysan5)
*
* This software is provided "as-is", without any express or implied warranty. In no event
@@ -60,12 +62,6 @@
#endif
#endif
-// Sound source type
-typedef struct Sound {
- unsigned int source; // Sound audio source id
- unsigned int buffer; // Sound audio buffer id
-} Sound;
-
// Wave type, defines audio wave data
typedef struct Wave {
unsigned int sampleCount; // Number of samples
@@ -75,9 +71,16 @@ typedef struct Wave {
void *data; // Buffer data pointer
} Wave;
+// Sound source type
+typedef struct Sound {
+ unsigned int source; // OpenAL audio source id
+ unsigned int buffer; // OpenAL audio buffer id
+ int format; // OpenAL audio format specifier
+} Sound;
+
// Music type (file streaming from memory)
// NOTE: Anything longer than ~10 seconds should be streamed
-typedef struct Music *Music;
+typedef struct MusicData *Music;
// Audio stream type
// NOTE: Useful to create custom audio streams not bound to a specific file
@@ -104,13 +107,16 @@ extern "C" { // Prevents name mangling of functions
// Module Functions Declaration
//----------------------------------------------------------------------------------
void InitAudioDevice(void); // Initialize audio device and context
-void CloseAudioDevice(void); // Close the audio device and context (and music stream)
+void CloseAudioDevice(void); // Close the audio device and context
bool IsAudioDeviceReady(void); // Check if audio device has been initialized successfully
-Sound LoadSound(char *fileName); // Load sound to memory
+Wave LoadWave(const char *fileName); // Load wave data from file into RAM
+Wave LoadWaveEx(float *data, int sampleCount, int sampleRate, int sampleSize, int channels); // Load wave data from float array data (32bit)
+Sound LoadSound(const char *fileName); // Load sound to memory
Sound LoadSoundFromWave(Wave wave); // Load sound to memory from wave data
Sound LoadSoundFromRES(const char *rresName, int resId); // Load sound to memory from rRES file (raylib Resource)
void UpdateSound(Sound sound, void *data, int numSamples); // Update sound buffer with new data
+void UnloadWave(Wave wave); // Unload wave data
void UnloadSound(Sound sound); // Unload sound
void PlaySound(Sound sound); // Play a sound
void PauseSound(Sound sound); // Pause a sound
@@ -119,12 +125,15 @@ void StopSound(Sound sound); // Stop playing
bool IsSoundPlaying(Sound sound); // Check if a sound is currently playing
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)
-
-Music LoadMusicStream(char *fileName); // Load music stream from file
+void WaveFormat(Wave *wave, int sampleRate, int sampleSize, int channels); // Convert wave data to desired format
+Wave WaveCopy(Wave wave); // Copy a wave to a new wave
+void WaveCrop(Wave *wave, int initSample, int finalSample); // Crop a wave to defined samples range
+float *GetWaveData(Wave wave); // Get samples data from wave as a floats array
+Music LoadMusicStream(const char *fileName); // Load music stream from file
void UnloadMusicStream(Music music); // Unload music stream
-void PlayMusicStream(Music music); // Start music playing (open stream)
+void PlayMusicStream(Music music); // Start music playing
void UpdateMusicStream(Music music); // Updates buffers for music streaming
-void StopMusicStream(Music music); // Stop music playing (close stream)
+void StopMusicStream(Music music); // Stop music playing
void PauseMusicStream(Music music); // Pause music playing
void ResumeMusicStream(Music music); // Resume playing paused music
bool IsMusicPlaying(Music music); // Check if music is playing
@@ -133,9 +142,9 @@ void SetMusicPitch(Music music, float pitch); // Set pitch for
float GetMusicTimeLength(Music music); // Get music time length (in seconds)
float GetMusicTimePlayed(Music music); // Get current music time played (in seconds)
-AudioStream InitAudioStream(unsigned int sampleRate,
- unsigned int sampleSize,
- unsigned int channels); // Init audio stream (to stream audio pcm data)
+AudioStream InitAudioStream(unsigned int sampleRate,
+ unsigned int sampleSize,
+ unsigned int channels); // Init audio stream (to stream raw audio pcm data)
void UpdateAudioStream(AudioStream stream, void *data, int numSamples); // Update audio stream buffers with data
void CloseAudioStream(AudioStream stream); // Close audio stream and free memory
bool IsAudioBufferProcessed(AudioStream stream); // Check if any audio stream buffers requires refill
diff --git a/src/core.c b/src/core.c
index 8e15eb96..241772ec 100644
--- a/src/core.c
+++ b/src/core.c
@@ -4,24 +4,25 @@
*
* Basic functions to manage windows, OpenGL context and input on multiple platforms
*
-* The following platforms are supported:
-* PLATFORM_DESKTOP - Windows, Linux, Mac (OSX)
-* PLATFORM_ANDROID - Only OpenGL ES 2.0 devices
-* PLATFORM_RPI - Rapsberry Pi (tested on Raspbian)
-* PLATFORM_WEB - Emscripten, HTML5
-* Oculus Rift CV1 (with desktop mirror) - View [rlgl] module to enable it
+* The following platforms are supported: Windows, Linux, Mac (OSX), Android, Raspberry Pi, HTML5, Oculus Rift CV1
*
-* On PLATFORM_DESKTOP, the external lib GLFW3 (www.glfw.com) is used to manage graphic
-* device, OpenGL context and input on multiple operating systems (Windows, Linux, OSX).
-*
-* On PLATFORM_ANDROID, graphic device is managed by EGL and input system by Android activity.
-*
-* On PLATFORM_RPI, graphic device is managed by EGL and input system is coded in raw mode.
+* External libs:
+* GLFW3 - Manage graphic device, OpenGL context and inputs on PLATFORM_DESKTOP (Windows, Linux, OSX)
+* raymath - 3D math functionality (Vector3, Matrix, Quaternion)
+* camera - Multiple 3D camera modes (free, orbital, 1st person, 3rd person)
+* gestures - Gestures system for touch-ready devices (or simulated from mouse inputs)
*
* Module Configuration Flags:
+* PLATFORM_DESKTOP - Windows, Linux, Mac (OSX)
+* PLATFORM_ANDROID - Android (only OpenGL ES 2.0 devices), graphic device is managed by EGL and input system by Android activity.
+* PLATFORM_RPI - Rapsberry Pi (tested on Raspbian), graphic device is managed by EGL and input system is coded in raw mode.
+* PLATFORM_WEB - HTML5 (using emscripten compiler)
*
* RL_LOAD_DEFAULT_FONT - Use external module functions to load default raylib font (module: text)
*
+* NOTE: Oculus Rift CV1 requires PLATFORM_DESKTOP for render mirror - View [rlgl] module to enable it
+*
+*
* Copyright (c) 2014-2016 Ramon Santamaria (@raysan5)
*
* This software is provided "as-is", without any express or implied warranty. In no event
@@ -667,7 +668,7 @@ void Begin3dMode(Camera camera)
{
rlglDraw(); // Draw Buffers (Only OpenGL 3+ and ES2)
- if (IsVrDeviceReady()) BeginVrDrawing();
+ if (IsVrDeviceReady() || IsVrSimulator()) BeginVrDrawing();
rlMatrixMode(RL_PROJECTION); // Switch to projection matrix
@@ -697,7 +698,7 @@ void End3dMode(void)
{
rlglDraw(); // Process internal buffers (update + draw)
- if (IsVrDeviceReady()) EndVrDrawing();
+ if (IsVrDeviceReady() || IsVrSimulator()) EndVrDrawing();
rlMatrixMode(RL_PROJECTION); // Switch to projection matrix
rlPopMatrix(); // Restore previous matrix (PROJECTION) from matrix stack
@@ -1174,12 +1175,14 @@ bool IsGamepadAvailable(int gamepad)
bool IsGamepadName(int gamepad, const char *name)
{
bool result = false;
+
+#if !defined(PLATFORM_ANDROID)
const char *gamepadName = NULL;
-
+
if (gamepadReady[gamepad]) gamepadName = GetGamepadName(gamepad);
-
if ((name != NULL) && (gamepadName != NULL)) result = (strcmp(name, gamepadName) == 0);
-
+#endif
+
return result;
}
@@ -1975,7 +1978,11 @@ static void PollInputEvents(void)
previousMouseWheelY = currentMouseWheelY;
currentMouseWheelY = 0;
-
+#endif
+
+// NOTE: GLFW3 joystick functionality not available in web
+// TODO: Support joysticks using emscripten API
+#if defined(PLATFORM_DESKTOP)
// Check if gamepads are ready
// NOTE: We do it here in case of disconection
for (int i = 0; i < MAX_GAMEPADS; i++)
diff --git a/src/external/stb_image.h b/src/external/stb_image.h
index ce87646d..5572a880 100644
--- a/src/external/stb_image.h
+++ b/src/external/stb_image.h
@@ -390,7 +390,7 @@ publish, and distribute this file as you see fit.
#define STBI_NO_HDR // RaySan: not required by raylib
-#define STBI_NO_SIMD // RaySan: issues when compiling with GCC 4.7.2
+//#define STBI_NO_SIMD // RaySan: issues when compiling with GCC 4.7.2
#ifndef STBI_NO_STDIO
#include <stdio.h>
@@ -398,7 +398,7 @@ publish, and distribute this file as you see fit.
// NOTE: Added to work with raylib on Android
#if defined(PLATFORM_ANDROID)
- #include "utils.h" // Android fopen function map
+ #include "utils.h" // RaySan: Android fopen function map
#endif
#define STBI_VERSION 1
diff --git a/src/models.c b/src/models.c
index b7e36b8a..48f8b813 100644
--- a/src/models.c
+++ b/src/models.c
@@ -4,6 +4,12 @@
*
* Basic functions to draw 3d shapes and load/draw 3d models (.OBJ)
*
+* External libs:
+* rlgl - raylib OpenGL abstraction layer
+*
+* Module Configuration Flags:
+* ...
+*
* Copyright (c) 2014-2016 Ramon Santamaria (@raysan5)
*
* This software is provided "as-is", without any express or implied warranty. In no event
@@ -34,8 +40,7 @@
#include <string.h> // Required for: strcmp()
#include <math.h> // Required for: sin(), cos()
-#include "rlgl.h" // raylib OpenGL abstraction layer to OpenGL 1.1, 3.3+ or ES2
-#include "raymath.h" // Matrix data type and Matrix functions
+#include "rlgl.h" // raylib OpenGL abstraction layer to OpenGL 1.1, 2.1, 3.3+ or ES2
//----------------------------------------------------------------------------------
// Defines and Macros
@@ -76,11 +81,11 @@ void DrawLine3D(Vector3 startPos, Vector3 endPos, Color color)
}
// Draw a circle in 3D world space
-void DrawCircle3D(Vector3 center, float radius, float rotationAngle, Vector3 rotation, Color color)
+void DrawCircle3D(Vector3 center, float radius, Vector3 rotationAxis, float rotationAngle, Color color)
{
rlPushMatrix();
rlTranslatef(center.x, center.y, center.z);
- rlRotatef(rotationAngle, rotation.x, rotation.y, rotation.z);
+ rlRotatef(rotationAngle, rotationAxis.x, rotationAxis.y, rotationAxis.z);
rlBegin(RL_LINES);
for (int i = 0; i < 360; i += 10)
@@ -579,9 +584,9 @@ void DrawLight(Light light)
{
DrawSphereWires(light->position, 0.3f*light->intensity, 8, 8, (light->enabled ? light->diffuse : GRAY));
- DrawCircle3D(light->position, light->radius, 0.0f, (Vector3){ 0, 0, 0 }, (light->enabled ? light->diffuse : GRAY));
- DrawCircle3D(light->position, light->radius, 90.0f, (Vector3){ 1, 0, 0 }, (light->enabled ? light->diffuse : GRAY));
- DrawCircle3D(light->position, light->radius, 90.0f, (Vector3){ 0, 1, 0 }, (light->enabled ? light->diffuse : GRAY));
+ DrawCircle3D(light->position, light->radius, (Vector3){ 0, 0, 0 }, 0.0f, (light->enabled ? light->diffuse : GRAY));
+ DrawCircle3D(light->position, light->radius, (Vector3){ 1, 0, 0 }, 90.0f, (light->enabled ? light->diffuse : GRAY));
+ DrawCircle3D(light->position, light->radius, (Vector3){ 0, 1, 0 },90.0f, (light->enabled ? light->diffuse : GRAY));
} break;
case LIGHT_DIRECTIONAL:
{
@@ -597,7 +602,7 @@ void DrawLight(Light light)
Vector3 dir = VectorSubtract(light->target, light->position);
VectorNormalize(&dir);
- DrawCircle3D(light->position, 0.5f, 0.0f, dir, (light->enabled ? light->diffuse : GRAY));
+ DrawCircle3D(light->position, 0.5f, dir, 0.0f, (light->enabled ? light->diffuse : GRAY));
//DrawCylinderWires(light->position, 0.0f, 0.3f*light->coneAngle/50, 0.6f, 5, (light->enabled ? light->diffuse : GRAY));
DrawCubeWires(light->target, 0.3f, 0.3f, 0.3f, (light->enabled ? light->diffuse : GRAY));
@@ -1812,6 +1817,7 @@ static Material LoadMTL(const char *fileName)
char buffer[MAX_BUFFER_SIZE];
Vector3 color = { 1.0f, 1.0f, 1.0f };
char mapFileName[128];
+ int result = 0;
FILE *mtlFile;
@@ -1896,13 +1902,13 @@ static Material LoadMTL(const char *fileName)
{
if (buffer[5] == 'd') // map_Kd string Diffuse color texture map.
{
- sscanf(buffer, "map_Kd %s", mapFileName);
- if (mapFileName != NULL) material.texDiffuse = LoadTexture(mapFileName);
+ result = sscanf(buffer, "map_Kd %s", mapFileName);
+ if (result != EOF) material.texDiffuse = LoadTexture(mapFileName);
}
else if (buffer[5] == 's') // map_Ks string Specular color texture map.
{
- sscanf(buffer, "map_Ks %s", mapFileName);
- if (mapFileName != NULL) material.texSpecular = LoadTexture(mapFileName);
+ result = sscanf(buffer, "map_Ks %s", mapFileName);
+ if (result != EOF) material.texSpecular = LoadTexture(mapFileName);
}
else if (buffer[5] == 'a') // map_Ka string Ambient color texture map.
{
@@ -1911,13 +1917,13 @@ static Material LoadMTL(const char *fileName)
} break;
case 'B': // map_Bump string Bump texture map.
{
- sscanf(buffer, "map_Bump %s", mapFileName);
- if (mapFileName != NULL) material.texNormal = LoadTexture(mapFileName);
+ result = sscanf(buffer, "map_Bump %s", mapFileName);
+ if (result != EOF) material.texNormal = LoadTexture(mapFileName);
} break;
case 'b': // map_bump string Bump texture map.
{
- sscanf(buffer, "map_bump %s", mapFileName);
- if (mapFileName != NULL) material.texNormal = LoadTexture(mapFileName);
+ result = sscanf(buffer, "map_bump %s", mapFileName);
+ if (result != EOF) material.texNormal = LoadTexture(mapFileName);
} break;
case 'd': // map_d string Opacity texture map.
{
@@ -1941,8 +1947,8 @@ static Material LoadMTL(const char *fileName)
} break;
case 'b': // bump string Bump texture map
{
- sscanf(buffer, "bump %s", mapFileName);
- if (mapFileName != NULL) material.texNormal = LoadTexture(mapFileName);
+ result = sscanf(buffer, "bump %s", mapFileName);
+ if (result != EOF) material.texNormal = LoadTexture(mapFileName);
} break;
case 'T': // Tr float Transparency Tr (alpha). Tr is inverse of d
{
diff --git a/src/physac.h b/src/physac.h
index dd4c4126..e807ffa6 100644
--- a/src/physac.h
+++ b/src/physac.h
@@ -1,8 +1,11 @@
/**********************************************************************************************
*
-* physac 1.0 - 2D Physics library for raylib (https://github.com/raysan5/raylib)
+* Physac - 2D Physics library for videogames
*
-* // TODO: Description...
+* Description: Physac is a small 2D physics engine written in pure C. The engine uses a fixed time-step thread loop
+* to simluate physics. A physics step contains the following phases: get collision information, apply dynamics,
+* collision solving and position correction. It uses a very simple struct for physic bodies with a position vector
+* to be used in any 3D rendering API.
*
* CONFIGURATION:
*
@@ -24,30 +27,21 @@
* internally in the library and input management and drawing functions must be provided by
* the user (check library implementation for further details).
*
+* #define PHYSAC_DEBUG
+* Traces log messages when creating and destroying physics bodies and detects errors in physics
+* calculations and reference exceptions; it is useful for debug purposes
+*
* #define PHYSAC_MALLOC()
* #define PHYSAC_FREE()
* You can define your own malloc/free implementation replacing stdlib.h malloc()/free() functions.
* Otherwise it will include stdlib.h and use the C standard library malloc()/free() function.
-*
-* LIMITATIONS:
-*
-* - There is a limit of 256 physic objects.
-* - Physics behaviour can be unexpected using bounciness or friction values out of 0.0f - 1.0f range.
-* - The module is limited to 2D axis oriented physics.
-* - Physics colliders must be rectangle or circle shapes (there is not a custom polygon collider type).
-*
-* VERSIONS:
*
-* 1.0 (14-Jun-2016) New module defines and fixed some delta time calculation bugs.
-* 0.9 (09-Jun-2016) Module names review and converted to header-only.
-* 0.8 (23-Mar-2016) Complete module redesign, steps-based for better physics resolution.
-* 0.3 (13-Feb-2016) Reviewed to add PhysicObjects pool.
-* 0.2 (03-Jan-2016) Improved physics calculations.
-* 0.1 (30-Dec-2015) Initial release.
+* VERY THANKS TO:
+* - Ramón Santamaria (@raysan5)
*
* LICENSE: zlib/libpng
*
-* Copyright (c) 2016 Victor Fisac (main developer) and Ramon Santamaria
+* Copyright (c) 2016 Victor Fisac
*
* This software is provided "as-is", without any express or implied warranty. In no event
* will the authors be held liable for any damages arising from the use of this software.
@@ -66,18 +60,18 @@
*
**********************************************************************************************/
-#ifndef PHYSAC_H
+#if !defined(PHYSAC_H)
#define PHYSAC_H
-#if !defined(RAYGUI_STANDALONE)
- #include "raylib.h"
-#endif
-
#define PHYSAC_STATIC
-#ifdef PHYSAC_STATIC
+// #define PHYSAC_NO_THREADS
+// #define PHYSAC_STANDALONE
+// #define PHYSAC_DEBUG
+
+#if defined(PHYSAC_STATIC)
#define PHYSACDEF static // Functions just visible to module including this file
#else
- #ifdef __cplusplus
+ #if defined(__cplusplus)
#define PHYSACDEF extern "C" // Functions visible from other files (no name mangling of functions in C++)
#else
#define PHYSACDEF extern // Functions visible from other files
@@ -87,87 +81,141 @@
//----------------------------------------------------------------------------------
// Defines and Macros
//----------------------------------------------------------------------------------
-// ...
+#define PHYSAC_MAX_BODIES 64
+#define PHYSAC_MAX_MANIFOLDS 4096
+#define PHYSAC_MAX_VERTICES 24
+#define PHYSAC_CIRCLE_VERTICES 24
+
+#define PHYSAC_DESIRED_DELTATIME 1.0/60.0
+#define PHYSAC_MAX_TIMESTEP 0.02
+#define PHYSAC_COLLISION_ITERATIONS 100
+#define PHYSAC_PENETRATION_ALLOWANCE 0.05f
+#define PHYSAC_PENETRATION_CORRECTION 0.4f
+
+#define PHYSAC_PI 3.14159265358979323846
+#define PHYSAC_DEG2RAD (PHYSAC_PI/180.0f)
+
+#define PHYSAC_MALLOC(size) malloc(size)
+#define PHYSAC_FREE(ptr) free(ptr)
//----------------------------------------------------------------------------------
// Types and Structures Definition
// NOTE: Below types are required for PHYSAC_STANDALONE usage
//----------------------------------------------------------------------------------
#if defined(PHYSAC_STANDALONE)
- #ifndef __cplusplus
- // Boolean type
- #ifndef true
- typedef enum { false, true } bool;
- #endif
- #endif
-
// Vector2 type
typedef struct Vector2 {
float x;
float y;
} Vector2;
- // Rectangle type
- typedef struct Rectangle {
- int x;
- int y;
- int width;
- int height;
- } Rectangle;
+ // Boolean type
+ #if !defined(_STDBOOL_H)
+ typedef enum { false, true } bool;
+ #define _STDBOOL_H
+ #endif
+#endif
+
+typedef enum PhysicsShapeType { PHYSICS_CIRCLE, PHYSICS_POLYGON } PhysicsShapeType;
+
+// Previously defined to be used in PhysicsShape struct as circular dependencies
+typedef struct PhysicsBodyData *PhysicsBody;
+
+// Mat2 type (used for polygon shape rotation matrix)
+typedef struct Mat2
+{
+ float m00;
+ float m01;
+ float m10;
+ float m11;
+} Mat2;
+
+typedef struct PolygonData {
+ unsigned int vertexCount; // Current used vertex and normals count
+ Vector2 vertices[PHYSAC_MAX_VERTICES]; // Polygon vertex positions vectors
+ Vector2 normals[PHYSAC_MAX_VERTICES]; // Polygon vertex normals vectors
+ Mat2 transform; // Vertices transform matrix 2x2
+} PolygonData;
+
+typedef struct PhysicsShape {
+ PhysicsShapeType type; // Physics shape type (circle or polygon)
+ PhysicsBody body; // Shape physics body reference
+ float radius; // Circle shape radius (used for circle shapes)
+ PolygonData vertexData; // Polygon shape vertices position and normals data (just used for polygon shapes)
+} PhysicsShape;
+
+typedef struct PhysicsBodyData {
+ unsigned int id; // Reference unique identifier
+ bool enabled; // Enabled dynamics state (collisions are calculated anyway)
+ Vector2 position; // Physics body shape pivot
+ Vector2 velocity; // Current linear velocity applied to position
+ Vector2 force; // Current linear force (reset to 0 every step)
+ float angularVelocity; // Current angular velocity applied to orient
+ float torque; // Current angular force (reset to 0 every step)
+ float orient; // Rotation in radians
+ float inertia; // Moment of inertia
+ float inverseInertia; // Inverse value of inertia
+ float mass; // Physics body mass
+ float inverseMass; // Inverse value of mass
+ float staticFriction; // Friction when the body has not movement (0 to 1)
+ float dynamicFriction; // Friction when the body has movement (0 to 1)
+ float restitution; // Restitution coefficient of the body (0 to 1)
+ bool useGravity; // Apply gravity force to dynamics
+ bool isGrounded; // Physics grounded on other body state
+ bool freezeOrient; // Physics rotation constraint
+ PhysicsShape shape; // Physics body shape information (type, radius, vertices, normals)
+} PhysicsBodyData;
+
+typedef struct PhysicsManifoldData {
+ unsigned int id; // Reference unique identifier
+ PhysicsBody bodyA; // Manifold first physics body reference
+ PhysicsBody bodyB; // Manifold second physics body reference
+ float penetration; // Depth of penetration from collision
+ Vector2 normal; // Normal direction vector from 'a' to 'b'
+ Vector2 contacts[2]; // Points of contact during collision
+ unsigned int contactsCount; // Current collision number of contacts
+ float restitution; // Mixed restitution during collision
+ float dynamicFriction; // Mixed dynamic friction during collision
+ float staticFriction; // Mixed static friction during collision
+} PhysicsManifoldData, *PhysicsManifold;
+
+#if defined(__cplusplus)
+extern "C" { // Prevents name mangling of functions
#endif
-typedef enum { COLLIDER_CIRCLE, COLLIDER_RECTANGLE } ColliderType;
-
-typedef struct Transform {
- Vector2 position;
- float rotation; // Radians (not used)
- Vector2 scale; // Just for rectangle physic objects, for circle physic objects use collider radius and keep scale as { 0, 0 }
-} Transform;
-
-typedef struct Rigidbody {
- bool enabled; // Acts as kinematic state (collisions are calculated anyway)
- float mass;
- Vector2 acceleration;
- Vector2 velocity;
- bool applyGravity;
- bool isGrounded;
- float friction; // Normalized value
- float bounciness;
-} Rigidbody;
-
-typedef struct Collider {
- bool enabled;
- ColliderType type;
- Rectangle bounds; // Used for COLLIDER_RECTANGLE
- int radius; // Used for COLLIDER_CIRCLE
-} Collider;
-
-typedef struct PhysicBodyData {
- unsigned int id;
- Transform transform;
- Rigidbody rigidbody;
- Collider collider;
- bool enabled;
-} PhysicBodyData, *PhysicBody;
+//----------------------------------------------------------------------------------
+// Global Variables Definition
+//----------------------------------------------------------------------------------
+//...
//----------------------------------------------------------------------------------
// Module Functions Declaration
//----------------------------------------------------------------------------------
-PHYSACDEF void InitPhysics(Vector2 gravity); // Initializes pointers array (just pointers, fixed size)
-PHYSACDEF void* PhysicsThread(void *arg); // Physics calculations thread function
-PHYSACDEF void ClosePhysics(); // Unitialize all physic objects and empty the objects pool
-
-PHYSACDEF PhysicBody CreatePhysicBody(Vector2 position, float rotation, Vector2 scale); // Create a new physic body dinamically, initialize it and add to pool
-PHYSACDEF void DestroyPhysicBody(PhysicBody pbody); // Destroy a specific physic body and take it out of the list
+PHYSACDEF void InitPhysics(void); // Initializes physics values, pointers and creates physics loop thread
+PHYSACDEF bool IsPhysicsEnabled(void); // Returns true if physics thread is currently enabled
+PHYSACDEF void SetPhysicsGravity(float x, float y); // Sets physics global gravity force
+PHYSACDEF PhysicsBody CreatePhysicsBodyCircle(Vector2 pos, float radius, float density); // Creates a new circle physics body with generic parameters
+PHYSACDEF PhysicsBody CreatePhysicsBodyRectangle(Vector2 pos, float width, float height, float density); // Creates a new rectangle physics body with generic parameters
+PHYSACDEF PhysicsBody CreatePhysicsBodyPolygon(Vector2 pos, float radius, int sides, float density); // Creates a new polygon physics body with generic parameters
+PHYSACDEF void PhysicsAddForce(PhysicsBody body, Vector2 force); // Adds a force to a physics body
+PHYSACDEF void PhysicsAddTorque(PhysicsBody body, float amount); // Adds an angular force to a physics body
+PHYSACDEF void PhysicsShatter(PhysicsBody body, Vector2 position, float force); // Shatters a polygon shape physics body to little physics bodies with explosion force
+PHYSACDEF int GetPhysicsBodiesCount(void); // Returns the current amount of created physics bodies
+PHYSACDEF PhysicsBody GetPhysicsBody(int index); // Returns a physics body of the bodies pool at a specific index
+PHYSACDEF int GetPhysicsShapeType(int index); // Returns the physics body shape type (PHYSICS_CIRCLE or PHYSICS_POLYGON)
+PHYSACDEF int GetPhysicsShapeVerticesCount(int index); // Returns the amount of vertices of a physics body shape
+PHYSACDEF Vector2 GetPhysicsShapeVertex(PhysicsBody body, int vertex); // Returns transformed position of a body shape (body position + vertex transformed position)
+PHYSACDEF void SetPhysicsBodyRotation(PhysicsBody body, float radians); // Sets physics body shape transform based on radians parameter
+PHYSACDEF void DestroyPhysicsBody(PhysicsBody body); // Unitializes and destroy a physics body
+PHYSACDEF void ResetPhysics(void); // Destroys created physics bodies and manifolds and resets global values
+PHYSACDEF void ClosePhysics(void); // Unitializes physics pointers and closes physics loop thread
-PHYSACDEF void ApplyForce(PhysicBody pbody, Vector2 force); // Apply directional force to a physic body
-PHYSACDEF void ApplyForceAtPosition(Vector2 position, float force, float radius); // Apply radial force to all physic objects in range
-
-PHYSACDEF Rectangle TransformToRectangle(Transform transform); // Convert Transform data type to Rectangle (position and scale)
+#if defined(__cplusplus)
+}
+#endif
#endif // PHYSAC_H
-
/***********************************************************************************
*
* PHYSAC IMPLEMENTATION
@@ -176,657 +224,1854 @@ PHYSACDEF Rectangle TransformToRectangle(Transform transform);
#if defined(PHYSAC_IMPLEMENTATION)
-// Check if custom malloc/free functions defined, if not, using standard ones
-#if !defined(PHYSAC_MALLOC)
- #include <stdlib.h> // Required for: malloc(), free()
-
- #define PHYSAC_MALLOC(size) malloc(size)
- #define PHYSAC_FREE(ptr) free(ptr)
+#if !defined(PHYSAC_NO_THREADS)
+ #include <pthread.h> // Required for: pthread_t, pthread_create()
#endif
-#include <math.h> // Required for: cos(), sin(), abs(), fminf()
-#include <stdint.h> // Required for typedef unsigned long long int uint64_t, used by hi-res timer
-
-#ifndef PHYSAC_NO_THREADS
- #include <pthread.h> // Required for: pthread_create()
+#if defined(PHYSAC_DEBUG)
+ #include <stdio.h> // Required for: printf()
#endif
-#if defined(PLATFORM_DESKTOP)
+#include <stdlib.h> // Required for: malloc(), free(), srand(), rand()
+#include <math.h> // Required for: cosf(), sinf(), fabs(), sqrtf()
+
+#if defined(_WIN32)
// Functions required to query time on Windows
int __stdcall QueryPerformanceCounter(unsigned long long int *lpPerformanceCount);
int __stdcall QueryPerformanceFrequency(unsigned long long int *lpFrequency);
-#elif defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI)
- #include <sys/time.h> // Required for: timespec
- #include <time.h> // Required for: clock_gettime()
+#elif defined(__linux)
+ #include <sys/time.h> // Required for: timespec
+ #include <time.h> // Required for: clock_gettime()
#endif
//----------------------------------------------------------------------------------
// Defines and Macros
//----------------------------------------------------------------------------------
-#define MAX_PHYSIC_BODIES 256 // Maximum available physic bodies slots in bodies pool
-#define PHYSICS_TIMESTEP 0.016666 // Physics fixed time step (1/fps)
-#define PHYSICS_ACCURACY 0.0001f // Velocity subtract operations round filter (friction)
-#define PHYSICS_ERRORPERCENT 0.001f // Collision resolve position fix
+#define min(a,b) (((a)<(b))?(a):(b))
+#define max(a,b) (((a)>(b))?(a):(b))
+#define PHYSAC_FLT_MAX 3.402823466e+38f
+#define PHYSAC_EPSILON 0.000001f
//----------------------------------------------------------------------------------
// Types and Structures Definition
-// NOTE: Below types are required for PHYSAC_STANDALONE usage
//----------------------------------------------------------------------------------
// ...
//----------------------------------------------------------------------------------
// Global Variables Definition
//----------------------------------------------------------------------------------
-static bool physicsThreadEnabled = false; // Physics calculations thread exit control
-static uint64_t baseTime; // Base time measure for hi-res timer
-static double currentTime, previousTime; // Used to track timmings
-static PhysicBody physicBodies[MAX_PHYSIC_BODIES]; // Physic bodies pool
-static int physicBodiesCount; // Counts current enabled physic bodies
-static Vector2 gravityForce; // Gravity force
+#if !defined(PHYSAC_NO_THREADS)
+ static pthread_t physicsThreadId; // Physics thread id
+#endif
+static unsigned int usedMemory = 0; // Total allocated dynamic memory
+static bool physicsThreadEnabled = false; // Physics thread enabled state
+static double currentTime = 0; // Current time in milliseconds
+#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI)
+ static double baseTime = 0; // Android and RPI platforms base time
+#endif
+static double startTime = 0; // Start time in milliseconds
+static double deltaTime = 0; // Delta time used for physics steps
+static double accumulator = 0; // Physics time step delta time accumulator
+static unsigned int stepsCount = 0; // Total physics steps processed
+static Vector2 gravityForce = { 0, 9.81f/1000 }; // Physics world gravity force
+static PhysicsBody bodies[PHYSAC_MAX_BODIES]; // Physics bodies pointers array
+static unsigned int physicsBodiesCount = 0; // Physics world current bodies counter
+static PhysicsManifold contacts[PHYSAC_MAX_MANIFOLDS]; // Physics bodies pointers array
+static unsigned int physicsManifoldsCount = 0; // Physics world current manifolds counter
//----------------------------------------------------------------------------------
-// Module specific Functions Declaration
+// Module Internal Functions Declaration
//----------------------------------------------------------------------------------
-static void UpdatePhysics(double deltaTime); // Update physic objects, calculating physic behaviours and collisions detection
-static void InitTimer(void); // Initialize hi-resolution timer
-static double GetCurrentTime(void); // Time measure returned are microseconds
-static float Vector2DotProduct(Vector2 v1, Vector2 v2); // Returns the dot product of two Vector2
-static float Vector2Length(Vector2 v); // Returns the length of a Vector2
+static PolygonData CreateRandomPolygon(float radius, int sides); // Creates a random polygon shape with max vertex distance from polygon pivot
+static PolygonData CreateRectanglePolygon(Vector2 pos, Vector2 size); // Creates a rectangle polygon shape based on a min and max positions
+static void *PhysicsLoop(void *arg); // Physics loop thread function
+static void PhysicsStep(void); // Physics steps calculations (dynamics, collisions and position corrections)
+static PhysicsManifold CreatePhysicsManifold(PhysicsBody a, PhysicsBody b); // Creates a new physics manifold to solve collision
+static void DestroyPhysicsManifold(PhysicsManifold manifold); // Unitializes and destroys a physics manifold
+static void SolvePhysicsManifold(PhysicsManifold manifold); // Solves a created physics manifold between two physics bodies
+static void SolveCircleToCircle(PhysicsManifold manifold); // Solves collision between two circle shape physics bodies
+static void SolveCircleToPolygon(PhysicsManifold manifold); // Solves collision between a circle to a polygon shape physics bodies
+static void SolvePolygonToCircle(PhysicsManifold manifold); // Solves collision between a polygon to a circle shape physics bodies
+static void SolvePolygonToPolygon(PhysicsManifold manifold); // Solves collision between two polygons shape physics bodies
+static void IntegratePhysicsForces(PhysicsBody body); // Integrates physics forces into velocity
+static void InitializePhysicsManifolds(PhysicsManifold manifold); // Initializes physics manifolds to solve collisions
+static void IntegratePhysicsImpulses(PhysicsManifold manifold); // Integrates physics collisions impulses to solve collisions
+static void IntegratePhysicsVelocity(PhysicsBody body); // Integrates physics velocity into position and forces
+static void CorrectPhysicsPositions(PhysicsManifold manifold); // Corrects physics bodies positions based on manifolds collision information
+static float FindAxisLeastPenetration(int *faceIndex, PhysicsShape shapeA, PhysicsShape shapeB); // Finds polygon shapes axis least penetration
+static void FindIncidentFace(Vector2 *v0, Vector2 *v1, PhysicsShape ref, PhysicsShape inc, int index); // Finds two polygon shapes incident face
+static int Clip(Vector2 normal, float clip, Vector2 *faceA, Vector2 *faceB); // Calculates clipping based on a normal and two faces
+static bool BiasGreaterThan(float valueA, float valueB); // Check if values are between bias range
+static Vector2 TriangleBarycenter(Vector2 v1, Vector2 v2, Vector2 v3); // Returns the barycenter of a triangle given by 3 points
+static void InitTimer(void); // Initializes hi-resolution timer
+static double GetCurrentTime(void); // Get current time in milliseconds
+static int GetRandomNumber(int min, int max); // Returns a random number between min and max (both included)
+
+static void MathClamp(double *value, double min, double max); // Clamp a value in a range
+static Vector2 MathCross(float value, Vector2 vector); // Returns the cross product of a vector and a value
+static float MathCrossVector2(Vector2 v1, Vector2 v2); // Returns the cross product of two vectors
+static float MathLenSqr(Vector2 vector); // Returns the len square root of a vector
+static float MathDot(Vector2 v1, Vector2 v2); // Returns the dot product of two vectors
+static inline float DistSqr(Vector2 v1, Vector2 v2); // Returns the square root of distance between two vectors
+static void MathNormalize(Vector2 *vector); // Returns the normalized values of a vector
+static Vector2 Vector2Add(Vector2 v1, Vector2 v2); // Returns the sum of two given vectors
+static Vector2 Vector2Subtract(Vector2 v1, Vector2 v2); // Returns the subtract of two given vectors
+
+static Mat2 Mat2Radians(float radians); // Creates a matrix 2x2 from a given radians value
+static void Mat2Set(Mat2 *matrix, float radians); // Set values from radians to a created matrix 2x2
+static Mat2 Mat2Transpose(Mat2 matrix); // Returns the transpose of a given matrix 2x2
+static Vector2 Mat2MultiplyVector2(Mat2 matrix, Vector2 vector); // Multiplies a vector by a matrix 2x2
//----------------------------------------------------------------------------------
// Module Functions Definition
//----------------------------------------------------------------------------------
+// Initializes physics values, pointers and creates physics loop thread
+PHYSACDEF void InitPhysics(void)
+{
+ #if defined(PHYSAC_DEBUG)
+ printf("[PHYSAC] physics module initialized successfully\n");
+ #endif
-// Initializes pointers array (just pointers, fixed size)
-PHYSACDEF void InitPhysics(Vector2 gravity)
-{
- // Initialize physics variables
- physicBodiesCount = 0;
- gravityForce = gravity;
-
- #ifndef PHYSAC_NO_THREADS // NOTE: if defined, user will need to create a thread for PhysicsThread function manually
- // Create physics thread
- pthread_t tid;
- pthread_create(&tid, NULL, &PhysicsThread, NULL);
+ #if !defined(PHYSAC_NO_THREADS)
+ // NOTE: if defined, user will need to create a thread for PhysicsThread function manually
+ // Create physics thread using POSIXS thread libraries
+ pthread_create(&physicsThreadId, NULL, &PhysicsLoop, NULL);
#endif
}
-// Unitialize all physic objects and empty the objects pool
-PHYSACDEF void ClosePhysics()
+// Returns true if physics thread is currently enabled
+PHYSACDEF bool IsPhysicsEnabled(void)
{
- // Exit physics thread loop
- physicsThreadEnabled = false;
-
- // Free all dynamic memory allocations
- for (int i = 0; i < physicBodiesCount; i++) PHYSAC_FREE(physicBodies[i]);
-
- // Reset enabled physic objects count
- physicBodiesCount = 0;
+ return physicsThreadEnabled;
}
-// Create a new physic body dinamically, initialize it and add to pool
-PHYSACDEF PhysicBody CreatePhysicBody(Vector2 position, float rotation, Vector2 scale)
+// Sets physics global gravity force
+PHYSACDEF void SetPhysicsGravity(float x, float y)
{
- // Allocate dynamic memory
- PhysicBody obj = (PhysicBody)PHYSAC_MALLOC(sizeof(PhysicBodyData));
-
- // Initialize physic body values with generic values
- obj->id = physicBodiesCount;
- obj->enabled = true;
-
- obj->transform = (Transform){ (Vector2){ position.x - scale.x/2, position.y - scale.y/2 }, rotation, scale };
-
- obj->rigidbody.enabled = false;
- obj->rigidbody.mass = 1.0f;
- obj->rigidbody.acceleration = (Vector2){ 0.0f, 0.0f };
- obj->rigidbody.velocity = (Vector2){ 0.0f, 0.0f };
- obj->rigidbody.applyGravity = false;
- obj->rigidbody.isGrounded = false;
- obj->rigidbody.friction = 0.0f;
- obj->rigidbody.bounciness = 0.0f;
-
- obj->collider.enabled = true;
- obj->collider.type = COLLIDER_RECTANGLE;
- obj->collider.bounds = TransformToRectangle(obj->transform);
- obj->collider.radius = 0.0f;
-
- // Add new physic body to the pointers array
- physicBodies[physicBodiesCount] = obj;
-
- // Increase enabled physic bodies count
- physicBodiesCount++;
-
- return obj;
+ gravityForce.x = x;
+ gravityForce.y = y;
}
-// Destroy a specific physic body and take it out of the list
-PHYSACDEF void DestroyPhysicBody(PhysicBody pbody)
+// Creates a new circle physics body with generic parameters
+PHYSACDEF PhysicsBody CreatePhysicsBodyCircle(Vector2 pos, float radius, float density)
{
- // Free dynamic memory allocation
- PHYSAC_FREE(physicBodies[pbody->id]);
-
- // Remove *obj from the pointers array
- for (int i = pbody->id; i < physicBodiesCount; i++)
+ PhysicsBody newBody = CreatePhysicsBodyPolygon(pos, radius, PHYSAC_CIRCLE_VERTICES, density);
+ return newBody;
+
+ /*PhysicsBody newBody = (PhysicsBody)PHYSAC_MALLOC(sizeof(PhysicsBodyData));
+ usedMemory += sizeof(PhysicsBodyData);
+
+ int newId = -1;
+ for (int i = 0; i < PHYSAC_MAX_BODIES; i++)
{
- // Resort all the following pointers of the array
- if ((i + 1) < physicBodiesCount)
+ int currentId = i;
+
+ // Check if current id already exist in other physics body
+ for (int k = 0; k < physicsBodiesCount; k++)
+ {
+ if (bodies[k]->id == currentId)
+ {
+ currentId++;
+ break;
+ }
+ }
+
+ // If it is not used, use it as new physics body id
+ if (currentId == i)
{
- physicBodies[i] = physicBodies[i + 1];
- physicBodies[i]->id = physicBodies[i + 1]->id;
+ newId = i;
+ break;
+ }
+ }
+
+ if (newId != -1)
+ {
+ // Initialize new body with generic values
+ newBody->id = newId;
+ newBody->enabled = true;
+ newBody->position = pos;
+ newBody->velocity = (Vector2){ 0 };
+ newBody->force = (Vector2){ 0 };
+ newBody->angularVelocity = 0;
+ newBody->torque = 0;
+ newBody->orient = 0;
+ newBody->mass = PHYSAC_PI*radius*radius*density;
+ newBody->inverseMass = ((newBody->mass != 0.0f) ? 1.0f/newBody->mass : 0.0f);
+ newBody->inertia = newBody->mass*radius*radius;
+ newBody->inverseInertia = ((newBody->inertia != 0.0f) ? 1.0f/newBody->inertia : 0.0f);
+ newBody->staticFriction = 0;
+ newBody->dynamicFriction = 0;
+ newBody->restitution = 0;
+ newBody->useGravity = true;
+ newBody->freezeOrient = false;
+ newBody->shape.type = PHYSICS_CIRCLE;
+ newBody->shape.body = newBody;
+ newBody->shape.radius = radius;
+
+ // Add new body to bodies pointers array and update bodies count
+ bodies[physicsBodiesCount] = newBody;
+ physicsBodiesCount++;
+
+ #if defined(PHYSAC_DEBUG)
+ printf("[PHYSAC] created circle physics body id %i\n", newBody->id);
+ #endif
+ }
+ #if defined(PHYSAC_DEBUG)
+ else printf("[PHYSAC] new physics body creation failed because there is any available id to use\n");
+ #endif
+
+ return newBody;*/
+}
+
+// Creates a new rectangle physics body with generic parameters
+PHYSACDEF PhysicsBody CreatePhysicsBodyRectangle(Vector2 pos, float width, float height, float density)
+{
+ PhysicsBody newBody = (PhysicsBody)PHYSAC_MALLOC(sizeof(PhysicsBodyData));
+ usedMemory += sizeof(PhysicsBodyData);
+
+ int newId = -1;
+ for (int i = 0; i < PHYSAC_MAX_BODIES; i++)
+ {
+ int currentId = i;
+
+ // Check if current id already exist in other physics body
+ for (int k = 0; k < physicsBodiesCount; k++)
+ {
+ if (bodies[k]->id == currentId)
+ {
+ currentId++;
+ break;
+ }
+ }
+
+ // If it is not used, use it as new physics body id
+ if (currentId == i)
+ {
+ newId = i;
+ break;
+ }
+ }
+
+ if (newId != -1)
+ {
+ // Initialize new body with generic values
+ newBody->id = newId;
+ newBody->enabled = true;
+ newBody->position = pos;
+ newBody->velocity = (Vector2){ 0 };
+ newBody->force = (Vector2){ 0 };
+ newBody->angularVelocity = 0;
+ newBody->torque = 0;
+ newBody->orient = 0;
+ newBody->shape.type = PHYSICS_POLYGON;
+ newBody->shape.body = newBody;
+ newBody->shape.vertexData = CreateRectanglePolygon(pos, (Vector2){ width, height });
+
+ // Calculate centroid and moment of inertia
+ Vector2 center = { 0 };
+ float area = 0;
+ float inertia = 0;
+ const float k = 1.0f/3.0f;
+
+ for (int i = 0; i < newBody->shape.vertexData.vertexCount; i++)
+ {
+ // Triangle vertices, third vertex implied as (0, 0)
+ Vector2 p1 = newBody->shape.vertexData.vertices[i];
+ int nextIndex = (((i + 1) < newBody->shape.vertexData.vertexCount) ? (i + 1) : 0);
+ Vector2 p2 = newBody->shape.vertexData.vertices[nextIndex];
+
+ float D = MathCrossVector2(p1, p2);
+ float triangleArea = D/2;
+
+ area += triangleArea;
+
+ // Use area to weight the centroid average, not just vertex position
+ center.x += triangleArea*k*(p1.x + p2.x);
+ center.y += triangleArea*k*(p1.y + p2.y);
+
+ float intx2 = p1.x*p1.x + p2.x*p1.x + p2.x*p2.x;
+ float inty2 = p1.y*p1.y + p2.y*p1.y + p2.y*p2.y;
+ inertia += (0.25f*k*D)*(intx2 + inty2);
+ }
+
+ center.x *= 1.0f/area;
+ center.y *= 1.0f/area;
+
+ // Translate vertices to centroid (make the centroid (0, 0) for the polygon in model space)
+ // Note: this is not really necessary
+ for (int i = 0; i < newBody->shape.vertexData.vertexCount; i++)
+ {
+ newBody->shape.vertexData.vertices[i].x -= center.x;
+ newBody->shape.vertexData.vertices[i].y -= center.y;
+ }
+
+ newBody->mass = density*area;
+ newBody->inverseMass = ((newBody->mass != 0.0f) ? 1.0f/newBody->mass : 0.0f);
+ newBody->inertia = density*inertia;
+ newBody->inverseInertia = ((newBody->inertia != 0.0f) ? 1.0f/newBody->inertia : 0.0f);
+ newBody->staticFriction = 0.4f;
+ newBody->dynamicFriction = 0.2f;
+ newBody->restitution = 0;
+ newBody->useGravity = true;
+ newBody->isGrounded = false;
+ newBody->freezeOrient = false;
+
+ // Add new body to bodies pointers array and update bodies count
+ bodies[physicsBodiesCount] = newBody;
+ physicsBodiesCount++;
+
+ #if defined(PHYSAC_DEBUG)
+ printf("[PHYSAC] created polygon physics body id %i\n", newBody->id);
+ #endif
+ }
+ #if defined(PHYSAC_DEBUG)
+ else printf("[PHYSAC] new physics body creation failed because there is any available id to use\n");
+ #endif
+
+ return newBody;
+}
+
+// Creates a new polygon physics body with generic parameters
+PHYSACDEF PhysicsBody CreatePhysicsBodyPolygon(Vector2 pos, float radius, int sides, float density)
+{
+ PhysicsBody newBody = (PhysicsBody)PHYSAC_MALLOC(sizeof(PhysicsBodyData));
+ usedMemory += sizeof(PhysicsBodyData);
+
+ int newId = -1;
+ for (int i = 0; i < PHYSAC_MAX_BODIES; i++)
+ {
+ int currentId = i;
+
+ // Check if current id already exist in other physics body
+ for (int k = 0; k < physicsBodiesCount; k++)
+ {
+ if (bodies[k]->id == currentId)
+ {
+ currentId++;
+ break;
+ }
+ }
+
+ // If it is not used, use it as new physics body id
+ if (currentId == i)
+ {
+ newId = i;
+ break;
+ }
+ }
+
+ if (newId != -1)
+ {
+ // Initialize new body with generic values
+ newBody->id = newId;
+ newBody->enabled = true;
+ newBody->position = pos;
+ newBody->velocity = (Vector2){ 0 };
+ newBody->force = (Vector2){ 0 };
+ newBody->angularVelocity = 0;
+ newBody->torque = 0;
+ newBody->orient = 0;
+ newBody->shape.type = PHYSICS_POLYGON;
+ newBody->shape.body = newBody;
+ newBody->shape.vertexData = CreateRandomPolygon(radius, sides);
+
+ // Calculate centroid and moment of inertia
+ Vector2 center = { 0 };
+ float area = 0;
+ float inertia = 0;
+ const float alpha = 1.0f/3.0f;
+
+ for (int i = 0; i < newBody->shape.vertexData.vertexCount; i++)
+ {
+ // Triangle vertices, third vertex implied as (0, 0)
+ Vector2 position1 = newBody->shape.vertexData.vertices[i];
+ int nextIndex = (((i + 1) < newBody->shape.vertexData.vertexCount) ? (i + 1) : 0);
+ Vector2 position2 = newBody->shape.vertexData.vertices[nextIndex];
+
+ float cross = MathCrossVector2(position1, position2);
+ float triangleArea = cross/2;
+
+ area += triangleArea;
+
+ // Use area to weight the centroid average, not just vertex position
+ center.x += triangleArea*alpha*(position1.x + position2.x);
+ center.y += triangleArea*alpha*(position1.y + position2.y);
+
+ float intx2 = position1.x*position1.x + position2.x*position1.x + position2.x*position2.x;
+ float inty2 = position1.y*position1.y + position2.y*position1.y + position2.y*position2.y;
+ inertia += (0.25f*alpha*cross)*(intx2 + inty2);
+ }
+
+ center.x *= 1.0f/area;
+ center.y *= 1.0f/area;
+
+ // Translate vertices to centroid (make the centroid (0, 0) for the polygon in model space)
+ // Note: this is not really necessary
+ for (int i = 0; i < newBody->shape.vertexData.vertexCount; i++)
+ {
+ newBody->shape.vertexData.vertices[i].x -= center.x;
+ newBody->shape.vertexData.vertices[i].y -= center.y;
+ }
+
+ newBody->mass = density*area;
+ newBody->inverseMass = ((newBody->mass != 0.0f) ? 1.0f/newBody->mass : 0.0f);
+ newBody->inertia = density*inertia;
+ newBody->inverseInertia = ((newBody->inertia != 0.0f) ? 1.0f/newBody->inertia : 0.0f);
+ newBody->staticFriction = 0.4f;
+ newBody->dynamicFriction = 0.2f;
+ newBody->restitution = 0;
+ newBody->useGravity = true;
+ newBody->isGrounded = false;
+ newBody->freezeOrient = false;
+
+ // Add new body to bodies pointers array and update bodies count
+ bodies[physicsBodiesCount] = newBody;
+ physicsBodiesCount++;
+
+ #if defined(PHYSAC_DEBUG)
+ printf("[PHYSAC] created polygon physics body id %i\n", newBody->id);
+ #endif
+ }
+ #if defined(PHYSAC_DEBUG)
+ else printf("[PHYSAC] new physics body creation failed because there is any available id to use\n");
+ #endif
+
+ return newBody;
+}
+
+// Adds a force to a physics body
+PHYSACDEF void PhysicsAddForce(PhysicsBody body, Vector2 force)
+{
+ if (body != NULL) body->force = Vector2Add(body->force, force);
+}
+
+// Adds an angular force to a physics body
+PHYSACDEF void PhysicsAddTorque(PhysicsBody body, float amount)
+{
+ if (body != NULL) body->torque += amount;
+}
+
+// Shatters a polygon shape physics body to little physics bodies with explosion force
+PHYSACDEF void PhysicsShatter(PhysicsBody body, Vector2 position, float force)
+{
+ if (body != NULL)
+ {
+ if (body->shape.type == PHYSICS_POLYGON)
+ {
+ PolygonData vertexData = body->shape.vertexData;
+ bool collision = false;
+
+ for (int i = 0; i < vertexData.vertexCount; i++)
+ {
+ Vector2 positionA = body->position;
+ Vector2 positionB = Mat2MultiplyVector2(vertexData.transform, Vector2Add(body->position, vertexData.vertices[i]));
+ int nextIndex = (((i + 1) < vertexData.vertexCount) ? (i + 1) : 0);
+ Vector2 positionC = Mat2MultiplyVector2(vertexData.transform, Vector2Add(body->position, vertexData.vertices[nextIndex]));
+
+ // Check collision between each triangle
+ float alpha = ((positionB.y - positionC.y)*(position.x - positionC.x) + (positionC.x - positionB.x)*(position.y - positionC.y))/
+ ((positionB.y - positionC.y)*(positionA.x - positionC.x) + (positionC.x - positionB.x)*(positionA.y - positionC.y));
+
+ float beta = ((positionC.y - positionA.y)*(position.x - positionC.x) + (positionA.x - positionC.x)*(position.y - positionC.y))/
+ ((positionB.y - positionC.y)*(positionA.x - positionC.x) + (positionC.x - positionB.x)*(positionA.y - positionC.y));
+
+ float gamma = 1.0f - alpha - beta;
+
+ if ((alpha > 0) && (beta > 0) & (gamma > 0))
+ {
+ collision = true;
+ break;
+ }
+ }
+
+ if (collision)
+ {
+ int count = vertexData.vertexCount;
+ Vector2 bodyPos = body->position;
+ Vector2 vertices[count];
+ Mat2 trans = vertexData.transform;
+ for (int i = 0; i < count; i++) vertices[i] = vertexData.vertices[i];
+
+ // Destroy shattered physics body
+ DestroyPhysicsBody(body);
+
+ for (int i = 0; i < count; i++)
+ {
+ int nextIndex = (((i + 1) < count) ? (i + 1) : 0);
+ Vector2 center = TriangleBarycenter(vertices[i], vertices[nextIndex], (Vector2){ 0, 0 });
+ center = Vector2Add(bodyPos, center);
+ Vector2 offset = Vector2Subtract(center, bodyPos);
+
+ PhysicsBody newBody = CreatePhysicsBodyPolygon(center, 10, 3, 10); // Create polygon physics body with relevant values
+
+ PolygonData newData = { 0 };
+ newData.vertexCount = 3;
+ newData.transform = trans;
+
+ newData.vertices[0] = Vector2Subtract(vertices[i], offset);
+ newData.vertices[1] = Vector2Subtract(vertices[nextIndex], offset);
+ newData.vertices[2] = Vector2Subtract(position, center);
+
+ // Separate vertices to avoid unnecessary physics collisions
+ newData.vertices[0].x *= 0.95f;
+ newData.vertices[0].y *= 0.95f;
+ newData.vertices[1].x *= 0.95f;
+ newData.vertices[1].y *= 0.95f;
+ newData.vertices[2].x *= 0.95f;
+ newData.vertices[2].y *= 0.95f;
+
+ // Calculate polygon faces normals
+ for (int j = 0; j < newData.vertexCount; j++)
+ {
+ int nextVertex = (((j + 1) < newData.vertexCount) ? (j + 1) : 0);
+ Vector2 face = Vector2Subtract(newData.vertices[nextVertex], newData.vertices[j]);
+
+ newData.normals[j] = (Vector2){ face.y, -face.x };
+ MathNormalize(&newData.normals[j]);
+ }
+
+ // Apply computed vertex data to new physics body shape
+ newBody->shape.vertexData = newData;
+
+ // Calculate centroid and moment of inertia
+ center = (Vector2){ 0 };
+ float area = 0;
+ float inertia = 0;
+ const float k = 1.0f/3.0f;
+
+ for (int j = 0; j < newBody->shape.vertexData.vertexCount; j++)
+ {
+ // Triangle vertices, third vertex implied as (0, 0)
+ Vector2 p1 = newBody->shape.vertexData.vertices[j];
+ int nextVertex = (((j + 1) < newBody->shape.vertexData.vertexCount) ? (j + 1) : 0);
+ Vector2 p2 = newBody->shape.vertexData.vertices[nextVertex];
+
+ float D = MathCrossVector2(p1, p2);
+ float triangleArea = D/2;
+
+ area += triangleArea;
+
+ // Use area to weight the centroid average, not just vertex position
+ center.x += triangleArea*k*(p1.x + p2.x);
+ center.y += triangleArea*k*(p1.y + p2.y);
+
+ float intx2 = p1.x*p1.x + p2.x*p1.x + p2.x*p2.x;
+ float inty2 = p1.y*p1.y + p2.y*p1.y + p2.y*p2.y;
+ inertia += (0.25f*k*D)*(intx2 + inty2);
+ }
+
+ center.x *= 1.0f/area;
+ center.y *= 1.0f/area;
+
+ newBody->mass = area;
+ newBody->inverseMass = ((newBody->mass != 0.0f) ? 1.0f/newBody->mass : 0.0f);
+ newBody->inertia = inertia;
+ newBody->inverseInertia = ((newBody->inertia != 0.0f) ? 1.0f/newBody->inertia : 0.0f);
+
+ // Calculate explosion force direction
+ Vector2 pointA = newBody->position;
+ Vector2 pointB = Vector2Subtract(newData.vertices[1], newData.vertices[0]);
+ pointB.x /= 2;
+ pointB.y /= 2;
+ Vector2 forceDirection = Vector2Subtract(Vector2Add(pointA, Vector2Add(newData.vertices[0], pointB)), newBody->position);
+ MathNormalize(&forceDirection);
+ forceDirection.x *= force;
+ forceDirection.y *= force;
+
+ // Apply force to new physics body
+ PhysicsAddForce(newBody, forceDirection);
+ }
+ }
}
- else PHYSAC_FREE(physicBodies[i]);
}
-
- // Decrease enabled physic bodies count
- physicBodiesCount--;
+ #if defined(PHYSAC_DEBUG)
+ else printf("[PHYSAC] error when trying to shatter a null reference physics body");
+ #endif
}
-// Apply directional force to a physic body
-PHYSACDEF void ApplyForce(PhysicBody pbody, Vector2 force)
+// Returns the current amount of created physics bodies
+PHYSACDEF int GetPhysicsBodiesCount(void)
{
- if (pbody->rigidbody.enabled)
+ return physicsBodiesCount;
+}
+
+// Returns a physics body of the bodies pool at a specific index
+PHYSACDEF PhysicsBody GetPhysicsBody(int index)
+{
+ if (index < physicsBodiesCount)
+ {
+ PhysicsBody body = bodies[index];
+ if (body != NULL) return body;
+ else
+ {
+ #if defined(PHYSAC_DEBUG)
+ printf("[PHYSAC] error when trying to get a null reference physics body");
+ #endif
+
+ return NULL;
+ }
+ }
+ #if defined(PHYSAC_DEBUG)
+ else
{
- pbody->rigidbody.velocity.x += force.x/pbody->rigidbody.mass;
- pbody->rigidbody.velocity.y += force.y/pbody->rigidbody.mass;
+ printf("[PHYSAC] physics body index is out of bounds");
+ return NULL;
}
+ #endif
}
-// Apply radial force to all physic objects in range
-PHYSACDEF void ApplyForceAtPosition(Vector2 position, float force, float radius)
+// Returns the physics body shape type (PHYSICS_CIRCLE or PHYSICS_POLYGON)
+PHYSACDEF int GetPhysicsShapeType(int index)
{
- for (int i = 0; i < physicBodiesCount; i++)
+ if (index < physicsBodiesCount)
{
- if (physicBodies[i]->rigidbody.enabled)
+ PhysicsBody body = bodies[index];
+ if (body != NULL) return body->shape.type;
+ #if defined(PHYSAC_DEBUG)
+ else
{
- // Calculate direction and distance between force and physic body position
- Vector2 distance = (Vector2){ physicBodies[i]->transform.position.x - position.x, physicBodies[i]->transform.position.y - position.y };
+ printf("[PHYSAC] error when trying to get a null reference physics body");
+ return -1;
+ }
+ #endif
+ }
+ #if defined(PHYSAC_DEBUG)
+ else
+ {
+ printf("[PHYSAC] physics body index is out of bounds");
+ return -1;
+ }
+ #endif
+}
- if (physicBodies[i]->collider.type == COLLIDER_RECTANGLE)
+// Returns the amount of vertices of a physics body shape
+PHYSACDEF int GetPhysicsShapeVerticesCount(int index)
+{
+ if (index < physicsBodiesCount)
+ {
+ PhysicsBody body = bodies[index];
+ if (body != NULL)
+ {
+ switch (body->shape.type)
{
- distance.x += physicBodies[i]->transform.scale.x/2;
- distance.y += physicBodies[i]->transform.scale.y/2;
+ case PHYSICS_CIRCLE: return PHYSAC_CIRCLE_VERTICES; break;
+ case PHYSICS_POLYGON: return body->shape.vertexData.vertexCount; break;
+ default: break;
}
-
- float distanceLength = Vector2Length(distance);
-
- // Check if physic body is in force range
- if (distanceLength <= radius)
+ }
+ #if defined(PHYSAC_DEBUG)
+ else
+ {
+ printf("[PHYSAC] error when trying to get a null reference physics body");
+ return 0;
+ }
+ #endif
+ }
+ #if defined(PHYSAC_DEBUG)
+ else
+ {
+ printf("[PHYSAC] physics body index is out of bounds");
+ return 0;
+ }
+ #endif
+}
+
+// Returns transformed position of a body shape (body position + vertex transformed position)
+PHYSACDEF Vector2 GetPhysicsShapeVertex(PhysicsBody body, int vertex)
+{
+ Vector2 position = { 0 };
+
+ if (body != NULL)
+ {
+ switch (body->shape.type)
+ {
+ case PHYSICS_CIRCLE:
+ {
+ position.x = body->position.x + cosf(360/PHYSAC_CIRCLE_VERTICES*vertex*PHYSAC_DEG2RAD)*body->shape.radius;
+ position.y = body->position.y + sinf(360/PHYSAC_CIRCLE_VERTICES*vertex*PHYSAC_DEG2RAD)*body->shape.radius;
+ } break;
+ case PHYSICS_POLYGON:
{
- // Normalize force direction
- distance.x /= distanceLength;
- distance.y /= -distanceLength;
-
- // Calculate final force
- Vector2 finalForce = { distance.x*force, distance.y*force };
-
- // Apply force to the physic body
- ApplyForce(physicBodies[i], finalForce);
+ PolygonData vertexData = body->shape.vertexData;
+ position = Vector2Add(body->position, Mat2MultiplyVector2(vertexData.transform, vertexData.vertices[vertex]));
+ } break;
+ default: break;
+ }
+ }
+ #if defined(PHYSAC_DEBUG)
+ else printf("[PHYSAC] error when trying to get a null reference physics body");
+ #endif
+
+ return position;
+}
+
+// Sets physics body shape transform based on radians parameter
+PHYSACDEF void SetPhysicsBodyRotation(PhysicsBody body, float radians)
+{
+ if (body != NULL)
+ {
+ body->orient = radians;
+
+ if (body->shape.type == PHYSICS_POLYGON) body->shape.vertexData.transform = Mat2Radians(radians);
+ }
+}
+
+// Unitializes and destroys a physics body
+PHYSACDEF void DestroyPhysicsBody(PhysicsBody body)
+{
+ if (body != NULL)
+ {
+ int id = body->id;
+ int index = -1;
+
+ for (int i = 0; i < physicsBodiesCount; i++)
+ {
+ if (bodies[i]->id == id)
+ {
+ index = i;
+ break;
}
}
+
+ #if defined(PHYSAC_DEBUG)
+ if (index == -1) printf("[PHYSAC] cannot find body id %i in pointers array\n", id);
+ #endif
+
+ // Free body allocated memory
+ PHYSAC_FREE(bodies[index]);
+ usedMemory -= sizeof(PhysicsBodyData);
+ bodies[index] = NULL;
+
+ // Reorder physics bodies pointers array and its catched index
+ for (int i = index; i < physicsBodiesCount; i++)
+ {
+ if ((i + 1) < physicsBodiesCount) bodies[i] = bodies[i + 1];
+ }
+
+ // Update physics bodies count
+ physicsBodiesCount--;
+
+ #if defined(PHYSAC_DEBUG)
+ printf("[PHYSAC] destroyed physics body id %i\n", id);
+ #endif
}
+ #if defined(PHYSAC_DEBUG)
+ else printf("[PHYSAC] error trying to destroy a null referenced body\n");
+ #endif
}
-// Convert Transform data type to Rectangle (position and scale)
-PHYSACDEF Rectangle TransformToRectangle(Transform transform)
+// Destroys created physics bodies and manifolds and resets global values
+PHYSACDEF void ResetPhysics(void)
{
- return (Rectangle){transform.position.x, transform.position.y, transform.scale.x, transform.scale.y};
+ // Unitialize physics bodies dynamic memory allocations
+ for (int i = physicsBodiesCount - 1; i >= 0; i--)
+ {
+ PhysicsBody body = bodies[i];
+
+ if (body != NULL)
+ {
+ PHYSAC_FREE(body);
+ body = NULL;
+ usedMemory -= sizeof(PhysicsBodyData);
+ }
+ }
+
+ physicsBodiesCount = 0;
+
+ // Unitialize physics manifolds dynamic memory allocations
+ for (int i = physicsManifoldsCount - 1; i >= 0; i--)
+ {
+ PhysicsManifold manifold = contacts[i];
+
+ if (manifold != NULL)
+ {
+ PHYSAC_FREE(manifold);
+ manifold = NULL;
+ usedMemory -= sizeof(PhysicsManifoldData);
+ }
+ }
+
+ physicsManifoldsCount = 0;
+
+ #if defined(PHYSAC_DEBUG)
+ printf("[PHYSAC] physics module reset successfully\n");
+ #endif
}
-// Physics calculations thread function
-PHYSACDEF void* PhysicsThread(void *arg)
+// Unitializes physics pointers and exits physics loop thread
+PHYSACDEF void ClosePhysics(void)
{
- // Initialize thread loop state
+ // Exit physics loop thread
+ physicsThreadEnabled = false;
+
+ #if !defined(PHYSAC_NO_THREADS)
+ pthread_join(physicsThreadId, NULL);
+ #endif
+}
+
+//----------------------------------------------------------------------------------
+// Module Internal Functions Definition
+//----------------------------------------------------------------------------------
+// Creates a random polygon shape with max vertex distance from polygon pivot
+static PolygonData CreateRandomPolygon(float radius, int sides)
+{
+ PolygonData data = { 0 };
+ data.vertexCount = sides;
+
+ float orient = GetRandomNumber(0, 360);
+ data.transform = Mat2Radians(orient*PHYSAC_DEG2RAD);
+
+ // Calculate polygon vertices positions
+ for (int i = 0; i < data.vertexCount; i++)
+ {
+ data.vertices[i].x = cosf(360/sides*i*PHYSAC_DEG2RAD)*radius;
+ data.vertices[i].y = sinf(360/sides*i*PHYSAC_DEG2RAD)*radius;
+ }
+
+ // Calculate polygon faces normals
+ for (int i = 0; i < data.vertexCount; i++)
+ {
+ int nextIndex = (((i + 1) < sides) ? (i + 1) : 0);
+ Vector2 face = Vector2Subtract(data.vertices[nextIndex], data.vertices[i]);
+
+ data.normals[i] = (Vector2){ face.y, -face.x };
+ MathNormalize(&data.normals[i]);
+ }
+
+ return data;
+}
+
+// Creates a rectangle polygon shape based on a min and max positions
+static PolygonData CreateRectanglePolygon(Vector2 pos, Vector2 size)
+{
+ PolygonData data = { 0 };
+
+ data.vertexCount = 4;
+ data.transform = Mat2Radians(0);
+
+ // Calculate polygon vertices positions
+ data.vertices[0] = (Vector2){ pos.x + size.x/2, pos.y - size.y/2 };
+ data.vertices[1] = (Vector2){ pos.x + size.x/2, pos.y + size.y/2 };
+ data.vertices[2] = (Vector2){ pos.x - size.x/2, pos.y + size.y/2 };
+ data.vertices[3] = (Vector2){ pos.x - size.x/2, pos.y - size.y/2 };
+
+ // Calculate polygon faces normals
+ for (int i = 0; i < data.vertexCount; i++)
+ {
+ int nextIndex = (((i + 1) < data.vertexCount) ? (i + 1) : 0);
+ Vector2 face = Vector2Subtract(data.vertices[nextIndex], data.vertices[i]);
+
+ data.normals[i] = (Vector2){ face.y, -face.x };
+ MathNormalize(&data.normals[i]);
+ }
+
+ return data;
+}
+
+// Physics loop thread function
+static void *PhysicsLoop(void *arg)
+{
+ #if defined(PHYSAC_DEBUG)
+ printf("[PHYSAC] physics thread created with successfully\n");
+ #endif
+
+ // Initialize physics loop thread values
physicsThreadEnabled = true;
-
- // Initialize hi-resolution timer
+ accumulator = 0;
+
+ // Initialize high resolution timer
InitTimer();
-
+
// Physics update loop
- while (physicsThreadEnabled)
+ while (physicsThreadEnabled)
{
+ // Calculate current time
currentTime = GetCurrentTime();
- double deltaTime = (double)(currentTime - previousTime);
- previousTime = currentTime;
- // Delta time value needs to be inverse multiplied by physics time step value (1/target fps)
- UpdatePhysics(deltaTime/PHYSICS_TIMESTEP);
+ // Calculate current delta time
+ deltaTime = currentTime - startTime;
+
+ // Store the time elapsed since the last frame began
+ accumulator += deltaTime;
+
+ // Clamp accumulator to max time step to avoid bad performance
+ MathClamp(&accumulator, 0, PHYSAC_MAX_TIMESTEP);
+
+ // Fixed time stepping loop
+ while (accumulator >= PHYSAC_DESIRED_DELTATIME)
+ {
+ PhysicsStep();
+ accumulator -= deltaTime;
+ }
+
+ // Record the starting of this frame
+ startTime = currentTime;
}
-
+
+ // Unitialize physics manifolds dynamic memory allocations
+ for (int i = physicsManifoldsCount - 1; i >= 0; i--) DestroyPhysicsManifold(contacts[i]);
+
+ // Unitialize physics bodies dynamic memory allocations
+ for (int i = physicsBodiesCount - 1; i >= 0; i--) DestroyPhysicsBody(bodies[i]);
+
+ #if defined(PHYSAC_DEBUG)
+ if (physicsBodiesCount > 0 || usedMemory != 0) printf("[PHYSAC] physics module closed with %i still allocated bodies [MEMORY: %i bytes]\n", physicsBodiesCount, usedMemory);
+ else if (physicsManifoldsCount > 0 || usedMemory != 0) printf("[PHYSAC] physics module closed with %i still allocated manifolds [MEMORY: %i bytes]\n", physicsManifoldsCount, usedMemory);
+ else printf("[PHYSAC] physics module closed successfully\n");
+ #endif
+
return NULL;
}
-//----------------------------------------------------------------------------------
-// Module specific Functions Definition
-//----------------------------------------------------------------------------------
-// Initialize hi-resolution timer
-static void InitTimer(void)
+// Physics steps calculations (dynamics, collisions and position corrections)
+static void PhysicsStep(void)
{
-#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI)
- struct timespec now;
+ stepsCount++;
- if (clock_gettime(CLOCK_MONOTONIC, &now) == 0) // Success
+ // Clear previous generated collisions information
+ for (int i = physicsManifoldsCount - 1; i >= 0; i--)
{
- baseTime = (uint64_t)now.tv_sec*1000000000LLU + (uint64_t)now.tv_nsec;
+ PhysicsManifold manifold = contacts[i];
+ if (manifold != NULL) DestroyPhysicsManifold(manifold);
}
-#endif
- previousTime = GetCurrentTime(); // Get time as double
-}
+ // Generate new collision information
+ for (int i = 0; i < physicsBodiesCount; i++)
+ {
+ PhysicsBody bodyA = bodies[i];
-// Time measure returned are microseconds
-static double GetCurrentTime(void)
-{
- double time;
-
-#if defined(PLATFORM_DESKTOP)
- unsigned long long int clockFrequency, currentTime;
-
- QueryPerformanceFrequency(&clockFrequency);
- QueryPerformanceCounter(&currentTime);
-
- time = (double)((double)currentTime/(double)clockFrequency);
-#endif
+ if (bodyA != NULL)
+ {
+ for (int j = i + 1; j < physicsBodiesCount; j++)
+ {
+ PhysicsBody bodyB = bodies[j];
-#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI)
- struct timespec ts;
- clock_gettime(CLOCK_MONOTONIC, &ts);
- uint64_t temp = (uint64_t)ts.tv_sec*1000000000LLU + (uint64_t)ts.tv_nsec;
+ if (bodyB != NULL)
+ {
+ if ((bodyA->inverseMass == 0) && (bodyB->inverseMass == 0)) continue;
- time = (double)(temp - baseTime)*1e-9;
-#endif
+ PhysicsManifold manifold = NULL;
+ if (bodyA->shape.type == PHYSICS_POLYGON && bodyB->shape.type == PHYSICS_CIRCLE) manifold = CreatePhysicsManifold(bodyB, bodyA);
+ else manifold = CreatePhysicsManifold(bodyA, bodyB);
+ SolvePhysicsManifold(manifold);
- return time;
+ if (manifold->contactsCount > 0)
+ {
+ // Create a new manifold with same information as previously solved manifold and add it to the manifolds pool last slot
+ PhysicsManifold newManifold = CreatePhysicsManifold(bodyA, bodyB);
+ newManifold->penetration = manifold->penetration;
+ newManifold->normal = manifold->normal;
+ newManifold->contacts[0] = manifold->contacts[0];
+ newManifold->contacts[1] = manifold->contacts[1];
+ newManifold->contactsCount = manifold->contactsCount;
+ newManifold->restitution = manifold->restitution;
+ newManifold->dynamicFriction = manifold->dynamicFriction;
+ newManifold->staticFriction = manifold->staticFriction;
+ }
+ }
+ }
+ }
+ }
+
+ // Integrate forces to physics bodies
+ for (int i = 0; i < physicsBodiesCount; i++)
+ {
+ PhysicsBody body = bodies[i];
+ if (body != NULL) IntegratePhysicsForces(body);
+ }
+
+ // Initialize physics manifolds to solve collisions
+ for (int i = 0; i < physicsManifoldsCount; i++)
+ {
+ PhysicsManifold manifold = contacts[i];
+ if (manifold != NULL) InitializePhysicsManifolds(manifold);
+ }
+
+ // Integrate physics collisions impulses to solve collisions
+ for (int i = 0; i < PHYSAC_COLLISION_ITERATIONS; i++)
+ {
+ for (int j = 0; j < physicsManifoldsCount; j++)
+ {
+ PhysicsManifold manifold = contacts[i];
+ if (manifold != NULL) IntegratePhysicsImpulses(manifold);
+ }
+ }
+
+ // Integrate velocity to physics bodies
+ for (int i = 0; i < physicsBodiesCount; i++)
+ {
+ PhysicsBody body = bodies[i];
+ if (body != NULL) IntegratePhysicsVelocity(body);
+ }
+
+ // Correct physics bodies positions based on manifolds collision information
+ for (int i = 0; i < physicsManifoldsCount; i++)
+ {
+ PhysicsManifold manifold = contacts[i];
+ if (manifold != NULL) CorrectPhysicsPositions(manifold);
+ }
+
+ // Clear physics bodies forces
+ for (int i = 0; i < physicsBodiesCount; i++)
+ {
+ PhysicsBody body = bodies[i];
+ if (body != NULL)
+ {
+ body->force = (Vector2){ 0 };
+ body->torque = 0;
+ }
+ }
}
-// Returns the dot product of two Vector2
-static float Vector2DotProduct(Vector2 v1, Vector2 v2)
+// Creates a new physics manifold to solve collision
+static PhysicsManifold CreatePhysicsManifold(PhysicsBody a, PhysicsBody b)
{
- float result;
+ PhysicsManifold newManifold = (PhysicsManifold)PHYSAC_MALLOC(sizeof(PhysicsManifoldData));
+ usedMemory += sizeof(PhysicsManifoldData);
- result = v1.x*v2.x + v1.y*v2.y;
+ int newId = -1;
+ for (int i = 0; i < PHYSAC_MAX_MANIFOLDS; i++)
+ {
+ int currentId = i;
- return result;
+ // Check if current id already exist in other physics body
+ for (int k = 0; k < physicsManifoldsCount; k++)
+ {
+ if (contacts[k]->id == currentId)
+ {
+ currentId++;
+ break;
+ }
+ }
+
+ // If it is not used, use it as new physics body id
+ if (currentId == i)
+ {
+ newId = i;
+ break;
+ }
+ }
+
+ if (newId != -1)
+ {
+ // Initialize new manifold with generic values
+ newManifold->id = newId;
+ newManifold->bodyA = a;
+ newManifold->bodyB = b;
+ newManifold->penetration = 0;
+ newManifold->normal = (Vector2){ 0 };
+ newManifold->contacts[0] = (Vector2){ 0 };
+ newManifold->contacts[1] = (Vector2){ 0 };
+ newManifold->contactsCount = 0;
+ newManifold->restitution = 0;
+ newManifold->dynamicFriction = 0;
+ newManifold->staticFriction = 0;
+
+ // Add new body to bodies pointers array and update bodies count
+ contacts[physicsManifoldsCount] = newManifold;
+ physicsManifoldsCount++;
+ }
+ #if defined(PHYSAC_DEBUG)
+ else printf("[PHYSAC] new physics manifold creation failed because there is any available id to use\n");
+ #endif
+
+ return newManifold;
}
-static float Vector2Length(Vector2 v)
+// Unitializes and destroys a physics manifold
+static void DestroyPhysicsManifold(PhysicsManifold manifold)
{
- float result;
-
- result = sqrt(v.x*v.x + v.y*v.y);
-
- return result;
+ if (manifold != NULL)
+ {
+ int id = manifold->id;
+ int index = -1;
+
+ for (int i = 0; i < physicsManifoldsCount; i++)
+ {
+ if (contacts[i]->id == id)
+ {
+ index = i;
+ break;
+ }
+ }
+
+ #if defined(PHYSAC_DEBUG)
+ if (index == -1) printf("[PHYSAC] cannot find manifold id %i in pointers array\n", id);
+ #endif
+
+ // Free manifold allocated memory
+ PHYSAC_FREE(contacts[index]);
+ usedMemory -= sizeof(PhysicsManifoldData);
+ contacts[index] = NULL;
+
+ // Reorder physics manifolds pointers array and its catched index
+ for (int i = index; i < physicsManifoldsCount; i++)
+ {
+ if ((i + 1) < physicsManifoldsCount) contacts[i] = contacts[i + 1];
+ }
+
+ // Update physics manifolds count
+ physicsManifoldsCount--;
+ }
+ #if defined(PHYSAC_DEBUG)
+ else printf("[PHYSAC] error trying to destroy a null referenced manifold\n");
+ #endif
}
-// Update physic objects, calculating physic behaviours and collisions detection
-static void UpdatePhysics(double deltaTime)
+// Solves a created physics manifold between two physics bodies
+static void SolvePhysicsManifold(PhysicsManifold manifold)
{
- for (int i = 0; i < physicBodiesCount; i++)
+ switch (manifold->bodyA->shape.type)
{
- if (physicBodies[i]->enabled)
+ case PHYSICS_CIRCLE:
{
- // Update physic behaviour
- if (physicBodies[i]->rigidbody.enabled)
+ switch (manifold->bodyB->shape.type)
{
- // Apply friction to acceleration in X axis
- if (physicBodies[i]->rigidbody.acceleration.x > PHYSICS_ACCURACY) physicBodies[i]->rigidbody.acceleration.x -= physicBodies[i]->rigidbody.friction*deltaTime;
- else if (physicBodies[i]->rigidbody.acceleration.x < PHYSICS_ACCURACY) physicBodies[i]->rigidbody.acceleration.x += physicBodies[i]->rigidbody.friction*deltaTime;
- else physicBodies[i]->rigidbody.acceleration.x = 0.0f;
-
- // Apply friction to acceleration in Y axis
- if (physicBodies[i]->rigidbody.acceleration.y > PHYSICS_ACCURACY) physicBodies[i]->rigidbody.acceleration.y -= physicBodies[i]->rigidbody.friction*deltaTime;
- else if (physicBodies[i]->rigidbody.acceleration.y < PHYSICS_ACCURACY) physicBodies[i]->rigidbody.acceleration.y += physicBodies[i]->rigidbody.friction*deltaTime;
- else physicBodies[i]->rigidbody.acceleration.y = 0.0f;
-
- // Apply friction to velocity in X axis
- if (physicBodies[i]->rigidbody.velocity.x > PHYSICS_ACCURACY) physicBodies[i]->rigidbody.velocity.x -= physicBodies[i]->rigidbody.friction*deltaTime;
- else if (physicBodies[i]->rigidbody.velocity.x < PHYSICS_ACCURACY) physicBodies[i]->rigidbody.velocity.x += physicBodies[i]->rigidbody.friction*deltaTime;
- else physicBodies[i]->rigidbody.velocity.x = 0.0f;
-
- // Apply friction to velocity in Y axis
- if (physicBodies[i]->rigidbody.velocity.y > PHYSICS_ACCURACY) physicBodies[i]->rigidbody.velocity.y -= physicBodies[i]->rigidbody.friction*deltaTime;
- else if (physicBodies[i]->rigidbody.velocity.y < PHYSICS_ACCURACY) physicBodies[i]->rigidbody.velocity.y += physicBodies[i]->rigidbody.friction*deltaTime;
- else physicBodies[i]->rigidbody.velocity.y = 0.0f;
-
- // Apply gravity to velocity
- if (physicBodies[i]->rigidbody.applyGravity)
- {
- physicBodies[i]->rigidbody.velocity.x += gravityForce.x*deltaTime;
- physicBodies[i]->rigidbody.velocity.y += gravityForce.y*deltaTime;
- }
-
- // Apply acceleration to velocity
- physicBodies[i]->rigidbody.velocity.x += physicBodies[i]->rigidbody.acceleration.x*deltaTime;
- physicBodies[i]->rigidbody.velocity.y += physicBodies[i]->rigidbody.acceleration.y*deltaTime;
-
- // Apply velocity to position
- physicBodies[i]->transform.position.x += physicBodies[i]->rigidbody.velocity.x*deltaTime;
- physicBodies[i]->transform.position.y -= physicBodies[i]->rigidbody.velocity.y*deltaTime;
+ case PHYSICS_CIRCLE: SolveCircleToCircle(manifold); break;
+ case PHYSICS_POLYGON: SolveCircleToPolygon(manifold); break;
+ default: break;
}
-
- // Update collision detection
- if (physicBodies[i]->collider.enabled)
+ } break;
+ case PHYSICS_POLYGON:
+ {
+ switch (manifold->bodyB->shape.type)
{
- // Update collider bounds
- physicBodies[i]->collider.bounds = TransformToRectangle(physicBodies[i]->transform);
-
- // Check collision with other colliders
- for (int k = 0; k < physicBodiesCount; k++)
- {
- if (physicBodies[k]->collider.enabled && i != k)
- {
- // Resolve physic collision
- // NOTE: collision resolve is generic for all directions and conditions (no axis separated cases behaviours)
- // and it is separated in rigidbody attributes resolve (velocity changes by impulse) and position correction (position overlap)
-
- // 1. Calculate collision normal
- // -------------------------------------------------------------------------------------------------------------------------------------
-
- // Define collision contact normal, direction and penetration depth
- Vector2 contactNormal = { 0.0f, 0.0f };
- Vector2 direction = { 0.0f, 0.0f };
- float penetrationDepth = 0.0f;
-
- switch (physicBodies[i]->collider.type)
- {
- case COLLIDER_RECTANGLE:
- {
- switch (physicBodies[k]->collider.type)
- {
- case COLLIDER_RECTANGLE:
- {
- // Check if colliders are overlapped
- if (CheckCollisionRecs(physicBodies[i]->collider.bounds, physicBodies[k]->collider.bounds))
- {
- // Calculate direction vector from i to k
- direction.x = (physicBodies[k]->transform.position.x + physicBodies[k]->transform.scale.x/2) - (physicBodies[i]->transform.position.x + physicBodies[i]->transform.scale.x/2);
- direction.y = (physicBodies[k]->transform.position.y + physicBodies[k]->transform.scale.y/2) - (physicBodies[i]->transform.position.y + physicBodies[i]->transform.scale.y/2);
-
- // Define overlapping and penetration attributes
- Vector2 overlap;
-
- // Calculate overlap on X axis
- overlap.x = (physicBodies[i]->transform.scale.x + physicBodies[k]->transform.scale.x)/2 - abs(direction.x);
-
- // SAT test on X axis
- if (overlap.x > 0.0f)
- {
- // Calculate overlap on Y axis
- overlap.y = (physicBodies[i]->transform.scale.y + physicBodies[k]->transform.scale.y)/2 - abs(direction.y);
-
- // SAT test on Y axis
- if (overlap.y > 0.0f)
- {
- // Find out which axis is axis of least penetration
- if (overlap.y > overlap.x)
- {
- // Point towards k knowing that direction points from i to k
- if (direction.x < 0.0f) contactNormal = (Vector2){ -1.0f, 0.0f };
- else contactNormal = (Vector2){ 1.0f, 0.0f };
-
- // Update penetration depth for position correction
- penetrationDepth = overlap.x;
- }
- else
- {
- // Point towards k knowing that direction points from i to k
- if (direction.y < 0.0f) contactNormal = (Vector2){ 0.0f, 1.0f };
- else contactNormal = (Vector2){ 0.0f, -1.0f };
-
- // Update penetration depth for position correction
- penetrationDepth = overlap.y;
- }
- }
- }
- }
- } break;
- case COLLIDER_CIRCLE:
- {
- if (CheckCollisionCircleRec(physicBodies[k]->transform.position, physicBodies[k]->collider.radius, physicBodies[i]->collider.bounds))
- {
- // Calculate direction vector between circles
- direction.x = physicBodies[k]->transform.position.x - physicBodies[i]->transform.position.x + physicBodies[i]->transform.scale.x/2;
- direction.y = physicBodies[k]->transform.position.y - physicBodies[i]->transform.position.y + physicBodies[i]->transform.scale.y/2;
-
- // Calculate closest point on rectangle to circle
- Vector2 closestPoint = { 0.0f, 0.0f };
- if (direction.x > 0.0f) closestPoint.x = physicBodies[i]->collider.bounds.x + physicBodies[i]->collider.bounds.width;
- else closestPoint.x = physicBodies[i]->collider.bounds.x;
-
- if (direction.y > 0.0f) closestPoint.y = physicBodies[i]->collider.bounds.y + physicBodies[i]->collider.bounds.height;
- else closestPoint.y = physicBodies[i]->collider.bounds.y;
-
- // Check if the closest point is inside the circle
- if (CheckCollisionPointCircle(closestPoint, physicBodies[k]->transform.position, physicBodies[k]->collider.radius))
- {
- // Recalculate direction based on closest point position
- direction.x = physicBodies[k]->transform.position.x - closestPoint.x;
- direction.y = physicBodies[k]->transform.position.y - closestPoint.y;
- float distance = Vector2Length(direction);
-
- // Calculate final contact normal
- contactNormal.x = direction.x/distance;
- contactNormal.y = -direction.y/distance;
-
- // Calculate penetration depth
- penetrationDepth = physicBodies[k]->collider.radius - distance;
- }
- else
- {
- if (abs(direction.y) < abs(direction.x))
- {
- // Calculate final contact normal
- if (direction.y > 0.0f)
- {
- contactNormal = (Vector2){ 0.0f, -1.0f };
- penetrationDepth = fabs(physicBodies[i]->collider.bounds.y - physicBodies[k]->transform.position.y - physicBodies[k]->collider.radius);
- }
- else
- {
- contactNormal = (Vector2){ 0.0f, 1.0f };
- penetrationDepth = fabs(physicBodies[i]->collider.bounds.y - physicBodies[k]->transform.position.y + physicBodies[k]->collider.radius);
- }
- }
- else
- {
- // Calculate final contact normal
- if (direction.x > 0.0f)
- {
- contactNormal = (Vector2){ 1.0f, 0.0f };
- penetrationDepth = fabs(physicBodies[k]->transform.position.x + physicBodies[k]->collider.radius - physicBodies[i]->collider.bounds.x);
- }
- else
- {
- contactNormal = (Vector2){ -1.0f, 0.0f };
- penetrationDepth = fabs(physicBodies[i]->collider.bounds.x + physicBodies[i]->collider.bounds.width - physicBodies[k]->transform.position.x - physicBodies[k]->collider.radius);
- }
- }
- }
- }
- } break;
- }
- } break;
- case COLLIDER_CIRCLE:
- {
- switch (physicBodies[k]->collider.type)
- {
- case COLLIDER_RECTANGLE:
- {
- if (CheckCollisionCircleRec(physicBodies[i]->transform.position, physicBodies[i]->collider.radius, physicBodies[k]->collider.bounds))
- {
- // Calculate direction vector between circles
- direction.x = physicBodies[k]->transform.position.x + physicBodies[i]->transform.scale.x/2 - physicBodies[i]->transform.position.x;
- direction.y = physicBodies[k]->transform.position.y + physicBodies[i]->transform.scale.y/2 - physicBodies[i]->transform.position.y;
-
- // Calculate closest point on rectangle to circle
- Vector2 closestPoint = { 0.0f, 0.0f };
- if (direction.x > 0.0f) closestPoint.x = physicBodies[k]->collider.bounds.x + physicBodies[k]->collider.bounds.width;
- else closestPoint.x = physicBodies[k]->collider.bounds.x;
-
- if (direction.y > 0.0f) closestPoint.y = physicBodies[k]->collider.bounds.y + physicBodies[k]->collider.bounds.height;
- else closestPoint.y = physicBodies[k]->collider.bounds.y;
-
- // Check if the closest point is inside the circle
- if (CheckCollisionPointCircle(closestPoint, physicBodies[i]->transform.position, physicBodies[i]->collider.radius))
- {
- // Recalculate direction based on closest point position
- direction.x = physicBodies[i]->transform.position.x - closestPoint.x;
- direction.y = physicBodies[i]->transform.position.y - closestPoint.y;
- float distance = Vector2Length(direction);
-
- // Calculate final contact normal
- contactNormal.x = direction.x/distance;
- contactNormal.y = -direction.y/distance;
-
- // Calculate penetration depth
- penetrationDepth = physicBodies[k]->collider.radius - distance;
- }
- else
- {
- if (abs(direction.y) < abs(direction.x))
- {
- // Calculate final contact normal
- if (direction.y > 0.0f)
- {
- contactNormal = (Vector2){ 0.0f, -1.0f };
- penetrationDepth = fabs(physicBodies[k]->collider.bounds.y - physicBodies[i]->transform.position.y - physicBodies[i]->collider.radius);
- }
- else
- {
- contactNormal = (Vector2){ 0.0f, 1.0f };
- penetrationDepth = fabs(physicBodies[k]->collider.bounds.y - physicBodies[i]->transform.position.y + physicBodies[i]->collider.radius);
- }
- }
- else
- {
- // Calculate final contact normal and penetration depth
- if (direction.x > 0.0f)
- {
- contactNormal = (Vector2){ 1.0f, 0.0f };
- penetrationDepth = fabs(physicBodies[i]->transform.position.x + physicBodies[i]->collider.radius - physicBodies[k]->collider.bounds.x);
- }
- else
- {
- contactNormal = (Vector2){ -1.0f, 0.0f };
- penetrationDepth = fabs(physicBodies[k]->collider.bounds.x + physicBodies[k]->collider.bounds.width - physicBodies[i]->transform.position.x - physicBodies[i]->collider.radius);
- }
- }
- }
- }
- } break;
- case COLLIDER_CIRCLE:
- {
- // Check if colliders are overlapped
- if (CheckCollisionCircles(physicBodies[i]->transform.position, physicBodies[i]->collider.radius, physicBodies[k]->transform.position, physicBodies[k]->collider.radius))
- {
- // Calculate direction vector between circles
- direction.x = physicBodies[k]->transform.position.x - physicBodies[i]->transform.position.x;
- direction.y = physicBodies[k]->transform.position.y - physicBodies[i]->transform.position.y;
-
- // Calculate distance between circles
- float distance = Vector2Length(direction);
-
- // Check if circles are not completely overlapped
- if (distance != 0.0f)
- {
- // Calculate contact normal direction (Y axis needs to be flipped)
- contactNormal.x = direction.x/distance;
- contactNormal.y = -direction.y/distance;
- }
- else contactNormal = (Vector2){ 1.0f, 0.0f }; // Choose random (but consistent) values
- }
- } break;
- default: break;
- }
- } break;
- default: break;
- }
-
- // Update rigidbody grounded state
- if (physicBodies[i]->rigidbody.enabled) physicBodies[i]->rigidbody.isGrounded = (contactNormal.y < 0.0f);
-
- // 2. Calculate collision impulse
- // -------------------------------------------------------------------------------------------------------------------------------------
-
- // Calculate relative velocity
- Vector2 relVelocity = { 0.0f, 0.0f };
- relVelocity.x = physicBodies[k]->rigidbody.velocity.x - physicBodies[i]->rigidbody.velocity.x;
- relVelocity.y = physicBodies[k]->rigidbody.velocity.y - physicBodies[i]->rigidbody.velocity.y;
-
- // Calculate relative velocity in terms of the normal direction
- float velAlongNormal = Vector2DotProduct(relVelocity, contactNormal);
-
- // Dot not resolve if velocities are separating
- if (velAlongNormal <= 0.0f)
- {
- // Calculate minimum bounciness value from both objects
- float e = fminf(physicBodies[i]->rigidbody.bounciness, physicBodies[k]->rigidbody.bounciness);
-
- // Calculate impulse scalar value
- float j = -(1.0f + e)*velAlongNormal;
- j /= 1.0f/physicBodies[i]->rigidbody.mass + 1.0f/physicBodies[k]->rigidbody.mass;
-
- // Calculate final impulse vector
- Vector2 impulse = { j*contactNormal.x, j*contactNormal.y };
-
- // Calculate collision mass ration
- float massSum = physicBodies[i]->rigidbody.mass + physicBodies[k]->rigidbody.mass;
- float ratio = 0.0f;
-
- // Apply impulse to current rigidbodies velocities if they are enabled
- if (physicBodies[i]->rigidbody.enabled)
- {
- // Calculate inverted mass ration
- ratio = physicBodies[i]->rigidbody.mass/massSum;
-
- // Apply impulse direction to velocity
- physicBodies[i]->rigidbody.velocity.x -= impulse.x*ratio*(1.0f+physicBodies[i]->rigidbody.bounciness);
- physicBodies[i]->rigidbody.velocity.y -= impulse.y*ratio*(1.0f+physicBodies[i]->rigidbody.bounciness);
- }
-
- if (physicBodies[k]->rigidbody.enabled)
- {
- // Calculate inverted mass ration
- ratio = physicBodies[k]->rigidbody.mass/massSum;
-
- // Apply impulse direction to velocity
- physicBodies[k]->rigidbody.velocity.x += impulse.x*ratio*(1.0f+physicBodies[i]->rigidbody.bounciness);
- physicBodies[k]->rigidbody.velocity.y += impulse.y*ratio*(1.0f+physicBodies[i]->rigidbody.bounciness);
- }
-
- // 3. Correct colliders overlaping (transform position)
- // ---------------------------------------------------------------------------------------------------------------------------------
-
- // Calculate transform position penetration correction
- Vector2 posCorrection;
- posCorrection.x = penetrationDepth/((1.0f/physicBodies[i]->rigidbody.mass) + (1.0f/physicBodies[k]->rigidbody.mass))*PHYSICS_ERRORPERCENT*contactNormal.x;
- posCorrection.y = penetrationDepth/((1.0f/physicBodies[i]->rigidbody.mass) + (1.0f/physicBodies[k]->rigidbody.mass))*PHYSICS_ERRORPERCENT*contactNormal.y;
-
- // Fix transform positions
- if (physicBodies[i]->rigidbody.enabled)
- {
- // Fix physic objects transform position
- physicBodies[i]->transform.position.x -= 1.0f/physicBodies[i]->rigidbody.mass*posCorrection.x;
- physicBodies[i]->transform.position.y += 1.0f/physicBodies[i]->rigidbody.mass*posCorrection.y;
-
- // Update collider bounds
- physicBodies[i]->collider.bounds = TransformToRectangle(physicBodies[i]->transform);
-
- if (physicBodies[k]->rigidbody.enabled)
- {
- // Fix physic objects transform position
- physicBodies[k]->transform.position.x += 1.0f/physicBodies[k]->rigidbody.mass*posCorrection.x;
- physicBodies[k]->transform.position.y -= 1.0f/physicBodies[k]->rigidbody.mass*posCorrection.y;
-
- // Update collider bounds
- physicBodies[k]->collider.bounds = TransformToRectangle(physicBodies[k]->transform);
- }
- }
- }
- }
- }
+ case PHYSICS_CIRCLE: SolvePolygonToCircle(manifold); break;
+ case PHYSICS_POLYGON: SolvePolygonToPolygon(manifold); break;
+ default: break;
}
+ } break;
+ default: break;
+ }
+
+ // Update physics body grounded state if normal direction is downside
+ manifold->bodyB->isGrounded = (manifold->normal.y < 0);
+}
+
+// Solves collision between two circle shape physics bodies
+static void SolveCircleToCircle(PhysicsManifold manifold)
+{
+ PhysicsBody bodyA = manifold->bodyA;
+ PhysicsBody bodyB = manifold->bodyB;
+
+ // Calculate translational vector, which is normal
+ Vector2 normal = Vector2Subtract(bodyB->position, bodyA->position);
+
+ float distSqr = MathLenSqr(normal);
+ float radius = bodyA->shape.radius + bodyB->shape.radius;
+
+ // Check if circles are not in contact
+ if (distSqr >= radius*radius)
+ {
+ manifold->contactsCount = 0;
+ return;
+ }
+
+ float distance = sqrtf(distSqr);
+ manifold->contactsCount = 1;
+
+ if (distance == 0)
+ {
+ manifold->penetration = bodyA->shape.radius;
+ manifold->normal = (Vector2){ 1, 0 };
+ manifold->contacts[0] = bodyA->position;
+ }
+ else
+ {
+ manifold->penetration = radius - distance;
+ manifold->normal = (Vector2){ normal.x/distance, normal.y/distance }; // Faster than using MathNormalize() due to sqrt is already performed
+ manifold->contacts[0] = (Vector2){ manifold->normal.x*bodyA->shape.radius + bodyA->position.x, manifold->normal.y*bodyA->shape.radius + bodyA->position.y };
+ }
+
+ // Update physics body grounded state if normal direction is down
+ if (manifold->normal.y < 0) bodyA->isGrounded = true;
+}
+
+// Solves collision between a circle to a polygon shape physics bodies
+static void SolveCircleToPolygon(PhysicsManifold manifold)
+{
+ PhysicsBody bodyA = manifold->bodyA;
+ PhysicsBody bodyB = manifold->bodyB;
+
+ manifold->contactsCount = 0;
+
+ // Transform circle center to polygon transform space
+ Vector2 center = bodyA->position;
+ center = Mat2MultiplyVector2(Mat2Transpose(bodyB->shape.vertexData.transform), Vector2Subtract(center, bodyB->position));
+
+ // Find edge with minimum penetration
+ // It is the same concept as using support points in SolvePolygonToPolygon
+ float separation = -PHYSAC_FLT_MAX;
+ int faceNormal = 0;
+ PolygonData vertexData = bodyB->shape.vertexData;
+
+ for (int i = 0; i < vertexData.vertexCount; i++)
+ {
+ float currentSeparation = MathDot(vertexData.normals[i], Vector2Subtract(center, vertexData.vertices[i]));
+
+ if (currentSeparation > bodyA->shape.radius) return;
+
+ if (currentSeparation > separation)
+ {
+ separation = currentSeparation;
+ faceNormal = i;
}
}
+
+ // Grab face's vertices
+ Vector2 v1 = vertexData.vertices[faceNormal];
+ int nextIndex = (((faceNormal + 1) < vertexData.vertexCount) ? (faceNormal + 1) : 0);
+ Vector2 v2 = vertexData.vertices[nextIndex];
+
+ // Check to see if center is within polygon
+ if (separation < PHYSAC_EPSILON)
+ {
+ manifold->contactsCount = 1;
+ Vector2 normal = Mat2MultiplyVector2(vertexData.transform, vertexData.normals[faceNormal]);
+ manifold->normal = (Vector2){ -normal.x, -normal.y };
+ manifold->contacts[0] = (Vector2){ manifold->normal.x*bodyA->shape.radius + bodyA->position.x, manifold->normal.y*bodyA->shape.radius + bodyA->position.y };
+ manifold->penetration = bodyA->shape.radius;
+ return;
+ }
+
+ // Determine which voronoi region of the edge center of circle lies within
+ float dot1 = MathDot(Vector2Subtract(center, v1), Vector2Subtract(v2, v1));
+ float dot2 = MathDot(Vector2Subtract(center, v2), Vector2Subtract(v1, v2));
+ manifold->penetration = bodyA->shape.radius - separation;
+
+ if (dot1 <= 0) // Closest to v1
+ {
+ if (DistSqr(center, v1) > bodyA->shape.radius*bodyA->shape.radius) return;
+
+ manifold->contactsCount = 1;
+ Vector2 normal = Vector2Subtract(v1, center);
+ normal = Mat2MultiplyVector2(vertexData.transform, normal);
+ MathNormalize(&normal);
+ manifold->normal = normal;
+ v1 = Mat2MultiplyVector2(vertexData.transform, v1);
+ v1 = Vector2Add(v1, bodyB->position);
+ manifold->contacts[0] = v1;
+ }
+ else if (dot2 <= 0) // Closest to v2
+ {
+ if (DistSqr(center, v2) > bodyA->shape.radius*bodyA->shape.radius) return;
+
+ manifold->contactsCount = 1;
+ Vector2 normal = Vector2Subtract(v2, center);
+ v2 = Mat2MultiplyVector2(vertexData.transform, v2);
+ v2 = Vector2Add(v2, bodyB->position);
+ manifold->contacts[0] = v2;
+ normal = Mat2MultiplyVector2(vertexData.transform, normal);
+ MathNormalize(&normal);
+ manifold->normal = normal;
+ }
+ else // Closest to face
+ {
+ Vector2 normal = vertexData.normals[faceNormal];
+
+ if (MathDot(Vector2Subtract(center, v1), normal) > bodyA->shape.radius) return;
+
+ normal = Mat2MultiplyVector2(vertexData.transform, normal);
+ manifold->normal = (Vector2){ -normal.x, -normal.y };
+ manifold->contacts[0] = (Vector2){ manifold->normal.x*bodyA->shape.radius + bodyA->position.x, manifold->normal.y*bodyA->shape.radius + bodyA->position.y };
+ manifold->contactsCount = 1;
+ }
+}
+
+// Solves collision between a polygon to a circle shape physics bodies
+static void SolvePolygonToCircle(PhysicsManifold manifold)
+{
+ PhysicsBody bodyA = manifold->bodyA;
+ PhysicsBody bodyB = manifold->bodyB;
+
+ manifold->bodyA = bodyB;
+ manifold->bodyB = bodyA;
+ SolveCircleToPolygon(manifold);
+
+ manifold->normal.x *= -1;
+ manifold->normal.y *= -1;
+}
+
+// Solves collision between two polygons shape physics bodies
+static void SolvePolygonToPolygon(PhysicsManifold manifold)
+{
+ PhysicsShape bodyA = manifold->bodyA->shape;
+ PhysicsShape bodyB = manifold->bodyB->shape;
+ manifold->contactsCount = 0;
+
+ // Check for separating axis with A shape's face planes
+ int faceA = 0;
+ float penetrationA = FindAxisLeastPenetration(&faceA, bodyA, bodyB);
+ if (penetrationA >= 0) return;
+
+ // Check for separating axis with B shape's face planes
+ int faceB = 0;
+ float penetrationB = FindAxisLeastPenetration(&faceB, bodyB, bodyA);
+ if (penetrationB >= 0) return;
+
+ int referenceIndex = 0;
+ bool flip = false; // Always point from A shape to B shape
+
+ PhysicsShape refPoly; // Reference
+ PhysicsShape incPoly; // Incident
+
+ // Determine which shape contains reference face
+ if (BiasGreaterThan(penetrationA, penetrationB))
+ {
+ refPoly = bodyA;
+ incPoly = bodyB;
+ referenceIndex = faceA;
+ }
+ else
+ {
+ refPoly = bodyB;
+ incPoly = bodyA;
+ referenceIndex = faceB;
+ flip = true;
+ }
+
+ // World space incident face
+ Vector2 incidentFace[2];
+ FindIncidentFace(&incidentFace[0], &incidentFace[1], refPoly, incPoly, referenceIndex);
+
+ // Setup reference face vertices
+ PolygonData refData = refPoly.vertexData;
+ Vector2 v1 = refData.vertices[referenceIndex];
+ referenceIndex = (((referenceIndex + 1) < refData.vertexCount) ? (referenceIndex + 1) : 0);
+ Vector2 v2 = refData.vertices[referenceIndex];
+
+ // Transform vertices to world space
+ v1 = Mat2MultiplyVector2(refData.transform, v1);
+ v1 = Vector2Add(v1, refPoly.body->position);
+ v2 = Mat2MultiplyVector2(refData.transform, v2);
+ v2 = Vector2Add(v2, refPoly.body->position);
+
+ // Calculate reference face side normal in world space
+ Vector2 sidePlaneNormal = Vector2Subtract(v2, v1);
+ MathNormalize(&sidePlaneNormal);
+
+ // Orthogonalize
+ Vector2 refFaceNormal = { sidePlaneNormal.y, -sidePlaneNormal.x };
+ float refC = MathDot(refFaceNormal, v1);
+ float negSide = MathDot(sidePlaneNormal, v1)*-1;
+ float posSide = MathDot(sidePlaneNormal, v2);
+
+ // Clip incident face to reference face side planes (due to floating point error, possible to not have required points
+ if (Clip((Vector2){ -sidePlaneNormal.x, -sidePlaneNormal.y }, negSide, &incidentFace[0], &incidentFace[1]) < 2) return;
+ if (Clip(sidePlaneNormal, posSide, &incidentFace[0], &incidentFace[1]) < 2) return;
+
+ // Flip normal if required
+ manifold->normal = (flip ? (Vector2){ -refFaceNormal.x, -refFaceNormal.y } : refFaceNormal);
+
+ // Keep points behind reference face
+ int currentPoint = 0; // Clipped points behind reference face
+ float separation = MathDot(refFaceNormal, incidentFace[0]) - refC;
+ if (separation <= 0)
+ {
+ manifold->contacts[currentPoint] = incidentFace[0];
+ manifold->penetration = -separation;
+ currentPoint++;
+ }
+ else manifold->penetration = 0;
+
+ separation = MathDot(refFaceNormal, incidentFace[1]) - refC;
+
+ if (separation <= 0)
+ {
+ manifold->contacts[currentPoint] = incidentFace[1];
+ manifold->penetration += -separation;
+ currentPoint++;
+
+ // Calculate total penetration average
+ manifold->penetration /= currentPoint;
+ }
+
+ manifold->contactsCount = currentPoint;
+}
+
+// Integrates physics forces into velocity
+static void IntegratePhysicsForces(PhysicsBody body)
+{
+ if (body->inverseMass == 0 || !body->enabled) return;
+
+ body->velocity.x += (body->force.x*body->inverseMass)*(deltaTime/2);
+ body->velocity.y += (body->force.y*body->inverseMass)*(deltaTime/2);
+
+ if (body->useGravity)
+ {
+ body->velocity.x += gravityForce.x*(deltaTime/2);
+ body->velocity.y += gravityForce.y*(deltaTime/2);
+ }
+
+ if (!body->freezeOrient) body->angularVelocity += body->torque*body->inverseInertia*(deltaTime/2);
+}
+
+// Initializes physics manifolds to solve collisions
+static void InitializePhysicsManifolds(PhysicsManifold manifold)
+{
+ PhysicsBody bodyA = manifold->bodyA;
+ PhysicsBody bodyB = manifold->bodyB;
+
+ // Calculate average restitution, static and dynamic friction
+ manifold->restitution = sqrtf(bodyA->restitution*bodyB->restitution);
+ manifold->staticFriction = sqrtf(bodyA->staticFriction*bodyB->staticFriction);
+ manifold->dynamicFriction = sqrtf(bodyA->dynamicFriction*bodyB->dynamicFriction);
+
+ for (int i = 0; i < 2; i++)
+ {
+ // Caculate radius from center of mass to contact
+ Vector2 radiusA = Vector2Subtract(manifold->contacts[i], bodyA->position);
+ Vector2 radiusB = Vector2Subtract(manifold->contacts[i], bodyB->position);
+
+ Vector2 crossA = MathCross(bodyA->angularVelocity, radiusA);
+ Vector2 crossB = MathCross(bodyB->angularVelocity, radiusB);
+
+ Vector2 radiusV = { 0 };
+ radiusV.x = bodyB->velocity.x + crossB.x - bodyA->velocity.x - crossA.x;
+ radiusV.y = bodyB->velocity.y + crossB.y - bodyA->velocity.y - crossA.y;
+
+ // Determine if we should perform a resting collision or not;
+ // The idea is if the only thing moving this object is gravity, then the collision should be performed without any restitution
+ if (MathLenSqr(radiusV) < (MathLenSqr((Vector2){ gravityForce.x*deltaTime, gravityForce.y*deltaTime }) + PHYSAC_EPSILON)) manifold->restitution = 0;
+ }
+}
+
+// Integrates physics collisions impulses to solve collisions
+static void IntegratePhysicsImpulses(PhysicsManifold manifold)
+{
+ PhysicsBody bodyA = manifold->bodyA;
+ PhysicsBody bodyB = manifold->bodyB;
+
+ // Early out and positional correct if both objects have infinite mass
+ if (fabs(bodyA->inverseMass + bodyB->inverseMass) <= PHYSAC_EPSILON)
+ {
+ bodyA->velocity = (Vector2){ 0 };
+ bodyB->velocity = (Vector2){ 0 };
+ return;
+ }
+
+ for (int i = 0; i < manifold->contactsCount; i++)
+ {
+ // Calculate radius from center of mass to contact
+ Vector2 radiusA = Vector2Subtract(manifold->contacts[i], bodyA->position);
+ Vector2 radiusB = Vector2Subtract(manifold->contacts[i], bodyB->position);
+
+ // Calculate relative velocity
+ Vector2 radiusV = { 0 };
+ radiusV.x = bodyB->velocity.x + MathCross(bodyB->angularVelocity, radiusB).x - bodyA->velocity.x - MathCross(bodyA->angularVelocity, radiusA).x;
+ radiusV.y = bodyB->velocity.y + MathCross(bodyB->angularVelocity, radiusB).y - bodyA->velocity.y - MathCross(bodyA->angularVelocity, radiusA).y;
+
+ // Relative velocity along the normal
+ float contactVelocity = MathDot(radiusV, manifold->normal);
+
+ // Do not resolve if velocities are separating
+ if (contactVelocity > 0) return;
+
+ float raCrossN = MathCrossVector2(radiusA, manifold->normal);
+ float rbCrossN = MathCrossVector2(radiusB, manifold->normal);
+
+ float inverseMassSum = bodyA->inverseMass + bodyB->inverseMass + (raCrossN*raCrossN)*bodyA->inverseInertia + (rbCrossN*rbCrossN)*bodyB->inverseInertia;
+
+ // Calculate impulse scalar value
+ float impulse = -(1.0f + manifold->restitution)*contactVelocity;
+ impulse /= inverseMassSum;
+ impulse /= (float)manifold->contactsCount;
+
+ // Apply impulse to each physics body
+ Vector2 impulseV = { manifold->normal.x*impulse, manifold->normal.y*impulse };
+
+ if (bodyA->enabled)
+ {
+ bodyA->velocity.x += bodyA->inverseMass*(-impulseV.x);
+ bodyA->velocity.y += bodyA->inverseMass*(-impulseV.y);
+ if (!bodyA->freezeOrient) bodyA->angularVelocity += bodyA->inverseInertia*MathCrossVector2(radiusA, (Vector2){ -impulseV.x, -impulseV.y });
+ }
+
+ if (bodyB->enabled)
+ {
+ bodyB->velocity.x += bodyB->inverseMass*(impulseV.x);
+ bodyB->velocity.y += bodyB->inverseMass*(impulseV.y);
+ if (!bodyB->freezeOrient) bodyB->angularVelocity += bodyB->inverseInertia*MathCrossVector2(radiusB, impulseV);
+ }
+
+ // Apply friction impulse to each physics body
+ radiusV.x = bodyB->velocity.x + MathCross(bodyB->angularVelocity, radiusB).x - bodyA->velocity.x - MathCross(bodyA->angularVelocity, radiusA).x;
+ radiusV.y = bodyB->velocity.y + MathCross(bodyB->angularVelocity, radiusB).y - bodyA->velocity.y - MathCross(bodyA->angularVelocity, radiusA).y;
+
+ Vector2 tangent = { radiusV.x - (manifold->normal.x*MathDot(radiusV, manifold->normal)), radiusV.y - (manifold->normal.y*MathDot(radiusV, manifold->normal)) };
+ MathNormalize(&tangent);
+
+ // Calculate impulse tangent magnitude
+ float impulseTangent = -MathDot(radiusV, tangent);
+ impulseTangent /= inverseMassSum;
+ impulseTangent /= (float)manifold->contactsCount;
+
+ float absImpulseTangent = fabs(impulseTangent);
+
+ // Don't apply tiny friction impulses
+ if (absImpulseTangent <= PHYSAC_EPSILON) return;
+
+ // Apply coulumb's law
+ Vector2 tangentImpulse = { 0 };
+ if (absImpulseTangent < impulse*manifold->staticFriction) tangentImpulse = (Vector2){ tangent.x*impulseTangent, tangent.y*impulseTangent };
+ else tangentImpulse = (Vector2){ tangent.x*-impulse*manifold->dynamicFriction, tangent.y*-impulse*manifold->dynamicFriction };
+
+ // Apply friction impulse
+ if (bodyA->enabled)
+ {
+ bodyA->velocity.x += bodyA->inverseMass*(-tangentImpulse.x);
+ bodyA->velocity.y += bodyA->inverseMass*(-tangentImpulse.y);
+
+ if (!bodyA->freezeOrient) bodyA->angularVelocity += bodyA->inverseInertia*MathCrossVector2(radiusA, (Vector2){ -tangentImpulse.x, -tangentImpulse.y });
+ }
+
+ if (bodyB->enabled)
+ {
+ bodyB->velocity.x += bodyB->inverseMass*(tangentImpulse.x);
+ bodyB->velocity.y += bodyB->inverseMass*(tangentImpulse.y);
+
+ if (!bodyB->freezeOrient) bodyB->angularVelocity += bodyB->inverseInertia*MathCrossVector2(radiusB, tangentImpulse);
+ }
+ }
+}
+
+// Integrates physics velocity into position and forces
+static void IntegratePhysicsVelocity(PhysicsBody body)
+{
+ if (!body->enabled) return;
+
+ body->position.x += body->velocity.x*deltaTime;
+ body->position.y += body->velocity.y*deltaTime;
+
+ if (!body->freezeOrient) body->orient += body->angularVelocity*deltaTime;
+ Mat2Set(&body->shape.vertexData.transform, body->orient);
+
+ IntegratePhysicsForces(body);
+}
+
+// Corrects physics bodies positions based on manifolds collision information
+static void CorrectPhysicsPositions(PhysicsManifold manifold)
+{
+ PhysicsBody bodyA = manifold->bodyA;
+ PhysicsBody bodyB = manifold->bodyB;
+
+ Vector2 correction = { 0 };
+ correction.x = (max(manifold->penetration - PHYSAC_PENETRATION_ALLOWANCE, 0)/(bodyA->inverseMass + bodyB->inverseMass))*manifold->normal.x*PHYSAC_PENETRATION_CORRECTION;
+ correction.y = (max(manifold->penetration - PHYSAC_PENETRATION_ALLOWANCE, 0)/(bodyA->inverseMass + bodyB->inverseMass))*manifold->normal.y*PHYSAC_PENETRATION_CORRECTION;
+
+ if (bodyA->enabled)
+ {
+ bodyA->position.x -= correction.x*bodyA->inverseMass;
+ bodyA->position.y -= correction.y*bodyA->inverseMass;
+ }
+
+ if (bodyB->enabled)
+ {
+ bodyB->position.x += correction.x*bodyB->inverseMass;
+ bodyB->position.y += correction.y*bodyB->inverseMass;
+ }
+}
+
+// Returns the extreme point along a direction within a polygon
+static Vector2 GetSupport(PhysicsShape shape, Vector2 dir)
+{
+ float bestProjection = -PHYSAC_FLT_MAX;
+ Vector2 bestVertex = { 0 };
+ PolygonData data = shape.vertexData;
+
+ for (int i = 0; i < data.vertexCount; i++)
+ {
+ Vector2 vertex = data.vertices[i];
+ float projection = MathDot(vertex, dir);
+
+ if (projection > bestProjection)
+ {
+ bestVertex = vertex;
+ bestProjection = projection;
+ }
+ }
+
+ return bestVertex;
+}
+
+// Finds polygon shapes axis least penetration
+static float FindAxisLeastPenetration(int *faceIndex, PhysicsShape shapeA, PhysicsShape shapeB)
+{
+ float bestDistance = -PHYSAC_FLT_MAX;
+ int bestIndex = 0;
+
+ PolygonData dataA = shapeA.vertexData;
+ PolygonData dataB = shapeB.vertexData;
+
+ for (int i = 0; i < dataA.vertexCount; i++)
+ {
+ // Retrieve a face normal from A shape
+ Vector2 normal = dataA.normals[i];
+ Vector2 transNormal = Mat2MultiplyVector2(dataA.transform, normal);
+
+ // Transform face normal into B shape's model space
+ Mat2 buT = Mat2Transpose(dataB.transform);
+ normal = Mat2MultiplyVector2(buT, transNormal);
+
+ // Retrieve support point from B shape along -n
+ Vector2 support = GetSupport(shapeB, (Vector2){ -normal.x, -normal.y });
+
+ // Retrieve vertex on face from A shape, transform into B shape's model space
+ Vector2 vertex = dataA.vertices[i];
+ vertex = Mat2MultiplyVector2(dataA.transform, vertex);
+ vertex = Vector2Add(vertex, shapeA.body->position);
+ vertex = Vector2Subtract(vertex, shapeB.body->position);
+ vertex = Mat2MultiplyVector2(buT, vertex);
+
+ // Compute penetration distance in B shape's model space
+ float distance = MathDot(normal, Vector2Subtract(support, vertex));
+
+ // Store greatest distance
+ if (distance > bestDistance)
+ {
+ bestDistance = distance;
+ bestIndex = i;
+ }
+ }
+
+ *faceIndex = bestIndex;
+ return bestDistance;
+}
+
+// Finds two polygon shapes incident face
+static void FindIncidentFace(Vector2 *v0, Vector2 *v1, PhysicsShape ref, PhysicsShape inc, int index)
+{
+ PolygonData refData = ref.vertexData;
+ PolygonData incData = inc.vertexData;
+
+ Vector2 referenceNormal = refData.normals[index];
+
+ // Calculate normal in incident's frame of reference
+ referenceNormal = Mat2MultiplyVector2(refData.transform, referenceNormal); // To world space
+ referenceNormal = Mat2MultiplyVector2(Mat2Transpose(incData.transform), referenceNormal); // To incident's model space
+
+ // Find most anti-normal face on polygon
+ int incidentFace = 0;
+ float minDot = PHYSAC_FLT_MAX;
+
+ for (int i = 0; i < incData.vertexCount; i++)
+ {
+ float dot = MathDot(referenceNormal, incData.normals[i]);
+
+ if (dot < minDot)
+ {
+ minDot = dot;
+ incidentFace = i;
+ }
+ }
+
+ // Assign face vertices for incident face
+ *v0 = Mat2MultiplyVector2(incData.transform, incData.vertices[incidentFace]);
+ *v0 = Vector2Add(*v0, inc.body->position);
+ incidentFace = (((incidentFace + 1) < incData.vertexCount) ? (incidentFace + 1) : 0);
+ *v1 = Mat2MultiplyVector2(incData.transform, incData.vertices[incidentFace]);
+ *v1 = Vector2Add(*v1, inc.body->position);
+}
+
+// Calculates clipping based on a normal and two faces
+static int Clip(Vector2 normal, float clip, Vector2 *faceA, Vector2 *faceB)
+{
+ int sp = 0;
+ Vector2 out[2] = { *faceA, *faceB };
+
+ // Retrieve distances from each endpoint to the line
+ float distanceA = MathDot(normal, *faceA) - clip;
+ float distanceB = MathDot(normal, *faceB) - clip;
+
+ // If negative (behind plane)
+ if (distanceA <= 0) out[sp++] = *faceA;
+ if (distanceB <= 0) out[sp++] = *faceB;
+
+ // If the points are on different sides of the plane
+ if ((distanceA*distanceB) < 0)
+ {
+ // Push intersection point
+ float alpha = distanceA/(distanceA - distanceB);
+ out[sp] = *faceA;
+ Vector2 delta = Vector2Subtract(*faceB, *faceA);
+ delta.x *= alpha;
+ delta.y *= alpha;
+ out[sp] = Vector2Add(out[sp], delta);
+ sp++;
+ }
+
+ // Assign the new converted values
+ *faceA = out[0];
+ *faceB = out[1];
+
+ return sp;
+}
+
+// Check if values are between bias range
+static bool BiasGreaterThan(float valueA, float valueB)
+{
+ return (valueA >= (valueB*0.95f + valueA*0.01f));
+}
+
+// Returns the barycenter of a triangle given by 3 points
+static Vector2 TriangleBarycenter(Vector2 v1, Vector2 v2, Vector2 v3)
+{
+ Vector2 result = { 0 };
+
+ result.x = (v1.x + v2.x + v3.x)/3;
+ result.y = (v1.y + v2.y + v3.y)/3;
+
+ return result;
+}
+
+// Initializes hi-resolution timer
+static void InitTimer(void)
+{
+ srand(time(NULL)); // Initialize random seed
+
+ #if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI)
+ struct timespec now;
+ if (clock_gettime(CLOCK_MONOTONIC, &now) == 0) baseTime = (uint64_t)now.tv_sec*1000000000LLU + (uint64_t)now.tv_nsec;
+ #endif
+
+ startTime = GetCurrentTime();
+}
+
+// Get current time in milliseconds
+static double GetCurrentTime(void)
+{
+ double time = 0;
+
+ #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
+ unsigned long long int clockFrequency, currentTime;
+
+ QueryPerformanceFrequency(&clockFrequency);
+ QueryPerformanceCounter(&currentTime);
+
+ time = (double)((double)currentTime/clockFrequency)*1000;
+ #endif
+
+ #if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI)
+ struct timespec ts;
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ uint64_t temp = (uint64_t)ts.tv_sec*1000000000LLU + (uint64_t)ts.tv_nsec;
+
+ time = (double)((double)(temp - baseTime)*1e-6);
+ #endif
+
+ return time;
+}
+
+// Returns a random number between min and max (both included)
+static int GetRandomNumber(int min, int max)
+{
+ if (min > max)
+ {
+ int tmp = max;
+ max = min;
+ min = tmp;
+ }
+
+ return (rand()%(abs(max - min) + 1) + min);
+}
+
+// Clamp a value in a range
+static inline void MathClamp(double *value, double min, double max)
+{
+ if (*value < min) *value = min;
+ else if (*value > max) *value = max;
+}
+
+// Returns the cross product of a vector and a value
+static inline Vector2 MathCross(float value, Vector2 vector)
+{
+ return (Vector2){ -value*vector.y, value*vector.x };
+}
+
+// Returns the cross product of two vectors
+static inline float MathCrossVector2(Vector2 v1, Vector2 v2)
+{
+ return (v1.x*v2.y - v1.y*v2.x);
+}
+
+// Returns the len square root of a vector
+static inline float MathLenSqr(Vector2 vector)
+{
+ return (vector.x*vector.x + vector.y*vector.y);
+}
+
+// Returns the dot product of two vectors
+static inline float MathDot(Vector2 v1, Vector2 v2)
+{
+ return (v1.x*v2.x + v1.y*v2.y);
+}
+
+// Returns the square root of distance between two vectors
+static inline float DistSqr(Vector2 v1, Vector2 v2)
+{
+ Vector2 dir = Vector2Subtract(v1, v2);
+ return MathDot(dir, dir);
+}
+
+// Returns the normalized values of a vector
+static void MathNormalize(Vector2 *vector)
+{
+ float length, ilength;
+
+ Vector2 aux = *vector;
+ length = sqrtf(aux.x*aux.x + aux.y*aux.y);
+
+ if (length == 0) length = 1.0f;
+
+ ilength = 1.0f/length;
+
+ vector->x *= ilength;
+ vector->y *= ilength;
+}
+
+// Returns the sum of two given vectors
+static inline Vector2 Vector2Add(Vector2 v1, Vector2 v2)
+{
+ return (Vector2){ v1.x + v2.x, v1.y + v2.y };
+}
+
+// Returns the subtract of two given vectors
+static inline Vector2 Vector2Subtract(Vector2 v1, Vector2 v2)
+{
+ return (Vector2){ v1.x - v2.x, v1.y - v2.y };
+}
+
+// Creates a matrix 2x2 from a given radians value
+static inline Mat2 Mat2Radians(float radians)
+{
+ float c = cosf(radians);
+ float s = sinf(radians);
+
+ return (Mat2){ c, -s, s, c };
+}
+
+// Set values from radians to a created matrix 2x2
+static void Mat2Set(Mat2 *matrix, float radians)
+{
+ float cos = cosf(radians);
+ float sin = sinf(radians);
+
+ matrix->m00 = cos;
+ matrix->m01 = -sin;
+ matrix->m10 = sin;
+ matrix->m11 = cos;
+}
+
+// Returns the transpose of a given matrix 2x2
+static inline Mat2 Mat2Transpose(Mat2 matrix)
+{
+ return (Mat2){ matrix.m00, matrix.m10, matrix.m01, matrix.m11 };
+}
+
+// Multiplies a vector by a matrix 2x2
+static inline Vector2 Mat2MultiplyVector2(Mat2 matrix, Vector2 vector)
+{
+ return (Vector2){ matrix.m00*vector.x + matrix.m01*vector.y, matrix.m10*vector.x + matrix.m11*vector.y };
}
-#endif // PHYSAC_IMPLEMENTATION \ No newline at end of file
+#endif // PHYSAC_IMPLEMENTATION
diff --git a/src/raylib.h b/src/raylib.h
index f6243304..d28b07a3 100644
--- a/src/raylib.h
+++ b/src/raylib.h
@@ -6,37 +6,39 @@
*
* Features:
* Library written in plain C code (C99)
-* Uses C# PascalCase/camelCase notation
+* Uses PascalCase/camelCase notation
* Hardware accelerated with OpenGL (1.1, 2.1, 3.3 or ES 2.0)
* Unique OpenGL abstraction layer (usable as standalone module): [rlgl]
* Powerful fonts module with SpriteFonts support (XNA bitmap fonts, AngelCode fonts, TTF)
* Multiple textures support, including compressed formats and mipmaps generation
* Basic 3d support for Shapes, Models, Billboards, Heightmaps and Cubicmaps
* Materials (diffuse, normal, specular) and Lighting (point, directional, spot) support
-* Powerful math module for Vector, Matrix and Quaternion operations [raymath]
-* Audio loading and playing with streaming support and mixing channels (WAV, OGG, XM, MOD)
+* Powerful math module for Vector, Matrix and Quaternion operations: [raymath]
+* Audio loading and playing with streaming support and mixing channels [audio]
* VR stereo rendering support with configurable HMD device parameters
* Multiple platforms support: Windows, Linux, Mac, Android, Raspberry Pi, HTML5 and Oculus Rift CV1
* Custom color palette for fancy visuals on raywhite background
* Minimal external dependencies (GLFW3, OpenGL, OpenAL)
+* Complete binding for LUA [rlua]
*
-* Used external libs:
-* GLFW3 (www.glfw.org) for window/context management and input
-* GLAD for OpenGL extensions loading (3.3 Core profile, only PLATFORM_DESKTOP)
-* 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
-* stb_truetype (Sean Barret) for ttf fonts loading
-* jar_xm (Joshua Reisenauer) for XM audio module loading
-* jar_mod (Joshua Reisenauer) for MOD audio module loading
-* OpenAL Soft for audio device/context management
-* tinfl for data decompression (DEFLATE algorithm)
+* External libs:
+* GLFW3 (www.glfw.org) for window/context management and input [core]
+* GLAD for OpenGL extensions loading (3.3 Core profile, only PLATFORM_DESKTOP) [rlgl]
+* stb_image (Sean Barret) for images loading (JPEG, PNG, BMP, TGA) [textures]
+* stb_image_write (Sean Barret) for image writting (PNG) [utils]
+* stb_truetype (Sean Barret) for ttf fonts loading [text]
+* stb_vorbis (Sean Barret) for ogg audio loading [audio]
+* jar_xm (Joshua Reisenauer) for XM audio module loading [audio]
+* jar_mod (Joshua Reisenauer) for MOD audio module loading [audio]
+* dr_flac (David Reid) for FLAC audio file loading [audio]
+* OpenAL Soft for audio device/context management [audio]
+* tinfl for data decompression (DEFLATE algorithm) [utils]
*
* Some design decisions:
* 32bit Colors - All defined color are always RGBA (struct Color is 4 byte)
-* One custom default font is loaded automatically when InitWindow()
+* One custom default font could be loaded automatically when InitWindow() [core]
* If using OpenGL 3.3 or ES2, several vertex buffers (VAO/VBO) are created to manage lines-triangles-quads
-* If using OpenGL 3.3 or ES2, two default shaders are loaded automatically (internally defined)
+* If using OpenGL 3.3 or ES2, two default shaders could be loaded automatically (internally defined)
*
* -- LICENSE --
*
@@ -305,9 +307,6 @@
#endif
#endif
-// byte type
-typedef unsigned char byte;
-
// Vector2 type
typedef struct Vector2 {
float x;
@@ -725,7 +724,7 @@ RLAPI float GetGesturePinchAngle(void); // Get gesture pin
//------------------------------------------------------------------------------------
// Camera System Functions (Module: camera)
//------------------------------------------------------------------------------------
-RLAPI void SetCameraMode(Camera, int mode); // Set camera mode (multiple camera modes available)
+RLAPI void SetCameraMode(Camera camera, int mode); // Set camera mode (multiple camera modes available)
RLAPI void UpdateCamera(Camera *camera); // Update camera position for selected mode
RLAPI void SetCameraPanControl(int panKey); // Set camera pan key to combine with mouse movement (free camera)
@@ -803,7 +802,7 @@ RLAPI void ImageColorInvert(Image *image);
RLAPI void ImageColorGrayscale(Image *image); // Modify image color: grayscale
RLAPI void ImageColorContrast(Image *image, float contrast); // Modify image color: contrast (-100 to 100)
RLAPI void ImageColorBrightness(Image *image, int brightness); // Modify image color: brightness (-255 to 255)
-RLAPI void GenTextureMipmaps(Texture2D texture); // Generate GPU mipmaps for a texture
+RLAPI void GenTextureMipmaps(Texture2D *texture); // Generate GPU mipmaps for a texture
RLAPI void SetTextureFilter(Texture2D texture, int filterMode); // Set texture scaling filter mode
RLAPI void SetTextureWrap(Texture2D texture, int wrapMode); // Set texture wrapping mode
@@ -836,7 +835,7 @@ RLAPI const char *SubText(const char *text, int position, int length);
// Basic 3d Shapes Drawing Functions (Module: models)
//------------------------------------------------------------------------------------
RLAPI void DrawLine3D(Vector3 startPos, Vector3 endPos, Color color); // Draw a line in 3D world space
-RLAPI void DrawCircle3D(Vector3 center, float radius, float rotationAngle, Vector3 rotation, Color color); // Draw a circle in 3D world space
+RLAPI void DrawCircle3D(Vector3 center, float radius, Vector3 rotationAxis, float rotationAngle, Color color); // Draw a circle in 3D world space
RLAPI void DrawCube(Vector3 position, float width, float height, float length, Color color); // Draw cube
RLAPI void DrawCubeV(Vector3 position, Vector3 size, Color color); // Draw cube (Vector version)
RLAPI void DrawCubeWires(Vector3 position, float width, float height, float length, Color color); // Draw cube wires
@@ -851,7 +850,7 @@ RLAPI void DrawRay(Ray ray, Color color);
RLAPI void DrawGrid(int slices, float spacing); // Draw a grid (centered at (0, 0, 0))
RLAPI void DrawGizmo(Vector3 position); // Draw simple gizmo
RLAPI void DrawLight(Light light); // Draw light in 3D world
-//DrawTorus(), DrawTeapot() are useless...
+//DrawTorus(), DrawTeapot() could be useful?
//------------------------------------------------------------------------------------
// Model 3d Loading and Drawing Functions (Module: models)
@@ -918,7 +917,7 @@ RLAPI void DestroyLight(Light light); // Des
//------------------------------------------------------------------------------------
RLAPI void InitVrDevice(int vdDevice); // Init VR device
RLAPI void CloseVrDevice(void); // Close VR device
-RLAPI bool IsVrDeviceReady(void); // Detect if VR device (or simulator) is ready
+RLAPI bool IsVrDeviceReady(void); // Detect if VR device is ready
RLAPI bool IsVrSimulator(void); // Detect if VR simulator is running
RLAPI void UpdateVrTracking(Camera *camera); // Update VR tracking (position and orientation) and camera
RLAPI void ToggleVrMode(void); // Enable/Disable VR experience (device or simulator)
@@ -927,7 +926,7 @@ RLAPI void ToggleVrMode(void); // Enable/Disable VR experienc
// Audio Loading and Playing Functions (Module: audio)
//------------------------------------------------------------------------------------
RLAPI void InitAudioDevice(void); // Initialize audio device and context
-RLAPI void CloseAudioDevice(void); // Close the audio device and context (and music stream)
+RLAPI void CloseAudioDevice(void); // Close the audio device and context
RLAPI bool IsAudioDeviceReady(void); // Check if audio device has been initialized successfully
RLAPI Wave LoadWave(const char *fileName); // Load wave data from file into RAM
@@ -951,9 +950,9 @@ RLAPI void WaveCrop(Wave *wave, int initSample, int finalSample); // Crop a
RLAPI float *GetWaveData(Wave wave); // Get samples data from wave as a floats array
RLAPI Music LoadMusicStream(const char *fileName); // Load music stream from file
RLAPI void UnloadMusicStream(Music music); // Unload music stream
-RLAPI void PlayMusicStream(Music music); // Start music playing (open stream)
+RLAPI void PlayMusicStream(Music music); // Start music playing
RLAPI void UpdateMusicStream(Music music); // Updates buffers for music streaming
-RLAPI void StopMusicStream(Music music); // Stop music playing (close stream)
+RLAPI void StopMusicStream(Music music); // Stop music playing
RLAPI void PauseMusicStream(Music music); // Pause music playing
RLAPI void ResumeMusicStream(Music music); // Resume playing paused music
RLAPI bool IsMusicPlaying(Music music); // Check if music is playing
@@ -964,7 +963,7 @@ RLAPI float GetMusicTimePlayed(Music music); // Get cur
RLAPI AudioStream InitAudioStream(unsigned int sampleRate,
unsigned int sampleSize,
- unsigned int channels); // Init audio stream (to stream audio pcm data)
+ unsigned int channels); // Init audio stream (to stream raw audio pcm data)
RLAPI void UpdateAudioStream(AudioStream stream, void *data, int numSamples); // Update audio stream buffers with data
RLAPI void CloseAudioStream(AudioStream stream); // Close audio stream and free memory
RLAPI bool IsAudioBufferProcessed(AudioStream stream); // Check if any audio stream buffers requires refill
diff --git a/src/resources b/src/resources
index e0fe66ba..5c76749d 100644
--- a/src/resources
+++ b/src/resources
Binary files differ
diff --git a/src/rlgl.c b/src/rlgl.c
index d3bba07b..629d7967 100644
--- a/src/rlgl.c
+++ b/src/rlgl.c
@@ -2,11 +2,30 @@
*
* rlgl - raylib OpenGL abstraction layer
*
-* raylib now uses OpenGL 1.1 style functions (rlVertex) that are mapped to selected OpenGL version:
-* OpenGL 1.1 - Direct map rl* -> gl*
-* OpenGL 2.1 - Vertex data is stored in VBOs, call rlglDraw() to render
-* OpenGL 3.3 - Vertex data is stored in VAOs, call rlglDraw() to render
-* OpenGL ES 2 - Vertex data is stored in VBOs or VAOs (when available), call rlglDraw() to render
+* rlgl allows usage of OpenGL 1.1 style functions (rlVertex) that are internally mapped to
+* selected OpenGL version (1.1, 2.1, 3.3 Core, ES 2.0).
+*
+* When chosing an OpenGL version greater than OpenGL 1.1, rlgl stores vertex data on internal
+* VBO buffers (and VAOs if available). It requires calling 3 functions:
+* rlglInit() - Initialize internal buffers and auxiliar resources
+* rlglDraw() - Process internal buffers and send required draw calls
+* rlglClose() - De-initialize internal buffers data and other auxiliar resources
+*
+* External libs:
+* raymath - 3D math functionality (Vector3, Matrix, Quaternion)
+* GLAD - OpenGL extensions loading (OpenGL 3.3 Core only)
+*
+* Module Configuration Flags:
+* GRAPHICS_API_OPENGL_11 - Use OpenGL 1.1 backend
+* GRAPHICS_API_OPENGL_21 - Use OpenGL 2.1 backend
+* GRAPHICS_API_OPENGL_33 - Use OpenGL 3.3 Core profile backend
+* GRAPHICS_API_OPENGL_ES2 - Use OpenGL ES 2.0 backend
+*
+* RLGL_STANDALONE - Use rlgl as standalone library (no raylib dependency)
+* RLGL_NO_STANDARD_SHADER - Avoid standard shader (shader_standard.h) inclusion
+* RLGL_NO_DISTORTION_SHADER - Avoid stereo rendering distortion sahder (shader_distortion.h) inclusion
+* RLGL_OCULUS_SUPPORT - Enable Oculus Rift CV1 functionality
+*
*
* Copyright (c) 2014-2016 Ramon Santamaria (@raysan5)
*
@@ -1700,15 +1719,15 @@ void rlglUpdateTexture(unsigned int id, int width, int height, int format, void
}
// Generate mipmap data for selected texture
-void rlglGenerateMipmaps(Texture2D texture)
+void rlglGenerateMipmaps(Texture2D *texture)
{
- glBindTexture(GL_TEXTURE_2D, texture.id);
+ glBindTexture(GL_TEXTURE_2D, texture->id);
// Check if texture is power-of-two (POT)
bool texIsPOT = false;
- if (((texture.width > 0) && ((texture.width & (texture.width - 1)) == 0)) &&
- ((texture.height > 0) && ((texture.height & (texture.height - 1)) == 0))) texIsPOT = true;
+ if (((texture->width > 0) && ((texture->width & (texture->width - 1)) == 0)) &&
+ ((texture->height > 0) && ((texture->height & (texture->height - 1)) == 0))) texIsPOT = true;
if ((texIsPOT) || (npotSupported))
{
@@ -1718,13 +1737,13 @@ void rlglGenerateMipmaps(Texture2D texture)
// NOTE: data size is reallocated to fit mipmaps data
// NOTE: CPU mipmap generation only supports RGBA 32bit data
- int mipmapCount = GenerateMipmaps(data, texture.width, texture.height);
+ int mipmapCount = GenerateMipmaps(data, texture->width, texture->height);
- int size = texture.width*texture.height*4; // RGBA 32bit only
+ int size = texture->width*texture->height*4; // RGBA 32bit only
int offset = size;
- int mipWidth = texture.width/2;
- int mipHeight = texture.height/2;
+ int mipWidth = texture->width/2;
+ int mipHeight = texture->height/2;
// Load the mipmaps
for (int level = 1; level < mipmapCount; level++)
@@ -1738,23 +1757,29 @@ void rlglGenerateMipmaps(Texture2D texture)
mipHeight /= 2;
}
- TraceLog(WARNING, "[TEX ID %i] Mipmaps generated manually on CPU side", texture.id);
+ TraceLog(WARNING, "[TEX ID %i] Mipmaps generated manually on CPU side", texture->id);
// NOTE: Once mipmaps have been generated and data has been uploaded to GPU VRAM, we can discard RAM data
free(data);
+ texture->mipmaps = mipmapCount + 1;
#endif
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
//glHint(GL_GENERATE_MIPMAP_HINT, GL_DONT_CARE); // Hint for mipmaps generation algorythm: GL_FASTEST, GL_NICEST, GL_DONT_CARE
glGenerateMipmap(GL_TEXTURE_2D); // Generate mipmaps automatically
- TraceLog(INFO, "[TEX ID %i] Mipmaps generated automatically", texture.id);
+ TraceLog(INFO, "[TEX ID %i] Mipmaps generated automatically", texture->id);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); // Activate Trilinear filtering for mipmaps (must be available)
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); // Activate Trilinear filtering for mipmaps
+
+ #define MIN(a,b) (((a)<(b))?(a):(b))
+ #define MAX(a,b) (((a)>(b))?(a):(b))
+
+ texture->mipmaps = 1 + floor(log2(MAX(texture->width, texture->height)));
#endif
}
- else TraceLog(WARNING, "[TEX ID %i] Mipmaps can not be generated", texture.id);
+ else TraceLog(WARNING, "[TEX ID %i] Mipmaps can not be generated", texture->id);
glBindTexture(GL_TEXTURE_2D, 0);
}
@@ -2767,13 +2792,13 @@ void CloseVrDevice(void)
// Detect if VR device is available
bool IsVrDeviceReady(void)
{
- return (vrDeviceReady || vrSimulator) && vrEnabled;
+ return (vrDeviceReady && vrEnabled);
}
// Detect if VR simulator is running
bool IsVrSimulator(void)
{
- return vrSimulator;
+ return (vrSimulator && vrEnabled);
}
// Enable/Disable VR experience (device or simulator)
diff --git a/src/rlgl.h b/src/rlgl.h
index 9be73f36..bc12db0f 100644
--- a/src/rlgl.h
+++ b/src/rlgl.h
@@ -2,11 +2,27 @@
*
* rlgl - raylib OpenGL abstraction layer
*
-* raylib now uses OpenGL 1.1 style functions (rlVertex) that are mapped to selected OpenGL version:
-* OpenGL 1.1 - Direct map rl* -> gl*
-* OpenGL 2.1 - Vertex data is stored in VBOs, call rlglDraw() to render
-* OpenGL 3.3 - Vertex data is stored in VAOs, call rlglDraw() to render
-* OpenGL ES 2 - Vertex data is stored in VBOs or VAOs (when available), call rlglDraw() to render
+* rlgl allows usage of OpenGL 1.1 style functions (rlVertex) that are internally mapped to
+* selected OpenGL version (1.1, 2.1, 3.3 Core, ES 2.0).
+*
+* When chosing an OpenGL version greater than OpenGL 1.1, rlgl stores vertex data on internal
+* VBO buffers (and VAOs if available). It requires calling 3 functions:
+* rlglInit() - Initialize internal buffers and auxiliar resources
+* rlglDraw() - Process internal buffers and send required draw calls
+* rlglClose() - De-initialize internal buffers data and other auxiliar resources
+*
+* External libs:
+* raymath - 3D math functionality (Vector3, Matrix, Quaternion)
+* GLAD - OpenGL extensions loading (OpenGL 3.3 Core only)
+*
+* Module Configuration Flags:
+* GRAPHICS_API_OPENGL_11 - Use OpenGL 1.1 backend
+* GRAPHICS_API_OPENGL_21 - Use OpenGL 2.1 backend
+* GRAPHICS_API_OPENGL_33 - Use OpenGL 3.3 Core profile backend
+* GRAPHICS_API_OPENGL_ES2 - Use OpenGL ES 2.0 backend
+*
+* RLGL_STANDALONE - Use rlgl as standalone library (no raylib dependency)
+*
*
* Copyright (c) 2014-2016 Ramon Santamaria (@raysan5)
*
@@ -117,15 +133,14 @@ typedef enum { RL_PROJECTION, RL_MODELVIEW, RL_TEXTURE } MatrixMode;
typedef enum { RL_LINES, RL_TRIANGLES, RL_QUADS } DrawMode;
+typedef unsigned char byte;
+
#if defined(RLGL_STANDALONE)
#ifndef __cplusplus
// Boolean type
typedef enum { false, true } bool;
#endif
- // byte type
- typedef unsigned char byte;
-
// Color type, RGBA (32bit)
typedef struct Color {
unsigned char r;
@@ -356,7 +371,7 @@ void rlglLoadExtensions(void *loader); // Load OpenGL extensions
unsigned int rlglLoadTexture(void *data, int width, int height, int textureFormat, int mipmapCount); // Load texture in GPU
RenderTexture2D rlglLoadRenderTexture(int width, int height); // Load a texture to be used for rendering (fbo with color and depth attachments)
void rlglUpdateTexture(unsigned int id, int width, int height, int format, void *data); // Update GPU texture with new data
-void rlglGenerateMipmaps(Texture2D texture); // Generate mipmap data for selected texture
+void rlglGenerateMipmaps(Texture2D *texture); // Generate mipmap data for selected texture
void rlglLoadMesh(Mesh *mesh, bool dynamic); // Upload vertex data into GPU and provided VAO/VBO ids
void rlglUpdateMesh(Mesh mesh, int buffer, int numVertex); // Update vertex data on GPU (upload new data to one buffer)
@@ -408,7 +423,8 @@ float *MatrixToFloat(Matrix mat);
void InitVrDevice(int vrDevice); // Init VR device
void CloseVrDevice(void); // Close VR device
-bool IsVrDeviceReady(void); // Detect if VR device (or simulator) is ready
+bool IsVrDeviceReady(void); // Detect if VR device is ready
+bool IsVrSimulator(void); // Detect if VR simulator is running
void UpdateVrTracking(Camera *camera); // Update VR tracking (position and orientation) and camera
void ToggleVrMode(void); // Enable/Disable VR experience (device or simulator)
diff --git a/src/rlua.h b/src/rlua.h
index 97d22922..961ed1c1 100644
--- a/src/rlua.h
+++ b/src/rlua.h
@@ -1095,14 +1095,14 @@ int lua_IsFileDropped(lua_State* L)
int lua_GetDroppedFiles(lua_State* L)
{
int count = 0;
- char ** result = GetDroppedFiles(&count);
+ char ** result = GetDroppedFiles(&count);
lua_createtable(L, count, 0);
for (int i = 0; i < count; i++)
{
lua_pushstring(L, result[i]);
lua_rawseti(L, -2, i + 1);
}
- return 1;
+ return 1;
}
int lua_ClearDroppedFiles(lua_State* L)
@@ -1130,7 +1130,6 @@ int lua_StorageLoadValue(lua_State* L)
//------------------------------------------------------------------------------------
// raylib [core] module functions - Input Handling
//------------------------------------------------------------------------------------
-#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) || defined(PLATFORM_WEB)
int lua_IsKeyPressed(lua_State* L)
{
int arg1 = LuaGetArgument_int(L, 1);
@@ -1185,12 +1184,22 @@ int lua_IsGamepadAvailable(lua_State* L)
return 1;
}
-int lua_GetGamepadAxisMovement(lua_State* L)
+int lua_IsGamepadName(lua_State* L)
{
int arg1 = LuaGetArgument_int(L, 1);
- int arg2 = LuaGetArgument_int(L, 2);
- float result = GetGamepadAxisMovement(arg1, arg2);
- lua_pushnumber(L, result);
+ const char * arg2 = LuaGetArgument_string(L, 2);
+ bool result = IsGamepadName(arg1, arg2);
+ lua_pushboolean(L, result);
+ return 1;
+}
+
+int lua_GetGamepadName(lua_State* L)
+{
+ // TODO: Return gamepad name id
+
+ int arg1 = LuaGetArgument_int(L, 1);
+ char * result = GetGamepadName(arg1);
+ //lua_pushboolean(L, result);
return 1;
}
@@ -1229,7 +1238,30 @@ int lua_IsGamepadButtonUp(lua_State* L)
lua_pushboolean(L, result);
return 1;
}
-#endif
+
+int lua_GetGamepadButtonPressed(lua_State* L)
+{
+ int result = GetGamepadButtonPressed();
+ lua_pushinteger(L, result);
+ return 1;
+}
+
+int lua_GetGamepadAxisCount(lua_State* L)
+{
+ int arg1 = LuaGetArgument_int(L, 1);
+ int result = GetGamepadAxisCount(arg1);
+ lua_pushinteger(L, result);
+ return 1;
+}
+
+int lua_GetGamepadAxisMovement(lua_State* L)
+{
+ int arg1 = LuaGetArgument_int(L, 1);
+ int arg2 = LuaGetArgument_int(L, 2);
+ float result = GetGamepadAxisMovement(arg1, arg2);
+ lua_pushnumber(L, result);
+ return 1;
+}
int lua_IsMouseButtonPressed(lua_State* L)
{
@@ -1419,8 +1451,9 @@ int lua_GetGesturePinchAngle(lua_State* L)
//------------------------------------------------------------------------------------
int lua_SetCameraMode(lua_State* L)
{
- int arg1 = LuaGetArgument_int(L, 1);
- SetCameraMode(arg1);
+ Camera arg1 = LuaGetArgument_Camera(L, 1);
+ int arg2 = LuaGetArgument_int(L, 2);
+ SetCameraMode(arg1, arg2);
return 0;
}
@@ -1432,37 +1465,6 @@ int lua_UpdateCamera(lua_State* L)
return 1;
}
-int lua_UpdateCameraPlayer(lua_State* L)
-{
- Camera arg1 = LuaGetArgument_Camera(L, 1);
- Vector3 arg2 = LuaGetArgument_Vector3(L, 2);
- UpdateCameraPlayer(&arg1, &arg2);
- LuaPush_Camera(L, arg1);
- LuaPush_Vector3(L, arg2);
- return 2;
-}
-
-int lua_SetCameraPosition(lua_State* L)
-{
- Vector3 arg1 = LuaGetArgument_Vector3(L, 1);
- SetCameraPosition(arg1);
- return 0;
-}
-
-int lua_SetCameraTarget(lua_State* L)
-{
- Vector3 arg1 = LuaGetArgument_Vector3(L, 1);
- SetCameraTarget(arg1);
- return 0;
-}
-
-int lua_SetCameraFovy(lua_State* L)
-{
- float arg1 = LuaGetArgument_float(L, 1);
- SetCameraFovy(arg1);
- return 0;
-}
-
int lua_SetCameraPanControl(lua_State* L)
{
int arg1 = LuaGetArgument_int(L, 1);
@@ -1496,13 +1498,6 @@ int lua_SetCameraMoveControls(lua_State* L)
return 0;
}
-int lua_SetCameraMouseSensitivity(lua_State* L)
-{
- float arg1 = LuaGetArgument_float(L, 1);
- SetCameraMouseSensitivity(arg1);
- return 0;
-}
-
//------------------------------------------------------------------------------------
// raylib [shapes] module functions - Basic Shapes Drawing
//------------------------------------------------------------------------------------
@@ -1790,6 +1785,8 @@ int lua_LoadImage(lua_State* L)
int lua_LoadImageEx(lua_State* L)
{
+ // TODO: Image LoadImageEx(Color *pixels, int width, int height);
+
GET_TABLE(Color, arg1, 1);
int arg2 = LuaGetArgument_int(L, 2);
int arg3 = LuaGetArgument_int(L, 3);
@@ -1888,6 +1885,8 @@ int lua_UnloadRenderTexture(lua_State* L)
int lua_GetImageData(lua_State* L)
{
+ // TODO: Color *GetImageData(Image image);
+
Image arg1 = LuaGetArgument_Image(L, 1);
Color * result = GetImageData(arg1);
lua_createtable(L, arg1.width*arg1.height, 0);
@@ -1908,6 +1907,16 @@ int lua_GetTextureData(lua_State* L)
return 1;
}
+int lua_UpdateTexture(lua_State* L)
+{
+ // TODO: void UpdateTexture(Texture2D texture, void *pixels);
+
+ Texture2D arg1 = LuaGetArgument_Texture2D(L, 1);
+ void * arg2 = (char *)LuaGetArgument_string(L, 2); // NOTE: Getting (void *) as string?
+ UpdateTexture(arg1, arg2); // ISSUE: #2 string expected, got table -> GetImageData() returns a table!
+ return 0;
+}
+
int lua_ImageToPOT(lua_State* L)
{
Image arg1 = LuaGetArgument_Image(L, 1);
@@ -2096,15 +2105,24 @@ int lua_ImageColorBrightness(lua_State* L)
int lua_GenTextureMipmaps(lua_State* L)
{
Texture2D arg1 = LuaGetArgument_Texture2D(L, 1);
- GenTextureMipmaps(arg1);
+ GenTextureMipmaps(&arg1);
+ LuaPush_Texture2D(L, arg1);
+ return 1;
+}
+
+int lua_SetTextureFilter(lua_State* L)
+{
+ Texture2D arg1 = LuaGetArgument_Texture2D(L, 1);
+ int arg2 = LuaGetArgument_int(L, 2);
+ SetTextureFilter(arg1, arg2);
return 0;
}
-int lua_UpdateTexture(lua_State* L)
+int lua_SetTextureWrap(lua_State* L)
{
Texture2D arg1 = LuaGetArgument_Texture2D(L, 1);
- void * arg2 = (char *)LuaGetArgument_string(L, 2); // NOTE: Getting (void *) as string?
- UpdateTexture(arg1, arg2); // ISSUE: #2 string expected, got table -> GetImageData() returns a table!
+ int arg2 = LuaGetArgument_int(L, 2);
+ SetTextureWrap(arg1, arg2);
return 0;
}
@@ -2178,6 +2196,18 @@ int lua_LoadSpriteFont(lua_State* L)
return 1;
}
+int lua_LoadSpriteFontTTF(lua_State* L)
+{
+ const char * arg1 = LuaGetArgument_string(L, 1);
+ int arg2 = LuaGetArgument_int(L, 2);
+ int arg3 = LuaGetArgument_int(L, 3);
+ int arg4 = LuaGetArgument_int(L, 4);
+ //LoadSpriteFontTTF(const char *fileName, int fontSize, int numChars, int *fontChars);
+ SpriteFont result = LoadSpriteFontTTF(arg1, arg2, arg3, &arg4);
+ LuaPush_SpriteFont(L, result);
+ return 1;
+}
+
int lua_UnloadSpriteFont(lua_State* L)
{
SpriteFont arg1 = LuaGetArgument_SpriteFont(L, 1);
@@ -2255,8 +2285,8 @@ int lua_DrawCircle3D(lua_State* L)
{
Vector3 arg1 = LuaGetArgument_Vector3(L, 1);
float arg2 = LuaGetArgument_float(L, 2);
- float arg3 = LuaGetArgument_float(L, 3);
- Vector3 arg4 = LuaGetArgument_Vector3(L, 4);
+ Vector3 arg3 = LuaGetArgument_Vector3(L, 3);
+ float arg4 = LuaGetArgument_float(L, 4);
Color arg5 = LuaGetArgument_Color(L, 5);
DrawCircle3D(arg1, arg2, arg3, arg4, arg5);
return 0;
@@ -2619,18 +2649,6 @@ int lua_CheckCollisionRayBox(lua_State* L)
return 1;
}
-int lua_ResolveCollisionCubicmap(lua_State* L)
-{
- Image arg1 = LuaGetArgument_Image(L, 1);
- Vector3 arg2 = LuaGetArgument_Vector3(L, 2);
- Vector3 arg3 = LuaGetArgument_Vector3(L, 3);
- float arg4 = LuaGetArgument_float(L, 4);
- Vector3 result = ResolveCollisionCubicmap(arg1, arg2, &arg3, arg4);
- LuaPush_Vector3(L, result);
- LuaPush_Vector3(L, arg3);
- return 2;
-}
-
//------------------------------------------------------------------------------------
// raylib [raymath] module functions - Shaders
//------------------------------------------------------------------------------------
@@ -2790,10 +2808,19 @@ int lua_IsVrDeviceReady(lua_State* L)
return 1;
}
+int lua_IsVrSimulator(lua_State* L)
+{
+ bool result = IsVrSimulator();
+ lua_pushboolean(L, result);
+ return 1;
+}
+
int lua_UpdateVrTracking(lua_State* L)
{
- UpdateVrTracking();
- return 0;
+ Camera arg1 = LuaGetArgument_Camera(L, 1);
+ UpdateVrTracking(&arg1);
+ LuaPush_Camera(L, arg1);
+ return 1;
}
int lua_ToggleVrMode(lua_State* L)
@@ -2873,6 +2900,8 @@ int lua_LoadSoundFromRES(lua_State* L)
int lua_UpdateSound(lua_State* L)
{
+ // TODO: void UpdateSound(Sound sound, void *data, int numSamples);
+
Sound arg1 = LuaGetArgument_Sound(L, 1);
const char * arg2 = LuaGetArgument_string(L, 2);
int * arg3 = LuaGetArgument_int(L, 3);
@@ -2952,11 +2981,11 @@ int lua_WaveFormat(lua_State* L)
int arg2 = LuaGetArgument_int(L, 2);
int arg3 = LuaGetArgument_int(L, 3);
int arg4 = LuaGetArgument_int(L, 4);
- WaveFormat(arg1, arg2, arg3, arg4);
+ WaveFormat(&arg1, arg2, arg3, arg4);
return 0;
}
-int lua_LoadMusicStream(lua_State* L)
+int lua_WaveCopy(lua_State* L)
{
Wave arg1 = LuaGetArgument_Wave(L, 1);
Wave result = WaveCopy(arg1);
@@ -2969,7 +2998,7 @@ int lua_WaveCrop(lua_State* L)
Wave arg1 = LuaGetArgument_Wave(L, 1);
int arg2 = LuaGetArgument_int(L, 2);
int arg3 = LuaGetArgument_int(L, 3);
- WaveCrop(arg1, arg2, arg3);
+ WaveCrop(&arg1, arg2, arg3);
return 0;
}
@@ -2978,9 +3007,10 @@ int lua_GetWaveData(lua_State* L)
// TODO: float *GetWaveData(Wave wave);
Wave arg1 = LuaGetArgument_Wave(L, 1);
- float result = GetWaveData(arg1);
- LuaPush_float(L, result);
- return 1;
+ float * result = GetWaveData(arg1);
+ //LuaPush_float(L, result);
+ //lua_pushnumber(L, result);
+ return 0;
}
int lua_LoadMusicStream(lua_State* L)
@@ -3012,7 +3042,6 @@ int lua_PlayMusicStream(lua_State* L)
return 0;
}
-
int lua_StopMusicStream(lua_State* L)
{
Music arg1 = LuaGetArgument_Music(L, 1);
@@ -3093,6 +3122,8 @@ int lua_CloseAudioStream(lua_State* L)
int lua_UpdateAudioStream(lua_State* L)
{
+ // TODO: void UpdateAudioStream(AudioStream stream, void *data, int numSamples);
+
AudioStream arg1 = LuaGetArgument_AudioStream(L, 1);
void * arg2 = (char *)LuaGetArgument_string(L, 2);
int arg3 = LuaGetArgument_int(L, 3);
@@ -3667,7 +3698,6 @@ static luaL_Reg raylib_functions[] = {
REG(StorageSaveValue)
REG(StorageLoadValue)
-#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) || defined(PLATFORM_WEB)
REG(IsKeyPressed)
REG(IsKeyDown)
REG(IsKeyReleased)
@@ -3676,12 +3706,15 @@ static luaL_Reg raylib_functions[] = {
REG(SetExitKey)
REG(IsGamepadAvailable)
- REG(GetGamepadAxisMovement)
+ REG(IsGamepadName)
+ REG(GetGamepadName)
REG(IsGamepadButtonPressed)
REG(IsGamepadButtonDown)
REG(IsGamepadButtonReleased)
REG(IsGamepadButtonUp)
-#endif
+ REG(GetGamepadButtonPressed)
+ REG(GetGamepadAxisCount)
+ REG(GetGamepadAxisMovement)
REG(IsMouseButtonPressed)
REG(IsMouseButtonDown)
@@ -3714,16 +3747,10 @@ static luaL_Reg raylib_functions[] = {
REG(SetCameraMode)
REG(UpdateCamera)
- REG(UpdateCameraPlayer)
- REG(SetCameraPosition)
- REG(SetCameraTarget)
- REG(SetCameraFovy)
-
REG(SetCameraPanControl)
REG(SetCameraAltControl)
REG(SetCameraSmoothZoomControl)
REG(SetCameraMoveControls)
- REG(SetCameraMouseSensitivity)
REG(DrawPixel)
REG(DrawPixelV)
@@ -3766,6 +3793,7 @@ static luaL_Reg raylib_functions[] = {
REG(UnloadRenderTexture)
REG(GetImageData)
REG(GetTextureData)
+ REG(UpdateTexture)
REG(ImageToPOT)
REG(ImageFormat)
REG(ImageDither)
@@ -3786,8 +3814,9 @@ static luaL_Reg raylib_functions[] = {
REG(ImageColorContrast)
REG(ImageColorBrightness)
REG(GenTextureMipmaps)
- REG(UpdateTexture)
-
+ REG(SetTextureFilter)
+ REG(SetTextureWrap)
+
REG(DrawTexture)
REG(DrawTextureV)
REG(DrawTextureEx)
@@ -3796,6 +3825,7 @@ static luaL_Reg raylib_functions[] = {
REG(GetDefaultFont)
REG(LoadSpriteFont)
+ REG(LoadSpriteFontTTF)
REG(UnloadSpriteFont)
REG(DrawText)
REG(DrawTextEx)
@@ -3845,7 +3875,6 @@ static luaL_Reg raylib_functions[] = {
REG(CheckCollisionRaySphere)
REG(CheckCollisionRaySphereEx)
REG(CheckCollisionRayBox)
- REG(ResolveCollisionCubicmap)
REG(LoadShader)
REG(UnloadShader)
@@ -3868,6 +3897,7 @@ static luaL_Reg raylib_functions[] = {
REG(InitVrDevice)
REG(CloseVrDevice)
REG(IsVrDeviceReady)
+ REG(IsVrSimulator)
REG(UpdateVrTracking)
REG(ToggleVrMode)
@@ -4067,24 +4097,36 @@ RLUADEF void InitLuaDevice(void)
LuaSetEnum("RIGHT_BUTTON", 1);
LuaSetEnum("MIDDLE_BUTTON", 2);
LuaEndEnum("MOUSE");
-
+
LuaStartEnum();
LuaSetEnum("PLAYER1", 0);
LuaSetEnum("PLAYER2", 1);
LuaSetEnum("PLAYER3", 2);
LuaSetEnum("PLAYER4", 3);
- LuaSetEnum("PS3_BUTTON_A", 2);
- LuaSetEnum("PS3_BUTTON_B", 1);
- LuaSetEnum("PS3_BUTTON_X", 3);
- LuaSetEnum("PS3_BUTTON_Y", 4);
- LuaSetEnum("PS3_BUTTON_R1", 7);
- LuaSetEnum("PS3_BUTTON_R2", 5);
+ LuaSetEnum("PS3_BUTTON_TRIANGLE", 0);
+ LuaSetEnum("PS3_BUTTON_CIRCLE", 1);
+ LuaSetEnum("PS3_BUTTON_CROSS", 2);
+ LuaSetEnum("PS3_BUTTON_SQUARE", 3);
LuaSetEnum("PS3_BUTTON_L1", 6);
- LuaSetEnum("PS3_BUTTON_L2", 8);
+ LuaSetEnum("PS3_BUTTON_R1", 7);
+ LuaSetEnum("PS3_BUTTON_L2", 4);
+ LuaSetEnum("PS3_BUTTON_R2", 5);
+ LuaSetEnum("PS3_BUTTON_START", 8);
LuaSetEnum("PS3_BUTTON_SELECT", 9);
- LuaSetEnum("PS3_BUTTON_START", 10);
-
+ LuaSetEnum("PS3_BUTTON_UP", 24);
+ LuaSetEnum("PS3_BUTTON_RIGHT", 25);
+ LuaSetEnum("PS3_BUTTON_DOWN", 26);
+ LuaSetEnum("PS3_BUTTON_LEFT", 27);
+ LuaSetEnum("PS3_BUTTON_PS", 12);
+ LuaSetEnum("PS3_AXIS_LEFT_X", 0);
+ LuaSetEnum("PS3_AXIS_LEFT_Y", 1);
+ LuaSetEnum("PS3_AXIS_RIGHT_X", 2);
+ LuaSetEnum("PS3_AXIS_RIGHT_Y", 5);
+ LuaSetEnum("PS3_AXIS_L2", 3); // [1..-1] (pressure-level)
+ LuaSetEnum("PS3_AXIS_R2", 4); // [1..-1] (pressure-level)
+
+// Xbox360 USB Controller Buttons
LuaSetEnum("XBOX_BUTTON_A", 0);
LuaSetEnum("XBOX_BUTTON_B", 1);
LuaSetEnum("XBOX_BUTTON_X", 2);
@@ -4093,25 +4135,26 @@ RLUADEF void InitLuaDevice(void)
LuaSetEnum("XBOX_BUTTON_RB", 5);
LuaSetEnum("XBOX_BUTTON_SELECT", 6);
LuaSetEnum("XBOX_BUTTON_START", 7);
-
-#if defined(PLATFORM_RPI)
- LuaSetEnum("XBOX_AXIS_DPAD_X", 7);
- LuaSetEnum("XBOX_AXIS_DPAD_Y", 6);
- LuaSetEnum("XBOX_AXIS_RIGHT_X", 3);
- LuaSetEnum("XBOX_AXIS_RIGHT_Y", 4);
- LuaSetEnum("XBOX_AXIS_LT", 2);
- LuaSetEnum("XBOX_AXIS_RT", 5);
-#else
LuaSetEnum("XBOX_BUTTON_UP", 10);
+ LuaSetEnum("XBOX_BUTTON_RIGHT", 11);
LuaSetEnum("XBOX_BUTTON_DOWN", 12);
LuaSetEnum("XBOX_BUTTON_LEFT", 13);
- LuaSetEnum("XBOX_BUTTON_RIGHT", 11);
- LuaSetEnum("XBOX_AXIS_RIGHT_X", 4);
- LuaSetEnum("XBOX_AXIS_RIGHT_Y", 3);
- LuaSetEnum("XBOX_AXIS_LT_RT", 2);
+ LuaSetEnum("XBOX_BUTTON_HOME", 8);
+#if defined(PLATFORM_RPI)
+ LuaSetEnum("XBOX_AXIS_LEFT_X", 0); // [-1..1] (left->right)
+ LuaSetEnum("XBOX_AXIS_LEFT_Y", 1); // [-1..1] (up->down)
+ LuaSetEnum("XBOX_AXIS_RIGHT_X", 3); // [-1..1] (left->right)
+ LuaSetEnum("XBOX_AXIS_RIGHT_Y", 4); // [-1..1] (up->down)
+ LuaSetEnum("XBOX_AXIS_LT", 2); // [-1..1] (pressure-level)
+ LuaSetEnum("XBOX_AXIS_RT", 5); // [-1..1] (pressure-level)
+#else
+ LuaSetEnum("XBOX_AXIS_LEFT_X", 0); // [-1..1] (left->right)
+ LuaSetEnum("XBOX_AXIS_LEFT_Y", 1); // [1..-1] (up->down)
+ LuaSetEnum("XBOX_AXIS_RIGHT_X", 2); // [-1..1] (left->right)
+ LuaSetEnum("XBOX_AXIS_RIGHT_Y", 3); // [1..-1] (up->down)
+ LuaSetEnum("XBOX_AXIS_LT", 4); // [-1..1] (pressure-level)
+ LuaSetEnum("XBOX_AXIS_RT", 5); // [-1..1] (pressure-level)
#endif
- LuaSetEnum("XBOX_AXIS_LEFT_X", 0);
- LuaSetEnum("XBOX_AXIS_LEFT_Y", 1);
LuaEndEnum("GAMEPAD");
lua_pushglobaltable(L);
@@ -4175,6 +4218,15 @@ RLUADEF void InitLuaDevice(void)
LuaSetEnum("DIRECTIONAL", LIGHT_DIRECTIONAL);
LuaSetEnum("SPOT", LIGHT_SPOT);
LuaEndEnum("LightType");
+
+ LuaStartEnum();
+ LuaSetEnum("POINT", FILTER_POINT);
+ LuaSetEnum("BILINEAR", FILTER_BILINEAR);
+ LuaSetEnum("TRILINEAR", FILTER_TRILINEAR);
+ LuaSetEnum("ANISOTROPIC_4X", FILTER_ANISOTROPIC_4X);
+ LuaSetEnum("ANISOTROPIC_8X", FILTER_ANISOTROPIC_8X);
+ LuaSetEnum("ANISOTROPIC_16X", FILTER_ANISOTROPIC_16X);
+ LuaEndEnum("TextureFilter");
LuaStartEnum();
LuaSetEnum("NONE", GESTURE_NONE);
diff --git a/src/shapes.c b/src/shapes.c
index 79cf567a..70aad59a 100644
--- a/src/shapes.c
+++ b/src/shapes.c
@@ -4,6 +4,12 @@
*
* Basic functions to draw 2d Shapes and check collisions
*
+* External libs:
+* rlgl - raylib OpenGL abstraction layer
+*
+* Module Configuration Flags:
+* ...
+*
* Copyright (c) 2014-2016 Ramon Santamaria (@raysan5)
*
* This software is provided "as-is", without any express or implied warranty. In no event
@@ -25,11 +31,11 @@
#include "raylib.h"
+#include "rlgl.h" // raylib OpenGL abstraction layer to OpenGL 1.1, 2.1, 3.3+ or ES2
+
#include <stdlib.h> // Required for: abs()
#include <math.h> // Required for: sinf(), cosf(), sqrtf()
-#include "rlgl.h" // raylib OpenGL abstraction layer to OpenGL 1.1, 3.3+ or ES2
-
//----------------------------------------------------------------------------------
// Defines and Macros
//----------------------------------------------------------------------------------
diff --git a/src/text.c b/src/text.c
index 72d273d4..c394889e 100644
--- a/src/text.c
+++ b/src/text.c
@@ -4,6 +4,12 @@
*
* Basic functions to load SpriteFonts and draw Text
*
+* External libs:
+* stb_truetype - Load TTF file and rasterize characters data
+*
+* Module Configuration Flags:
+* ...
+*
* Copyright (c) 2014-2016 Ramon Santamaria (@raysan5)
*
* This software is provided "as-is", without any express or implied warranty. In no event
@@ -33,7 +39,7 @@
#include "utils.h" // Required for: GetExtension(), GetNextPOT()
// Following libs are used on LoadTTF()
-//#define STBTT_STATIC
+#define STBTT_STATIC // Define stb_truetype functions static to this module
#define STB_TRUETYPE_IMPLEMENTATION
#include "external/stb_truetype.h" // Required for: stbtt_BakeFontBitmap()
@@ -869,10 +875,18 @@ static SpriteFont LoadBMFont(const char *fileName)
TraceLog(DEBUG, "[%s] Font texture loading path: %s", fileName, texPath);
Image imFont = LoadImage(texPath);
+
+ if (imFont.format == UNCOMPRESSED_GRAYSCALE)
+ {
+ Image imCopy = ImageCopy(imFont);
+
+ for (int i = 0; i < imCopy.width*imCopy.height; i++) ((unsigned char *)imCopy.data)[i] = 0xff; // WHITE pixel
- if (imFont.format == UNCOMPRESSED_GRAYSCALE) ImageAlphaMask(&imFont, imFont);
-
- font.texture = LoadTextureFromImage(imFont);
+ ImageAlphaMask(&imCopy, imFont);
+ font.texture = LoadTextureFromImage(imCopy);
+ UnloadImage(imCopy);
+ }
+ else font.texture = LoadTextureFromImage(imFont);
font.size = fontSize;
font.numChars = numChars;
diff --git a/src/textures.c b/src/textures.c
index 5354a74f..126adad3 100644
--- a/src/textures.c
+++ b/src/textures.c
@@ -4,9 +4,13 @@
*
* Basic functions to load and draw Textures (2d)
*
-* Uses external lib:
-* stb_image - Multiple formats image loading (JPEG, PNG, BMP, TGA, PSD, GIF, PIC)
-* NOTE: stb_image has been slightly modified, original library: https://github.com/nothings/stb
+* External libs:
+* stb_image - Multiple image formats loading (JPEG, PNG, BMP, TGA, PSD, GIF, PIC)
+* NOTE: stb_image has been slightly modified to support Android platform.
+* stb_image_resize - Multiple image resize algorythms
+*
+* Module Configuration Flags:
+* ...
*
* Copyright (c) 2014-2016 Ramon Santamaria (@raysan5)
*
@@ -1512,14 +1516,14 @@ void ImageColorBrightness(Image *image, int brightness)
}
// Generate GPU mipmaps for a texture
-void GenTextureMipmaps(Texture2D texture)
+void GenTextureMipmaps(Texture2D *texture)
{
#if PLATFORM_WEB
- int potWidth = GetNextPOT(texture.width);
- int potHeight = GetNextPOT(texture.height);
+ int potWidth = GetNextPOT(texture->width);
+ int potHeight = GetNextPOT(texture->height);
// Check if texture is POT
- if ((potWidth != texture.width) || (potHeight != texture.height))
+ if ((potWidth != texture->width) || (potHeight != texture->height))
{
TraceLog(WARNING, "Limited NPOT support, no mipmaps available for NPOT textures");
}
diff --git a/src/utils.c b/src/utils.c
index b96e2c70..640c5720 100644
--- a/src/utils.c
+++ b/src/utils.c
@@ -2,12 +2,16 @@
*
* raylib.utils
*
-* Utils Functions Definitions
+* Some utility functions
*
-* Uses external libs:
-* tinfl - zlib DEFLATE algorithm decompression lib
+* External libs:
+* tinfl - zlib DEFLATE algorithm decompression
* stb_image_write - PNG writting functions
*
+* Module Configuration Flags:
+* DO_NOT_TRACE_DEBUG_MSGS - Avoid showing DEBUG TraceLog() messages
+*
+*
* Copyright (c) 2014-2016 Ramon Santamaria (@raysan5)
*
* This software is provided "as-is", without any express or implied warranty. In no event
@@ -46,7 +50,9 @@
#endif
#include "external/tinfl.c" // Required for: tinfl_decompress_mem_to_mem()
- // NOTE: Deflate algorythm data decompression
+ // NOTE: DEFLATE algorythm data decompression
+
+#define DO_NOT_TRACE_DEBUG_MSGS // Avoid DEBUG messages tracing
//----------------------------------------------------------------------------------
// Global Variables Definition
diff --git a/src/utils.h b/src/utils.h
index 899cf583..045b0692 100644
--- a/src/utils.h
+++ b/src/utils.h
@@ -2,9 +2,9 @@
*
* raylib.utils
*
-* Some utility functions: rRES files data decompression
+* Some utility functions
*
-* Copyright (c) 2014 Ramon Santamaria (@raysan5)
+* Copyright (c) 2014-2016 Ramon Santamaria (@raysan5)
*
* This software is provided "as-is", without any express or implied warranty. In no event
* will the authors be held liable for any damages arising from the use of this software.
@@ -34,8 +34,6 @@
//----------------------------------------------------------------------------------
// Some basic Defines
//----------------------------------------------------------------------------------
-#define DO_NOT_TRACE_DEBUG_MSGS // Use this define to avoid DEBUG tracing
-
#if defined(PLATFORM_ANDROID)
#define fopen(name, mode) android_fopen(name, mode)
#endif