Update C sources

This commit is contained in:
Milan Nikolic 2024-05-07 21:54:39 +02:00
parent 2a66186c7d
commit 1868520849
No known key found for this signature in database
GPG key ID: 9229D0EAA3AA4E75
27 changed files with 4582 additions and 2234 deletions

View file

@ -1,6 +1,6 @@
package rl
/*
#cgo CFLAGS: -std=gnu99 -Wno-missing-braces -Wno-unused-result -Wno-implicit-function-declaration
#cgo CFLAGS: -std=gnu99 -Wno-missing-braces -Wno-unused-result -Wno-implicit-function-declaration -Wno-int-to-pointer-cast
*/
import "C"

View file

@ -6,7 +6,7 @@
*
* LICENSE: zlib/libpng
*
* Copyright (c) 2018-2023 Ahmad Fatoum & Ramon Santamaria (@raysan5)
* Copyright (c) 2018-2024 Ahmad Fatoum & 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.
@ -81,6 +81,7 @@
#define MAX_GAMEPADS 4 // Maximum number of gamepads supported
#define MAX_GAMEPAD_AXIS 8 // Maximum number of axis supported (per gamepad)
#define MAX_GAMEPAD_BUTTONS 32 // Maximum number of buttons supported (per gamepad)
#define MAX_GAMEPAD_VIBRATION_TIME 2.0f // Maximum vibration time in seconds
#define MAX_TOUCH_POINTS 8 // Maximum number of touch points supported
#define MAX_KEY_PRESSED_QUEUE 16 // Maximum number of keys in the key input queue
#define MAX_CHAR_PRESSED_QUEUE 16 // Maximum number of characters in the char input queue
@ -111,14 +112,22 @@
#define RL_CULL_DISTANCE_NEAR 0.01 // Default projection matrix near cull distance
#define RL_CULL_DISTANCE_FAR 1000.0 // Default projection matrix far cull distance
// Default shader vertex attribute locations
#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION 0
#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD 1
#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL 2
#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR 3
#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT 4
#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2 5
// Default shader vertex attribute names to set location points
// NOTE: When a new shader is loaded, the following locations are tried to be set for convenience
#define RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION "vertexPosition" // Bound by default to shader location: 0
#define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD "vertexTexCoord" // Bound by default to shader location: 1
#define RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL "vertexNormal" // Bound by default to shader location: 2
#define RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR "vertexColor" // Bound by default to shader location: 3
#define RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT "vertexTangent" // Bound by default to shader location: 4
#define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2 "vertexTexCoord2" // Bound by default to shader location: 5
#define RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION "vertexPosition" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION
#define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD "vertexTexCoord" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD
#define RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL "vertexNormal" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL
#define RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR "vertexColor" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR
#define RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT "vertexTangent" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT
#define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2 "vertexTexCoord2" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2
#define RL_DEFAULT_SHADER_UNIFORM_NAME_MVP "mvp" // model-view-projection matrix
#define RL_DEFAULT_SHADER_UNIFORM_NAME_VIEW "matView" // view matrix
@ -138,6 +147,10 @@
// Some lines-based shapes could still use lines
#define SUPPORT_QUADS_DRAW_MODE 1
// rshapes: Configuration values
//------------------------------------------------------------------------------------
#define SPLINE_SEGMENT_DIVISIONS 24 // Spline segments subdivisions
//------------------------------------------------------------------------------------
// Module: rtextures - Configuration Flags
@ -175,8 +188,9 @@
// NOTE: If enabled, uses external module functions to load default raylib font
#define SUPPORT_DEFAULT_FONT 1
// Selected desired font fileformats to be supported for loading
#define SUPPORT_FILEFORMAT_FNT 1
#define SUPPORT_FILEFORMAT_TTF 1
#define SUPPORT_FILEFORMAT_FNT 1
#define SUPPORT_FILEFORMAT_BDF 1
// Support text management functions
// If not defined, still some functions are supported: TextLength(), TextFormat()

View file

@ -63,6 +63,11 @@
* By passing null for the output pointer, users can find out how many floats are required in the
* output buffer.
*
* `cgltf_accessor_unpack_indices` reads in the index data from an accessor. Assumes that
* `cgltf_load_buffers` has already been called. By passing null for the output pointer, users can
* find out how many indices are required in the output buffer. Returns 0 if the accessor is
* sparse or if the output component size is less than the accessor's component size.
*
* `cgltf_num_components` is a tiny utility that tells you the dimensionality of
* a certain accessor type. This can be used before `cgltf_accessor_unpack_floats` to help allocate
* the necessary amount of memory. `cgltf_component_size` and `cgltf_calc_size` exist for
@ -197,6 +202,7 @@ typedef enum cgltf_type
typedef enum cgltf_primitive_type
{
cgltf_primitive_type_invalid,
cgltf_primitive_type_points,
cgltf_primitive_type_lines,
cgltf_primitive_type_line_loop,
@ -499,6 +505,11 @@ typedef struct cgltf_anisotropy
cgltf_texture_view anisotropy_texture;
} cgltf_anisotropy;
typedef struct cgltf_dispersion
{
cgltf_float dispersion;
} cgltf_dispersion;
typedef struct cgltf_material
{
char* name;
@ -513,6 +524,7 @@ typedef struct cgltf_material
cgltf_bool has_emissive_strength;
cgltf_bool has_iridescence;
cgltf_bool has_anisotropy;
cgltf_bool has_dispersion;
cgltf_pbr_metallic_roughness pbr_metallic_roughness;
cgltf_pbr_specular_glossiness pbr_specular_glossiness;
cgltf_clearcoat clearcoat;
@ -524,6 +536,7 @@ typedef struct cgltf_material
cgltf_emissive_strength emissive_strength;
cgltf_iridescence iridescence;
cgltf_anisotropy anisotropy;
cgltf_dispersion dispersion;
cgltf_texture_view normal_texture;
cgltf_texture_view occlusion_texture;
cgltf_texture_view emissive_texture;
@ -838,7 +851,7 @@ cgltf_size cgltf_component_size(cgltf_component_type component_type);
cgltf_size cgltf_calc_size(cgltf_type type, cgltf_component_type component_type);
cgltf_size cgltf_accessor_unpack_floats(const cgltf_accessor* accessor, cgltf_float* out, cgltf_size float_count);
cgltf_size cgltf_accessor_unpack_indices(const cgltf_accessor* accessor, cgltf_uint* out, cgltf_size index_count);
cgltf_size cgltf_accessor_unpack_indices(const cgltf_accessor* accessor, void* out, cgltf_size out_component_size, cgltf_size index_count);
/* this function is deprecated and will be removed in the future; use cgltf_extras::data instead */
cgltf_result cgltf_copy_extras_json(const cgltf_data* data, const cgltf_extras* extras, char* dest, cgltf_size* dest_size);
@ -938,8 +951,8 @@ static int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, jsmntok_t
#ifndef CGLTF_CONSTS
static const cgltf_size GlbHeaderSize = 12;
static const cgltf_size GlbChunkHeaderSize = 8;
#define GlbHeaderSize 12
#define GlbChunkHeaderSize 8
static const uint32_t GlbVersion = 2;
static const uint32_t GlbMagic = 0x46546C67;
static const uint32_t GlbMagicJsonChunk = 0x4E4F534A;
@ -1141,7 +1154,7 @@ cgltf_result cgltf_parse(const cgltf_options* options, const void* data, cgltf_s
// JSON chunk: length
uint32_t json_length;
memcpy(&json_length, json_chunk, 4);
if (GlbHeaderSize + GlbChunkHeaderSize + json_length > size)
if (json_length > size - GlbHeaderSize - GlbChunkHeaderSize)
{
return cgltf_result_data_too_short;
}
@ -1158,7 +1171,7 @@ cgltf_result cgltf_parse(const cgltf_options* options, const void* data, cgltf_s
const void* bin = NULL;
cgltf_size bin_size = 0;
if (GlbHeaderSize + GlbChunkHeaderSize + json_length + GlbChunkHeaderSize <= size)
if (GlbChunkHeaderSize <= size - GlbHeaderSize - GlbChunkHeaderSize - json_length)
{
// We can read another chunk
const uint8_t* bin_chunk = json_chunk + json_length;
@ -1166,7 +1179,7 @@ cgltf_result cgltf_parse(const cgltf_options* options, const void* data, cgltf_s
// Bin chunk: length
uint32_t bin_length;
memcpy(&bin_length, bin_chunk, 4);
if (GlbHeaderSize + GlbChunkHeaderSize + json_length + GlbChunkHeaderSize + bin_length > size)
if (bin_length > size - GlbHeaderSize - GlbChunkHeaderSize - json_length - GlbChunkHeaderSize)
{
return cgltf_result_data_too_short;
}
@ -1552,6 +1565,9 @@ cgltf_result cgltf_validate(cgltf_data* data)
{
cgltf_accessor* accessor = &data->accessors[i];
CGLTF_ASSERT_IF(data->accessors[i].component_type == cgltf_component_type_invalid, cgltf_result_invalid_gltf);
CGLTF_ASSERT_IF(data->accessors[i].type == cgltf_type_invalid, cgltf_result_invalid_gltf);
cgltf_size element_size = cgltf_calc_size(accessor->type, accessor->component_type);
if (accessor->buffer_view)
@ -1565,7 +1581,7 @@ cgltf_result cgltf_validate(cgltf_data* data)
{
cgltf_accessor_sparse* sparse = &accessor->sparse;
cgltf_size indices_component_size = cgltf_calc_size(cgltf_type_scalar, sparse->indices_component_type);
cgltf_size indices_component_size = cgltf_component_size(sparse->indices_component_type);
cgltf_size indices_req_size = sparse->indices_byte_offset + indices_component_size * sparse->count;
cgltf_size values_req_size = sparse->values_byte_offset + element_size * sparse->count;
@ -1631,12 +1647,15 @@ cgltf_result cgltf_validate(cgltf_data* data)
for (cgltf_size j = 0; j < data->meshes[i].primitives_count; ++j)
{
CGLTF_ASSERT_IF(data->meshes[i].primitives[j].type == cgltf_primitive_type_invalid, cgltf_result_invalid_gltf);
CGLTF_ASSERT_IF(data->meshes[i].primitives[j].targets_count != data->meshes[i].primitives[0].targets_count, cgltf_result_invalid_gltf);
if (data->meshes[i].primitives[j].attributes_count)
{
CGLTF_ASSERT_IF(data->meshes[i].primitives[j].attributes_count == 0, cgltf_result_invalid_gltf);
cgltf_accessor* first = data->meshes[i].primitives[j].attributes[0].data;
CGLTF_ASSERT_IF(first->count == 0, cgltf_result_invalid_gltf);
for (cgltf_size k = 0; k < data->meshes[i].primitives[j].attributes_count; ++k)
{
CGLTF_ASSERT_IF(data->meshes[i].primitives[j].attributes[k].data->count != first->count, cgltf_result_invalid_gltf);
@ -1657,6 +1676,9 @@ cgltf_result cgltf_validate(cgltf_data* data)
indices->component_type != cgltf_component_type_r_16u &&
indices->component_type != cgltf_component_type_r_32u, cgltf_result_invalid_gltf);
CGLTF_ASSERT_IF(indices && indices->type != cgltf_type_scalar, cgltf_result_invalid_gltf);
CGLTF_ASSERT_IF(indices && indices->stride != cgltf_component_size(indices->component_type), cgltf_result_invalid_gltf);
if (indices && indices->buffer_view && indices->buffer_view->buffer->data)
{
cgltf_size index_bound = cgltf_calc_index_bound(indices->buffer_view, indices->offset, indices->component_type, indices->count);
@ -1670,7 +1692,6 @@ cgltf_result cgltf_validate(cgltf_data* data)
}
}
}
}
for (cgltf_size i = 0; i < data->nodes_count; ++i)
{
@ -1724,10 +1745,15 @@ cgltf_result cgltf_validate(cgltf_data* data)
cgltf_size values = channel->sampler->interpolation == cgltf_interpolation_type_cubic_spline ? 3 : 1;
CGLTF_ASSERT_IF(channel->sampler->input->count * components * values != channel->sampler->output->count, cgltf_result_data_too_short);
CGLTF_ASSERT_IF(channel->sampler->input->count * components * values != channel->sampler->output->count, cgltf_result_invalid_gltf);
}
}
for (cgltf_size i = 0; i < data->variants_count; ++i)
{
CGLTF_ASSERT_IF(!data->variants[i].name, cgltf_result_invalid_gltf);
}
return cgltf_result_success;
}
@ -2550,7 +2576,7 @@ cgltf_size cgltf_animation_channel_index(const cgltf_animation* animation, const
return (cgltf_size)(object - animation->channels);
}
cgltf_size cgltf_accessor_unpack_indices(const cgltf_accessor* accessor, cgltf_uint* out, cgltf_size index_count)
cgltf_size cgltf_accessor_unpack_indices(const cgltf_accessor* accessor, void* out, cgltf_size out_component_size, cgltf_size index_count)
{
if (out == NULL)
{
@ -2558,6 +2584,7 @@ cgltf_size cgltf_accessor_unpack_indices(const cgltf_accessor* accessor, cgltf_u
}
index_count = accessor->count < index_count ? accessor->count : index_count;
cgltf_size index_component_size = cgltf_component_size(accessor->component_type);
if (accessor->is_sparse)
{
@ -2567,6 +2594,10 @@ cgltf_size cgltf_accessor_unpack_indices(const cgltf_accessor* accessor, cgltf_u
{
return 0;
}
if (index_component_size > out_component_size)
{
return 0;
}
const uint8_t* element = cgltf_buffer_view_data(accessor->buffer_view);
if (element == NULL)
{
@ -2574,18 +2605,29 @@ cgltf_size cgltf_accessor_unpack_indices(const cgltf_accessor* accessor, cgltf_u
}
element += accessor->offset;
if (accessor->component_type == cgltf_component_type_r_32u && accessor->stride == sizeof(cgltf_uint))
if (index_component_size == out_component_size && accessor->stride == out_component_size)
{
memcpy(out, element, index_count * sizeof(cgltf_uint));
memcpy(out, element, index_count * index_component_size);
return index_count;
}
else
{
cgltf_uint* dest = out;
for (cgltf_size index = 0; index < index_count; index++, dest++, element += accessor->stride)
// The component size of the output array is larger than the component size of the index data, so index data will be padded.
switch (out_component_size)
{
*dest = (cgltf_uint)cgltf_component_read_index(element, accessor->component_type);
case 2:
for (cgltf_size index = 0; index < index_count; index++, element += accessor->stride)
{
((uint16_t*)out)[index] = (uint16_t)cgltf_component_read_index(element, accessor->component_type);
}
break;
case 4:
for (cgltf_size index = 0; index < index_count; index++, element += accessor->stride)
{
((uint32_t*)out)[index] = (uint32_t)cgltf_component_read_index(element, accessor->component_type);
}
break;
default:
break;
}
return index_count;
@ -2596,7 +2638,7 @@ cgltf_size cgltf_accessor_unpack_indices(const cgltf_accessor* accessor, cgltf_u
#define CGLTF_ERROR_LEGACY -3
#define CGLTF_CHECK_TOKTYPE(tok_, type_) if ((tok_).type != (type_)) { return CGLTF_ERROR_JSON; }
#define CGLTF_CHECK_TOKTYPE_RETTYPE(tok_, type_, ret_) if ((tok_).type != (type_)) { return (ret_)CGLTF_ERROR_JSON; }
#define CGLTF_CHECK_TOKTYPE_RET(tok_, type_, ret_) if ((tok_).type != (type_)) { return ret_; }
#define CGLTF_CHECK_KEY(tok_) if ((tok_).type != JSMN_STRING || (tok_).size == 0) { return CGLTF_ERROR_JSON; } /* checking size for 0 verifies that a value follows the key */
#define CGLTF_PTRINDEX(type, idx) (type*)((cgltf_size)idx + 1)
@ -2623,12 +2665,13 @@ static int cgltf_json_to_int(jsmntok_t const* tok, const uint8_t* json_chunk)
static cgltf_size cgltf_json_to_size(jsmntok_t const* tok, const uint8_t* json_chunk)
{
CGLTF_CHECK_TOKTYPE_RETTYPE(*tok, JSMN_PRIMITIVE, cgltf_size);
CGLTF_CHECK_TOKTYPE_RET(*tok, JSMN_PRIMITIVE, 0);
char tmp[128];
int size = (size_t)(tok->end - tok->start) < sizeof(tmp) ? (int)(tok->end - tok->start) : (int)(sizeof(tmp) - 1);
strncpy(tmp, (const char*)json_chunk + tok->start, size);
tmp[size] = 0;
return (cgltf_size)CGLTF_ATOLL(tmp);
long long res = CGLTF_ATOLL(tmp);
return res < 0 ? 0 : (cgltf_size)res;
}
static cgltf_float cgltf_json_to_float(jsmntok_t const* tok, const uint8_t* json_chunk)
@ -2810,6 +2853,11 @@ static void cgltf_parse_attribute_type(const char* name, cgltf_attribute_type* o
if (us && *out_type != cgltf_attribute_type_invalid)
{
*out_index = CGLTF_ATOI(us + 1);
if (*out_index < 0)
{
*out_type = cgltf_attribute_type_invalid;
*out_index = 0;
}
}
}
@ -3142,6 +3190,31 @@ static int cgltf_parse_json_material_mappings(cgltf_options* options, jsmntok_t
return i;
}
static cgltf_primitive_type cgltf_json_to_primitive_type(jsmntok_t const* tok, const uint8_t* json_chunk)
{
int type = cgltf_json_to_int(tok, json_chunk);
switch (type)
{
case 0:
return cgltf_primitive_type_points;
case 1:
return cgltf_primitive_type_lines;
case 2:
return cgltf_primitive_type_line_loop;
case 3:
return cgltf_primitive_type_line_strip;
case 4:
return cgltf_primitive_type_triangles;
case 5:
return cgltf_primitive_type_triangle_strip;
case 6:
return cgltf_primitive_type_triangle_fan;
default:
return cgltf_primitive_type_invalid;
}
}
static int cgltf_parse_json_primitive(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_primitive* out_prim)
{
CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
@ -3158,9 +3231,7 @@ static int cgltf_parse_json_primitive(cgltf_options* options, jsmntok_t const* t
if (cgltf_json_strcmp(tokens+i, json_chunk, "mode") == 0)
{
++i;
out_prim->type
= (cgltf_primitive_type)
cgltf_json_to_int(tokens+i, json_chunk);
out_prim->type = cgltf_json_to_primitive_type(tokens+i, json_chunk);
++i;
}
else if (cgltf_json_strcmp(tokens+i, json_chunk, "indices") == 0)
@ -3410,7 +3481,7 @@ static int cgltf_parse_json_accessor_sparse(jsmntok_t const* tokens, int i, cons
if (cgltf_json_strcmp(tokens+i, json_chunk, "count") == 0)
{
++i;
out_sparse->count = cgltf_json_to_int(tokens + i, json_chunk);
out_sparse->count = cgltf_json_to_size(tokens + i, json_chunk);
++i;
}
else if (cgltf_json_strcmp(tokens+i, json_chunk, "indices") == 0)
@ -3546,8 +3617,7 @@ static int cgltf_parse_json_accessor(cgltf_options* options, jsmntok_t const* to
else if (cgltf_json_strcmp(tokens+i, json_chunk, "count") == 0)
{
++i;
out_accessor->count =
cgltf_json_to_int(tokens+i, json_chunk);
out_accessor->count = cgltf_json_to_size(tokens+i, json_chunk);
++i;
}
else if (cgltf_json_strcmp(tokens+i, json_chunk, "type") == 0)
@ -4234,6 +4304,37 @@ static int cgltf_parse_json_anisotropy(cgltf_options* options, jsmntok_t const*
return i;
}
static int cgltf_parse_json_dispersion(jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_dispersion* out_dispersion)
{
CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
int size = tokens[i].size;
++i;
for (int j = 0; j < size; ++j)
{
CGLTF_CHECK_KEY(tokens[i]);
if (cgltf_json_strcmp(tokens + i, json_chunk, "dispersion") == 0)
{
++i;
out_dispersion->dispersion = cgltf_json_to_float(tokens + i, json_chunk);
++i;
}
else
{
i = cgltf_skip_json(tokens, i + 1);
}
if (i < 0)
{
return i;
}
}
return i;
}
static int cgltf_parse_json_image(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_image* out_image)
{
CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
@ -4627,6 +4728,11 @@ static int cgltf_parse_json_material(cgltf_options* options, jsmntok_t const* to
out_material->has_anisotropy = 1;
i = cgltf_parse_json_anisotropy(options, tokens, i + 1, json_chunk, &out_material->anisotropy);
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "KHR_materials_dispersion") == 0)
{
out_material->has_dispersion = 1;
i = cgltf_parse_json_dispersion(tokens, i + 1, json_chunk, &out_material->dispersion);
}
else
{
i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, &(out_material->extensions[out_material->extensions_count++]));
@ -4786,7 +4892,7 @@ static int cgltf_parse_json_meshopt_compression(cgltf_options* options, jsmntok_
else if (cgltf_json_strcmp(tokens+i, json_chunk, "count") == 0)
{
++i;
out_meshopt_compression->count = cgltf_json_to_int(tokens+i, json_chunk);
out_meshopt_compression->count = cgltf_json_to_size(tokens+i, json_chunk);
++i;
}
else if (cgltf_json_strcmp(tokens+i, json_chunk, "mode") == 0)

View file

@ -229,7 +229,7 @@ void *rl_load_dds_from_memory(const unsigned char *file_data, unsigned int file_
}
}
}
else if (header->ddspf.flags == 0x40 && header->ddspf.rgb_bit_count == 24) // DDS_RGB, no compressed
else if ((header->ddspf.flags == 0x40) && (header->ddspf.rgb_bit_count == 24)) // DDS_RGB, no compressed
{
int data_size = image_pixel_size*3*sizeof(unsigned char);
image_data = RL_MALLOC(data_size);
@ -238,7 +238,7 @@ void *rl_load_dds_from_memory(const unsigned char *file_data, unsigned int file_
*format = PIXELFORMAT_UNCOMPRESSED_R8G8B8;
}
else if (header->ddspf.flags == 0x41 && header->ddspf.rgb_bit_count == 32) // DDS_RGBA, no compressed
else if ((header->ddspf.flags == 0x41) && (header->ddspf.rgb_bit_count == 32)) // DDS_RGBA, no compressed
{
int data_size = image_pixel_size*4*sizeof(unsigned char);
image_data = RL_MALLOC(data_size);

View file

@ -147,8 +147,13 @@ RPRANDAPI void rprand_unload_sequence(int *sequence); // Unload pseudo
//----------------------------------------------------------------------------------
// Global Variables Definition
//----------------------------------------------------------------------------------
static uint64_t rprand_seed = 0; // SplitMix64 actual seed
static uint32_t rprand_state[4] = { 0 }; // Xoshiro128** state, nitialized by SplitMix64
static uint64_t rprand_seed = 0xAABBCCDD; // SplitMix64 default seed (aligned to rprand_state)
static uint32_t rprand_state[4] = { // Xoshiro128** state, initialized by SplitMix64
0x96ea83c1,
0x218b21e5,
0xaa91febd,
0x976414d4
};
//----------------------------------------------------------------------------------
// Module internal functions declaration
@ -200,10 +205,9 @@ int *rprand_load_sequence(unsigned int count, int min, int max)
for (unsigned int i = 0; i < count;)
{
value = ((int)rprand_xoshiro()%(abs(max - min) + 1)) + min;
value_is_dup = false;
value = ((unsigned int)rprand_xoshiro()%(abs(max - min) + 1)) + min;
for (int j = 0; j < i; j++)
for (unsigned int j = 0; j < i; j++)
{
if (sequence[j] == value)
{
@ -217,6 +221,8 @@ int *rprand_load_sequence(unsigned int count, int min, int max)
sequence[i] = value;
i++;
}
value_is_dup = false;
}
return sequence;

View file

@ -171,7 +171,7 @@ extern int zsinflate(void *out, int cap, const void *in, int size);
static int
sinfl_bsr(unsigned n) {
#ifdef _MSC_VER
#if defined(_MSC_VER) && !defined(__clang__)
_BitScanReverse(&n, n);
return n;
#elif defined(__GNUC__) || defined(__clang__)
@ -231,13 +231,13 @@ sinfl_refill(struct sinfl *s) {
}
static int
sinfl_peek(struct sinfl *s, int cnt) {
assert(cnt >= 0 && cnt <= 56);
assert(cnt <= s->bitcnt);
//assert(cnt >= 0 && cnt <= 56); // @raysan5: commented to avoid crash on decompression
//assert(cnt <= s->bitcnt);
return s->bitbuf & ((1ull << cnt) - 1);
}
static void
sinfl_eat(struct sinfl *s, int cnt) {
assert(cnt <= s->bitcnt);
//assert(cnt <= s->bitcnt); // @raysan5: commented
s->bitbuf >>= cnt;
s->bitcnt -= cnt;
}

View file

@ -123,6 +123,7 @@ typedef struct {
// Arrays for mesh build
ArrayVector3 vertices;
ArrayVector3 normals;
ArrayUShort indices;
ArrayColor colors;
@ -292,6 +293,16 @@ const VoxVector3 SolidVertex[] = {
{1, 1, 1} //7
};
const VoxVector3 FacesPerSideNormal[] = {
{ -1, 0, 0 }, //-X
{1, 0, 0 }, //+X
{0,-1, 0}, //-Y
{0, 1, 0}, //+Y
{0, 0, -1}, //-Z
{0, 0, 1}, //+Z
};
// Allocated VoxArray3D size
static void Vox_AllocArray(VoxArray3D* pvoxarray, int _sx, int _sy, int _sz)
{
@ -508,6 +519,11 @@ static void Vox_Build_Voxel(VoxArray3D* pvoxArray, int x, int y, int z, int matI
insertArrayVector3(&pvoxArray->vertices, vertComputed[v2]);
insertArrayVector3(&pvoxArray->vertices, vertComputed[v3]);
insertArrayVector3(&pvoxArray->normals, FacesPerSideNormal[i]);
insertArrayVector3(&pvoxArray->normals, FacesPerSideNormal[i]);
insertArrayVector3(&pvoxArray->normals, FacesPerSideNormal[i]);
insertArrayVector3(&pvoxArray->normals, FacesPerSideNormal[i]);
VoxColor col = pvoxArray->palette[matID];
insertArrayColor(&pvoxArray->colors, col);
@ -653,6 +669,7 @@ int Vox_LoadFromMemory(unsigned char* pvoxData, unsigned int voxDataSize, VoxArr
// Init Arrays
initArrayVector3(&pvoxarray->vertices, 3 * 1024);
initArrayVector3(&pvoxarray->normals, 3 * 1024);
initArrayUShort(&pvoxarray->indices, 3 * 1024);
initArrayColor(&pvoxarray->colors, 3 * 1024);

View file

@ -27,7 +27,7 @@
*
* LICENSE: zlib/libpng
*
* Copyright (c) 2013-2023 Ramon Santamaria (@raysan5) and contributors
* Copyright (c) 2013-2024 Ramon Santamaria (@raysan5) and contributors
*
* 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.
@ -77,6 +77,175 @@ extern CoreData CORE; // Global CORE state context
static PlatformData platform = { 0 }; // Platform specific data
//----------------------------------------------------------------------------------
// Local Variables Definition
//----------------------------------------------------------------------------------
#define KEYCODE_MAP_SIZE 162
static const KeyboardKey KeycodeMap[KEYCODE_MAP_SIZE] = {
KEY_NULL, // AKEYCODE_UNKNOWN
0, // AKEYCODE_SOFT_LEFT
0, // AKEYCODE_SOFT_RIGHT
0, // AKEYCODE_HOME
KEY_BACK, // AKEYCODE_BACK
0, // AKEYCODE_CALL
0, // AKEYCODE_ENDCALL
KEY_ZERO, // AKEYCODE_0
KEY_ONE, // AKEYCODE_1
KEY_TWO, // AKEYCODE_2
KEY_THREE, // AKEYCODE_3
KEY_FOUR, // AKEYCODE_4
KEY_FIVE, // AKEYCODE_5
KEY_SIX, // AKEYCODE_6
KEY_SEVEN, // AKEYCODE_7
KEY_EIGHT, // AKEYCODE_8
KEY_NINE, // AKEYCODE_9
0, // AKEYCODE_STAR
0, // AKEYCODE_POUND
KEY_UP, // AKEYCODE_DPAD_UP
KEY_DOWN, // AKEYCODE_DPAD_DOWN
KEY_LEFT, // AKEYCODE_DPAD_LEFT
KEY_RIGHT, // AKEYCODE_DPAD_RIGHT
0, // AKEYCODE_DPAD_CENTER
KEY_VOLUME_UP, // AKEYCODE_VOLUME_UP
KEY_VOLUME_DOWN, // AKEYCODE_VOLUME_DOWN
0, // AKEYCODE_POWER
0, // AKEYCODE_CAMERA
0, // AKEYCODE_CLEAR
KEY_A, // AKEYCODE_A
KEY_B, // AKEYCODE_B
KEY_C, // AKEYCODE_C
KEY_D, // AKEYCODE_D
KEY_E, // AKEYCODE_E
KEY_F, // AKEYCODE_F
KEY_G, // AKEYCODE_G
KEY_H, // AKEYCODE_H
KEY_I, // AKEYCODE_I
KEY_J, // AKEYCODE_J
KEY_K, // AKEYCODE_K
KEY_L, // AKEYCODE_L
KEY_M, // AKEYCODE_M
KEY_N, // AKEYCODE_N
KEY_O, // AKEYCODE_O
KEY_P, // AKEYCODE_P
KEY_Q, // AKEYCODE_Q
KEY_R, // AKEYCODE_R
KEY_S, // AKEYCODE_S
KEY_T, // AKEYCODE_T
KEY_U, // AKEYCODE_U
KEY_V, // AKEYCODE_V
KEY_W, // AKEYCODE_W
KEY_X, // AKEYCODE_X
KEY_Y, // AKEYCODE_Y
KEY_Z, // AKEYCODE_Z
KEY_COMMA, // AKEYCODE_COMMA
KEY_PERIOD, // AKEYCODE_PERIOD
KEY_LEFT_ALT, // AKEYCODE_ALT_LEFT
KEY_RIGHT_ALT, // AKEYCODE_ALT_RIGHT
KEY_LEFT_SHIFT, // AKEYCODE_SHIFT_LEFT
KEY_RIGHT_SHIFT, // AKEYCODE_SHIFT_RIGHT
KEY_TAB, // AKEYCODE_TAB
KEY_SPACE, // AKEYCODE_SPACE
0, // AKEYCODE_SYM
0, // AKEYCODE_EXPLORER
0, // AKEYCODE_ENVELOPE
KEY_ENTER, // AKEYCODE_ENTER
KEY_BACKSPACE, // AKEYCODE_DEL
KEY_GRAVE, // AKEYCODE_GRAVE
KEY_MINUS, // AKEYCODE_MINUS
KEY_EQUAL, // AKEYCODE_EQUALS
KEY_LEFT_BRACKET, // AKEYCODE_LEFT_BRACKET
KEY_RIGHT_BRACKET, // AKEYCODE_RIGHT_BRACKET
KEY_BACKSLASH, // AKEYCODE_BACKSLASH
KEY_SEMICOLON, // AKEYCODE_SEMICOLON
KEY_APOSTROPHE, // AKEYCODE_APOSTROPHE
KEY_SLASH, // AKEYCODE_SLASH
0, // AKEYCODE_AT
0, // AKEYCODE_NUM
0, // AKEYCODE_HEADSETHOOK
0, // AKEYCODE_FOCUS
0, // AKEYCODE_PLUS
KEY_MENU, // AKEYCODE_MENU
0, // AKEYCODE_NOTIFICATION
0, // AKEYCODE_SEARCH
0, // AKEYCODE_MEDIA_PLAY_PAUSE
0, // AKEYCODE_MEDIA_STOP
0, // AKEYCODE_MEDIA_NEXT
0, // AKEYCODE_MEDIA_PREVIOUS
0, // AKEYCODE_MEDIA_REWIND
0, // AKEYCODE_MEDIA_FAST_FORWARD
0, // AKEYCODE_MUTE
KEY_PAGE_UP, // AKEYCODE_PAGE_UP
KEY_PAGE_DOWN, // AKEYCODE_PAGE_DOWN
0, // AKEYCODE_PICTSYMBOLS
0, // AKEYCODE_SWITCH_CHARSET
0, // AKEYCODE_BUTTON_A
0, // AKEYCODE_BUTTON_B
0, // AKEYCODE_BUTTON_C
0, // AKEYCODE_BUTTON_X
0, // AKEYCODE_BUTTON_Y
0, // AKEYCODE_BUTTON_Z
0, // AKEYCODE_BUTTON_L1
0, // AKEYCODE_BUTTON_R1
0, // AKEYCODE_BUTTON_L2
0, // AKEYCODE_BUTTON_R2
0, // AKEYCODE_BUTTON_THUMBL
0, // AKEYCODE_BUTTON_THUMBR
0, // AKEYCODE_BUTTON_START
0, // AKEYCODE_BUTTON_SELECT
0, // AKEYCODE_BUTTON_MODE
KEY_ESCAPE, // AKEYCODE_ESCAPE
KEY_DELETE, // AKEYCODE_FORWARD_DELL
KEY_LEFT_CONTROL, // AKEYCODE_CTRL_LEFT
KEY_RIGHT_CONTROL, // AKEYCODE_CTRL_RIGHT
KEY_CAPS_LOCK, // AKEYCODE_CAPS_LOCK
KEY_SCROLL_LOCK, // AKEYCODE_SCROLL_LOCK
KEY_LEFT_SUPER, // AKEYCODE_META_LEFT
KEY_RIGHT_SUPER, // AKEYCODE_META_RIGHT
0, // AKEYCODE_FUNCTION
KEY_PRINT_SCREEN, // AKEYCODE_SYSRQ
KEY_PAUSE, // AKEYCODE_BREAK
KEY_HOME, // AKEYCODE_MOVE_HOME
KEY_END, // AKEYCODE_MOVE_END
KEY_INSERT, // AKEYCODE_INSERT
0, // AKEYCODE_FORWARD
0, // AKEYCODE_MEDIA_PLAY
0, // AKEYCODE_MEDIA_PAUSE
0, // AKEYCODE_MEDIA_CLOSE
0, // AKEYCODE_MEDIA_EJECT
0, // AKEYCODE_MEDIA_RECORD
KEY_F1, // AKEYCODE_F1
KEY_F2, // AKEYCODE_F2
KEY_F3, // AKEYCODE_F3
KEY_F4, // AKEYCODE_F4
KEY_F5, // AKEYCODE_F5
KEY_F6, // AKEYCODE_F6
KEY_F7, // AKEYCODE_F7
KEY_F8, // AKEYCODE_F8
KEY_F9, // AKEYCODE_F9
KEY_F10, // AKEYCODE_F10
KEY_F11, // AKEYCODE_F11
KEY_F12, // AKEYCODE_F12
KEY_NUM_LOCK, // AKEYCODE_NUM_LOCK
KEY_KP_0, // AKEYCODE_NUMPAD_0
KEY_KP_1, // AKEYCODE_NUMPAD_1
KEY_KP_2, // AKEYCODE_NUMPAD_2
KEY_KP_3, // AKEYCODE_NUMPAD_3
KEY_KP_4, // AKEYCODE_NUMPAD_4
KEY_KP_5, // AKEYCODE_NUMPAD_5
KEY_KP_6, // AKEYCODE_NUMPAD_6
KEY_KP_7, // AKEYCODE_NUMPAD_7
KEY_KP_8, // AKEYCODE_NUMPAD_8
KEY_KP_9, // AKEYCODE_NUMPAD_9
KEY_KP_DIVIDE, // AKEYCODE_NUMPAD_DIVIDE
KEY_KP_MULTIPLY, // AKEYCODE_NUMPAD_MULTIPLY
KEY_KP_SUBTRACT, // AKEYCODE_NUMPAD_SUBTRACT
KEY_KP_ADD, // AKEYCODE_NUMPAD_ADD
KEY_KP_DECIMAL, // AKEYCODE_NUMPAD_DOT
0, // AKEYCODE_NUMPAD_COMMA
KEY_KP_ENTER, // AKEYCODE_NUMPAD_ENTER
KEY_KP_EQUAL // AKEYCODE_NUMPAD_EQUALS
};
//----------------------------------------------------------------------------------
// Module Internal Functions Declaration
//----------------------------------------------------------------------------------
@ -446,6 +615,12 @@ int SetGamepadMappings(const char *mappings)
return 0;
}
// Set gamepad vibration
void SetGamepadVibration(int gamepad, float leftMotor, float rightMotor)
{
TRACELOG(LOG_WARNING, "GamepadSetVibration() not implemented on target platform");
}
// Set mouse position XY
void SetMousePosition(int x, int y)
{
@ -478,6 +653,16 @@ void PollInputEvents(void)
CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN
//CORE.Input.Gamepad.axisCount = 0;
for (int i = 0; i < MAX_GAMEPADS; i++)
{
if (CORE.Input.Gamepad.ready[i]) // Check if gamepad is available
{
// Register previous gamepad states
for (int k = 0; k < MAX_GAMEPAD_BUTTONS; k++)
CORE.Input.Gamepad.previousButtonState[i][k] = CORE.Input.Gamepad.currentButtonState[i][k];
}
}
// Register previous touch states
for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.previousTouchState[i] = CORE.Input.Touch.currentTouchState[i];
@ -1018,17 +1203,21 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event)
return 1; // Handled gamepad button
}
// Save current button and its state
KeyboardKey key = (keycode > 0 && keycode < KEYCODE_MAP_SIZE) ? KeycodeMap[keycode] : KEY_NULL;
if (key != KEY_NULL)
{
// Save current key and its state
// NOTE: Android key action is 0 for down and 1 for up
if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_DOWN)
{
CORE.Input.Keyboard.currentKeyState[keycode] = 1; // Key down
CORE.Input.Keyboard.currentKeyState[key] = 1; // Key down
CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = keycode;
CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = key;
CORE.Input.Keyboard.keyPressedQueueCount++;
}
else if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_MULTIPLE) CORE.Input.Keyboard.keyRepeatInFrame[keycode] = 1;
else CORE.Input.Keyboard.currentKeyState[keycode] = 0; // Key up
else if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_MULTIPLE) CORE.Input.Keyboard.keyRepeatInFrame[key] = 1;
else CORE.Input.Keyboard.currentKeyState[key] = 0; // Key up
}
if (keycode == AKEYCODE_POWER)
{

View file

@ -30,7 +30,7 @@
*
* LICENSE: zlib/libpng
*
* Copyright (c) 2013-2023 Ramon Santamaria (@raysan5) and contributors
* Copyright (c) 2013-2024 Ramon Santamaria (@raysan5) and contributors
*
* 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.
@ -74,7 +74,6 @@
//#define GLFW_EXPOSE_NATIVE_X11 // WARNING: Exposing Xlib.h > X.h results in dup symbols for Font type
//#define GLFW_EXPOSE_NATIVE_WAYLAND
//#define GLFW_EXPOSE_NATIVE_MIR
#include "GLFW/glfw3native.h" // Required for: glfwGetX11Window()
#endif
#if defined(__APPLE__)
@ -85,15 +84,6 @@
#include "GLFW/glfw3native.h" // Required for: glfwGetCocoaWindow()
#endif
//----------------------------------------------------------------------------------
// Defines and Macros
//----------------------------------------------------------------------------------
// TODO: HACK: Added flag if not provided by GLFW when using external library
// Latest GLFW release (GLFW 3.3.8) does not implement this flag, it was added for 3.4.0-dev
#if !defined(GLFW_MOUSE_PASSTHROUGH)
#define GLFW_MOUSE_PASSTHROUGH 0x0002000D
#endif
//----------------------------------------------------------------------------------
// Types and Structures Definition
//----------------------------------------------------------------------------------
@ -123,13 +113,14 @@ static void WindowIconifyCallback(GLFWwindow *window, int iconified);
static void WindowMaximizeCallback(GLFWwindow* window, int maximized); // GLFW3 Window Maximize Callback, runs when window is maximized
static void WindowFocusCallback(GLFWwindow *window, int focused); // GLFW3 WindowFocus Callback, runs when window get/lose focus
static void WindowDropCallback(GLFWwindow *window, int count, const char **paths); // GLFW3 Window Drop Callback, runs when drop files into window
static void WindowContentScaleCallback(GLFWwindow *window, float scalex, float scaley); // GLFW3 Window Content Scale Callback, runs when window changes scale
// Input callbacks events
static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods); // GLFW3 Keyboard Callback, runs on key pressed
static void CharCallback(GLFWwindow *window, unsigned int key); // GLFW3 Char Key Callback, runs on key pressed (get char value)
static void CharCallback(GLFWwindow *window, unsigned int codepoint); // GLFW3 Char Callback, runs on key pressed (get codepoint value)
static void MouseButtonCallback(GLFWwindow *window, int button, int action, int mods); // GLFW3 Mouse Button Callback, runs on mouse button pressed
static void MouseCursorPosCallback(GLFWwindow *window, double x, double y); // GLFW3 Cursor Position Callback, runs on mouse move
static void MouseScrollCallback(GLFWwindow *window, double xoffset, double yoffset); // GLFW3 Srolling Callback, runs on mouse wheel
static void MouseScrollCallback(GLFWwindow *window, double xoffset, double yoffset); // GLFW3 Scrolling Callback, runs on mouse wheel
static void CursorEnterCallback(GLFWwindow *window, int enter); // GLFW3 Cursor Enter Callback, cursor enters client area
static void JoystickCallback(int jid, int event); // GLFW3 Joystick Connected/Disconnected Callback
@ -941,31 +932,8 @@ Vector2 GetWindowPosition(void)
// Get window scale DPI factor for current monitor
Vector2 GetWindowScaleDPI(void)
{
float xdpi = 1.0;
float ydpi = 1.0;
Vector2 scale = { 1.0f, 1.0f };
Vector2 windowPos = GetWindowPosition();
int monitorCount = 0;
GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
// Check window monitor
for (int i = 0; i < monitorCount; i++)
{
glfwGetMonitorContentScale(monitors[i], &xdpi, &ydpi);
int xpos, ypos, width, height;
glfwGetMonitorWorkarea(monitors[i], &xpos, &ypos, &width, &height);
if ((windowPos.x >= xpos) && (windowPos.x < xpos + width) &&
(windowPos.y >= ypos) && (windowPos.y < ypos + height))
{
scale.x = xdpi;
scale.y = ydpi;
break;
}
}
Vector2 scale = {0};
glfwGetWindowContentScale(platform.handle, &scale.x, &scale.y);
return scale;
}
@ -1004,6 +972,8 @@ void EnableCursor(void)
// Set cursor position in the middle
SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2);
if (glfwRawMouseMotionSupported()) glfwSetInputMode(platform.handle, GLFW_RAW_MOUSE_MOTION, GLFW_FALSE);
CORE.Input.Mouse.cursorHidden = false;
}
@ -1015,6 +985,8 @@ void DisableCursor(void)
// Set cursor position in the middle
SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2);
if (glfwRawMouseMotionSupported()) glfwSetInputMode(platform.handle, GLFW_RAW_MOUSE_MOTION, GLFW_TRUE);
CORE.Input.Mouse.cursorHidden = true;
}
@ -1072,6 +1044,12 @@ int SetGamepadMappings(const char *mappings)
return glfwUpdateGamepadMappings(mappings);
}
// Set gamepad vibration
void SetGamepadVibration(int gamepad, float leftMotor, float rightMotor)
{
TRACELOG(LOG_WARNING, "GamepadSetVibration() not available on target platform");
}
// Set mouse position XY
void SetMousePosition(int x, int y)
{
@ -1137,7 +1115,7 @@ void PollInputEvents(void)
//for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.position[i] = (Vector2){ 0, 0 };
// Map touch position to mouse position for convenience
// WARNING: If the target desktop device supports touch screen, this behavious should be reviewed!
// WARNING: If the target desktop device supports touch screen, this behaviour should be reviewed!
// TODO: GLFW does not support multi-touch input just yet
// https://www.codeproject.com/Articles/668404/Programming-for-Multi-Touch
// https://docs.microsoft.com/en-us/windows/win32/wintouch/getting-started-with-multi-touch-messages
@ -1240,6 +1218,19 @@ void PollInputEvents(void)
// Module Internal Functions Definition
//----------------------------------------------------------------------------------
static void SetDimensionsFromMonitor(GLFWmonitor *monitor)
{
const GLFWvidmode *mode = glfwGetVideoMode(monitor);
// Default display resolution to that of the current mode
CORE.Window.display.width = mode->width;
CORE.Window.display.height = mode->height;
// Set screen width/height to the display width/height if they are 0
if (CORE.Window.screen.width == 0) CORE.Window.screen.width = CORE.Window.display.width;
if (CORE.Window.screen.height == 0) CORE.Window.screen.height = CORE.Window.display.height;
}
// Initialize platform: graphics, inputs and more
int InitPlatform(void)
{
@ -1380,26 +1371,22 @@ int InitPlatform(void)
// REF: https://github.com/raysan5/raylib/issues/1554
glfwSetJoystickCallback(NULL);
// Find monitor resolution
GLFWmonitor *monitor = glfwGetPrimaryMonitor();
GLFWmonitor *monitor = NULL;
if (CORE.Window.fullscreen)
{
// According to glfwCreateWindow(), if the user does not have a choice, fullscreen applications
// should default to the primary monitor.
monitor = glfwGetPrimaryMonitor();
if (!monitor)
{
TRACELOG(LOG_WARNING, "GLFW: Failed to get primary monitor");
return -1;
}
const GLFWvidmode *mode = glfwGetVideoMode(monitor);
SetDimensionsFromMonitor(monitor);
CORE.Window.display.width = mode->width;
CORE.Window.display.height = mode->height;
// Set screen width/height to the display width/height if they are 0
if (CORE.Window.screen.width == 0) CORE.Window.screen.width = CORE.Window.display.width;
if (CORE.Window.screen.height == 0) CORE.Window.screen.height = CORE.Window.display.height;
if (CORE.Window.fullscreen)
{
// remember center for switchinging from fullscreen to window
// Remember center for switching from fullscreen to window
if ((CORE.Window.screen.height == CORE.Window.display.height) && (CORE.Window.screen.width == CORE.Window.display.width))
{
// If screen width/height equal to the display, we can't calculate the window pos for toggling full-screened/windowed.
@ -1418,7 +1405,7 @@ int InitPlatform(void)
// Obtain recommended CORE.Window.display.width/CORE.Window.display.height from a valid videomode for the monitor
int count = 0;
const GLFWvidmode *modes = glfwGetVideoModes(glfwGetPrimaryMonitor(), &count);
const GLFWvidmode *modes = glfwGetVideoModes(monitor, &count);
// Get closest video mode to desired CORE.Window.screen.width/CORE.Window.screen.height
for (int i = 0; i < count; i++)
@ -1448,21 +1435,55 @@ int InitPlatform(void)
// HighDPI monitors are properly considered in a following similar function: SetupViewport()
SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height);
platform.handle = glfwCreateWindow(CORE.Window.display.width, CORE.Window.display.height, (CORE.Window.title != 0)? CORE.Window.title : " ", glfwGetPrimaryMonitor(), NULL);
platform.handle = glfwCreateWindow(CORE.Window.display.width, CORE.Window.display.height, (CORE.Window.title != 0)? CORE.Window.title : " ", monitor, NULL);
// NOTE: Full-screen change, not working properly...
//glfwSetWindowMonitor(platform.handle, glfwGetPrimaryMonitor(), 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE);
}
else
{
// If we are windowed fullscreen, ensures that window does not minimize when focus is lost
if ((CORE.Window.screen.height == CORE.Window.display.height) && (CORE.Window.screen.width == CORE.Window.display.width))
// No-fullscreen window creation
bool wantWindowedFullscreen = (CORE.Window.screen.height == 0) && (CORE.Window.screen.width == 0);
// If we are windowed fullscreen, ensures that window does not minimize when focus is lost.
// This hinting code will not work if the user already specified the correct monitor dimensions;
// at this point we don't know the monitor's dimensions. (Though, how did the user then?)
if (wantWindowedFullscreen)
{
glfwWindowHint(GLFW_AUTO_ICONIFY, 0);
}
// No-fullscreen window creation
platform.handle = glfwCreateWindow(CORE.Window.screen.width, CORE.Window.screen.height, (CORE.Window.title != 0)? CORE.Window.title : " ", NULL, NULL);
// Default to at least one pixel in size, as creation with a zero dimension is not allowed.
int creationWidth = CORE.Window.screen.width != 0 ? CORE.Window.screen.width : 1;
int creationHeight = CORE.Window.screen.height != 0 ? CORE.Window.screen.height : 1;
platform.handle = glfwCreateWindow(creationWidth, creationHeight, (CORE.Window.title != 0)? CORE.Window.title : " ", NULL, NULL);
// After the window was created, determine the monitor that the window manager assigned.
// Derive display sizes, and, if possible, window size in case it was zero at beginning.
int monitorCount = 0;
int monitorIndex = GetCurrentMonitor();
GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
if (monitorIndex < monitorCount)
{
monitor = monitors[monitorIndex];
SetDimensionsFromMonitor(monitor);
TRACELOG(LOG_INFO, "wantWindowed: %d, size: %dx%d", wantWindowedFullscreen, CORE.Window.screen.width, CORE.Window.screen.height);
if (wantWindowedFullscreen)
{
glfwSetWindowSize(platform.handle, CORE.Window.screen.width, CORE.Window.screen.height);
}
}
else
{
// The monitor for the window-manager-created window can not be determined, so it can not be centered.
glfwTerminate();
TRACELOG(LOG_WARNING, "GLFW: Failed to determine Monitor to center Window");
return -1;
}
if (platform.handle)
{
@ -1537,7 +1558,21 @@ int InitPlatform(void)
// If graphic device is no properly initialized, we end program
if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return -1; }
else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor())/2 - CORE.Window.screen.width/2, GetMonitorHeight(GetCurrentMonitor())/2 - CORE.Window.screen.height/2);
else
{
// Try to center window on screen but avoiding window-bar outside of screen
int monitorX = 0;
int monitorY = 0;
int monitorWidth = 0;
int monitorHeight = 0;
glfwGetMonitorWorkarea(monitor, &monitorX, &monitorY, &monitorWidth, &monitorHeight);
int posX = monitorX + (monitorWidth - (int)CORE.Window.screen.width)/2;
int posY = monitorY + (monitorHeight - (int)CORE.Window.screen.height)/2;
if (posX < monitorX) posX = monitorX;
if (posY < monitorY) posY = monitorY;
SetWindowPosition(posX, posY);
}
// Load OpenGL extensions
// NOTE: GL procedures address loader is required to load extensions
@ -1553,6 +1588,11 @@ int InitPlatform(void)
glfwSetWindowFocusCallback(platform.handle, WindowFocusCallback);
glfwSetDropCallback(platform.handle, WindowDropCallback);
if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0)
{
glfwSetWindowContentScaleCallback(platform.handle, WindowContentScaleCallback);
}
// Set input callback events
glfwSetKeyCallback(platform.handle, KeyCallback);
glfwSetCharCallback(platform.handle, CharCallback);
@ -1581,6 +1621,17 @@ int InitPlatform(void)
CORE.Storage.basePath = GetWorkingDirectory();
//----------------------------------------------------------------------------
char* glfwPlatform = "";
switch (glfwGetPlatform())
{
case GLFW_PLATFORM_WIN32: glfwPlatform = "Win32"; break;
case GLFW_PLATFORM_COCOA: glfwPlatform = "Cocoa"; break;
case GLFW_PLATFORM_WAYLAND: glfwPlatform = "Wayland"; break;
case GLFW_PLATFORM_X11: glfwPlatform = "X11"; break;
case GLFW_PLATFORM_NULL: glfwPlatform = "Null"; break;
}
TRACELOG(LOG_INFO, "GLFW platform: %s", glfwPlatform);
TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (GLFW): Initialized successfully");
return 0;
@ -1638,6 +1689,11 @@ static void WindowSizeCallback(GLFWwindow *window, int width, int height)
// NOTE: Postprocessing texture is not scaled to new size
}
static void WindowContentScaleCallback(GLFWwindow *window, float scalex, float scaley)
{
CORE.Window.screenScale = MatrixScale(scalex, scaley, 1.0f);
}
// GLFW3 WindowIconify Callback, runs when window is minimized/restored
static void WindowIconifyCallback(GLFWwindow *window, int iconified)
{
@ -1714,10 +1770,10 @@ static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, i
if ((key == CORE.Input.Keyboard.exitKey) && (action == GLFW_PRESS)) glfwSetWindowShouldClose(platform.handle, GLFW_TRUE);
}
// GLFW3 Char Key Callback, runs on key down (gets equivalent unicode char value)
static void CharCallback(GLFWwindow *window, unsigned int key)
// GLFW3 Char Callback, get unicode codepoint value
static void CharCallback(GLFWwindow *window, unsigned int codepoint)
{
//TRACELOG(LOG_DEBUG, "Char Callback: KEY:%i(%c)", key, key);
//TRACELOG(LOG_DEBUG, "Char Callback: Codepoint: %i", codepoint);
// NOTE: Registers any key down considering OS keyboard layout but
// does not detect action events, those should be managed by user...
@ -1728,7 +1784,7 @@ static void CharCallback(GLFWwindow *window, unsigned int key)
if (CORE.Input.Keyboard.charPressedQueueCount < MAX_CHAR_PRESSED_QUEUE)
{
// Add character to the queue
CORE.Input.Keyboard.charPressedQueue[CORE.Input.Keyboard.charPressedQueueCount] = key;
CORE.Input.Keyboard.charPressedQueue[CORE.Input.Keyboard.charPressedQueueCount] = codepoint;
CORE.Input.Keyboard.charPressedQueueCount++;
}
}
@ -1739,6 +1795,7 @@ static void MouseButtonCallback(GLFWwindow *window, int button, int action, int
// WARNING: GLFW could only return GLFW_PRESS (1) or GLFW_RELEASE (0) for now,
// but future releases may add more actions (i.e. GLFW_REPEAT)
CORE.Input.Mouse.currentButtonState[button] = action;
CORE.Input.Touch.currentTouchState[button] = action;
#if defined(SUPPORT_GESTURES_SYSTEM) && defined(SUPPORT_MOUSE_GESTURES)
// Process mouse events as touches to be able to use mouse-gestures

View file

@ -29,7 +29,7 @@
*
* LICENSE: zlib/libpng
*
* Copyright (c) 2013-2023 Ramon Santamaria (@raysan5) and contributors
* Copyright (c) 2013-2024 Ramon Santamaria (@raysan5) and contributors
*
* 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.
@ -48,8 +48,14 @@
*
**********************************************************************************************/
#include "SDL.h" // SDL base library (window/rendered, input, timming... functionality)
#include "SDL.h" // SDL base library (window/rendered, input, timing... functionality)
#if defined(GRAPHICS_API_OPENGL_ES2)
// It seems it does not need to be included to work
//#include "SDL_opengles2.h"
#else
#include "SDL_opengl.h" // SDL OpenGL functionality (if required, instead of internal renderer)
#endif
//----------------------------------------------------------------------------------
// Types and Structures Definition
@ -58,7 +64,7 @@ typedef struct {
SDL_Window *window;
SDL_GLContext glContext;
SDL_Joystick *gamepad;
SDL_Joystick *gamepad[MAX_GAMEPADS];
SDL_Cursor *cursor;
bool cursorRelative;
} PlatformData;
@ -493,9 +499,9 @@ void SetWindowIcon(Image image)
bmask = 0x001F, amask = 0;
depth = 16, pitch = image.width * 2;
break;
case PIXELFORMAT_UNCOMPRESSED_R8G8B8:
rmask = 0xFF0000, gmask = 0x00FF00;
bmask = 0x0000FF, amask = 0;
case PIXELFORMAT_UNCOMPRESSED_R8G8B8: // Uses BGR for 24-bit
rmask = 0x0000FF, gmask = 0x00FF00;
bmask = 0xFF0000, amask = 0;
depth = 24, pitch = image.width * 3;
break;
case PIXELFORMAT_UNCOMPRESSED_R5G5B5A1:
@ -592,7 +598,7 @@ void SetWindowMonitor(int monitor)
// NOTE:
// 1. SDL started supporting moving exclusive fullscreen windows between displays on SDL3,
// see commit https://github.com/libsdl-org/SDL/commit/3f5ef7dd422057edbcf3e736107e34be4b75d9ba
// 2. A workround for SDL2 is leaving fullscreen, moving the window, then entering full screen again.
// 2. A workaround for SDL2 is leaving fullscreen, moving the window, then entering full screen again.
const bool wasFullscreen = ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0) ? true : false;
const int screenWidth = CORE.Window.screen.width;
@ -611,7 +617,7 @@ void SetWindowMonitor(int monitor)
// ending up positioned partly outside the target display.
// 2. The workaround for that is, previously to moving the window,
// setting the window size to the target display size, so they match.
// 3. It was't done here because we can't assume changing the window size automatically
// 3. It wasn't done here because we can't assume changing the window size automatically
// is acceptable behavior by the user.
SDL_SetWindowPosition(platform.window, usableBounds.x, usableBounds.y);
CORE.Window.position.x = usableBounds.x;
@ -932,9 +938,26 @@ int SetGamepadMappings(const char *mappings)
return SDL_GameControllerAddMapping(mappings);
}
// Set gamepad vibration
void SetGamepadVibration(int gamepad, float leftMotor, float rightMotor)
{
//Limit input values to between 0.0f and 1.0f
leftMotor = (0.0f > leftMotor) ? 0.0f : leftMotor;
rightMotor = (0.0f > rightMotor) ? 0.0f : rightMotor;
leftMotor = (1.0f < leftMotor) ? 1.0f : leftMotor;
rightMotor = (1.0f < rightMotor) ? 1.0f : rightMotor;
if (IsGamepadAvailable(gamepad))
{
SDL_JoystickRumble(platform.gamepad[gamepad], (Uint16)(leftMotor*65535.0f), (Uint16)(rightMotor*65535.0f), (Uint32)(MAX_GAMEPAD_VIBRATION_TIME*1000.0f));
}
}
// Set mouse position XY
void SetMousePosition(int x, int y)
{
SDL_WarpMouseInWindow(platform.window, x, y);
CORE.Input.Mouse.currentPosition = (Vector2){ (float)x, (float)y };
CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
}
@ -948,6 +971,22 @@ void SetMouseCursor(int cursor)
CORE.Input.Mouse.cursor = cursor;
}
static void UpdateTouchPointsSDL(SDL_TouchFingerEvent event)
{
CORE.Input.Touch.pointCount = SDL_GetNumTouchFingers(event.touchId);
for (int i = 0; i < CORE.Input.Touch.pointCount; i++)
{
SDL_Finger *finger = SDL_GetTouchFinger(event.touchId, i);
CORE.Input.Touch.pointId[i] = finger->id;
CORE.Input.Touch.position[i].x = finger->x*CORE.Window.screen.width;
CORE.Input.Touch.position[i].y = finger->y*CORE.Window.screen.height;
CORE.Input.Touch.currentTouchState[i] = 1;
}
for (int i = CORE.Input.Touch.pointCount; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.currentTouchState[i] = 0;
}
// Register all input events
void PollInputEvents(void)
{
@ -961,9 +1000,6 @@ void PollInputEvents(void)
CORE.Input.Keyboard.keyPressedQueueCount = 0;
CORE.Input.Keyboard.charPressedQueueCount = 0;
// Reset key repeats
for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) CORE.Input.Keyboard.keyRepeatInFrame[i] = 0;
// Reset mouse wheel
CORE.Input.Mouse.currentWheelMove.x = 0;
CORE.Input.Mouse.currentWheelMove.y = 0;
@ -973,25 +1009,27 @@ void PollInputEvents(void)
else CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
// Reset last gamepad button/axis registered state
CORE.Input.Gamepad.lastButtonPressed = GAMEPAD_BUTTON_UNKNOWN;
for (int i = 0; i < MAX_GAMEPADS; i++) CORE.Input.Gamepad.axisCount[i] = 0;
for (int i = 0; (i < SDL_NumJoysticks()) && (i < MAX_GAMEPADS); i++)
{
// Check if gamepad is available
if (CORE.Input.Gamepad.ready[i])
{
// Register previous gamepad button states
for (int k = 0; k < MAX_GAMEPAD_BUTTONS; k++)
{
CORE.Input.Gamepad.previousButtonState[i][k] = CORE.Input.Gamepad.currentButtonState[i][k];
}
}
}
// Register previous touch states
for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.previousTouchState[i] = CORE.Input.Touch.currentTouchState[i];
// Reset touch positions
// TODO: It resets on target platform the mouse position and not filled again until a move-event,
// so, if mouse is not moved it returns a (0, 0) position... this behaviour should be reviewed!
//for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.position[i] = (Vector2){ 0, 0 };
// Map touch position to mouse position for convenience
// WARNING: If the target desktop device supports touch screen, this behavious should be reviewed!
// https://www.codeproject.com/Articles/668404/Programming-for-Multi-Touch
// https://docs.microsoft.com/en-us/windows/win32/wintouch/getting-started-with-multi-touch-messages
CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition;
int touchAction = -1; // 0-TOUCH_ACTION_UP, 1-TOUCH_ACTION_DOWN, 2-TOUCH_ACTION_MOVE
bool gestureUpdate = false; // Flag to note gestures require to update
bool realTouch = false; // Flag to differentiate real touch gestures from mouse ones
// Register previous keys states
// NOTE: Android supports up to 260 keys
@ -1004,7 +1042,7 @@ void PollInputEvents(void)
// Register previous mouse states
for (int i = 0; i < MAX_MOUSE_BUTTONS; i++) CORE.Input.Mouse.previousButtonState[i] = CORE.Input.Mouse.currentButtonState[i];
// Poll input events for current plaform
// Poll input events for current platform
//-----------------------------------------------------------------------------
/*
// WARNING: Indexes into this array are obtained by using SDL_Scancode values, not SDL_Keycode values
@ -1070,11 +1108,17 @@ void PollInputEvents(void)
CORE.Window.currentFbo.height = height;
CORE.Window.resizedLastFrame = true;
} break;
case SDL_WINDOWEVENT_ENTER:
{
CORE.Input.Mouse.cursorOnScreen = true;
} break;
case SDL_WINDOWEVENT_LEAVE:
{
CORE.Input.Mouse.cursorOnScreen = false;
} break;
case SDL_WINDOWEVENT_HIDDEN:
case SDL_WINDOWEVENT_MINIMIZED:
case SDL_WINDOWEVENT_FOCUS_LOST:
case SDL_WINDOWEVENT_ENTER:
case SDL_WINDOWEVENT_SHOWN:
case SDL_WINDOWEVENT_FOCUS_GAINED:
case SDL_WINDOWEVENT_MAXIMIZED:
@ -1087,7 +1131,19 @@ void PollInputEvents(void)
case SDL_KEYDOWN:
{
KeyboardKey key = ConvertScancodeToKey(event.key.keysym.scancode);
if (key != KEY_NULL) CORE.Input.Keyboard.currentKeyState[key] = 1;
if (key != KEY_NULL) {
// If key was up, add it to the key pressed queue
if ((CORE.Input.Keyboard.currentKeyState[key] == 0) && (CORE.Input.Keyboard.keyPressedQueueCount < MAX_KEY_PRESSED_QUEUE))
{
CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = key;
CORE.Input.Keyboard.keyPressedQueueCount++;
}
CORE.Input.Keyboard.currentKeyState[key] = 1;
}
if (event.key.repeat) CORE.Input.Keyboard.keyRepeatInFrame[key] = 1;
// TODO: Put exitKey verification outside the switch?
if (CORE.Input.Keyboard.currentKeyState[CORE.Input.Keyboard.exitKey])
@ -1102,6 +1158,21 @@ void PollInputEvents(void)
if (key != KEY_NULL) CORE.Input.Keyboard.currentKeyState[key] = 0;
} break;
case SDL_TEXTINPUT:
{
// NOTE: event.text.text data comes an UTF-8 text sequence but we register codepoints (int)
int codepointSize = 0;
// Check if there is space available in the queue
if (CORE.Input.Keyboard.charPressedQueueCount < MAX_CHAR_PRESSED_QUEUE)
{
// Add character (codepoint) to the queue
CORE.Input.Keyboard.charPressedQueue[CORE.Input.Keyboard.charPressedQueueCount] = GetCodepointNext(event.text.text, &codepointSize);
CORE.Input.Keyboard.charPressedQueueCount++;
}
} break;
// Check mouse events
case SDL_MOUSEBUTTONDOWN:
{
@ -1112,9 +1183,9 @@ void PollInputEvents(void)
else if (btn == 1) btn = 2;
CORE.Input.Mouse.currentButtonState[btn] = 1;
CORE.Input.Touch.currentTouchState[btn] = 1;
touchAction = 1;
gestureUpdate = true;
} break;
case SDL_MOUSEBUTTONUP:
{
@ -1125,9 +1196,9 @@ void PollInputEvents(void)
else if (btn == 1) btn = 2;
CORE.Input.Mouse.currentButtonState[btn] = 0;
CORE.Input.Touch.currentTouchState[btn] = 0;
touchAction = 0;
gestureUpdate = true;
} break;
case SDL_MOUSEWHEEL:
{
@ -1150,24 +1221,160 @@ void PollInputEvents(void)
CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition;
touchAction = 2;
gestureUpdate = true;
} break;
case SDL_FINGERDOWN:
{
UpdateTouchPointsSDL(event.tfinger);
touchAction = 1;
realTouch = true;
} break;
case SDL_FINGERUP:
{
UpdateTouchPointsSDL(event.tfinger);
touchAction = 0;
realTouch = true;
} break;
case SDL_FINGERMOTION:
{
UpdateTouchPointsSDL(event.tfinger);
touchAction = 2;
realTouch = true;
} break;
// Check gamepad events
case SDL_JOYDEVICEADDED:
{
int jid = event.jdevice.which;
if (!CORE.Input.Gamepad.ready[jid] && (jid < MAX_GAMEPADS))
{
platform.gamepad[jid] = SDL_JoystickOpen(jid);
if (platform.gamepad[jid])
{
CORE.Input.Gamepad.ready[jid] = true;
CORE.Input.Gamepad.axisCount[jid] = SDL_JoystickNumAxes(platform.gamepad[jid]);
CORE.Input.Gamepad.axisState[jid][GAMEPAD_AXIS_LEFT_TRIGGER] = -1.0f;
CORE.Input.Gamepad.axisState[jid][GAMEPAD_AXIS_RIGHT_TRIGGER] = -1.0f;
strncpy(CORE.Input.Gamepad.name[jid], SDL_JoystickName(platform.gamepad[jid]), 63);
CORE.Input.Gamepad.name[jid][63] = '\0';
}
else
{
TRACELOG(LOG_WARNING, "PLATFORM: Unable to open game controller [ERROR: %s]", SDL_GetError());
}
}
} break;
case SDL_JOYDEVICEREMOVED:
{
int jid = event.jdevice.which;
if (jid == SDL_JoystickInstanceID(platform.gamepad[jid]))
{
SDL_JoystickClose(platform.gamepad[jid]);
platform.gamepad[jid] = SDL_JoystickOpen(0);
CORE.Input.Gamepad.ready[jid] = false;
memset(CORE.Input.Gamepad.name[jid], 0, 64);
}
} break;
case SDL_JOYBUTTONDOWN:
{
int button = -1;
switch (event.jbutton.button)
{
case SDL_CONTROLLER_BUTTON_Y: button = GAMEPAD_BUTTON_RIGHT_FACE_UP; break;
case SDL_CONTROLLER_BUTTON_B: button = GAMEPAD_BUTTON_RIGHT_FACE_RIGHT; break;
case SDL_CONTROLLER_BUTTON_A: button = GAMEPAD_BUTTON_RIGHT_FACE_DOWN; break;
case SDL_CONTROLLER_BUTTON_X: button = GAMEPAD_BUTTON_RIGHT_FACE_LEFT; break;
case SDL_CONTROLLER_BUTTON_LEFTSHOULDER: button = GAMEPAD_BUTTON_LEFT_TRIGGER_1; break;
case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: button = GAMEPAD_BUTTON_RIGHT_TRIGGER_1; break;
case SDL_CONTROLLER_BUTTON_BACK: button = GAMEPAD_BUTTON_MIDDLE_LEFT; break;
case SDL_CONTROLLER_BUTTON_GUIDE: button = GAMEPAD_BUTTON_MIDDLE; break;
case SDL_CONTROLLER_BUTTON_START: button = GAMEPAD_BUTTON_MIDDLE_RIGHT; break;
case SDL_CONTROLLER_BUTTON_DPAD_UP: button = GAMEPAD_BUTTON_LEFT_FACE_UP; break;
case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: button = GAMEPAD_BUTTON_LEFT_FACE_RIGHT; break;
case SDL_CONTROLLER_BUTTON_DPAD_DOWN: button = GAMEPAD_BUTTON_LEFT_FACE_DOWN; break;
case SDL_CONTROLLER_BUTTON_DPAD_LEFT: button = GAMEPAD_BUTTON_LEFT_FACE_LEFT; break;
case SDL_CONTROLLER_BUTTON_LEFTSTICK: button = GAMEPAD_BUTTON_LEFT_THUMB; break;
case SDL_CONTROLLER_BUTTON_RIGHTSTICK: button = GAMEPAD_BUTTON_RIGHT_THUMB; break;
default: break;
}
if (button >= 0)
{
CORE.Input.Gamepad.currentButtonState[event.jbutton.which][button] = 1;
CORE.Input.Gamepad.lastButtonPressed = button;
}
} break;
case SDL_JOYBUTTONUP:
{
int button = -1;
switch (event.jbutton.button)
{
case SDL_CONTROLLER_BUTTON_Y: button = GAMEPAD_BUTTON_RIGHT_FACE_UP; break;
case SDL_CONTROLLER_BUTTON_B: button = GAMEPAD_BUTTON_RIGHT_FACE_RIGHT; break;
case SDL_CONTROLLER_BUTTON_A: button = GAMEPAD_BUTTON_RIGHT_FACE_DOWN; break;
case SDL_CONTROLLER_BUTTON_X: button = GAMEPAD_BUTTON_RIGHT_FACE_LEFT; break;
case SDL_CONTROLLER_BUTTON_LEFTSHOULDER: button = GAMEPAD_BUTTON_LEFT_TRIGGER_1; break;
case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: button = GAMEPAD_BUTTON_RIGHT_TRIGGER_1; break;
case SDL_CONTROLLER_BUTTON_BACK: button = GAMEPAD_BUTTON_MIDDLE_LEFT; break;
case SDL_CONTROLLER_BUTTON_GUIDE: button = GAMEPAD_BUTTON_MIDDLE; break;
case SDL_CONTROLLER_BUTTON_START: button = GAMEPAD_BUTTON_MIDDLE_RIGHT; break;
case SDL_CONTROLLER_BUTTON_DPAD_UP: button = GAMEPAD_BUTTON_LEFT_FACE_UP; break;
case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: button = GAMEPAD_BUTTON_LEFT_FACE_RIGHT; break;
case SDL_CONTROLLER_BUTTON_DPAD_DOWN: button = GAMEPAD_BUTTON_LEFT_FACE_DOWN; break;
case SDL_CONTROLLER_BUTTON_DPAD_LEFT: button = GAMEPAD_BUTTON_LEFT_FACE_LEFT; break;
case SDL_CONTROLLER_BUTTON_LEFTSTICK: button = GAMEPAD_BUTTON_LEFT_THUMB; break;
case SDL_CONTROLLER_BUTTON_RIGHTSTICK: button = GAMEPAD_BUTTON_RIGHT_THUMB; break;
default: break;
}
if (button >= 0)
{
CORE.Input.Gamepad.currentButtonState[event.jbutton.which][button] = 0;
if (CORE.Input.Gamepad.lastButtonPressed == button) CORE.Input.Gamepad.lastButtonPressed = 0;
}
} break;
case SDL_JOYAXISMOTION:
{
// Motion on gamepad 0
if (event.jaxis.which == 0)
int axis = -1;
switch (event.jaxis.axis)
{
// X axis motion
if (event.jaxis.axis == 0)
{
//...
case SDL_CONTROLLER_AXIS_LEFTX: axis = GAMEPAD_AXIS_LEFT_X; break;
case SDL_CONTROLLER_AXIS_LEFTY: axis = GAMEPAD_AXIS_LEFT_Y; break;
case SDL_CONTROLLER_AXIS_RIGHTX: axis = GAMEPAD_AXIS_RIGHT_X; break;
case SDL_CONTROLLER_AXIS_RIGHTY: axis = GAMEPAD_AXIS_RIGHT_Y; break;
case SDL_CONTROLLER_AXIS_TRIGGERLEFT: axis = GAMEPAD_AXIS_LEFT_TRIGGER; break;
case SDL_CONTROLLER_AXIS_TRIGGERRIGHT: axis = GAMEPAD_AXIS_RIGHT_TRIGGER; break;
default: break;
}
// Y axis motion
else if (event.jaxis.axis == 1)
if (axis >= 0)
{
//...
// SDL axis value range is -32768 to 32767, we normalize it to RayLib's -1.0 to 1.0f range
float value = event.jaxis.value / (float) 32767;
CORE.Input.Gamepad.axisState[event.jaxis.which][axis] = value;
// Register button state for triggers in addition to their axes
if ((axis == GAMEPAD_AXIS_LEFT_TRIGGER) || (axis == GAMEPAD_AXIS_RIGHT_TRIGGER))
{
int button = (axis == GAMEPAD_AXIS_LEFT_TRIGGER) ? GAMEPAD_BUTTON_LEFT_TRIGGER_2 : GAMEPAD_BUTTON_RIGHT_TRIGGER_2;
int pressed = (value > 0.1f);
CORE.Input.Gamepad.currentButtonState[event.jaxis.which][button] = pressed;
if (pressed) CORE.Input.Gamepad.lastButtonPressed = button;
else if (CORE.Input.Gamepad.lastButtonPressed == button) CORE.Input.Gamepad.lastButtonPressed = 0;
}
}
} break;
@ -1175,7 +1382,7 @@ void PollInputEvents(void)
}
#if defined(SUPPORT_GESTURES_SYSTEM)
if (gestureUpdate)
if (touchAction > -1)
{
// Process mouse events as touches to be able to use mouse-gestures
GestureEvent gestureEvent = { 0 };
@ -1190,7 +1397,7 @@ void PollInputEvents(void)
gestureEvent.pointCount = 1;
// Register touch points position, only one point registered
if (touchAction == 2) gestureEvent.position[0] = CORE.Input.Touch.position[0];
if (touchAction == 2 || realTouch) gestureEvent.position[0] = CORE.Input.Touch.position[0];
else gestureEvent.position[0] = GetMousePosition();
// Normalize gestureEvent.position[0] for CORE.Window.screen.width and CORE.Window.screen.height
@ -1199,6 +1406,8 @@ void PollInputEvents(void)
// Gesture data is sent to gestures-system for processing
ProcessGestureEvent(gestureEvent);
touchAction = -1;
}
#endif
}
@ -1212,8 +1421,9 @@ void PollInputEvents(void)
// Initialize platform: graphics, inputs and more
int InitPlatform(void)
{
// Initialize SDL internal global state
int result = SDL_Init(SDL_INIT_EVERYTHING);
// Initialize SDL internal global state, only required systems
// NOTE: Not all systems need to be initialized, SDL_INIT_AUDIO is not required, managed by miniaudio
int result = SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_EVENTS | SDL_INIT_GAMECONTROLLER);
if (result < 0) { TRACELOG(LOG_WARNING, "SDL: Failed to initialize SDL"); return -1; }
// Initialize graphic device: display/window and graphic context
@ -1265,11 +1475,7 @@ int InitPlatform(void)
{
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
#if defined(__APPLE__)
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG); // OSX Requires forward compatibility
#else
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
#endif
}
else if (rlGetVersion() == RL_OPENGL_43)
{
@ -1310,7 +1516,7 @@ int InitPlatform(void)
// Init OpenGL context
platform.glContext = SDL_GL_CreateContext(platform.window);
// Check window and glContext have been initialized succesfully
// Check window and glContext have been initialized successfully
if ((platform.window != NULL) && (platform.glContext != NULL))
{
CORE.Window.ready = true;
@ -1345,19 +1551,38 @@ int InitPlatform(void)
// Initialize input events system
//----------------------------------------------------------------------------
if (SDL_NumJoysticks() >= 1)
// Initialize gamepads
for (int i = 0; (i < SDL_NumJoysticks()) && (i < MAX_GAMEPADS); i++)
{
platform.gamepad = SDL_JoystickOpen(0);
//if (platform.gamepadgamepad == NULL) TRACELOG(LOG_WARNING, "PLATFORM: Unable to open game controller [ERROR: %s]", SDL_GetError());
platform.gamepad[i] = SDL_JoystickOpen(i);
if (platform.gamepad[i])
{
CORE.Input.Gamepad.ready[i] = true;
CORE.Input.Gamepad.axisCount[i] = SDL_JoystickNumAxes(platform.gamepad[i]);
CORE.Input.Gamepad.axisState[i][GAMEPAD_AXIS_LEFT_TRIGGER] = -1.0f;
CORE.Input.Gamepad.axisState[i][GAMEPAD_AXIS_RIGHT_TRIGGER] = -1.0f;
strncpy(CORE.Input.Gamepad.name[i], SDL_JoystickName(platform.gamepad[i]), 63);
CORE.Input.Gamepad.name[i][63] = '\0';
}
else TRACELOG(LOG_WARNING, "PLATFORM: Unable to open game controller [ERROR: %s]", SDL_GetError());
}
// Disable mouse events being interpreted as touch events
// NOTE: This is wanted because there are SDL_FINGER* events available which provide unique data
// Due to the way PollInputEvents() and rgestures.h are currently implemented, setting this won't break SUPPORT_MOUSE_GESTURES
SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "0");
SDL_EventState(SDL_DROPFILE, SDL_ENABLE);
//----------------------------------------------------------------------------
// Initialize timming system
// Initialize timing system
//----------------------------------------------------------------------------
// NOTE: No need to call InitTimer(), let SDL manage it internally
CORE.Time.previous = GetTime(); // Get time as double
#if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP)
SDL_SetHint(SDL_HINT_TIMER_RESOLUTION, "1"); // SDL equivalent of timeBeginPeriod() and timeEndPeriod()
#endif
//----------------------------------------------------------------------------
// Initialize storage system

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
/**********************************************************************************************
*
* raylib v5.0 - A simple and easy-to-use library to enjoy videogames programming (www.raylib.com)
* raylib v5.1-dev - A simple and easy-to-use library to enjoy videogames programming (www.raylib.com)
*
* FEATURES:
* - NO external dependencies, all required libraries included with raylib
@ -57,7 +57,7 @@
* raylib is licensed under an unmodified zlib/libpng license, which is an OSI-certified,
* BSD-like license that allows static linking with closed source software:
*
* Copyright (c) 2013-2023 Ramon Santamaria (@raysan5)
* Copyright (c) 2013-2024 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.
@ -82,21 +82,26 @@
#include <stdarg.h> // Required for: va_list - Only used by TraceLogCallback
#define RAYLIB_VERSION_MAJOR 5
#define RAYLIB_VERSION_MINOR 0
#define RAYLIB_VERSION_MINOR 1
#define RAYLIB_VERSION_PATCH 0
#define RAYLIB_VERSION "5.0"
#define RAYLIB_VERSION "5.1-dev"
// Function specifiers in case library is build/used as a shared library (Windows)
// Function specifiers in case library is build/used as a shared library
// NOTE: Microsoft specifiers to tell compiler that symbols are imported/exported from a .dll
// NOTE: visibility("default") attribute makes symbols "visible" when compiled with -fvisibility=hidden
#if defined(_WIN32)
#if defined(BUILD_LIBTYPE_SHARED)
#if defined(__TINYC__)
#define __declspec(x) __attribute__((x))
#endif
#if defined(BUILD_LIBTYPE_SHARED)
#define RLAPI __declspec(dllexport) // We are building the library as a Win32 shared library (.dll)
#elif defined(USE_LIBTYPE_SHARED)
#define RLAPI __declspec(dllimport) // We are using the library as a Win32 shared library (.dll)
#endif
#else
#if defined(BUILD_LIBTYPE_SHARED)
#define RLAPI __attribute__((visibility("default"))) // We are building as a Unix shared library (.so/.dylib)
#endif
#endif
#ifndef RLAPI
@ -479,7 +484,6 @@ typedef struct VrDeviceInfo {
int vResolution; // Vertical resolution in pixels
float hScreenSize; // Horizontal size in meters
float vScreenSize; // Vertical size in meters
float vScreenCenter; // Screen center in meters
float eyeToScreenDistance; // Distance between eye and display in meters
float lensSeparationDistance; // Lens separation distance in meters
float interpupillaryDistance; // IPD (distance between pupils) in meters
@ -673,7 +677,7 @@ typedef enum {
KEY_KP_EQUAL = 336, // Key: Keypad =
// Android key buttons
KEY_BACK = 4, // Key: Android back button
KEY_MENU = 82, // Key: Android menu button
KEY_MENU = 5, // Key: Android menu button
KEY_VOLUME_UP = 24, // Key: Android volume up button
KEY_VOLUME_DOWN = 25 // Key: Android volume down button
} KeyboardKey;
@ -717,12 +721,12 @@ typedef enum {
GAMEPAD_BUTTON_LEFT_FACE_DOWN, // Gamepad left DPAD down button
GAMEPAD_BUTTON_LEFT_FACE_LEFT, // Gamepad left DPAD left button
GAMEPAD_BUTTON_RIGHT_FACE_UP, // Gamepad right button up (i.e. PS3: Triangle, Xbox: Y)
GAMEPAD_BUTTON_RIGHT_FACE_RIGHT, // Gamepad right button right (i.e. PS3: Square, Xbox: X)
GAMEPAD_BUTTON_RIGHT_FACE_RIGHT, // Gamepad right button right (i.e. PS3: Circle, Xbox: B)
GAMEPAD_BUTTON_RIGHT_FACE_DOWN, // Gamepad right button down (i.e. PS3: Cross, Xbox: A)
GAMEPAD_BUTTON_RIGHT_FACE_LEFT, // Gamepad right button left (i.e. PS3: Circle, Xbox: B)
GAMEPAD_BUTTON_RIGHT_FACE_LEFT, // Gamepad right button left (i.e. PS3: Square, Xbox: X)
GAMEPAD_BUTTON_LEFT_TRIGGER_1, // Gamepad top/back trigger left (first), it could be a trailing button
GAMEPAD_BUTTON_LEFT_TRIGGER_2, // Gamepad top/back trigger left (second), it could be a trailing button
GAMEPAD_BUTTON_RIGHT_TRIGGER_1, // Gamepad top/back trigger right (one), it could be a trailing button
GAMEPAD_BUTTON_RIGHT_TRIGGER_1, // Gamepad top/back trigger right (first), it could be a trailing button
GAMEPAD_BUTTON_RIGHT_TRIGGER_2, // Gamepad top/back trigger right (second), it could be a trailing button
GAMEPAD_BUTTON_MIDDLE_LEFT, // Gamepad center buttons, left one (i.e. PS3: Select)
GAMEPAD_BUTTON_MIDDLE, // Gamepad center buttons, middle one (i.e. PS3: PS, Xbox: XBOX)
@ -909,11 +913,11 @@ typedef enum {
// Camera system modes
typedef enum {
CAMERA_CUSTOM = 0, // Custom camera
CAMERA_FREE, // Free camera
CAMERA_ORBITAL, // Orbital camera
CAMERA_FIRST_PERSON, // First person camera
CAMERA_THIRD_PERSON // Third person camera
CAMERA_CUSTOM = 0, // Camera custom, controlled by user (UpdateCamera() does nothing)
CAMERA_FREE, // Camera free mode
CAMERA_ORBITAL, // Camera orbital, around target, zoom supported
CAMERA_FIRST_PERSON, // Camera first person
CAMERA_THIRD_PERSON // Camera third person
} CameraMode;
// Camera projection
@ -1045,13 +1049,15 @@ RLAPI void SetShaderValueTexture(Shader shader, int locIndex, Texture2D texture)
RLAPI void UnloadShader(Shader shader); // Unload shader from GPU memory (VRAM)
// Screen-space-related functions
RLAPI Ray GetMouseRay(Vector2 mousePosition, Camera camera); // Get a ray trace from mouse position
RLAPI Matrix GetCameraMatrix(Camera camera); // Get camera transform matrix (view matrix)
RLAPI Matrix GetCameraMatrix2D(Camera2D camera); // Get camera 2d transform matrix
#define GetMouseRay GetScreenToWorldRay // Compatibility hack for previous raylib versions
RLAPI Ray GetScreenToWorldRay(Vector2 position, Camera camera); // Get a ray trace from screen position (i.e mouse)
RLAPI Ray GetScreenToWorldRayEx(Vector2 position, Camera camera, int width, int height); // Get a ray trace from screen position (i.e mouse) in a viewport
RLAPI Vector2 GetWorldToScreen(Vector3 position, Camera camera); // Get the screen space position for a 3d world space position
RLAPI Vector2 GetScreenToWorld2D(Vector2 position, Camera2D camera); // Get the world space position for a 2d camera screen space position
RLAPI Vector2 GetWorldToScreenEx(Vector3 position, Camera camera, int width, int height); // Get size position for a 3d world space position
RLAPI Vector2 GetWorldToScreen2D(Vector2 position, Camera2D camera); // Get the screen space position for a 2d camera world space position
RLAPI Vector2 GetScreenToWorld2D(Vector2 position, Camera2D camera); // Get the world space position for a 2d camera screen space position
RLAPI Matrix GetCameraMatrix(Camera camera); // Get camera transform matrix (view matrix)
RLAPI Matrix GetCameraMatrix2D(Camera2D camera); // Get camera 2d transform matrix
// Timing-related functions
RLAPI void SetTargetFPS(int fps); // Set target FPS (maximum)
@ -1134,7 +1140,7 @@ RLAPI unsigned char *DecodeDataBase64(const unsigned char *data, int *outputSize
// Automation events functionality
RLAPI AutomationEventList LoadAutomationEventList(const char *fileName); // Load automation events list from file, NULL for empty list, capacity = MAX_AUTOMATION_EVENTS
RLAPI void UnloadAutomationEventList(AutomationEventList *list); // Unload automation events list from file
RLAPI void UnloadAutomationEventList(AutomationEventList list); // Unload automation events list from file
RLAPI bool ExportAutomationEventList(AutomationEventList list, const char *fileName); // Export automation events list as text file
RLAPI void SetAutomationEventList(AutomationEventList *list); // Set automation event list to record to
RLAPI void SetAutomationEventBaseFrame(int frame); // Set automation event internal base frame to start recording
@ -1167,6 +1173,7 @@ RLAPI int GetGamepadButtonPressed(void); // Get the last ga
RLAPI int GetGamepadAxisCount(int gamepad); // Get gamepad axis count for a gamepad
RLAPI float GetGamepadAxisMovement(int gamepad, int axis); // Get axis movement value for a gamepad axis
RLAPI int SetGamepadMappings(const char *mappings); // Set internal gamepad mappings (SDL_GameControllerDB)
RLAPI void SetGamepadVibration(int gamepad, float leftMotor, float rightMotor); // Set gamepad vibration for both motors
// Input-related functions: mouse
RLAPI bool IsMouseButtonPressed(int button); // Check if a mouse button has been pressed once
@ -1216,6 +1223,8 @@ RLAPI void UpdateCameraPro(Camera *camera, Vector3 movement, Vector3 rotation, f
// NOTE: It can be useful when using basic shapes and one single font,
// defining a font char white rectangle would allow drawing everything in a single draw call
RLAPI void SetShapesTexture(Texture2D texture, Rectangle source); // Set texture and rectangle to be used on shapes drawing
RLAPI Texture2D GetShapesTexture(void); // Get texture that is used for shapes drawing
RLAPI Rectangle GetShapesTextureRectangle(void); // Get texture source rectangle that is used for shapes drawing
// Basic shapes drawing functions
RLAPI void DrawPixel(int posX, int posY, Color color); // Draw a pixel
@ -1246,7 +1255,8 @@ RLAPI void DrawRectangleGradientEx(Rectangle rec, Color col1, Color col2, Color
RLAPI void DrawRectangleLines(int posX, int posY, int width, int height, Color color); // Draw rectangle outline
RLAPI void DrawRectangleLinesEx(Rectangle rec, float lineThick, Color color); // Draw rectangle outline with extended parameters
RLAPI void DrawRectangleRounded(Rectangle rec, float roundness, int segments, Color color); // Draw rectangle with rounded edges
RLAPI void DrawRectangleRoundedLines(Rectangle rec, float roundness, int segments, float lineThick, Color color); // Draw rectangle with rounded edges outline
RLAPI void DrawRectangleRoundedLines(Rectangle rec, float roundness, int segments, Color color); // Draw rectangle lines with rounded edges
RLAPI void DrawRectangleRoundedLinesEx(Rectangle rec, float roundness, int segments, float lineThick, Color color); // Draw rectangle with rounded edges outline
RLAPI void DrawTriangle(Vector2 v1, Vector2 v2, Vector2 v3, Color color); // Draw a color-filled triangle (vertex in counter-clockwise order!)
RLAPI void DrawTriangleLines(Vector2 v1, Vector2 v2, Vector2 v3, Color color); // Draw triangle outline (vertex in counter-clockwise order!)
RLAPI void DrawTriangleFan(Vector2 *points, int pointCount, Color color); // Draw a triangle fan defined by points (first vertex is the center)
@ -1296,6 +1306,7 @@ RLAPI Image LoadImage(const char *fileName);
RLAPI Image LoadImageRaw(const char *fileName, int width, int height, int format, int headerSize); // Load image from RAW file data
RLAPI Image LoadImageSvg(const char *fileNameOrString, int width, int height); // Load image from SVG file data or string with specified size
RLAPI Image LoadImageAnim(const char *fileName, int *frames); // Load image sequence from file (frames appended to image.data)
RLAPI Image LoadImageAnimFromMemory(const char *fileType, const unsigned char *fileData, int dataSize, int *frames); // Load image sequence from memory buffer
RLAPI Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, int dataSize); // Load image from memory buffer, fileType refers to extension: i.e. '.png'
RLAPI Image LoadImageFromTexture(Texture2D texture); // Load image from GPU texture data
RLAPI Image LoadImageFromScreen(void); // Load image from screen buffer and (screenshot)
@ -1329,6 +1340,7 @@ RLAPI void ImageAlphaClear(Image *image, Color color, float threshold);
RLAPI void ImageAlphaMask(Image *image, Image alphaMask); // Apply alpha mask to image
RLAPI void ImageAlphaPremultiply(Image *image); // Premultiply alpha channel
RLAPI void ImageBlurGaussian(Image *image, int blurSize); // Apply Gaussian blur using a box blur approximation
RLAPI void ImageKernelConvolution(Image *image, float* kernel, int kernelSize); // Apply Custom Square image convolution kernel
RLAPI void ImageResize(Image *image, int newWidth, int newHeight); // Resize image (Bicubic scaling algorithm)
RLAPI void ImageResizeNN(Image *image, int newWidth,int newHeight); // Resize image (Nearest-Neighbor scaling algorithm)
RLAPI void ImageResizeCanvas(Image *image, int newWidth, int newHeight, int offsetX, int offsetY, Color fill); // Resize canvas and fill with color
@ -1398,8 +1410,9 @@ RLAPI void DrawTexturePro(Texture2D texture, Rectangle source, Rectangle dest, V
RLAPI void DrawTextureNPatch(Texture2D texture, NPatchInfo nPatchInfo, Rectangle dest, Vector2 origin, float rotation, Color tint); // Draws a texture (or part of it) that stretches or shrinks nicely
// Color/pixel related functions
RLAPI bool ColorIsEqual(Color col1, Color col2); // Check if two colors are equal
RLAPI Color Fade(Color color, float alpha); // Get color with alpha applied, alpha goes from 0.0f to 1.0f
RLAPI int ColorToInt(Color color); // Get hexadecimal value for a Color
RLAPI int ColorToInt(Color color); // Get hexadecimal value for a Color (0xRRGGBBAA)
RLAPI Vector4 ColorNormalize(Color color); // Get Color normalized as float [0..1]
RLAPI Color ColorFromNormalized(Vector4 normalized); // Get Color from normalized values [0..1]
RLAPI Vector3 ColorToHSV(Color color); // Get HSV values for a Color, hue [0..360], saturation/value [0..1]
@ -1465,7 +1478,7 @@ RLAPI bool TextIsEqual(const char *text1, const char *text2);
RLAPI unsigned int TextLength(const char *text); // Get text length, checks for '\0' ending
RLAPI const char *TextFormat(const char *text, ...); // Text formatting with variables (sprintf() style)
RLAPI const char *TextSubtext(const char *text, int position, int length); // Get a piece of a text string
RLAPI char *TextReplace(char *text, const char *replace, const char *by); // Replace text string (WARNING: memory must be freed!)
RLAPI char *TextReplace(const char *text, const char *replace, const char *by); // Replace text string (WARNING: memory must be freed!)
RLAPI char *TextInsert(const char *text, const char *insert, int position); // Insert text in a position (WARNING: memory must be freed!)
RLAPI const char *TextJoin(const char **textList, int count, const char *delimiter); // Join text strings with delimiter
RLAPI const char **TextSplit(const char *text, char delimiter, int *count); // Split text into multiple strings
@ -1475,6 +1488,7 @@ RLAPI const char *TextToUpper(const char *text); // Get upp
RLAPI const char *TextToLower(const char *text); // Get lower case version of provided string
RLAPI const char *TextToPascal(const char *text); // Get Pascal case notation version of provided string
RLAPI int TextToInteger(const char *text); // Get integer value from text (negative values not supported)
RLAPI float TextToFloat(const char *text); // Get float value from text (negative values not supported)
//------------------------------------------------------------------------------------
// Basic 3d Shapes Drawing Functions (Module: models)
@ -1530,9 +1544,10 @@ RLAPI void UpdateMeshBuffer(Mesh mesh, int index, const void *data, int dataSize
RLAPI void UnloadMesh(Mesh mesh); // Unload mesh data from CPU and GPU
RLAPI void DrawMesh(Mesh mesh, Material material, Matrix transform); // Draw a 3d mesh with material and transform
RLAPI void DrawMeshInstanced(Mesh mesh, Material material, const Matrix *transforms, int instances); // Draw multiple mesh instances with material and different transforms
RLAPI bool ExportMesh(Mesh mesh, const char *fileName); // Export mesh data to file, returns true on success
RLAPI BoundingBox GetMeshBoundingBox(Mesh mesh); // Compute mesh bounding box limits
RLAPI void GenMeshTangents(Mesh *mesh); // Compute mesh tangents
RLAPI bool ExportMesh(Mesh mesh, const char *fileName); // Export mesh data to file, returns true on success
RLAPI bool ExportMeshAsCode(Mesh mesh, const char *fileName); // Export mesh as code file (.h) defining multiple arrays of vertex attributes
// Mesh generation functions
RLAPI Mesh GenMeshPoly(int sides, float radius); // Generate polygonal mesh
@ -1649,10 +1664,10 @@ RLAPI void SetAudioStreamPan(AudioStream stream, float pan); // Set pan
RLAPI void SetAudioStreamBufferSizeDefault(int size); // Default size for new audio streams
RLAPI void SetAudioStreamCallback(AudioStream stream, AudioCallback callback); // Audio thread callback to request new data
RLAPI void AttachAudioStreamProcessor(AudioStream stream, AudioCallback processor); // Attach audio stream processor to stream, receives the samples as <float>s
RLAPI void AttachAudioStreamProcessor(AudioStream stream, AudioCallback processor); // Attach audio stream processor to stream, receives the samples as 'float'
RLAPI void DetachAudioStreamProcessor(AudioStream stream, AudioCallback processor); // Detach audio stream processor from stream
RLAPI void AttachAudioMixedProcessor(AudioCallback processor); // Attach audio stream processor to the entire audio pipeline, receives the samples as <float>s
RLAPI void AttachAudioMixedProcessor(AudioCallback processor); // Attach audio stream processor to the entire audio pipeline, receives the samples as 'float'
RLAPI void DetachAudioMixedProcessor(AudioCallback processor); // Detach audio stream processor from the entire audio pipeline
#if defined(__cplusplus)

View file

@ -30,7 +30,7 @@
*
* LICENSE: zlib/libpng
*
* Copyright (c) 2015-2023 Ramon Santamaria (@raysan5)
* Copyright (c) 2015-2024 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.
@ -59,7 +59,9 @@
// Function specifiers definition
#if defined(RAYMATH_IMPLEMENTATION)
#if defined(_WIN32) && defined(BUILD_LIBTYPE_SHARED)
#define RMAPI __declspec(dllexport) extern inline // We are building raylib as a Win32 shared library (.dll).
#define RMAPI __declspec(dllexport) extern inline // We are building raylib as a Win32 shared library (.dll)
#elif defined(BUILD_LIBTYPE_SHARED)
#define RMAPI __attribute__((visibility("default"))) // We are building raylib as a Unix shared library (.so/.dylib)
#elif defined(_WIN32) && defined(USE_LIBTYPE_SHARED)
#define RMAPI __declspec(dllimport) // We are using raylib as a Win32 shared library (.dll)
#else
@ -163,7 +165,7 @@ typedef struct float16 {
float v[16];
} float16;
#include <math.h> // Required for: sinf(), cosf(), tan(), atan2f(), sqrtf(), floor(), fminf(), fmaxf(), fabs()
#include <math.h> // Required for: sinf(), cosf(), tan(), atan2f(), sqrtf(), floor(), fminf(), fmaxf(), fabsf()
//----------------------------------------------------------------------------------
// Module Functions Definition - Utils math
@ -429,6 +431,28 @@ RMAPI Vector2 Vector2Reflect(Vector2 v, Vector2 normal)
return result;
}
// Get min value for each pair of components
RMAPI Vector2 Vector2Min(Vector2 v1, Vector2 v2)
{
Vector2 result = { 0 };
result.x = fminf(v1.x, v2.x);
result.y = fminf(v1.y, v2.y);
return result;
}
// Get max value for each pair of components
RMAPI Vector2 Vector2Max(Vector2 v1, Vector2 v2)
{
Vector2 result = { 0 };
result.x = fmaxf(v1.x, v2.x);
result.y = fmaxf(v1.y, v2.y);
return result;
}
// Rotate vector by angle
RMAPI Vector2 Vector2Rotate(Vector2 v, float angle)
{
@ -492,19 +516,19 @@ RMAPI Vector2 Vector2ClampValue(Vector2 v, float min, float max)
{
length = sqrtf(length);
float scale = 1; // By default, 1 as the neutral element.
if (length < min)
{
float scale = min/length;
result.x = v.x*scale;
result.y = v.y*scale;
scale = min/length;
}
else if (length > max)
{
float scale = max/length;
scale = max/length;
}
result.x = v.x*scale;
result.y = v.y*scale;
}
}
return result;
}
@ -522,6 +546,31 @@ RMAPI int Vector2Equals(Vector2 p, Vector2 q)
return result;
}
// Compute the direction of a refracted ray
// v: normalized direction of the incoming ray
// n: normalized normal vector of the interface of two optical media
// r: ratio of the refractive index of the medium from where the ray comes
// to the refractive index of the medium on the other side of the surface
RMAPI Vector2 Vector2Refract(Vector2 v, Vector2 n, float r)
{
Vector2 result = { 0 };
float dot = v.x*n.x + v.y*n.y;
float d = 1.0f - r*r*(1.0f - dot*dot);
if (d >= 0.0f)
{
d = sqrtf(d);
v.x = r*v.x - (r*dot + d)*n.x;
v.y = r*v.y - (r*dot + d)*n.y;
result = v;
}
return result;
}
//----------------------------------------------------------------------------------
// Module Functions Definition - Vector3 math
//----------------------------------------------------------------------------------
@ -603,12 +652,12 @@ RMAPI Vector3 Vector3Perpendicular(Vector3 v)
{
Vector3 result = { 0 };
float min = (float) fabs(v.x);
float min = fabsf(v.x);
Vector3 cardinalAxis = {1.0f, 0.0f, 0.0f};
if (fabsf(v.y) < min)
{
min = (float) fabs(v.y);
min = fabsf(v.y);
Vector3 tmp = {0.0f, 1.0f, 0.0f};
cardinalAxis = tmp;
}
@ -873,6 +922,27 @@ RMAPI Vector3 Vector3RotateByAxisAngle(Vector3 v, Vector3 axis, float angle)
return result;
}
// Move Vector towards target
RMAPI Vector3 Vector3MoveTowards(Vector3 v, Vector3 target, float maxDistance)
{
Vector3 result = { 0 };
float dx = target.x - v.x;
float dy = target.y - v.y;
float dz = target.z - v.z;
float value = (dx*dx) + (dy*dy) + (dz*dz);
if ((value == 0) || ((maxDistance >= 0) && (value <= maxDistance*maxDistance))) return target;
float dist = sqrtf(value);
result.x = v.x + dx/dist*maxDistance;
result.y = v.y + dy/dist*maxDistance;
result.z = v.z + dz/dist*maxDistance;
return result;
}
// Calculate linear interpolation between two vectors
RMAPI Vector3 Vector3Lerp(Vector3 v1, Vector3 v2, float amount)
{
@ -885,6 +955,22 @@ RMAPI Vector3 Vector3Lerp(Vector3 v1, Vector3 v2, float amount)
return result;
}
// Calculate cubic hermite interpolation between two vectors and their tangents
// as described in the GLTF 2.0 specification: https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#interpolation-cubic
RMAPI Vector3 Vector3CubicHermite(Vector3 v1, Vector3 tangent1, Vector3 v2, Vector3 tangent2, float amount)
{
Vector3 result = { 0 };
float amountPow2 = amount * amount;
float amountPow3 = amount * amount * amount;
result.x = (2 * amountPow3 - 3 * amountPow2 + 1) * v1.x + (amountPow3 - 2 * amountPow2 + amount) * tangent1.x + (-2 * amountPow3 + 3 * amountPow2) * v2.x + (amountPow3 - amountPow2) * tangent2.x;
result.y = (2 * amountPow3 - 3 * amountPow2 + 1) * v1.y + (amountPow3 - 2 * amountPow2 + amount) * tangent1.y + (-2 * amountPow3 + 3 * amountPow2) * v2.y + (amountPow3 - amountPow2) * tangent2.y;
result.z = (2 * amountPow3 - 3 * amountPow2 + 1) * v1.z + (amountPow3 - 2 * amountPow2 + amount) * tangent1.z + (-2 * amountPow3 + 3 * amountPow2) * v2.z + (amountPow3 - amountPow2) * tangent2.z;
return result;
}
// Calculate reflected vector to normal
RMAPI Vector3 Vector3Reflect(Vector3 v, Vector3 normal)
{
@ -1078,21 +1164,20 @@ RMAPI Vector3 Vector3ClampValue(Vector3 v, float min, float max)
{
length = sqrtf(length);
float scale = 1; // By default, 1 as the neutral element.
if (length < min)
{
float scale = min/length;
result.x = v.x*scale;
result.y = v.y*scale;
result.z = v.z*scale;
scale = min/length;
}
else if (length > max)
{
float scale = max/length;
scale = max/length;
}
result.x = v.x*scale;
result.y = v.y*scale;
result.z = v.z*scale;
}
}
return result;
}
@ -1136,6 +1221,233 @@ RMAPI Vector3 Vector3Refract(Vector3 v, Vector3 n, float r)
return result;
}
//----------------------------------------------------------------------------------
// Module Functions Definition - Vector4 math
//----------------------------------------------------------------------------------
RMAPI Vector4 Vector4Zero(void)
{
Vector4 result = { 0.0f, 0.0f, 0.0f, 0.0f };
return result;
}
RMAPI Vector4 Vector4One(void)
{
Vector4 result = { 1.0f, 1.0f, 1.0f, 1.0f };
return result;
}
RMAPI Vector4 Vector4Add(Vector4 v1, Vector4 v2)
{
Vector4 result = {
v1.x + v2.x,
v1.y + v2.y,
v1.z + v2.z,
v1.w + v2.w
};
return result;
}
RMAPI Vector4 Vector4AddValue(Vector4 v, float add)
{
Vector4 result = {
v.x + add,
v.y + add,
v.z + add,
v.w + add
};
return result;
}
RMAPI Vector4 Vector4Subtract(Vector4 v1, Vector4 v2)
{
Vector4 result = {
v1.x - v2.x,
v1.y - v2.y,
v1.z - v2.z,
v1.w - v2.w
};
return result;
}
RMAPI Vector4 Vector4SubtractValue(Vector4 v, float add)
{
Vector4 result = {
v.x - add,
v.y - add,
v.z - add,
v.w - add
};
return result;
}
RMAPI float Vector4Length(Vector4 v)
{
float result = sqrtf((v.x*v.x) + (v.y*v.y) + (v.z*v.z) + (v.w*v.w));
return result;
}
RMAPI float Vector4LengthSqr(Vector4 v)
{
float result = (v.x*v.x) + (v.y*v.y) + (v.z*v.z) + (v.w*v.w);
return result;
}
RMAPI float Vector4DotProduct(Vector4 v1, Vector4 v2)
{
float result = (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z + v1.w*v2.w);
return result;
}
// Calculate distance between two vectors
RMAPI float Vector4Distance(Vector4 v1, Vector4 v2)
{
float result = sqrtf(
(v1.x - v2.x)*(v1.x - v2.x) + (v1.y - v2.y)*(v1.y - v2.y) +
(v1.z - v2.z)*(v1.z - v2.z) + (v1.w - v2.w)*(v1.w - v2.w));
return result;
}
// Calculate square distance between two vectors
RMAPI float Vector4DistanceSqr(Vector4 v1, Vector4 v2)
{
float result =
(v1.x - v2.x)*(v1.x - v2.x) + (v1.y - v2.y)*(v1.y - v2.y) +
(v1.z - v2.z)*(v1.z - v2.z) + (v1.w - v2.w)*(v1.w - v2.w);
return result;
}
RMAPI Vector4 Vector4Scale(Vector4 v, float scale)
{
Vector4 result = { v.x*scale, v.y*scale, v.z*scale, v.w*scale };
return result;
}
// Multiply vector by vector
RMAPI Vector4 Vector4Multiply(Vector4 v1, Vector4 v2)
{
Vector4 result = { v1.x*v2.x, v1.y*v2.y, v1.z*v2.z, v1.w*v2.w };
return result;
}
// Negate vector
RMAPI Vector4 Vector4Negate(Vector4 v)
{
Vector4 result = { -v.x, -v.y, -v.z, -v.w };
return result;
}
// Divide vector by vector
RMAPI Vector4 Vector4Divide(Vector4 v1, Vector4 v2)
{
Vector4 result = { v1.x/v2.x, v1.y/v2.y, v1.z/v2.z, v1.w/v2.w };
return result;
}
// Normalize provided vector
RMAPI Vector4 Vector4Normalize(Vector4 v)
{
Vector4 result = { 0 };
float length = sqrtf((v.x*v.x) + (v.y*v.y) + (v.z*v.z) + (v.w*v.w));
if (length > 0)
{
float ilength = 1.0f/length;
result.x = v.x*ilength;
result.y = v.y*ilength;
result.z = v.z*ilength;
result.w = v.w*ilength;
}
return result;
}
// Get min value for each pair of components
RMAPI Vector4 Vector4Min(Vector4 v1, Vector4 v2)
{
Vector4 result = { 0 };
result.x = fminf(v1.x, v2.x);
result.y = fminf(v1.y, v2.y);
result.z = fminf(v1.z, v2.z);
result.w = fminf(v1.w, v2.w);
return result;
}
// Get max value for each pair of components
RMAPI Vector4 Vector4Max(Vector4 v1, Vector4 v2)
{
Vector4 result = { 0 };
result.x = fmaxf(v1.x, v2.x);
result.y = fmaxf(v1.y, v2.y);
result.z = fmaxf(v1.z, v2.z);
result.w = fmaxf(v1.w, v2.w);
return result;
}
// Calculate linear interpolation between two vectors
RMAPI Vector4 Vector4Lerp(Vector4 v1, Vector4 v2, float amount)
{
Vector4 result = { 0 };
result.x = v1.x + amount*(v2.x - v1.x);
result.y = v1.y + amount*(v2.y - v1.y);
result.z = v1.z + amount*(v2.z - v1.z);
result.w = v1.w + amount*(v2.w - v1.w);
return result;
}
// Move Vector towards target
RMAPI Vector4 Vector4MoveTowards(Vector4 v, Vector4 target, float maxDistance)
{
Vector4 result = { 0 };
float dx = target.x - v.x;
float dy = target.y - v.y;
float dz = target.z - v.z;
float dw = target.w - v.w;
float value = (dx*dx) + (dy*dy) + (dz*dz) + (dw*dw);
if ((value == 0) || ((maxDistance >= 0) && (value <= maxDistance*maxDistance))) return target;
float dist = sqrtf(value);
result.x = v.x + dx/dist*maxDistance;
result.y = v.y + dy/dist*maxDistance;
result.z = v.z + dz/dist*maxDistance;
result.w = v.w + dw/dist*maxDistance;
return result;
}
// Invert the given vector
RMAPI Vector4 Vector4Invert(Vector4 v)
{
Vector4 result = { 1.0f/v.x, 1.0f/v.y, 1.0f/v.z, 1.0f/v.w };
return result;
}
// Check whether two given vectors are almost equal
RMAPI int Vector4Equals(Vector4 p, Vector4 q)
{
#if !defined(EPSILON)
#define EPSILON 0.000001f
#endif
int result = ((fabsf(p.x - q.x)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.x), fabsf(q.x))))) &&
((fabsf(p.y - q.y)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.y), fabsf(q.y))))) &&
((fabsf(p.z - q.z)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.z), fabsf(q.z))))) &&
((fabsf(p.w - q.w)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.w), fabsf(q.w)))));
return result;
}
//----------------------------------------------------------------------------------
// Module Functions Definition - Matrix math
//----------------------------------------------------------------------------------
@ -1901,6 +2213,32 @@ RMAPI Quaternion QuaternionSlerp(Quaternion q1, Quaternion q2, float amount)
return result;
}
// Calculate quaternion cubic spline interpolation using Cubic Hermite Spline algorithm
// as described in the GLTF 2.0 specification: https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#interpolation-cubic
RMAPI Quaternion QuaternionCubicHermiteSpline(Quaternion q1, Quaternion outTangent1, Quaternion q2, Quaternion inTangent2, float t)
{
float t2 = t * t;
float t3 = t2 * t;
float h00 = 2 * t3 - 3 * t2 + 1;
float h10 = t3 - 2 * t2 + t;
float h01 = -2 * t3 + 3 * t2;
float h11 = t3 - t2;
Quaternion p0 = QuaternionScale(q1, h00);
Quaternion m0 = QuaternionScale(outTangent1, h10);
Quaternion p1 = QuaternionScale(q2, h01);
Quaternion m1 = QuaternionScale(inTangent2, h11);
Quaternion result = { 0 };
result = QuaternionAdd(p0, m0);
result = QuaternionAdd(result, p1);
result = QuaternionAdd(result, m1);
result = QuaternionNormalize(result);
return result;
}
// Calculate quaternion based on the rotation from one vector to another
RMAPI Quaternion QuaternionFromVector3ToVector3(Vector3 from, Vector3 to)
{
@ -2042,8 +2380,7 @@ RMAPI Quaternion QuaternionFromAxisAngle(Vector3 axis, float angle)
float ilength = 0.0f;
// Vector3Normalize(axis)
Vector3 v = axis;
length = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z);
length = axisLength;
if (length == 0.0f) length = 1.0f;
ilength = 1.0f/length;
axis.x *= ilength;

View file

@ -20,7 +20,7 @@
*
* LICENSE: zlib/libpng
*
* Copyright (c) 2022-2023 Christoph Wagner (@Crydsch) & Ramon Santamaria (@raysan5)
* Copyright (c) 2022-2024 Christoph Wagner (@Crydsch) & 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.
@ -202,18 +202,10 @@ RLAPI Matrix GetCameraProjectionMatrix(Camera* camera, float aspect);
// Camera mouse movement sensitivity
#define CAMERA_MOUSE_MOVE_SENSITIVITY 0.003f // TODO: it should be independant of framerate
#define CAMERA_MOUSE_SCROLL_SENSITIVITY 1.5f
// Camera orbital speed in CAMERA_ORBITAL mode
#define CAMERA_ORBITAL_SPEED 0.5f // Radians per second
#define CAMERA_FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER 8.0f
#define CAMERA_FIRST_PERSON_STEP_DIVIDER 30.0f
#define CAMERA_FIRST_PERSON_WAVING_DIVIDER 200.0f
// PLAYER (used by camera)
#define PLAYER_MOVEMENT_SENSITIVITY 20.0f
//----------------------------------------------------------------------------------
// Types and Structures Definition
//----------------------------------------------------------------------------------
@ -251,7 +243,7 @@ Vector3 GetCameraRight(Camera *camera)
Vector3 forward = GetCameraForward(camera);
Vector3 up = GetCameraUp(camera);
return Vector3CrossProduct(forward, up);
return Vector3Normalize(Vector3CrossProduct(forward, up));
}
// Moves the camera in its forward direction
@ -449,10 +441,11 @@ void UpdateCamera(Camera *camera, int mode)
bool moveInWorldPlane = ((mode == CAMERA_FIRST_PERSON) || (mode == CAMERA_THIRD_PERSON));
bool rotateAroundTarget = ((mode == CAMERA_THIRD_PERSON) || (mode == CAMERA_ORBITAL));
bool lockView = ((mode == CAMERA_FIRST_PERSON) || (mode == CAMERA_THIRD_PERSON) || (mode == CAMERA_ORBITAL));
bool lockView = ((mode == CAMERA_FREE) || (mode == CAMERA_FIRST_PERSON) || (mode == CAMERA_THIRD_PERSON) || (mode == CAMERA_ORBITAL));
bool rotateUp = false;
if (mode == CAMERA_ORBITAL)
if (mode == CAMERA_CUSTOM) {}
else if (mode == CAMERA_ORBITAL)
{
// Orbital can just orbit
Matrix rotation = MatrixRotate(GetCameraUp(camera), CAMERA_ORBITAL_SPEED*GetFrameTime());
@ -471,8 +464,6 @@ void UpdateCamera(Camera *camera, int mode)
if (IsKeyDown(KEY_E)) CameraRoll(camera, CAMERA_ROTATION_SPEED);
// Camera movement
if (!IsGamepadAvailable(0))
{
// Camera pan (for CAMERA_FREE)
if ((mode == CAMERA_FREE) && (IsMouseButtonDown(MOUSE_BUTTON_MIDDLE)))
{
@ -494,8 +485,9 @@ void UpdateCamera(Camera *camera, int mode)
if (IsKeyDown(KEY_A)) CameraMoveRight(camera, -CAMERA_MOVE_SPEED, moveInWorldPlane);
if (IsKeyDown(KEY_S)) CameraMoveForward(camera, -CAMERA_MOVE_SPEED, moveInWorldPlane);
if (IsKeyDown(KEY_D)) CameraMoveRight(camera, CAMERA_MOVE_SPEED, moveInWorldPlane);
}
else
// Gamepad movement
if (IsGamepadAvailable(0))
{
// Gamepad controller support
CameraYaw(camera, -(GetGamepadAxisMovement(0, GAMEPAD_AXIS_RIGHT_X) * 2)*CAMERA_MOUSE_MOVE_SENSITIVITY, rotateAroundTarget);

View file

@ -63,7 +63,7 @@
*
* LICENSE: zlib/libpng
*
* Copyright (c) 2013-2023 Ramon Santamaria (@raysan5) and contributors
* Copyright (c) 2013-2024 Ramon Santamaria (@raysan5) and contributors
*
* 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.
@ -82,6 +82,19 @@
*
**********************************************************************************************/
//----------------------------------------------------------------------------------
// Feature Test Macros required for this module
//----------------------------------------------------------------------------------
#if (defined(__linux__) || defined(PLATFORM_WEB)) && (_XOPEN_SOURCE < 500)
#undef _XOPEN_SOURCE
#define _XOPEN_SOURCE 500 // Required for: readlink if compiled with c99 without gnu ext.
#endif
#if (defined(__linux__) || defined(PLATFORM_WEB)) && (_POSIX_C_SOURCE < 199309L)
#undef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE 199309L // Required for: CLOCK_MONOTONIC if compiled with c99 without gnu ext.
#endif
#include "raylib.h" // Declares module functions
// Check if config flags have been externally provided on compilation line
@ -141,13 +154,15 @@
#endif
// Platform specific defines to handle GetApplicationDirectory()
#if defined(_WIN32)
#if (defined(_WIN32) && !defined(PLATFORM_DESKTOP_RGFW)) || (defined(_MSC_VER) && defined(PLATFORM_DESKTOP_RGFW))
#ifndef MAX_PATH
#define MAX_PATH 1025
#endif
__declspec(dllimport) unsigned long __stdcall GetModuleFileNameA(void *hModule, void *lpFilename, unsigned long nSize);
__declspec(dllimport) unsigned long __stdcall GetModuleFileNameW(void *hModule, void *lpFilename, unsigned long nSize);
__declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, void *widestr, int cchwide, void *str, int cbmb, void *defchar, int *used_default);
unsigned int __stdcall timeBeginPeriod(unsigned int uPeriod);
unsigned int __stdcall timeEndPeriod(unsigned int uPeriod);
#elif defined(__linux__)
#include <unistd.h>
#elif defined(__APPLE__)
@ -211,6 +226,9 @@ __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigne
#ifndef MAX_GAMEPAD_BUTTONS
#define MAX_GAMEPAD_BUTTONS 32 // Maximum number of buttons supported (per gamepad)
#endif
#ifndef MAX_GAMEPAD_VIBRATION_TIME
#define MAX_GAMEPAD_VIBRATION_TIME 2.0f // Maximum vibration time in seconds
#endif
#ifndef MAX_TOUCH_POINTS
#define MAX_TOUCH_POINTS 8 // Maximum number of touch points supported
#endif
@ -235,11 +253,6 @@ __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigne
#define FLAG_TOGGLE(n, f) ((n) ^= (f))
#define FLAG_CHECK(n, f) ((n) & (f))
#if (defined(__linux__) || defined(PLATFORM_WEB)) && (_POSIX_C_SOURCE < 199309L)
#undef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE 199309L // Required for: CLOCK_MONOTONIC if compiled with c99 without gnu ext.
#endif
//----------------------------------------------------------------------------------
// Types and Structures Definition
//----------------------------------------------------------------------------------
@ -285,7 +298,7 @@ typedef struct CoreData {
char previousKeyState[MAX_KEYBOARD_KEYS]; // Registers previous frame key state
// NOTE: Since key press logic involves comparing prev vs cur key state, we need to handle key repeats specially
char keyRepeatInFrame[MAX_KEYBOARD_KEYS]; // Registers key repeats for current frame.
char keyRepeatInFrame[MAX_KEYBOARD_KEYS]; // Registers key repeats for current frame
int keyPressedQueue[MAX_KEY_PRESSED_QUEUE]; // Input keys queue
int keyPressedQueueCount; // Input keys queue count
@ -354,7 +367,7 @@ static int screenshotCounter = 0; // Screenshots counter
#endif
#if defined(SUPPORT_GIF_RECORDING)
int gifFrameCounter = 0; // GIF frames counter
unsigned int gifFrameCounter = 0; // GIF frames counter
bool gifRecording = false; // GIF recording state
MsfGifState gifState = { 0 }; // MSGIF context state
#endif
@ -471,7 +484,7 @@ static void ScanDirectoryFilesRecursively(const char *basePath, FilePathList *li
static void RecordAutomationEvent(void); // Record frame events (to internal events array)
#endif
#if defined(_WIN32)
#if defined(_WIN32) && !defined(PLATFORM_DESKTOP_RGFW)
// NOTE: We declare Sleep() function symbol to avoid including windows.h (kernel32.lib linkage required)
void __stdcall Sleep(unsigned long msTimeout); // Required for: WaitTime()
#endif
@ -485,6 +498,8 @@ const char *TextFormat(const char *text, ...); // Formatting of tex
#include "platforms/rcore_desktop.c"
#elif defined(PLATFORM_DESKTOP_SDL)
#include "platforms/rcore_desktop_sdl.c"
#elif defined(PLATFORM_DESKTOP_RGFW)
#include "platforms/rcore_desktop_rgfw.c"
#elif defined(PLATFORM_WEB)
#include "platforms/rcore_web.c"
#elif defined(PLATFORM_DRM)
@ -795,7 +810,7 @@ bool IsCursorHidden(void)
return CORE.Input.Mouse.cursorHidden;
}
// Check if cursor is on the current screen.
// Check if cursor is on the current screen
bool IsCursorOnScreen(void)
{
return CORE.Input.Mouse.cursorOnScreen;
@ -838,23 +853,33 @@ void EndDrawing(void)
// Draw record indicator
if (gifRecording)
{
#ifndef GIF_RECORD_FRAMERATE
#define GIF_RECORD_FRAMERATE 10
gifFrameCounter++;
#endif
gifFrameCounter += GetFrameTime()*1000;
// NOTE: We record one gif frame every 10 game frames
if ((gifFrameCounter%GIF_RECORD_FRAMERATE) == 0)
// NOTE: We record one gif frame depending on the desired gif framerate
if (gifFrameCounter > 1000/GIF_RECORD_FRAMERATE)
{
// Get image data for the current frame (from backbuffer)
// NOTE: This process is quite slow... :(
Vector2 scale = GetWindowScaleDPI();
unsigned char *screenData = rlReadScreenPixels((int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y));
msf_gif_frame(&gifState, screenData, 10, 16, (int)((float)CORE.Window.render.width*scale.x)*4);
#ifndef GIF_RECORD_BITRATE
#define GIF_RECORD_BITRATE 16
#endif
// Add the frame to the gif recording, given how many frames have passed in centiseconds
msf_gif_frame(&gifState, screenData, gifFrameCounter/10, GIF_RECORD_BITRATE, (int)((float)CORE.Window.render.width*scale.x)*4);
gifFrameCounter -= 1000/GIF_RECORD_FRAMERATE;
RL_FREE(screenData); // Free image data
}
#if defined(SUPPORT_MODULE_RSHAPES) && defined(SUPPORT_MODULE_RTEXT)
if (((gifFrameCounter/15)%2) == 1)
// Display the recording indicator every half-second
if ((int)(GetTime()/0.5)%2 == 1)
{
DrawCircle(30, CORE.Window.screen.height - 20, 10, MAROON); // WARNING: Module required: rshapes
DrawText("GIF RECORDING", 50, CORE.Window.screen.height - 25, 10, RED); // WARNING: Module required: rtext
@ -944,9 +969,6 @@ void BeginMode2D(Camera2D camera)
// Apply 2d camera transformation to modelview
rlMultMatrixf(MatrixToFloat(GetCameraMatrix2D(camera)));
// Apply screen scaling if required
rlMultMatrixf(MatrixToFloat(CORE.Window.screenScale));
}
// Ends 2D mode with custom camera
@ -955,7 +977,8 @@ void EndMode2D(void)
rlDrawRenderBatchActive(); // Update and draw internal render batch
rlLoadIdentity(); // Reset current matrix (modelview)
rlMultMatrixf(MatrixToFloat(CORE.Window.screenScale)); // Apply screen scaling if required
if (rlGetActiveFramebuffer() == 0) rlMultMatrixf(MatrixToFloat(CORE.Window.screenScale)); // Apply screen scaling if required
}
// Initializes 3D mode with custom camera (3D)
@ -973,10 +996,10 @@ void BeginMode3D(Camera camera)
if (camera.projection == CAMERA_PERSPECTIVE)
{
// Setup perspective projection
double top = RL_CULL_DISTANCE_NEAR*tan(camera.fovy*0.5*DEG2RAD);
double top = rlGetCullDistanceNear()*tan(camera.fovy*0.5*DEG2RAD);
double right = top*aspect;
rlFrustum(-right, right, -top, top, RL_CULL_DISTANCE_NEAR, RL_CULL_DISTANCE_FAR);
rlFrustum(-right, right, -top, top, rlGetCullDistanceNear(), rlGetCullDistanceFar());
}
else if (camera.projection == CAMERA_ORTHOGRAPHIC)
{
@ -984,7 +1007,7 @@ void BeginMode3D(Camera camera)
double top = camera.fovy/2.0;
double right = top*aspect;
rlOrtho(-right, right, -top,top, RL_CULL_DISTANCE_NEAR, RL_CULL_DISTANCE_FAR);
rlOrtho(-right, right, -top,top, rlGetCullDistanceNear(), rlGetCullDistanceFar());
}
rlMatrixMode(RL_MODELVIEW); // Switch back to modelview matrix
@ -1008,7 +1031,7 @@ void EndMode3D(void)
rlMatrixMode(RL_MODELVIEW); // Switch back to modelview matrix
rlLoadIdentity(); // Reset current matrix (modelview)
rlMultMatrixf(MatrixToFloat(CORE.Window.screenScale)); // Apply screen scaling if required
if (rlGetActiveFramebuffer() == 0) rlMultMatrixf(MatrixToFloat(CORE.Window.screenScale)); // Apply screen scaling if required
rlDisableDepthTest(); // Disable DEPTH_TEST for 2D
}
@ -1054,6 +1077,11 @@ void EndTextureMode(void)
// Set viewport to default framebuffer size
SetupViewport(CORE.Window.render.width, CORE.Window.render.height);
// Go back to the modelview state from BeginDrawing since we are back to the default FBO
rlMatrixMode(RL_MODELVIEW); // Switch back to modelview matrix
rlLoadIdentity(); // Reset current matrix (modelview)
rlMultMatrixf(MatrixToFloat(CORE.Window.screenScale)); // Apply screen scaling if required
// Reset current fbo to screen size
CORE.Window.currentFbo.width = CORE.Window.render.width;
CORE.Window.currentFbo.height = CORE.Window.render.height;
@ -1183,17 +1211,17 @@ VrStereoConfig LoadVrStereoConfig(VrDeviceInfo device)
// Compute camera projection matrices
float projOffset = 4.0f*lensShift; // Scaled to projection space coordinates [-1..1]
Matrix proj = MatrixPerspective(fovy, aspect, RL_CULL_DISTANCE_NEAR, RL_CULL_DISTANCE_FAR);
Matrix proj = MatrixPerspective(fovy, aspect, rlGetCullDistanceNear(), rlGetCullDistanceFar());
config.projection[0] = MatrixMultiply(proj, MatrixTranslate(projOffset, 0.0f, 0.0f));
config.projection[1] = MatrixMultiply(proj, MatrixTranslate(-projOffset, 0.0f, 0.0f));
// Compute camera transformation matrices
// NOTE: Camera movement might seem more natural if we model the head.
// NOTE: Camera movement might seem more natural if we model the head
// Our axis of rotation is the base of our head, so we might want to add
// some y (base of head to eye level) and -z (center of head to eye protrusion) to the camera positions.
config.viewOffset[0] = MatrixTranslate(-device.interpupillaryDistance*0.5f, 0.075f, 0.045f);
config.viewOffset[1] = MatrixTranslate(device.interpupillaryDistance*0.5f, 0.075f, 0.045f);
// some y (base of head to eye level) and -z (center of head to eye protrusion) to the camera positions
config.viewOffset[0] = MatrixTranslate(device.interpupillaryDistance*0.5f, 0.075f, 0.045f);
config.viewOffset[1] = MatrixTranslate(-device.interpupillaryDistance*0.5f, 0.075f, 0.045f);
// Compute eyes Viewports
/*
@ -1392,15 +1420,23 @@ void SetShaderValueTexture(Shader shader, int locIndex, Texture2D texture)
// Module Functions Definition: Screen-space Queries
//----------------------------------------------------------------------------------
// Get a ray trace from mouse position
Ray GetMouseRay(Vector2 mouse, Camera camera)
// Get a ray trace from screen position (i.e mouse)
Ray GetScreenToWorldRay(Vector2 position, Camera camera)
{
Ray ray = GetScreenToWorldRayEx(position, camera, GetScreenWidth(), GetScreenHeight());
return ray;
}
// Get a ray trace from the screen position (i.e mouse) within a specific section of the screen
Ray GetScreenToWorldRayEx(Vector2 position, Camera camera, int width, int height)
{
Ray ray = { 0 };
// Calculate normalized device coordinates
// NOTE: y value is negative
float x = (2.0f*mouse.x)/(float)GetScreenWidth() - 1.0f;
float y = 1.0f - (2.0f*mouse.y)/(float)GetScreenHeight();
float x = (2.0f*position.x)/(float)width - 1.0f;
float y = 1.0f - (2.0f*position.y)/(float)height;
float z = 1.0f;
// Store values in a vector
@ -1414,11 +1450,11 @@ Ray GetMouseRay(Vector2 mouse, Camera camera)
if (camera.projection == CAMERA_PERSPECTIVE)
{
// Calculate projection matrix from perspective
matProj = MatrixPerspective(camera.fovy*DEG2RAD, ((double)GetScreenWidth()/(double)GetScreenHeight()), RL_CULL_DISTANCE_NEAR, RL_CULL_DISTANCE_FAR);
matProj = MatrixPerspective(camera.fovy*DEG2RAD, ((double)width/(double)height), rlGetCullDistanceNear(), rlGetCullDistanceFar());
}
else if (camera.projection == CAMERA_ORTHOGRAPHIC)
{
double aspect = (double)CORE.Window.screen.width/(double)CORE.Window.screen.height;
double aspect = (double)width/(double)height;
double top = camera.fovy/2.0;
double right = top*aspect;
@ -1430,9 +1466,10 @@ Ray GetMouseRay(Vector2 mouse, Camera camera)
Vector3 nearPoint = Vector3Unproject((Vector3){ deviceCoords.x, deviceCoords.y, 0.0f }, matProj, matView);
Vector3 farPoint = Vector3Unproject((Vector3){ deviceCoords.x, deviceCoords.y, 1.0f }, matProj, matView);
// Unproject the mouse cursor in the near plane.
// We need this as the source position because orthographic projects, compared to perspective doesn't have a
// convergence point, meaning that the "eye" of the camera is more like a plane than a point.
// Unproject the mouse cursor in the near plane
// We need this as the source position because orthographic projects,
// compared to perspective doesn't have a convergence point,
// meaning that the "eye" of the camera is more like a plane than a point
Vector3 cameraPlanePointerPos = Vector3Unproject((Vector3){ deviceCoords.x, deviceCoords.y, -1.0f }, matProj, matView);
// Calculate normalized direction vector
@ -1450,7 +1487,9 @@ Ray GetMouseRay(Vector2 mouse, Camera camera)
// Get transform matrix for camera
Matrix GetCameraMatrix(Camera camera)
{
return MatrixLookAt(camera.position, camera.target, camera.up);
Matrix mat = MatrixLookAt(camera.position, camera.target, camera.up);
return mat;
}
// Get camera 2d transform matrix
@ -1461,12 +1500,12 @@ Matrix GetCameraMatrix2D(Camera2D camera)
// 1. Move it to target
// 2. Rotate by -rotation and scale by (1/zoom)
// When setting higher scale, it's more intuitive for the world to become bigger (= camera become smaller),
// not for the camera getting bigger, hence the invert. Same deal with rotation.
// not for the camera getting bigger, hence the invert. Same deal with rotation
// 3. Move it by (-offset);
// Offset defines target transform relative to screen, but since we're effectively "moving" screen (camera)
// we need to do it into opposite direction (inverse transform)
// Having camera transform in world-space, inverse of it gives the modelview transform.
// Having camera transform in world-space, inverse of it gives the modelview transform
// Since (A*B*C)' = C'*B'*A', the modelview is
// 1. Move to offset
// 2. Rotate and Scale
@ -1498,7 +1537,7 @@ Vector2 GetWorldToScreenEx(Vector3 position, Camera camera, int width, int heigh
if (camera.projection == CAMERA_PERSPECTIVE)
{
// Calculate projection matrix from perspective
matProj = MatrixPerspective(camera.fovy*DEG2RAD, ((double)width/(double)height), RL_CULL_DISTANCE_NEAR, RL_CULL_DISTANCE_FAR);
matProj = MatrixPerspective(camera.fovy*DEG2RAD, ((double)width/(double)height), rlGetCullDistanceNear(), rlGetCullDistanceFar());
}
else if (camera.projection == CAMERA_ORTHOGRAPHIC)
{
@ -1507,7 +1546,7 @@ Vector2 GetWorldToScreenEx(Vector3 position, Camera camera, int width, int heigh
double right = top*aspect;
// Calculate projection matrix from orthographic
matProj = MatrixOrtho(-right, right, -top, top, RL_CULL_DISTANCE_NEAR, RL_CULL_DISTANCE_FAR);
matProj = MatrixOrtho(-right, right, -top, top, rlGetCullDistanceNear(), rlGetCullDistanceFar());
}
// Calculate view matrix from camera look at (and transpose it)
@ -1631,7 +1670,7 @@ float GetFrameTime(void)
// Ref: http://www.geisswerks.com/ryan/FAQS/timing.html --> All about timing on Win32!
void WaitTime(double seconds)
{
if (seconds < 0) return;
if (seconds < 0) return; // Security check
#if defined(SUPPORT_BUSY_WAIT_LOOP) || defined(SUPPORT_PARTIALBUSY_WAIT_LOOP)
double destinationTime = GetTime() + seconds;
@ -1657,7 +1696,7 @@ void WaitTime(double seconds)
req.tv_sec = sec;
req.tv_nsec = nsec;
// NOTE: Use nanosleep() on Unix platforms... usleep() it's deprecated.
// NOTE: Use nanosleep() on Unix platforms... usleep() it's deprecated
while (nanosleep(&req, &req) == -1) continue;
#endif
#if defined(__APPLE__)
@ -1677,7 +1716,6 @@ void WaitTime(double seconds)
// NOTE: Functions with a platform-specific implementation on rcore_<platform>.c
//void OpenURL(const char *url)
// Set the seed for the random number generator
void SetRandomSeed(unsigned int seed)
{
@ -1725,7 +1763,7 @@ int *LoadRandomSequence(unsigned int count, int min, int max)
#if defined(SUPPORT_RPRAND_GENERATOR)
values = rprand_load_sequence(count, min, max);
#else
if (count > ((unsigned int)abs(max - min) + 1)) return values;
if (count > ((unsigned int)abs(max - min) + 1)) return values; // Security check
values = (int *)RL_CALLOC(count, sizeof(int));
@ -1793,7 +1831,7 @@ void TakeScreenshot(const char *fileName)
// Setup window configuration flags (view FLAGS)
// NOTE: This function is expected to be called before window creation,
// because it sets up some flags for the window creation process.
// because it sets up some flags for the window creation process
// To configure window states after creation, just use SetWindowState()
void SetConfigFlags(unsigned int flags)
{
@ -1829,7 +1867,7 @@ bool FileExists(const char *fileName)
// NOTE: Extensions checking is not case-sensitive
bool IsFileExtension(const char *fileName, const char *ext)
{
#define MAX_FILE_EXTENSION_SIZE 16
#define MAX_FILE_EXTENSION_LENGTH 16
bool result = false;
const char *fileExt = GetFileExtension(fileName);
@ -1840,8 +1878,8 @@ bool IsFileExtension(const char *fileName, const char *ext)
int extCount = 0;
const char **checkExts = TextSplit(ext, ';', &extCount); // WARNING: Module required: rtext
char fileExtLower[MAX_FILE_EXTENSION_SIZE + 1] = { 0 };
strncpy(fileExtLower, TextToLower(fileExt), MAX_FILE_EXTENSION_SIZE); // WARNING: Module required: rtext
char fileExtLower[MAX_FILE_EXTENSION_LENGTH + 1] = { 0 };
strncpy(fileExtLower, TextToLower(fileExt), MAX_FILE_EXTENSION_LENGTH); // WARNING: Module required: rtext
for (int i = 0; i < extCount; i++)
{
@ -1917,7 +1955,9 @@ const char *GetFileExtension(const char *fileName)
static const char *strprbrk(const char *s, const char *charset)
{
const char *latestMatch = NULL;
for (; s = strpbrk(s, charset), s != NULL; latestMatch = s++) { }
return latestMatch;
}
@ -1925,9 +1965,10 @@ static const char *strprbrk(const char *s, const char *charset)
const char *GetFileName(const char *filePath)
{
const char *fileName = NULL;
if (filePath != NULL) fileName = strprbrk(filePath, "\\/");
if (!fileName) return filePath;
if (fileName == NULL) return filePath;
return fileName + 1;
}
@ -1935,16 +1976,17 @@ const char *GetFileName(const char *filePath)
// Get filename string without extension (uses static string)
const char *GetFileNameWithoutExt(const char *filePath)
{
#define MAX_FILENAMEWITHOUTEXT_LENGTH 256
#define MAX_FILENAME_LENGTH 256
static char fileName[MAX_FILENAMEWITHOUTEXT_LENGTH] = { 0 };
memset(fileName, 0, MAX_FILENAMEWITHOUTEXT_LENGTH);
if (filePath != NULL) strcpy(fileName, GetFileName(filePath)); // Get filename with extension
static char fileName[MAX_FILENAME_LENGTH] = { 0 };
memset(fileName, 0, MAX_FILENAME_LENGTH);
if (filePath != NULL)
{
strcpy(fileName, GetFileName(filePath)); // Get filename.ext without path
int size = (int)strlen(fileName); // Get size in bytes
for (int i = 0; (i < size) && (i < MAX_FILENAMEWITHOUTEXT_LENGTH); i++)
for (int i = size; i > 0; i--) // Reverse search '.'
{
if (fileName[i] == '.')
{
@ -1953,6 +1995,7 @@ const char *GetFileNameWithoutExt(const char *filePath)
break;
}
}
}
return fileName;
}
@ -2204,8 +2247,11 @@ bool IsPathFile(const char *path)
// Check if a file has been dropped into window
bool IsFileDropped(void)
{
if (CORE.Window.dropFileCount > 0) return true;
else return false;
bool result = false;
if (CORE.Window.dropFileCount > 0) result = true;
return result;
}
// Load dropped filepaths
@ -2239,15 +2285,16 @@ void UnloadDroppedFiles(FilePathList files)
long GetFileModTime(const char *fileName)
{
struct stat result = { 0 };
long modTime = 0;
if (stat(fileName, &result) == 0)
{
time_t mod = result.st_mtime;
return (long)mod;
modTime = (long)mod;
}
return 0;
return modTime;
}
//----------------------------------------------------------------------------------
@ -2316,7 +2363,7 @@ char *EncodeDataBase64(const unsigned char *data, int dataSize, int *outputSize)
char *encodedData = (char *)RL_MALLOC(*outputSize);
if (encodedData == NULL) return NULL;
if (encodedData == NULL) return NULL; // Security check
for (int i = 0, j = 0; i < dataSize;)
{
@ -2480,13 +2527,10 @@ AutomationEventList LoadAutomationEventList(const char *fileName)
}
// Unload automation events list from file
void UnloadAutomationEventList(AutomationEventList *list)
void UnloadAutomationEventList(AutomationEventList list)
{
#if defined(SUPPORT_AUTOMATION_EVENTS)
RL_FREE(list->events);
list->events = NULL;
list->count = 0;
list->capacity = 0;
RL_FREE(list.events);
#endif
}
@ -2623,8 +2667,9 @@ void PlayAutomationEvent(AutomationEvent event)
{
CORE.Input.Gamepad.axisState[event.params[0]][event.params[1]] = ((float)event.params[2]/32768.0f);
} break;
#if defined(SUPPORT_GESTURES_SYSTEM)
case INPUT_GESTURE: GESTURES.current = event.params[0]; break; // param[0]: gesture (enum Gesture) -> rgestures.h: GESTURES.current
#endif
// Window event
case WINDOW_CLOSE: CORE.Window.shouldClose = true; break;
case WINDOW_MAXIMIZE: MaximizeWindow(); break;
@ -2632,11 +2677,13 @@ void PlayAutomationEvent(AutomationEvent event)
case WINDOW_RESIZE: SetWindowSize(event.params[0], event.params[1]); break;
// Custom event
#if defined(SUPPORT_SCREEN_CAPTURE)
case ACTION_TAKE_SCREENSHOT:
{
TakeScreenshot(TextFormat("screenshot%03i.png", screenshotCounter));
screenshotCounter++;
} break;
#endif
case ACTION_SETTARGETFPS: SetTargetFPS(event.params[0]); break;
default: break;
}
@ -2918,13 +2965,15 @@ bool IsMouseButtonUp(int button)
// Get mouse position X
int GetMouseX(void)
{
return (int)((CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x);
int mouseX = (int)((CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x);
return mouseX;
}
// Get mouse position Y
int GetMouseY(void)
{
return (int)((CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y);
int mouseY = (int)((CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y);
return mouseY;
}
// Get mouse position XY
@ -2991,13 +3040,15 @@ Vector2 GetMouseWheelMoveV(void)
// Get touch position X for touch point 0 (relative to screen size)
int GetTouchX(void)
{
return (int)CORE.Input.Touch.position[0].x;
int touchX = (int)CORE.Input.Touch.position[0].x;
return touchX;
}
// Get touch position Y for touch point 0 (relative to screen size)
int GetTouchY(void)
{
return (int)CORE.Input.Touch.position[0].y;
int touchY = (int)CORE.Input.Touch.position[0].y;
return touchY;
}
// Get touch position XY for a touch point index (relative to screen size)
@ -3039,10 +3090,10 @@ int GetTouchPointCount(void)
// Initialize hi-resolution timer
void InitTimer(void)
{
// Setting a higher resolution can improve the accuracy of time-out intervals in wait functions.
// However, it can also reduce overall system performance, because the thread scheduler switches tasks more often.
// High resolutions can also prevent the CPU power management system from entering power-saving modes.
// Setting a higher resolution does not improve the accuracy of the high-resolution performance counter.
// Setting a higher resolution can improve the accuracy of time-out intervals in wait functions
// However, it can also reduce overall system performance, because the thread scheduler switches tasks more often
// High resolutions can also prevent the CPU power management system from entering power-saving modes
// Setting a higher resolution does not improve the accuracy of the high-resolution performance counter
#if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) && !defined(PLATFORM_DESKTOP_SDL)
timeBeginPeriod(1); // Setup high-resolution timer to 1ms (granularity of 1-2 ms)
#endif
@ -3265,7 +3316,7 @@ static void ScanDirectoryFilesRecursively(const char *basePath, FilePathList *fi
#if defined(SUPPORT_AUTOMATION_EVENTS)
// Automation event recording
// NOTE: Recording is by default done at EndDrawing(), after PollInputEvents()
// NOTE: Recording is by default done at EndDrawing(), before PollInputEvents()
static void RecordAutomationEvent(void)
{
// Checking events in current frame and save them into currentEventList
@ -3507,6 +3558,7 @@ static void RecordAutomationEvent(void)
}
//-------------------------------------------------------------------------------------
#if defined(SUPPORT_GESTURES_SYSTEM)
// Gestures input currentEventList->events recording
//-------------------------------------------------------------------------------------
if (GESTURES.current != GESTURE_NONE)
@ -3524,16 +3576,7 @@ static void RecordAutomationEvent(void)
if (currentEventList->count == currentEventList->capacity) return; // Security check
}
//-------------------------------------------------------------------------------------
// Window events recording
//-------------------------------------------------------------------------------------
// TODO.
//-------------------------------------------------------------------------------------
// Custom actions events recording
//-------------------------------------------------------------------------------------
// TODO.
//-------------------------------------------------------------------------------------
#endif
}
#endif

View file

@ -920,7 +920,7 @@ func LoadAutomationEventList(fileName string) AutomationEventList {
// UnloadAutomationEventList - Unload automation events list from file
func UnloadAutomationEventList(list *AutomationEventList) {
C.UnloadAutomationEventList(list.cptr())
C.UnloadAutomationEventList(*list.cptr())
}
// ExportAutomationEventList - Export automation events list as text file

View file

@ -21,7 +21,7 @@
*
* LICENSE: zlib/libpng
*
* Copyright (c) 2014-2023 Ramon Santamaria (@raysan5)
* Copyright (c) 2014-2024 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.

View file

@ -1,6 +1,6 @@
/**********************************************************************************************
*
* rlgl v4.5 - A multi-OpenGL abstraction layer with an immediate-mode style API
* rlgl v5.0 - A multi-OpenGL abstraction layer with an immediate-mode style API
*
* DESCRIPTION:
* An abstraction layer for multiple OpenGL versions (1.1, 2.1, 3.3 Core, 4.3 Core, ES 2.0)
@ -62,17 +62,17 @@
* When loading a shader, the following vertex attributes and uniform
* location names are tried to be set automatically:
*
* #define RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION "vertexPosition" // Bound by default to shader location: 0
* #define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD "vertexTexCoord" // Bound by default to shader location: 1
* #define RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL "vertexNormal" // Bound by default to shader location: 2
* #define RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR "vertexColor" // Bound by default to shader location: 3
* #define RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT "vertexTangent" // Bound by default to shader location: 4
* #define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2 "vertexTexCoord2" // Bound by default to shader location: 5
* #define RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION "vertexPosition" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION
* #define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD "vertexTexCoord" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD
* #define RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL "vertexNormal" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL
* #define RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR "vertexColor" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR
* #define RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT "vertexTangent" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT
* #define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2 "vertexTexCoord2" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2
* #define RL_DEFAULT_SHADER_UNIFORM_NAME_MVP "mvp" // model-view-projection matrix
* #define RL_DEFAULT_SHADER_UNIFORM_NAME_VIEW "matView" // view matrix
* #define RL_DEFAULT_SHADER_UNIFORM_NAME_PROJECTION "matProjection" // projection matrix
* #define RL_DEFAULT_SHADER_UNIFORM_NAME_MODEL "matModel" // model matrix
* #define RL_DEFAULT_SHADER_UNIFORM_NAME_NORMAL "matNormal" // normal matrix (transpose(inverse(matModelView))
* #define RL_DEFAULT_SHADER_UNIFORM_NAME_NORMAL "matNormal" // normal matrix (transpose(inverse(matModelView)))
* #define RL_DEFAULT_SHADER_UNIFORM_NAME_COLOR "colDiffuse" // color diffuse (base tint color, multiplied by texture color)
* #define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE0 "texture0" // texture0 (texture slot active 0)
* #define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE1 "texture1" // texture1 (texture slot active 1)
@ -85,7 +85,7 @@
*
* LICENSE: zlib/libpng
*
* Copyright (c) 2014-2023 Ramon Santamaria (@raysan5)
* Copyright (c) 2014-2024 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.
@ -107,17 +107,18 @@
#ifndef RLGL_H
#define RLGL_H
#define RLGL_VERSION "4.5"
#define RLGL_VERSION "5.0"
// Function specifiers in case library is build/used as a shared library (Windows)
// Function specifiers in case library is build/used as a shared library
// NOTE: Microsoft specifiers to tell compiler that symbols are imported/exported from a .dll
#if defined(_WIN32)
#if defined(BUILD_LIBTYPE_SHARED)
// NOTE: visibility(default) attribute makes symbols "visible" when compiled with -fvisibility=hidden
#if defined(_WIN32) && defined(BUILD_LIBTYPE_SHARED)
#define RLAPI __declspec(dllexport) // We are building the library as a Win32 shared library (.dll)
#elif defined(USE_LIBTYPE_SHARED)
#elif defined(BUILD_LIBTYPE_SHARED)
#define RLAPI __attribute__((visibility("default"))) // We are building the library as a Unix shared library (.so/.dylib)
#elif defined(_WIN32) && defined(USE_LIBTYPE_SHARED)
#define RLAPI __declspec(dllimport) // We are using the library as a Win32 shared library (.dll)
#endif
#endif
// Function specifiers definition
#ifndef RLAPI
@ -318,6 +319,28 @@
#define RL_BLEND_SRC_ALPHA 0x80CB // GL_BLEND_SRC_ALPHA
#define RL_BLEND_COLOR 0x8005 // GL_BLEND_COLOR
#define RL_READ_FRAMEBUFFER 0x8CA8 // GL_READ_FRAMEBUFFER
#define RL_DRAW_FRAMEBUFFER 0x8CA9 // GL_DRAW_FRAMEBUFFER
// Default shader vertex attribute locations
#ifndef RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION
#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION 0
#endif
#ifndef RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD
#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD 1
#endif
#ifndef RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL
#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL 2
#endif
#ifndef RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR
#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR 3
#endif
#ifndef RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT
#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT 4
#endif
#ifndef RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2
#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2 5
#endif
//----------------------------------------------------------------------------------
// Types and Structures Definition
@ -346,6 +369,7 @@ typedef struct rlVertexBuffer {
float *vertices; // Vertex position (XYZ - 3 components per vertex) (shader-location = 0)
float *texcoords; // Vertex texture coordinates (UV - 2 components per vertex) (shader-location = 1)
float *normals; // Vertex normal (XYZ - 3 components per vertex) (shader-location = 2)
unsigned char *colors; // Vertex colors (RGBA - 4 components per vertex) (shader-location = 3)
#if defined(GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_33)
unsigned int *indices; // Vertex indices (in case vertex data comes indexed) (6 indices per quad)
@ -354,7 +378,7 @@ typedef struct rlVertexBuffer {
unsigned short *indices; // Vertex indices (in case vertex data comes indexed) (6 indices per quad)
#endif
unsigned int vaoId; // OpenGL Vertex Array Object id
unsigned int vboId[4]; // OpenGL Vertex Buffer Objects id (4 types of vertex data)
unsigned int vboId[5]; // OpenGL Vertex Buffer Objects id (5 types of vertex data)
} rlVertexBuffer;
// Draw call type
@ -566,6 +590,9 @@ RLAPI void rlMultMatrixf(const float *matf); // Multiply the curr
RLAPI void rlFrustum(double left, double right, double bottom, double top, double znear, double zfar);
RLAPI void rlOrtho(double left, double right, double bottom, double top, double znear, double zfar);
RLAPI void rlViewport(int x, int y, int width, int height); // Set the viewport area
RLAPI void rlSetClipPlanes(double near, double far); // Set clip planes distances
RLAPI double rlGetCullDistanceNear(); // Get cull plane distance near
RLAPI double rlGetCullDistanceFar(); // Get cull plane distance far
//------------------------------------------------------------------------------------
// Functions Declaration - Vertex level operations
@ -617,8 +644,10 @@ RLAPI void rlDisableShader(void); // Disable shader progra
// Framebuffer state
RLAPI void rlEnableFramebuffer(unsigned int id); // Enable render texture (fbo)
RLAPI void rlDisableFramebuffer(void); // Disable render texture (fbo), return to default framebuffer
RLAPI unsigned int rlGetActiveFramebuffer(void); // Get the currently active render texture (fbo), 0 for default framebuffer
RLAPI void rlActiveDrawBuffers(int count); // Activate multiple draw color buffers
RLAPI void rlBlitFramebuffer(int srcX, int srcY, int srcWidth, int srcHeight, int dstX, int dstY, int dstWidth, int dstHeight, int bufferMask); // Blit active framebuffer to main framebuffer
RLAPI void rlBindFramebuffer(unsigned int target, unsigned int framebuffer); // Bind framebuffer (FBO)
// General render state
RLAPI void rlEnableColorBlend(void); // Enable color blending
@ -629,6 +658,7 @@ RLAPI void rlEnableDepthMask(void); // Enable depth write
RLAPI void rlDisableDepthMask(void); // Disable depth write
RLAPI void rlEnableBackfaceCulling(void); // Enable backface culling
RLAPI void rlDisableBackfaceCulling(void); // Disable backface culling
RLAPI void rlColorMask(bool r, bool g, bool b, bool a); // Color mask control
RLAPI void rlSetCullFace(int mode); // Set face culling mode
RLAPI void rlEnableScissorTest(void); // Enable scissor test
RLAPI void rlDisableScissorTest(void); // Disable scissor test
@ -684,25 +714,25 @@ RLAPI void rlSetTexture(unsigned int id); // Set current texture f
// Vertex buffers management
RLAPI unsigned int rlLoadVertexArray(void); // Load vertex array (vao) if supported
RLAPI unsigned int rlLoadVertexBuffer(const void *buffer, int size, bool dynamic); // Load a vertex buffer attribute
RLAPI unsigned int rlLoadVertexBufferElement(const void *buffer, int size, bool dynamic); // Load a new attributes element buffer
RLAPI void rlUpdateVertexBuffer(unsigned int bufferId, const void *data, int dataSize, int offset); // Update GPU buffer with new data
RLAPI void rlUpdateVertexBufferElements(unsigned int id, const void *data, int dataSize, int offset); // Update vertex buffer elements with new data
RLAPI void rlUnloadVertexArray(unsigned int vaoId);
RLAPI void rlUnloadVertexBuffer(unsigned int vboId);
RLAPI void rlSetVertexAttribute(unsigned int index, int compSize, int type, bool normalized, int stride, const void *pointer);
RLAPI void rlSetVertexAttributeDivisor(unsigned int index, int divisor);
RLAPI void rlSetVertexAttributeDefault(int locIndex, const void *value, int attribType, int count); // Set vertex attribute default value
RLAPI void rlDrawVertexArray(int offset, int count);
RLAPI void rlDrawVertexArrayElements(int offset, int count, const void *buffer);
RLAPI void rlDrawVertexArrayInstanced(int offset, int count, int instances);
RLAPI void rlDrawVertexArrayElementsInstanced(int offset, int count, const void *buffer, int instances);
RLAPI unsigned int rlLoadVertexBuffer(const void *buffer, int size, bool dynamic); // Load a vertex buffer object
RLAPI unsigned int rlLoadVertexBufferElement(const void *buffer, int size, bool dynamic); // Load vertex buffer elements object
RLAPI void rlUpdateVertexBuffer(unsigned int bufferId, const void *data, int dataSize, int offset); // Update vertex buffer object data on GPU buffer
RLAPI void rlUpdateVertexBufferElements(unsigned int id, const void *data, int dataSize, int offset); // Update vertex buffer elements data on GPU buffer
RLAPI void rlUnloadVertexArray(unsigned int vaoId); // Unload vertex array (vao)
RLAPI void rlUnloadVertexBuffer(unsigned int vboId); // Unload vertex buffer object
RLAPI void rlSetVertexAttribute(unsigned int index, int compSize, int type, bool normalized, int stride, int offset); // Set vertex attribute data configuration
RLAPI void rlSetVertexAttributeDivisor(unsigned int index, int divisor); // Set vertex attribute data divisor
RLAPI void rlSetVertexAttributeDefault(int locIndex, const void *value, int attribType, int count); // Set vertex attribute default value, when attribute to provided
RLAPI void rlDrawVertexArray(int offset, int count); // Draw vertex array (currently active vao)
RLAPI void rlDrawVertexArrayElements(int offset, int count, const void *buffer); // Draw vertex array elements
RLAPI void rlDrawVertexArrayInstanced(int offset, int count, int instances); // Draw vertex array (currently active vao) with instancing
RLAPI void rlDrawVertexArrayElementsInstanced(int offset, int count, const void *buffer, int instances); // Draw vertex array elements with instancing
// Textures management
RLAPI unsigned int rlLoadTexture(const void *data, int width, int height, int format, int mipmapCount); // Load texture in GPU
RLAPI unsigned int rlLoadTexture(const void *data, int width, int height, int format, int mipmapCount); // Load texture data
RLAPI unsigned int rlLoadTextureDepth(int width, int height, bool useRenderBuffer); // Load depth texture/renderbuffer (to be attached to fbo)
RLAPI unsigned int rlLoadTextureCubemap(const void *data, int size, int format); // Load texture cubemap
RLAPI void rlUpdateTexture(unsigned int id, int offsetX, int offsetY, int width, int height, int format, const void *data); // Update GPU texture with new data
RLAPI unsigned int rlLoadTextureCubemap(const void *data, int size, int format); // Load texture cubemap data
RLAPI void rlUpdateTexture(unsigned int id, int offsetX, int offsetY, int width, int height, int format, const void *data); // Update texture with new data on GPU
RLAPI void rlGetGlTextureFormats(int format, unsigned int *glInternalFormat, unsigned int *glFormat, unsigned int *glType); // Get OpenGL internal formats
RLAPI const char *rlGetPixelFormatName(unsigned int format); // Get name string for pixel format
RLAPI void rlUnloadTexture(unsigned int id); // Unload texture from GPU memory
@ -711,7 +741,7 @@ RLAPI void *rlReadTexturePixels(unsigned int id, int width, int height, int form
RLAPI unsigned char *rlReadScreenPixels(int width, int height); // Read screen pixel data (color buffer)
// Framebuffer management (fbo)
RLAPI unsigned int rlLoadFramebuffer(int width, int height); // Load an empty framebuffer
RLAPI unsigned int rlLoadFramebuffer(void); // Load an empty framebuffer
RLAPI void rlFramebufferAttach(unsigned int fboId, unsigned int texId, int attachType, int texType, int mipLevel); // Attach texture/renderbuffer to a framebuffer
RLAPI bool rlFramebufferComplete(unsigned int id); // Verify framebuffer is complete
RLAPI void rlUnloadFramebuffer(unsigned int id); // Delete framebuffer from GPU
@ -773,6 +803,12 @@ RLAPI void rlLoadDrawQuad(void); // Load and draw a quad
#if defined(RLGL_IMPLEMENTATION)
// Expose OpenGL functions from glad in raylib
#if defined(BUILD_LIBTYPE_SHARED)
#define GLAD_API_CALL_EXPORT
#define GLAD_API_CALL_EXPORT_BUILD
#endif
#if defined(GRAPHICS_API_OPENGL_11)
#if defined(__APPLE__)
#include <OpenGL/gl.h> // OpenGL 1.1 library for OSX
@ -891,6 +927,14 @@ RLAPI void rlLoadDrawQuad(void); // Load and draw a quad
#define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE
#endif
#ifndef GL_PROGRAM_POINT_SIZE
#define GL_PROGRAM_POINT_SIZE 0x8642
#endif
#ifndef GL_LINE_WIDTH
#define GL_LINE_WIDTH 0x0B21
#endif
#if defined(GRAPHICS_API_OPENGL_11)
#define GL_UNSIGNED_SHORT_5_6_5 0x8363
#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034
@ -912,22 +956,22 @@ RLAPI void rlLoadDrawQuad(void); // Load and draw a quad
// Default shader vertex attribute names to set location points
#ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION
#define RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION "vertexPosition" // Bound by default to shader location: 0
#define RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION "vertexPosition" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION
#endif
#ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD
#define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD "vertexTexCoord" // Bound by default to shader location: 1
#define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD "vertexTexCoord" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD
#endif
#ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL
#define RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL "vertexNormal" // Bound by default to shader location: 2
#define RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL "vertexNormal" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL
#endif
#ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR
#define RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR "vertexColor" // Bound by default to shader location: 3
#define RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR "vertexColor" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR
#endif
#ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT
#define RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT "vertexTangent" // Bound by default to shader location: 4
#define RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT "vertexTangent" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT
#endif
#ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2
#define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2 "vertexTexCoord2" // Bound by default to shader location: 5
#define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2 "vertexTexCoord2" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2
#endif
#ifndef RL_DEFAULT_SHADER_UNIFORM_NAME_MVP
@ -1042,6 +1086,9 @@ typedef void *(*rlglLoadProc)(const char *name); // OpenGL extension functions
//----------------------------------------------------------------------------------
// Global Variables Definition
//----------------------------------------------------------------------------------
static double rlCullDistanceNear = RL_CULL_DISTANCE_NEAR;
static double rlCullDistanceFar = RL_CULL_DISTANCE_FAR;
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
static rlglData RLGL = { 0 };
#endif // GRAPHICS_API_OPENGL_33 || GRAPHICS_API_OPENGL_ES2
@ -1072,8 +1119,15 @@ static const char *rlGetCompressedFormatName(int format); // Get compressed form
static int rlGetPixelDataSize(int width, int height, int format); // Get pixel data size in bytes (image or texture)
// Auxiliar matrix math functions
typedef struct rl_float16 {
float v[16];
} rl_float16;
static rl_float16 rlMatrixToFloatV(Matrix mat); // Get float array of matrix data
#define rlMatrixToFloat(mat) (rlMatrixToFloatV(mat).v) // Get float vector for Matrix
static Matrix rlMatrixIdentity(void); // Get identity matrix
static Matrix rlMatrixMultiply(Matrix left, Matrix right); // Multiply two matrices
static Matrix rlMatrixTranspose(Matrix mat); // Transposes provided matrix
static Matrix rlMatrixInvert(Matrix mat); // Invert provided matrix
//----------------------------------------------------------------------------------
// Module Functions Definition - Matrix operations
@ -1242,7 +1296,7 @@ void rlMultMatrixf(const float *matf)
matf[2], matf[6], matf[10], matf[14],
matf[3], matf[7], matf[11], matf[15] };
*RLGL.State.currentMatrix = rlMatrixMultiply(*RLGL.State.currentMatrix, mat);
*RLGL.State.currentMatrix = rlMatrixMultiply(mat, *RLGL.State.currentMatrix);
}
// Multiply the current matrix by a perspective matrix generated by parameters
@ -1316,6 +1370,25 @@ void rlViewport(int x, int y, int width, int height)
glViewport(x, y, width, height);
}
// Set clip planes distances
void rlSetClipPlanes(double near, double far)
{
rlCullDistanceNear = near;
rlCullDistanceFar = far;
}
// Get cull plane distance near
double rlGetCullDistanceNear(void)
{
return rlCullDistanceNear;
}
// Get cull plane distance far
double rlGetCullDistanceFar(void)
{
return rlCullDistanceFar;
}
//----------------------------------------------------------------------------------
// Module Functions Definition - Vertex level operations
//----------------------------------------------------------------------------------
@ -1436,7 +1509,10 @@ void rlVertex3f(float x, float y, float z)
RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].texcoords[2*RLGL.State.vertexCounter] = RLGL.State.texcoordx;
RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].texcoords[2*RLGL.State.vertexCounter + 1] = RLGL.State.texcoordy;
// WARNING: By default rlVertexBuffer struct does not store normals
// Add current normal
RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].normals[3*RLGL.State.vertexCounter] = RLGL.State.normalx;
RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].normals[3*RLGL.State.vertexCounter + 1] = RLGL.State.normaly;
RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].normals[3*RLGL.State.vertexCounter + 2] = RLGL.State.normalz;
// Add current color
RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].colors[4*RLGL.State.vertexCounter] = RLGL.State.colorr;
@ -1472,9 +1548,26 @@ void rlTexCoord2f(float x, float y)
// NOTE: Normals limited to TRIANGLES only?
void rlNormal3f(float x, float y, float z)
{
RLGL.State.normalx = x;
RLGL.State.normaly = y;
RLGL.State.normalz = z;
float normalx = x;
float normaly = y;
float normalz = z;
if (RLGL.State.transformRequired)
{
normalx = RLGL.State.transform.m0*x + RLGL.State.transform.m4*y + RLGL.State.transform.m8*z;
normaly = RLGL.State.transform.m1*x + RLGL.State.transform.m5*y + RLGL.State.transform.m9*z;
normalz = RLGL.State.transform.m2*x + RLGL.State.transform.m6*y + RLGL.State.transform.m10*z;
}
float length = sqrtf(normalx*normalx + normaly*normaly + normalz*normalz);
if (length != 0.0f)
{
float ilength = 1.0f / length;
normalx *= ilength;
normaly *= ilength;
normalz *= ilength;
}
RLGL.State.normalx = normalx;
RLGL.State.normaly = normaly;
RLGL.State.normalz = normalz;
}
// Define one vertex (color)
@ -1713,6 +1806,16 @@ void rlEnableFramebuffer(unsigned int id)
#endif
}
// return the active render texture (fbo)
unsigned int rlGetActiveFramebuffer(void)
{
GLint fboId = 0;
#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES3)) && defined(RLGL_RENDER_TEXTURES_HINT)
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &fboId);
#endif
return fboId;
}
// Disable rendering to texture
void rlDisableFramebuffer(void)
{
@ -1729,6 +1832,14 @@ void rlBlitFramebuffer(int srcX, int srcY, int srcWidth, int srcHeight, int dstX
#endif
}
// Bind framebuffer object (fbo)
void rlBindFramebuffer(unsigned int target, unsigned int framebuffer)
{
#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) && defined(RLGL_RENDER_TEXTURES_HINT)
glBindFramebuffer(target, framebuffer);
#endif
}
// Activate multiple draw color buffers
// NOTE: One color buffer is always active by default
void rlActiveDrawBuffers(int count)
@ -1805,6 +1916,9 @@ void rlEnableBackfaceCulling(void) { glEnable(GL_CULL_FACE); }
// Disable backface culling
void rlDisableBackfaceCulling(void) { glDisable(GL_CULL_FACE); }
// Set color mask active for screen read/draw
void rlColorMask(bool r, bool g, bool b, bool a) { glColorMask(r, g, b, a); }
// Set face culling mode
void rlSetCullFace(int mode)
{
@ -2122,7 +2236,10 @@ void rlglInit(int width, int height)
RLGL.State.currentShaderLocs = RLGL.State.defaultShaderLocs;
// Init default vertex arrays buffers
// Simulate that the default shader has the location RL_SHADER_LOC_VERTEX_NORMAL to bind the normal buffer for the default render batch
RLGL.State.currentShaderLocs[RL_SHADER_LOC_VERTEX_NORMAL] = RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL;
RLGL.defaultBatch = rlLoadRenderBatch(RL_DEFAULT_BATCH_BUFFERS, RL_DEFAULT_BATCH_BUFFER_ELEMENTS);
RLGL.State.currentShaderLocs[RL_SHADER_LOC_VERTEX_NORMAL] = -1;
RLGL.currentBatch = &RLGL.defaultBatch;
// Init stack matrices (emulating OpenGL 1.1)
@ -2567,6 +2684,7 @@ rlRenderBatch rlLoadRenderBatch(int numBuffers, int bufferElements)
batch.vertexBuffer[i].vertices = (float *)RL_MALLOC(bufferElements*3*4*sizeof(float)); // 3 float by vertex, 4 vertex by quad
batch.vertexBuffer[i].texcoords = (float *)RL_MALLOC(bufferElements*2*4*sizeof(float)); // 2 float by texcoord, 4 texcoord by quad
batch.vertexBuffer[i].normals = (float *)RL_MALLOC(bufferElements*3*4*sizeof(float)); // 3 float by vertex, 4 vertex by quad
batch.vertexBuffer[i].colors = (unsigned char *)RL_MALLOC(bufferElements*4*4*sizeof(unsigned char)); // 4 float by color, 4 colors by quad
#if defined(GRAPHICS_API_OPENGL_33)
batch.vertexBuffer[i].indices = (unsigned int *)RL_MALLOC(bufferElements*6*sizeof(unsigned int)); // 6 int by quad (indices)
@ -2577,6 +2695,7 @@ rlRenderBatch rlLoadRenderBatch(int numBuffers, int bufferElements)
for (int j = 0; j < (3*4*bufferElements); j++) batch.vertexBuffer[i].vertices[j] = 0.0f;
for (int j = 0; j < (2*4*bufferElements); j++) batch.vertexBuffer[i].texcoords[j] = 0.0f;
for (int j = 0; j < (3*4*bufferElements); j++) batch.vertexBuffer[i].normals[j] = 0.0f;
for (int j = 0; j < (4*4*bufferElements); j++) batch.vertexBuffer[i].colors[j] = 0;
int k = 0;
@ -2626,16 +2745,23 @@ rlRenderBatch rlLoadRenderBatch(int numBuffers, int bufferElements)
glEnableVertexAttribArray(RLGL.State.currentShaderLocs[RL_SHADER_LOC_VERTEX_TEXCOORD01]);
glVertexAttribPointer(RLGL.State.currentShaderLocs[RL_SHADER_LOC_VERTEX_TEXCOORD01], 2, GL_FLOAT, 0, 0, 0);
// Vertex color buffer (shader-location = 3)
// Vertex normal buffer (shader-location = 2)
glGenBuffers(1, &batch.vertexBuffer[i].vboId[2]);
glBindBuffer(GL_ARRAY_BUFFER, batch.vertexBuffer[i].vboId[2]);
glBufferData(GL_ARRAY_BUFFER, bufferElements*3*4*sizeof(float), batch.vertexBuffer[i].normals, GL_DYNAMIC_DRAW);
glEnableVertexAttribArray(RLGL.State.currentShaderLocs[RL_SHADER_LOC_VERTEX_NORMAL]);
glVertexAttribPointer(RLGL.State.currentShaderLocs[RL_SHADER_LOC_VERTEX_NORMAL], 3, GL_FLOAT, 0, 0, 0);
// Vertex color buffer (shader-location = 3)
glGenBuffers(1, &batch.vertexBuffer[i].vboId[3]);
glBindBuffer(GL_ARRAY_BUFFER, batch.vertexBuffer[i].vboId[3]);
glBufferData(GL_ARRAY_BUFFER, bufferElements*4*4*sizeof(unsigned char), batch.vertexBuffer[i].colors, GL_DYNAMIC_DRAW);
glEnableVertexAttribArray(RLGL.State.currentShaderLocs[RL_SHADER_LOC_VERTEX_COLOR]);
glVertexAttribPointer(RLGL.State.currentShaderLocs[RL_SHADER_LOC_VERTEX_COLOR], 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0);
// Fill index buffer
glGenBuffers(1, &batch.vertexBuffer[i].vboId[3]);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, batch.vertexBuffer[i].vboId[3]);
glGenBuffers(1, &batch.vertexBuffer[i].vboId[4]);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, batch.vertexBuffer[i].vboId[4]);
#if defined(GRAPHICS_API_OPENGL_33)
glBufferData(GL_ELEMENT_ARRAY_BUFFER, bufferElements*6*sizeof(int), batch.vertexBuffer[i].indices, GL_STATIC_DRAW);
#endif
@ -2690,10 +2816,10 @@ void rlUnloadRenderBatch(rlRenderBatch batch)
if (RLGL.ExtSupported.vao)
{
glBindVertexArray(batch.vertexBuffer[i].vaoId);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glDisableVertexAttribArray(2);
glDisableVertexAttribArray(3);
glDisableVertexAttribArray(RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION);
glDisableVertexAttribArray(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD);
glDisableVertexAttribArray(RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL);
glDisableVertexAttribArray(RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR);
glBindVertexArray(0);
}
@ -2702,6 +2828,7 @@ void rlUnloadRenderBatch(rlRenderBatch batch)
glDeleteBuffers(1, &batch.vertexBuffer[i].vboId[1]);
glDeleteBuffers(1, &batch.vertexBuffer[i].vboId[2]);
glDeleteBuffers(1, &batch.vertexBuffer[i].vboId[3]);
glDeleteBuffers(1, &batch.vertexBuffer[i].vboId[4]);
// Delete VAOs from GPU (VRAM)
if (RLGL.ExtSupported.vao) glDeleteVertexArrays(1, &batch.vertexBuffer[i].vaoId);
@ -2709,6 +2836,7 @@ void rlUnloadRenderBatch(rlRenderBatch batch)
// Free vertex arrays memory from CPU (RAM)
RL_FREE(batch.vertexBuffer[i].vertices);
RL_FREE(batch.vertexBuffer[i].texcoords);
RL_FREE(batch.vertexBuffer[i].normals);
RL_FREE(batch.vertexBuffer[i].colors);
RL_FREE(batch.vertexBuffer[i].indices);
}
@ -2743,8 +2871,13 @@ void rlDrawRenderBatch(rlRenderBatch *batch)
glBufferSubData(GL_ARRAY_BUFFER, 0, RLGL.State.vertexCounter*2*sizeof(float), batch->vertexBuffer[batch->currentBuffer].texcoords);
//glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*4*batch->vertexBuffer[batch->currentBuffer].elementCount, batch->vertexBuffer[batch->currentBuffer].texcoords, GL_DYNAMIC_DRAW); // Update all buffer
// Colors buffer
// Normals buffer
glBindBuffer(GL_ARRAY_BUFFER, batch->vertexBuffer[batch->currentBuffer].vboId[2]);
glBufferSubData(GL_ARRAY_BUFFER, 0, RLGL.State.vertexCounter*3*sizeof(float), batch->vertexBuffer[batch->currentBuffer].normals);
//glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*4*batch->vertexBuffer[batch->currentBuffer].elementCount, batch->vertexBuffer[batch->currentBuffer].normals, GL_DYNAMIC_DRAW); // Update all buffer
// Colors buffer
glBindBuffer(GL_ARRAY_BUFFER, batch->vertexBuffer[batch->currentBuffer].vboId[3]);
glBufferSubData(GL_ARRAY_BUFFER, 0, RLGL.State.vertexCounter*4*sizeof(unsigned char), batch->vertexBuffer[batch->currentBuffer].colors);
//glBufferData(GL_ARRAY_BUFFER, sizeof(float)*4*4*batch->vertexBuffer[batch->currentBuffer].elementCount, batch->vertexBuffer[batch->currentBuffer].colors, GL_DYNAMIC_DRAW); // Update all buffer
@ -2797,13 +2930,30 @@ void rlDrawRenderBatch(rlRenderBatch *batch)
// Create modelview-projection matrix and upload to shader
Matrix matMVP = rlMatrixMultiply(RLGL.State.modelview, RLGL.State.projection);
float matMVPfloat[16] = {
matMVP.m0, matMVP.m1, matMVP.m2, matMVP.m3,
matMVP.m4, matMVP.m5, matMVP.m6, matMVP.m7,
matMVP.m8, matMVP.m9, matMVP.m10, matMVP.m11,
matMVP.m12, matMVP.m13, matMVP.m14, matMVP.m15
};
glUniformMatrix4fv(RLGL.State.currentShaderLocs[RL_SHADER_LOC_MATRIX_MVP], 1, false, matMVPfloat);
glUniformMatrix4fv(RLGL.State.currentShaderLocs[RL_SHADER_LOC_MATRIX_MVP], 1, false, rlMatrixToFloat(matMVP));
if (RLGL.State.currentShaderLocs[RL_SHADER_LOC_MATRIX_PROJECTION] != -1)
{
glUniformMatrix4fv(RLGL.State.currentShaderLocs[RL_SHADER_LOC_MATRIX_PROJECTION], 1, false, rlMatrixToFloat(RLGL.State.projection));
}
// WARNING: For the following setup of the view, model, and normal matrices, it is expected that
// transformations and rendering occur between rlPushMatrix and rlPopMatrix.
if (RLGL.State.currentShaderLocs[RL_SHADER_LOC_MATRIX_VIEW] != -1)
{
glUniformMatrix4fv(RLGL.State.currentShaderLocs[RL_SHADER_LOC_MATRIX_VIEW], 1, false, rlMatrixToFloat(RLGL.State.modelview));
}
if (RLGL.State.currentShaderLocs[RL_SHADER_LOC_MATRIX_MODEL] != -1)
{
glUniformMatrix4fv(RLGL.State.currentShaderLocs[RL_SHADER_LOC_MATRIX_MODEL], 1, false, rlMatrixToFloat(RLGL.State.transform));
}
if (RLGL.State.currentShaderLocs[RL_SHADER_LOC_MATRIX_NORMAL] != -1)
{
glUniformMatrix4fv(RLGL.State.currentShaderLocs[RL_SHADER_LOC_MATRIX_NORMAL], 1, false, rlMatrixToFloat(rlMatrixTranspose(rlMatrixInvert(RLGL.State.transform))));
}
if (RLGL.ExtSupported.vao) glBindVertexArray(batch->vertexBuffer[batch->currentBuffer].vaoId);
else
@ -2818,12 +2968,17 @@ void rlDrawRenderBatch(rlRenderBatch *batch)
glVertexAttribPointer(RLGL.State.currentShaderLocs[RL_SHADER_LOC_VERTEX_TEXCOORD01], 2, GL_FLOAT, 0, 0, 0);
glEnableVertexAttribArray(RLGL.State.currentShaderLocs[RL_SHADER_LOC_VERTEX_TEXCOORD01]);
// Bind vertex attrib: color (shader-location = 3)
// Bind vertex attrib: normal (shader-location = 2)
glBindBuffer(GL_ARRAY_BUFFER, batch->vertexBuffer[batch->currentBuffer].vboId[2]);
glVertexAttribPointer(RLGL.State.currentShaderLocs[RL_SHADER_LOC_VERTEX_NORMAL], 3, GL_FLOAT, 0, 0, 0);
glEnableVertexAttribArray(RLGL.State.currentShaderLocs[RL_SHADER_LOC_VERTEX_NORMAL]);
// Bind vertex attrib: color (shader-location = 3)
glBindBuffer(GL_ARRAY_BUFFER, batch->vertexBuffer[batch->currentBuffer].vboId[3]);
glVertexAttribPointer(RLGL.State.currentShaderLocs[RL_SHADER_LOC_VERTEX_COLOR], 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0);
glEnableVertexAttribArray(RLGL.State.currentShaderLocs[RL_SHADER_LOC_VERTEX_COLOR]);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, batch->vertexBuffer[batch->currentBuffer].vboId[3]);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, batch->vertexBuffer[batch->currentBuffer].vboId[4]);
}
// Setup some default shader values
@ -3378,7 +3533,6 @@ void rlGenTextureMipmaps(unsigned int id, int width, int height, int format, int
#endif
}
// Read texture pixel data
void *rlReadTexturePixels(unsigned int id, int width, int height, int format)
{
@ -3422,7 +3576,7 @@ void *rlReadTexturePixels(unsigned int id, int width, int height, int format)
// 2 - Create an fbo, activate it, render quad with texture, glReadPixels()
// We are using Option 1, just need to care for texture format on retrieval
// NOTE: This behaviour could be conditioned by graphic driver...
unsigned int fboId = rlLoadFramebuffer(width, height);
unsigned int fboId = rlLoadFramebuffer();
glBindFramebuffer(GL_FRAMEBUFFER, fboId);
glBindTexture(GL_TEXTURE_2D, 0);
@ -3476,7 +3630,7 @@ unsigned char *rlReadScreenPixels(int width, int height)
//-----------------------------------------------------------------------------------------
// Load a framebuffer to be used for rendering
// NOTE: No textures attached
unsigned int rlLoadFramebuffer(int width, int height)
unsigned int rlLoadFramebuffer(void)
{
unsigned int fboId = 0;
@ -3784,10 +3938,14 @@ unsigned int rlLoadVertexArray(void)
}
// Set vertex attribute
void rlSetVertexAttribute(unsigned int index, int compSize, int type, bool normalized, int stride, const void *pointer)
void rlSetVertexAttribute(unsigned int index, int compSize, int type, bool normalized, int stride, int offset)
{
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
glVertexAttribPointer(index, compSize, type, normalized, stride, pointer);
// NOTE: Data type could be: GL_BYTE, GL_UNSIGNED_BYTE, GL_SHORT, GL_UNSIGNED_SHORT, GL_INT, GL_UNSIGNED_INT
// Additional types (depends on OpenGL version or extensions):
// - GL_HALF_FLOAT, GL_FLOAT, GL_DOUBLE, GL_FIXED,
// - GL_INT_2_10_10_10_REV, GL_UNSIGNED_INT_2_10_10_10_REV, GL_UNSIGNED_INT_10F_11F_11F_REV
glVertexAttribPointer(index, compSize, type, normalized, stride, (void *)offset);
#endif
}
@ -3970,12 +4128,12 @@ unsigned int rlLoadShaderProgram(unsigned int vShaderId, unsigned int fShaderId)
glAttachShader(program, fShaderId);
// NOTE: Default attribute shader locations must be Bound before linking
glBindAttribLocation(program, 0, RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION);
glBindAttribLocation(program, 1, RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD);
glBindAttribLocation(program, 2, RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL);
glBindAttribLocation(program, 3, RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR);
glBindAttribLocation(program, 4, RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT);
glBindAttribLocation(program, 5, RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2);
glBindAttribLocation(program, RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION, RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION);
glBindAttribLocation(program, RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD, RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD);
glBindAttribLocation(program, RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL, RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL);
glBindAttribLocation(program, RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR, RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR);
glBindAttribLocation(program, RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT, RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT);
glBindAttribLocation(program, RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2, RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2);
// NOTE: If some attrib name is no found on the shader, it locations becomes -1
@ -4108,7 +4266,14 @@ void rlSetUniformSampler(int locIndex, unsigned int textureId)
{
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
// Check if texture is already active
for (int i = 0; i < RL_DEFAULT_BATCH_MAX_TEXTURE_UNITS; i++) if (RLGL.State.activeTextureId[i] == textureId) return;
for (int i = 0; i < RL_DEFAULT_BATCH_MAX_TEXTURE_UNITS; i++)
{
if (RLGL.State.activeTextureId[i] == textureId)
{
glUniform1i(locIndex, 1 + i);
return;
}
}
// Register a new active texture for the internal batch system
// NOTE: Default texture is always activated as GL_TEXTURE0
@ -4431,10 +4596,10 @@ void rlLoadDrawQuad(void)
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), &vertices, GL_STATIC_DRAW);
// Bind vertex attributes (position, texcoords)
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5*sizeof(float), (void *)0); // Positions
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5*sizeof(float), (void *)(3*sizeof(float))); // Texcoords
glEnableVertexAttribArray(RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION);
glVertexAttribPointer(RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION, 3, GL_FLOAT, GL_FALSE, 5*sizeof(float), (void *)0); // Positions
glEnableVertexAttribArray(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD);
glVertexAttribPointer(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD, 2, GL_FLOAT, GL_FALSE, 5*sizeof(float), (void *)(3*sizeof(float))); // Texcoords
// Draw quad
glBindVertexArray(quadVAO);
@ -4505,12 +4670,12 @@ void rlLoadDrawCube(void)
// Bind vertex attributes (position, normals, texcoords)
glBindVertexArray(cubeVAO);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8*sizeof(float), (void *)0); // Positions
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8*sizeof(float), (void *)(3*sizeof(float))); // Normals
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8*sizeof(float), (void *)(6*sizeof(float))); // Texcoords
glEnableVertexAttribArray(RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION);
glVertexAttribPointer(RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION, 3, GL_FLOAT, GL_FALSE, 8*sizeof(float), (void *)0); // Positions
glEnableVertexAttribArray(RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL);
glVertexAttribPointer(RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL, 3, GL_FLOAT, GL_FALSE, 8*sizeof(float), (void *)(3*sizeof(float))); // Normals
glEnableVertexAttribArray(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD);
glVertexAttribPointer(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD, 2, GL_FLOAT, GL_FALSE, 8*sizeof(float), (void *)(6*sizeof(float))); // Texcoords
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
@ -4658,14 +4823,14 @@ static void rlLoadShaderDefault(void)
TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Default shader loaded successfully", RLGL.State.defaultShaderId);
// Set default shader locations: attributes locations
RLGL.State.defaultShaderLocs[RL_SHADER_LOC_VERTEX_POSITION] = glGetAttribLocation(RLGL.State.defaultShaderId, "vertexPosition");
RLGL.State.defaultShaderLocs[RL_SHADER_LOC_VERTEX_TEXCOORD01] = glGetAttribLocation(RLGL.State.defaultShaderId, "vertexTexCoord");
RLGL.State.defaultShaderLocs[RL_SHADER_LOC_VERTEX_COLOR] = glGetAttribLocation(RLGL.State.defaultShaderId, "vertexColor");
RLGL.State.defaultShaderLocs[RL_SHADER_LOC_VERTEX_POSITION] = glGetAttribLocation(RLGL.State.defaultShaderId, RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION);
RLGL.State.defaultShaderLocs[RL_SHADER_LOC_VERTEX_TEXCOORD01] = glGetAttribLocation(RLGL.State.defaultShaderId, RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD);
RLGL.State.defaultShaderLocs[RL_SHADER_LOC_VERTEX_COLOR] = glGetAttribLocation(RLGL.State.defaultShaderId, RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR);
// Set default shader locations: uniform locations
RLGL.State.defaultShaderLocs[RL_SHADER_LOC_MATRIX_MVP] = glGetUniformLocation(RLGL.State.defaultShaderId, "mvp");
RLGL.State.defaultShaderLocs[RL_SHADER_LOC_COLOR_DIFFUSE] = glGetUniformLocation(RLGL.State.defaultShaderId, "colDiffuse");
RLGL.State.defaultShaderLocs[RL_SHADER_LOC_MAP_DIFFUSE] = glGetUniformLocation(RLGL.State.defaultShaderId, "texture0");
RLGL.State.defaultShaderLocs[RL_SHADER_LOC_MATRIX_MVP] = glGetUniformLocation(RLGL.State.defaultShaderId, RL_DEFAULT_SHADER_UNIFORM_NAME_MVP);
RLGL.State.defaultShaderLocs[RL_SHADER_LOC_COLOR_DIFFUSE] = glGetUniformLocation(RLGL.State.defaultShaderId, RL_DEFAULT_SHADER_UNIFORM_NAME_COLOR);
RLGL.State.defaultShaderLocs[RL_SHADER_LOC_MAP_DIFFUSE] = glGetUniformLocation(RLGL.State.defaultShaderId, RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE0);
}
else TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Failed to load default shader", RLGL.State.defaultShaderId);
}
@ -4817,6 +4982,31 @@ static int rlGetPixelDataSize(int width, int height, int format)
// Auxiliar math functions
// Get float array of matrix data
static rl_float16 rlMatrixToFloatV(Matrix mat)
{
rl_float16 result = { 0 };
result.v[0] = mat.m0;
result.v[1] = mat.m1;
result.v[2] = mat.m2;
result.v[3] = mat.m3;
result.v[4] = mat.m4;
result.v[5] = mat.m5;
result.v[6] = mat.m6;
result.v[7] = mat.m7;
result.v[8] = mat.m8;
result.v[9] = mat.m9;
result.v[10] = mat.m10;
result.v[11] = mat.m11;
result.v[12] = mat.m12;
result.v[13] = mat.m13;
result.v[14] = mat.m14;
result.v[15] = mat.m15;
return result;
}
// Get identity matrix
static Matrix rlMatrixIdentity(void)
{
@ -4856,4 +5046,76 @@ static Matrix rlMatrixMultiply(Matrix left, Matrix right)
return result;
}
// Transposes provided matrix
static Matrix rlMatrixTranspose(Matrix mat)
{
Matrix result = { 0 };
result.m0 = mat.m0;
result.m1 = mat.m4;
result.m2 = mat.m8;
result.m3 = mat.m12;
result.m4 = mat.m1;
result.m5 = mat.m5;
result.m6 = mat.m9;
result.m7 = mat.m13;
result.m8 = mat.m2;
result.m9 = mat.m6;
result.m10 = mat.m10;
result.m11 = mat.m14;
result.m12 = mat.m3;
result.m13 = mat.m7;
result.m14 = mat.m11;
result.m15 = mat.m15;
return result;
}
// Invert provided matrix
static Matrix rlMatrixInvert(Matrix mat)
{
Matrix result = { 0 };
// Cache the matrix values (speed optimization)
float a00 = mat.m0, a01 = mat.m1, a02 = mat.m2, a03 = mat.m3;
float a10 = mat.m4, a11 = mat.m5, a12 = mat.m6, a13 = mat.m7;
float a20 = mat.m8, a21 = mat.m9, a22 = mat.m10, a23 = mat.m11;
float a30 = mat.m12, a31 = mat.m13, a32 = mat.m14, a33 = mat.m15;
float b00 = a00*a11 - a01*a10;
float b01 = a00*a12 - a02*a10;
float b02 = a00*a13 - a03*a10;
float b03 = a01*a12 - a02*a11;
float b04 = a01*a13 - a03*a11;
float b05 = a02*a13 - a03*a12;
float b06 = a20*a31 - a21*a30;
float b07 = a20*a32 - a22*a30;
float b08 = a20*a33 - a23*a30;
float b09 = a21*a32 - a22*a31;
float b10 = a21*a33 - a23*a31;
float b11 = a22*a33 - a23*a32;
// Calculate the invert determinant (inlined to avoid double-caching)
float invDet = 1.0f/(b00*b11 - b01*b10 + b02*b09 + b03*b08 - b04*b07 + b05*b06);
result.m0 = (a11*b11 - a12*b10 + a13*b09)*invDet;
result.m1 = (-a01*b11 + a02*b10 - a03*b09)*invDet;
result.m2 = (a31*b05 - a32*b04 + a33*b03)*invDet;
result.m3 = (-a21*b05 + a22*b04 - a23*b03)*invDet;
result.m4 = (-a10*b11 + a12*b08 - a13*b07)*invDet;
result.m5 = (a00*b11 - a02*b08 + a03*b07)*invDet;
result.m6 = (-a30*b05 + a32*b02 - a33*b01)*invDet;
result.m7 = (a20*b05 - a22*b02 + a23*b01)*invDet;
result.m8 = (a10*b10 - a11*b08 + a13*b06)*invDet;
result.m9 = (-a00*b10 + a01*b08 - a03*b06)*invDet;
result.m10 = (a30*b04 - a31*b02 + a33*b00)*invDet;
result.m11 = (-a20*b04 + a21*b02 - a23*b00)*invDet;
result.m12 = (-a10*b09 + a11*b07 - a12*b06)*invDet;
result.m13 = (a00*b09 - a01*b07 + a02*b06)*invDet;
result.m14 = (-a30*b03 + a31*b01 - a32*b00)*invDet;
result.m15 = (a20*b03 - a21*b01 + a22*b00)*invDet;
return result;
}
#endif // RLGL_IMPLEMENTATION

View file

@ -587,9 +587,7 @@ func LoadTextureDepth(width, height int32, useRenderBuffer bool) {
// LoadFramebuffer - Load an empty framebuffer
func LoadFramebuffer(width int32, height int32) uint32 {
cwidth := C.int(width)
cheight := C.int(height)
return uint32(C.rlLoadFramebuffer(cwidth, cheight))
return uint32(C.rlLoadFramebuffer())
}
// FramebufferAttach - Attach texture/renderbuffer to a framebuffer

File diff suppressed because it is too large Load diff

View file

@ -25,7 +25,7 @@
*
* LICENSE: zlib/libpng
*
* Copyright (c) 2013-2023 Ramon Santamaria (@raysan5)
* Copyright (c) 2013-2024 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.
@ -71,7 +71,6 @@
#define SPLINE_SEGMENT_DIVISIONS 24 // Spline segment divisions
#endif
//----------------------------------------------------------------------------------
// Types and Structures Definition
//----------------------------------------------------------------------------------
@ -112,6 +111,18 @@ void SetShapesTexture(Texture2D texture, Rectangle source)
}
}
// Get texture that is used for shapes drawing
Texture2D GetShapesTexture(void)
{
return texShapes;
}
// Get texture source rectangle that is used for shapes drawing
Rectangle GetShapesTextureRectangle(void)
{
return texShapesRec;
}
// Draw a pixel
void DrawPixel(int posX, int posY, Color color)
{
@ -122,23 +133,24 @@ void DrawPixel(int posX, int posY, Color color)
void DrawPixelV(Vector2 position, Color color)
{
#if defined(SUPPORT_QUADS_DRAW_MODE)
rlSetTexture(texShapes.id);
rlSetTexture(GetShapesTexture().id);
Rectangle shapeRect = GetShapesTextureRectangle();
rlBegin(RL_QUADS);
rlNormal3f(0.0f, 0.0f, 1.0f);
rlColor4ub(color.r, color.g, color.b, color.a);
rlTexCoord2f(texShapesRec.x/texShapes.width, texShapesRec.y/texShapes.height);
rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height);
rlVertex2f(position.x, position.y);
rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
rlVertex2f(position.x, position.y + 1);
rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
rlVertex2f(position.x + 1, position.y + 1);
rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height);
rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height);
rlVertex2f(position.x + 1, position.y);
rlEnd();
@ -166,8 +178,9 @@ void DrawLine(int startPosX, int startPosY, int endPosX, int endPosY, Color colo
{
rlBegin(RL_LINES);
rlColor4ub(color.r, color.g, color.b, color.a);
rlVertex2f((float)startPosX, (float)startPosY);
rlVertex2f((float)endPosX, (float)endPosY);
// WARNING: Adding 0.5f offset to "center" point on selected pixel
rlVertex2f((float)startPosX + 0.5f, (float)startPosY + 0.5f);
rlVertex2f((float)endPosX + 0.5f, (float)endPosY + 0.5f);
rlEnd();
}
@ -176,16 +189,17 @@ void DrawLineV(Vector2 startPos, Vector2 endPos, Color color)
{
rlBegin(RL_LINES);
rlColor4ub(color.r, color.g, color.b, color.a);
rlVertex2f(startPos.x, startPos.y);
rlVertex2f(endPos.x, endPos.y);
// WARNING: Adding 0.5f offset to "center" point on selected pixel
rlVertex2f(startPos.x + 0.5f, startPos.y + 0.5f);
rlVertex2f(endPos.x + 0.5f, endPos.y + 0.5f);
rlEnd();
}
// Draw lines sequuence (using gl lines)
void DrawLineStrip(Vector2 *points, int pointCount, Color color)
{
if (pointCount >= 2)
{
if (pointCount < 2) return; // Security check
rlBegin(RL_LINES);
rlColor4ub(color.r, color.g, color.b, color.a);
@ -196,7 +210,6 @@ void DrawLineStrip(Vector2 *points, int pointCount, Color color)
}
rlEnd();
}
}
// Draw line using cubic-bezier spline, in-out interpolation, no control points
void DrawLineBezier(Vector2 startPos, Vector2 endPos, float thick, Color color)
@ -300,7 +313,8 @@ void DrawCircleSector(Vector2 center, float radius, float startAngle, float endA
float angle = startAngle;
#if defined(SUPPORT_QUADS_DRAW_MODE)
rlSetTexture(texShapes.id);
rlSetTexture(GetShapesTexture().id);
Rectangle shapeRect = GetShapesTextureRectangle();
rlBegin(RL_QUADS);
@ -309,36 +323,36 @@ void DrawCircleSector(Vector2 center, float radius, float startAngle, float endA
{
rlColor4ub(color.r, color.g, color.b, color.a);
rlTexCoord2f(texShapesRec.x/texShapes.width, texShapesRec.y/texShapes.height);
rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height);
rlVertex2f(center.x, center.y);
rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height);
rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height);
rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength*2.0f))*radius, center.y + sinf(DEG2RAD*(angle + stepLength*2.0f))*radius);
rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*radius, center.y + sinf(DEG2RAD*(angle + stepLength))*radius);
rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
rlVertex2f(center.x + cosf(DEG2RAD*angle)*radius, center.y + sinf(DEG2RAD*angle)*radius);
angle += (stepLength*2.0f);
}
// NOTE: In case number of segments is odd, we add one last piece to the cake
if ((segments%2) == 1)
if (((unsigned int)segments%2) == 1)
{
rlColor4ub(color.r, color.g, color.b, color.a);
rlTexCoord2f(texShapesRec.x/texShapes.width, texShapesRec.y/texShapes.height);
rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height);
rlVertex2f(center.x, center.y);
rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*radius, center.y + sinf(DEG2RAD*(angle + stepLength))*radius);
rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
rlVertex2f(center.x + cosf(DEG2RAD*angle)*radius, center.y + sinf(DEG2RAD*angle)*radius);
rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height);
rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height);
rlVertex2f(center.x, center.y);
}
@ -528,23 +542,24 @@ void DrawRing(Vector2 center, float innerRadius, float outerRadius, float startA
float angle = startAngle;
#if defined(SUPPORT_QUADS_DRAW_MODE)
rlSetTexture(texShapes.id);
rlSetTexture(GetShapesTexture().id);
Rectangle shapeRect = GetShapesTextureRectangle();
rlBegin(RL_QUADS);
for (int i = 0; i < segments; i++)
{
rlColor4ub(color.r, color.g, color.b, color.a);
rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
rlVertex2f(center.x + cosf(DEG2RAD*angle)*outerRadius, center.y + sinf(DEG2RAD*angle)*outerRadius);
rlTexCoord2f(texShapesRec.x/texShapes.width, texShapesRec.y/texShapes.height);
rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height);
rlVertex2f(center.x + cosf(DEG2RAD*angle)*innerRadius, center.y + sinf(DEG2RAD*angle)*innerRadius);
rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height);
rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height);
rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*innerRadius);
rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*outerRadius);
angle += stepLength;
@ -707,23 +722,24 @@ void DrawRectanglePro(Rectangle rec, Vector2 origin, float rotation, Color color
}
#if defined(SUPPORT_QUADS_DRAW_MODE)
rlSetTexture(texShapes.id);
rlSetTexture(GetShapesTexture().id);
Rectangle shapeRect = GetShapesTextureRectangle();
rlBegin(RL_QUADS);
rlNormal3f(0.0f, 0.0f, 1.0f);
rlColor4ub(color.r, color.g, color.b, color.a);
rlTexCoord2f(texShapesRec.x/texShapes.width, texShapesRec.y/texShapes.height);
rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height);
rlVertex2f(topLeft.x, topLeft.y);
rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
rlVertex2f(bottomLeft.x, bottomLeft.y);
rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
rlVertex2f(bottomRight.x, bottomRight.y);
rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height);
rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height);
rlVertex2f(topRight.x, topRight.y);
rlEnd();
@ -764,26 +780,27 @@ void DrawRectangleGradientH(int posX, int posY, int width, int height, Color col
// NOTE: Colors refer to corners, starting at top-lef corner and counter-clockwise
void DrawRectangleGradientEx(Rectangle rec, Color col1, Color col2, Color col3, Color col4)
{
rlSetTexture(texShapes.id);
rlSetTexture(GetShapesTexture().id);
Rectangle shapeRect = GetShapesTextureRectangle();
rlBegin(RL_QUADS);
rlNormal3f(0.0f, 0.0f, 1.0f);
// NOTE: Default raylib font character 95 is a white square
rlColor4ub(col1.r, col1.g, col1.b, col1.a);
rlTexCoord2f(texShapesRec.x/texShapes.width, texShapesRec.y/texShapes.height);
rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height);
rlVertex2f(rec.x, rec.y);
rlColor4ub(col2.r, col2.g, col2.b, col2.a);
rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
rlVertex2f(rec.x, rec.y + rec.height);
rlColor4ub(col3.r, col3.g, col3.b, col3.a);
rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
rlVertex2f(rec.x + rec.width, rec.y + rec.height);
rlColor4ub(col4.r, col4.g, col4.b, col4.a);
rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height);
rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height);
rlVertex2f(rec.x + rec.width, rec.y);
rlEnd();
@ -791,30 +808,25 @@ void DrawRectangleGradientEx(Rectangle rec, Color col1, Color col2, Color col3,
}
// Draw rectangle outline
// NOTE: On OpenGL 3.3 and ES2 we use QUADS to avoid drawing order issues
// WARNING: All Draw*Lines() functions use RL_LINES for drawing,
// it implies flushing the current batch and changing draw mode to RL_LINES
// but it solves another issue: https://github.com/raysan5/raylib/issues/3884
void DrawRectangleLines(int posX, int posY, int width, int height, Color color)
{
#if defined(SUPPORT_QUADS_DRAW_MODE)
DrawRectangle(posX, posY, width, 1, color);
DrawRectangle(posX + width - 1, posY + 1, 1, height - 2, color);
DrawRectangle(posX, posY + height - 1, width, 1, color);
DrawRectangle(posX, posY + 1, 1, height - 2, color);
#else
rlBegin(RL_LINES);
rlColor4ub(color.r, color.g, color.b, color.a);
rlVertex2f(posX + 1, posY + 1);
rlVertex2f(posX + width, posY + 1);
rlVertex2f((float)posX, (float)posY);
rlVertex2f((float)posX + (float)width, (float)posY + 1);
rlVertex2f(posX + width, posY + 1);
rlVertex2f(posX + width, posY + height);
rlVertex2f((float)posX + (float)width, (float)posY + 1);
rlVertex2f((float)posX + (float)width, (float)posY + (float)height);
rlVertex2f(posX + width, posY + height);
rlVertex2f(posX + 1, posY + height);
rlVertex2f((float)posX + (float)width, (float)posY + (float)height);
rlVertex2f((float)posX + 1, (float)posY + (float)height);
rlVertex2f(posX + 1, posY + height);
rlVertex2f(posX + 1, posY + 1);
rlVertex2f((float)posX + 1, (float)posY + (float)height);
rlVertex2f((float)posX + 1, (float)posY + 1);
rlEnd();
#endif
}
// Draw rectangle outline with extended parameters
@ -904,7 +916,8 @@ void DrawRectangleRounded(Rectangle rec, float roundness, int segments, Color co
const float angles[4] = { 180.0f, 270.0f, 0.0f, 90.0f };
#if defined(SUPPORT_QUADS_DRAW_MODE)
rlSetTexture(texShapes.id);
rlSetTexture(GetShapesTexture().id);
Rectangle shapeRect = GetShapesTextureRectangle();
rlBegin(RL_QUADS);
// Draw all the 4 corners: [1] Upper Left Corner, [3] Upper Right Corner, [5] Lower Right Corner, [7] Lower Left Corner
@ -917,16 +930,16 @@ void DrawRectangleRounded(Rectangle rec, float roundness, int segments, Color co
for (int i = 0; i < segments/2; i++)
{
rlColor4ub(color.r, color.g, color.b, color.a);
rlTexCoord2f(texShapesRec.x/texShapes.width, texShapesRec.y/texShapes.height);
rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height);
rlVertex2f(center.x, center.y);
rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height);
rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height);
rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength*2))*radius, center.y + sinf(DEG2RAD*(angle + stepLength*2))*radius);
rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*radius, center.y + sinf(DEG2RAD*(angle + stepLength))*radius);
rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
rlVertex2f(center.x + cosf(DEG2RAD*angle)*radius, center.y + sinf(DEG2RAD*angle)*radius);
angle += (stepLength*2);
@ -936,73 +949,73 @@ void DrawRectangleRounded(Rectangle rec, float roundness, int segments, Color co
if (segments%2)
{
rlColor4ub(color.r, color.g, color.b, color.a);
rlTexCoord2f(texShapesRec.x/texShapes.width, texShapesRec.y/texShapes.height);
rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height);
rlVertex2f(center.x, center.y);
rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*radius, center.y + sinf(DEG2RAD*(angle + stepLength))*radius);
rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
rlVertex2f(center.x + cosf(DEG2RAD*angle)*radius, center.y + sinf(DEG2RAD*angle)*radius);
rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height);
rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height);
rlVertex2f(center.x, center.y);
}
}
// [2] Upper Rectangle
rlColor4ub(color.r, color.g, color.b, color.a);
rlTexCoord2f(texShapesRec.x/texShapes.width, texShapesRec.y/texShapes.height);
rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height);
rlVertex2f(point[0].x, point[0].y);
rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
rlVertex2f(point[8].x, point[8].y);
rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
rlVertex2f(point[9].x, point[9].y);
rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height);
rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height);
rlVertex2f(point[1].x, point[1].y);
// [4] Right Rectangle
rlColor4ub(color.r, color.g, color.b, color.a);
rlTexCoord2f(texShapesRec.x/texShapes.width, texShapesRec.y/texShapes.height);
rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height);
rlVertex2f(point[2].x, point[2].y);
rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
rlVertex2f(point[9].x, point[9].y);
rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
rlVertex2f(point[10].x, point[10].y);
rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height);
rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height);
rlVertex2f(point[3].x, point[3].y);
// [6] Bottom Rectangle
rlColor4ub(color.r, color.g, color.b, color.a);
rlTexCoord2f(texShapesRec.x/texShapes.width, texShapesRec.y/texShapes.height);
rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height);
rlVertex2f(point[11].x, point[11].y);
rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
rlVertex2f(point[5].x, point[5].y);
rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
rlVertex2f(point[4].x, point[4].y);
rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height);
rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height);
rlVertex2f(point[10].x, point[10].y);
// [8] Left Rectangle
rlColor4ub(color.r, color.g, color.b, color.a);
rlTexCoord2f(texShapesRec.x/texShapes.width, texShapesRec.y/texShapes.height);
rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height);
rlVertex2f(point[7].x, point[7].y);
rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
rlVertex2f(point[6].x, point[6].y);
rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
rlVertex2f(point[11].x, point[11].y);
rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height);
rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height);
rlVertex2f(point[8].x, point[8].y);
// [9] Middle Rectangle
rlColor4ub(color.r, color.g, color.b, color.a);
rlTexCoord2f(texShapesRec.x/texShapes.width, texShapesRec.y/texShapes.height);
rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height);
rlVertex2f(point[8].x, point[8].y);
rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
rlVertex2f(point[11].x, point[11].y);
rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
rlVertex2f(point[10].x, point[10].y);
rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height);
rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height);
rlVertex2f(point[9].x, point[9].y);
rlEnd();
@ -1073,8 +1086,15 @@ void DrawRectangleRounded(Rectangle rec, float roundness, int segments, Color co
#endif
}
// Draw rectangle with rounded edges
// TODO: This function should be refactored to use RL_LINES, for consistency with other Draw*Lines()
void DrawRectangleRoundedLines(Rectangle rec, float roundness, int segments, Color color)
{
DrawRectangleRoundedLinesEx(rec, roundness, segments, 1.0f, color);
}
// Draw rectangle with rounded edges outline
void DrawRectangleRoundedLines(Rectangle rec, float roundness, int segments, float lineThick, Color color)
void DrawRectangleRoundedLinesEx(Rectangle rec, float roundness, int segments, float lineThick, Color color)
{
if (lineThick < 0) lineThick = 0;
@ -1139,7 +1159,8 @@ void DrawRectangleRoundedLines(Rectangle rec, float roundness, int segments, flo
if (lineThick > 1)
{
#if defined(SUPPORT_QUADS_DRAW_MODE)
rlSetTexture(texShapes.id);
rlSetTexture(GetShapesTexture().id);
Rectangle shapeRect = GetShapesTextureRectangle();
rlBegin(RL_QUADS);
@ -1152,16 +1173,16 @@ void DrawRectangleRoundedLines(Rectangle rec, float roundness, int segments, flo
{
rlColor4ub(color.r, color.g, color.b, color.a);
rlTexCoord2f(texShapesRec.x/texShapes.width, texShapesRec.y/texShapes.height);
rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height);
rlVertex2f(center.x + cosf(DEG2RAD*angle)*innerRadius, center.y + sinf(DEG2RAD*angle)*innerRadius);
rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height);
rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height);
rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*innerRadius);
rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*outerRadius);
rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
rlVertex2f(center.x + cosf(DEG2RAD*angle)*outerRadius, center.y + sinf(DEG2RAD*angle)*outerRadius);
angle += stepLength;
@ -1170,46 +1191,46 @@ void DrawRectangleRoundedLines(Rectangle rec, float roundness, int segments, flo
// Upper rectangle
rlColor4ub(color.r, color.g, color.b, color.a);
rlTexCoord2f(texShapesRec.x/texShapes.width, texShapesRec.y/texShapes.height);
rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height);
rlVertex2f(point[0].x, point[0].y);
rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
rlVertex2f(point[8].x, point[8].y);
rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
rlVertex2f(point[9].x, point[9].y);
rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height);
rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height);
rlVertex2f(point[1].x, point[1].y);
// Right rectangle
rlColor4ub(color.r, color.g, color.b, color.a);
rlTexCoord2f(texShapesRec.x/texShapes.width, texShapesRec.y/texShapes.height);
rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height);
rlVertex2f(point[2].x, point[2].y);
rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
rlVertex2f(point[10].x, point[10].y);
rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
rlVertex2f(point[11].x, point[11].y);
rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height);
rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height);
rlVertex2f(point[3].x, point[3].y);
// Lower rectangle
rlColor4ub(color.r, color.g, color.b, color.a);
rlTexCoord2f(texShapesRec.x/texShapes.width, texShapesRec.y/texShapes.height);
rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height);
rlVertex2f(point[13].x, point[13].y);
rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
rlVertex2f(point[5].x, point[5].y);
rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
rlVertex2f(point[4].x, point[4].y);
rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height);
rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height);
rlVertex2f(point[12].x, point[12].y);
// Left rectangle
rlColor4ub(color.r, color.g, color.b, color.a);
rlTexCoord2f(texShapesRec.x/texShapes.width, texShapesRec.y/texShapes.height);
rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height);
rlVertex2f(point[15].x, point[15].y);
rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
rlVertex2f(point[7].x, point[7].y);
rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
rlVertex2f(point[6].x, point[6].y);
rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height);
rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height);
rlVertex2f(point[14].x, point[14].y);
rlEnd();
@ -1314,21 +1335,22 @@ void DrawRectangleRoundedLines(Rectangle rec, float roundness, int segments, flo
void DrawTriangle(Vector2 v1, Vector2 v2, Vector2 v3, Color color)
{
#if defined(SUPPORT_QUADS_DRAW_MODE)
rlSetTexture(texShapes.id);
rlSetTexture(GetShapesTexture().id);
Rectangle shapeRect = GetShapesTextureRectangle();
rlBegin(RL_QUADS);
rlColor4ub(color.r, color.g, color.b, color.a);
rlTexCoord2f(texShapesRec.x/texShapes.width, texShapesRec.y/texShapes.height);
rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height);
rlVertex2f(v1.x, v1.y);
rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
rlVertex2f(v2.x, v2.y);
rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
rlVertex2f(v2.x, v2.y);
rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height);
rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height);
rlVertex2f(v3.x, v3.y);
rlEnd();
@ -1367,22 +1389,24 @@ void DrawTriangleFan(Vector2 *points, int pointCount, Color color)
{
if (pointCount >= 3)
{
rlSetTexture(texShapes.id);
rlSetTexture(GetShapesTexture().id);
Rectangle shapeRect = GetShapesTextureRectangle();
rlBegin(RL_QUADS);
rlColor4ub(color.r, color.g, color.b, color.a);
for (int i = 1; i < pointCount - 1; i++)
{
rlTexCoord2f(texShapesRec.x/texShapes.width, texShapesRec.y/texShapes.height);
rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height);
rlVertex2f(points[0].x, points[0].y);
rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
rlVertex2f(points[i].x, points[i].y);
rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
rlVertex2f(points[i + 1].x, points[i + 1].y);
rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height);
rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height);
rlVertex2f(points[i + 1].x, points[i + 1].y);
}
rlEnd();
@ -1426,7 +1450,8 @@ void DrawPoly(Vector2 center, int sides, float radius, float rotation, Color col
float angleStep = 360.0f/(float)sides*DEG2RAD;
#if defined(SUPPORT_QUADS_DRAW_MODE)
rlSetTexture(texShapes.id);
rlSetTexture(GetShapesTexture().id);
Rectangle shapeRect = GetShapesTextureRectangle();
rlBegin(RL_QUADS);
for (int i = 0; i < sides; i++)
@ -1434,16 +1459,16 @@ void DrawPoly(Vector2 center, int sides, float radius, float rotation, Color col
rlColor4ub(color.r, color.g, color.b, color.a);
float nextAngle = centralAngle + angleStep;
rlTexCoord2f(texShapesRec.x/texShapes.width, texShapesRec.y/texShapes.height);
rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height);
rlVertex2f(center.x, center.y);
rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
rlVertex2f(center.x + cosf(centralAngle)*radius, center.y + sinf(centralAngle)*radius);
rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height);
rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height);
rlVertex2f(center.x + cosf(nextAngle)*radius, center.y + sinf(nextAngle)*radius);
rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
rlVertex2f(center.x + cosf(centralAngle)*radius, center.y + sinf(centralAngle)*radius);
centralAngle = nextAngle;
@ -1494,7 +1519,8 @@ void DrawPolyLinesEx(Vector2 center, int sides, float radius, float rotation, fl
float innerRadius = radius - (lineThick*cosf(DEG2RAD*exteriorAngle/2.0f));
#if defined(SUPPORT_QUADS_DRAW_MODE)
rlSetTexture(texShapes.id);
rlSetTexture(GetShapesTexture().id);
Rectangle shapeRect = GetShapesTextureRectangle();
rlBegin(RL_QUADS);
for (int i = 0; i < sides; i++)
@ -1502,16 +1528,16 @@ void DrawPolyLinesEx(Vector2 center, int sides, float radius, float rotation, fl
rlColor4ub(color.r, color.g, color.b, color.a);
float nextAngle = centralAngle + exteriorAngle;
rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
rlVertex2f(center.x + cosf(centralAngle)*radius, center.y + sinf(centralAngle)*radius);
rlTexCoord2f(texShapesRec.x/texShapes.width, texShapesRec.y/texShapes.height);
rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height);
rlVertex2f(center.x + cosf(centralAngle)*innerRadius, center.y + sinf(centralAngle)*innerRadius);
rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
rlVertex2f(center.x + cosf(nextAngle)*innerRadius, center.y + sinf(nextAngle)*innerRadius);
rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height);
rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height);
rlVertex2f(center.x + cosf(nextAngle)*radius, center.y + sinf(nextAngle)*radius);
centralAngle = nextAngle;
@ -1546,6 +1572,92 @@ void DrawPolyLinesEx(Vector2 center, int sides, float radius, float rotation, fl
// Draw spline: linear, minimum 2 points
void DrawSplineLinear(Vector2 *points, int pointCount, float thick, Color color)
{
if (pointCount < 2) return;
#if defined(SUPPORT_SPLINE_MITERS)
Vector2 prevNormal = (Vector2){-(points[1].y - points[0].y), (points[1].x - points[0].x)};
float prevLength = sqrtf(prevNormal.x*prevNormal.x + prevNormal.y*prevNormal.y);
if (prevLength > 0.0f)
{
prevNormal.x /= prevLength;
prevNormal.y /= prevLength;
}
else
{
prevNormal.x = 0.0f;
prevNormal.y = 0.0f;
}
Vector2 prevRadius = { 0.5f*thick*prevNormal.x, 0.5f*thick*prevNormal.y };
for (int i = 0; i < pointCount - 1; i++)
{
Vector2 normal = { 0 };
if (i < pointCount - 2)
{
normal = (Vector2){-(points[i + 2].y - points[i + 1].y), (points[i + 2].x - points[i + 1].x)};
float normalLength = sqrtf(normal.x*normal.x + normal.y*normal.y);
if (normalLength > 0.0f)
{
normal.x /= normalLength;
normal.y /= normalLength;
}
else
{
normal.x = 0.0f;
normal.y = 0.0f;
}
}
else
{
normal = prevNormal;
}
Vector2 radius = { prevNormal.x + normal.x, prevNormal.y + normal.y };
float radiusLength = sqrtf(radius.x*radius.x + radius.y*radius.y);
if (radiusLength > 0.0f)
{
radius.x /= radiusLength;
radius.y /= radiusLength;
}
else
{
radius.x = 0.0f;
radius.y = 0.0f;
}
float cosTheta = radius.x*normal.x + radius.y*normal.y;
if (cosTheta != 0.0f)
{
radius.x *= (thick*0.5f/cosTheta);
radius.y *= (thick*0.5f/cosTheta);
}
else
{
radius.x = 0.0f;
radius.y = 0.0f;
}
Vector2 strip[4] = {
{ points[i].x - prevRadius.x, points[i].y - prevRadius.y },
{ points[i].x + prevRadius.x, points[i].y + prevRadius.y },
{ points[i + 1].x - radius.x, points[i + 1].y - radius.y },
{ points[i + 1].x + radius.x, points[i + 1].y + radius.y }
};
DrawTriangleStrip(strip, 4, color);
prevRadius = radius;
prevNormal = normal;
}
#else // !SUPPORT_SPLINE_MITTERS
Vector2 delta = { 0 };
float length = 0.0f;
float scale = 0.0f;
@ -1567,8 +1679,10 @@ void DrawSplineLinear(Vector2 *points, int pointCount, float thick, Color color)
DrawTriangleStrip(strip, 4, color);
}
#if defined(SUPPORT_SPLINE_SEGMENT_CAPS)
#endif
#if defined(SUPPORT_SPLINE_SEGMENT_CAPS)
// TODO: Add spline segment rounded caps at the begin/end of the spline
#endif
}
@ -2083,21 +2197,21 @@ bool CheckCollisionPointTriangle(Vector2 point, Vector2 p1, Vector2 p2, Vector2
// NOTE: Based on http://jeffreythompson.org/collision-detection/poly-point.php
bool CheckCollisionPointPoly(Vector2 point, Vector2 *points, int pointCount)
{
bool collision = false;
bool inside = false;
if (pointCount > 2)
{
for (int i = 0; i < pointCount - 1; i++)
for (int i = 0, j = pointCount - 1; i < pointCount; j = i++)
{
Vector2 vc = points[i];
Vector2 vn = points[i + 1];
if ((((vc.y >= point.y) && (vn.y < point.y)) || ((vc.y < point.y) && (vn.y >= point.y))) &&
(point.x < ((vn.x - vc.x)*(point.y - vc.y)/(vn.y - vc.y) + vc.x))) collision = !collision;
if ((points[i].y > point.y) != (points[j].y > point.y) &&
(point.x < (points[j].x - points[i].x)*(point.y - points[i].y)/(points[j].y - points[i].y) + points[i].x))
{
inside = !inside;
}
}
}
return collision;
return inside;
}
// Check collision between two rectangles
@ -2132,11 +2246,11 @@ bool CheckCollisionCircleRec(Vector2 center, float radius, Rectangle rec)
{
bool collision = false;
int recCenterX = (int)(rec.x + rec.width/2.0f);
int recCenterY = (int)(rec.y + rec.height/2.0f);
float recCenterX = rec.x + rec.width/2.0f;
float recCenterY = rec.y + rec.height/2.0f;
float dx = fabsf(center.x - (float)recCenterX);
float dy = fabsf(center.y - (float)recCenterY);
float dx = fabsf(center.x - recCenterX);
float dy = fabsf(center.y - recCenterY);
if (dx > (rec.width/2.0f + radius)) { return false; }
if (dy > (rec.height/2.0f + radius)) { return false; }
@ -2234,11 +2348,16 @@ Rectangle GetCollisionRec(Rectangle rec1, Rectangle rec2)
// NOTE: Used by DrawLineBezier() only
static float EaseCubicInOut(float t, float b, float c, float d)
{
if ((t /= 0.5f*d) < 1) return 0.5f*c*t*t*t + b;
float result = 0.0f;
if ((t /= 0.5f*d) < 1) result = 0.5f*c*t*t*t + b;
else
{
t -= 2;
result = 0.5f*c*(t*t*t + 2.0f) + b;
}
return 0.5f*c*(t*t*t + 2.0f) + b;
return result;
}
#endif // SUPPORT_MODULE_RSHAPES

View file

@ -276,9 +276,8 @@ func DrawRectangleRoundedLines(rec Rectangle, roundness float32, segments, lineT
crec := rec.cptr()
croundness := (C.float)(roundness)
csegments := (C.int)(segments)
clineThick := (C.float)(lineThick)
ccolor := colorCptr(col)
C.DrawRectangleRoundedLines(*crec, croundness, csegments, clineThick, *ccolor)
C.DrawRectangleRoundedLines(*crec, croundness, csegments, *ccolor)
}
// DrawTriangle - Draw a color-filled triangle

View file

@ -12,6 +12,7 @@
*
* #define SUPPORT_FILEFORMAT_FNT
* #define SUPPORT_FILEFORMAT_TTF
* #define SUPPORT_FILEFORMAT_BDF
* Selected desired fileformats to be supported for loading. Some of those formats are
* supported by default, to remove support, just comment unrequired #define in this module
*
@ -33,7 +34,7 @@
*
* LICENSE: zlib/libpng
*
* Copyright (c) 2013-2023 Ramon Santamaria (@raysan5)
* Copyright (c) 2013-2024 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.
@ -70,14 +71,27 @@
#include <stdarg.h> // Required for: va_list, va_start(), vsprintf(), va_end() [Used in TextFormat()]
#include <ctype.h> // Required for: toupper(), tolower() [Used in TextToUpper(), TextToLower()]
#if defined(SUPPORT_FILEFORMAT_TTF)
#if defined(SUPPORT_FILEFORMAT_TTF) || defined(SUPPORT_FILEFORMAT_BDF)
#if defined(__GNUC__) // GCC and Clang
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-function"
#endif
#define STB_RECT_PACK_IMPLEMENTATION
#include "external/stb_rect_pack.h" // Required for: ttf font rectangles packaging
#include "external/stb_rect_pack.h" // Required for: ttf/bdf font rectangles packaging
#include <math.h> // Required for: ttf/bdf font rectangles packaging
#if defined(__GNUC__) // GCC and Clang
#pragma GCC diagnostic pop
#endif
#endif
#if defined(SUPPORT_FILEFORMAT_TTF)
#if defined(__GNUC__) // GCC and Clang
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-function"
#endif
#define STBTT_STATIC
#define STB_TRUETYPE_IMPLEMENTATION
@ -127,7 +141,10 @@ static Font defaultFont = { 0 };
#if defined(SUPPORT_FILEFORMAT_FNT)
static Font LoadBMFont(const char *fileName); // Load a BMFont file (AngelCode font file)
#endif
static int textLineSpacing = 15; // Text vertical line spacing in pixels
#if defined(SUPPORT_FILEFORMAT_BDF)
static GlyphInfo *LoadFontDataBDF(const unsigned char *fileData, int dataSize, int *codepoints, int codepointCount, int *outFontSize);
#endif
static int textLineSpacing = 2; // Text vertical line spacing in pixels (between lines)
#if defined(SUPPORT_DEFAULT_FONT)
extern void LoadFontDefault(void);
@ -334,6 +351,10 @@ Font LoadFont(const char *fileName)
#if defined(SUPPORT_FILEFORMAT_FNT)
if (IsFileExtension(fileName, ".fnt")) font = LoadBMFont(fileName);
else
#endif
#if defined(SUPPORT_FILEFORMAT_BDF)
if (IsFileExtension(fileName, ".bdf")) font = LoadFontEx(fileName, FONT_TTF_DEFAULT_SIZE, NULL, FONT_TTF_DEFAULT_NUMCHARS);
else
#endif
{
Image image = LoadImage(fileName);
@ -355,7 +376,7 @@ Font LoadFont(const char *fileName)
return font;
}
// Load Font from TTF font file with generation parameters
// Load Font from TTF or BDF font file with generation parameters
// NOTE: You can pass an array with desired characters, those characters should be available in the font
// if array is NULL, default char set is selected 32..126
Font LoadFontEx(const char *fileName, int fontSize, int *codepoints, int codepointCount)
@ -413,7 +434,7 @@ Font LoadFontFromImage(Image image, Color key, int firstChar)
if (!COLOR_EQUAL(pixels[y*image.width + x], key)) break;
}
if ((x == 0) || (y == 0)) return font;
if ((x == 0) || (y == 0)) return font; // Security check
charSpacing = x;
lineSpacing = y;
@ -509,17 +530,32 @@ Font LoadFontFromMemory(const char *fileType, const unsigned char *fileData, int
Font font = { 0 };
char fileExtLower[16] = { 0 };
strcpy(fileExtLower, TextToLower(fileType));
strncpy(fileExtLower, TextToLower(fileType), 16 - 1);
font.baseSize = fontSize;
font.glyphCount = (codepointCount > 0)? codepointCount : 95;
font.glyphPadding = 0;
#if defined(SUPPORT_FILEFORMAT_TTF)
if (TextIsEqual(fileExtLower, ".ttf") ||
TextIsEqual(fileExtLower, ".otf"))
{
font.baseSize = fontSize;
font.glyphCount = (codepointCount > 0)? codepointCount : 95;
font.glyphPadding = 0;
font.glyphs = LoadFontData(fileData, dataSize, font.baseSize, codepoints, font.glyphCount, FONT_DEFAULT);
}
else
#endif
#if defined(SUPPORT_FILEFORMAT_BDF)
if (TextIsEqual(fileExtLower, ".bdf"))
{
font.glyphs = LoadFontDataBDF(fileData, dataSize, codepoints, font.glyphCount, &font.baseSize);
}
else
#endif
{
font.glyphs = NULL;
}
#if defined(SUPPORT_FILEFORMAT_TTF) || defined(SUPPORT_FILEFORMAT_BDF)
if (font.glyphs != NULL)
{
font.glyphPadding = FONT_TTF_DEFAULT_CHARS_PADDING;
@ -539,7 +575,6 @@ Font LoadFontFromMemory(const char *fileType, const unsigned char *fileData, int
TRACELOG(LOG_INFO, "FONT: Data loaded successfully (%i pixel size | %i glyphs)", font.baseSize, font.glyphCount);
}
else font = GetFontDefault();
}
#else
font = GetFontDefault();
#endif
@ -604,7 +639,6 @@ GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSiz
// Fill fontChars in case not provided externally
// NOTE: By default we fill glyphCount consecutively, starting at 32 (Space)
if (codepoints == NULL)
{
codepoints = (int *)RL_MALLOC(codepointCount*sizeof(int));
@ -612,7 +646,7 @@ GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSiz
genFontChars = true;
}
chars = (GlyphInfo *)RL_MALLOC(codepointCount*sizeof(GlyphInfo));
chars = (GlyphInfo *)RL_CALLOC(codepointCount, sizeof(GlyphInfo));
// NOTE: Using simple packaging, one char after another
for (int i = 0; i < codepointCount; i++)
@ -626,10 +660,22 @@ GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSiz
// stbtt_GetCodepointBitmapBox() -- how big the bitmap must be
// stbtt_MakeCodepointBitmap() -- renders into bitmap you provide
if (type != FONT_SDF) chars[i].image.data = stbtt_GetCodepointBitmap(&fontInfo, scaleFactor, scaleFactor, ch, &chw, &chh, &chars[i].offsetX, &chars[i].offsetY);
else if (ch != 32) chars[i].image.data = stbtt_GetCodepointSDF(&fontInfo, scaleFactor, ch, FONT_SDF_CHAR_PADDING, FONT_SDF_ON_EDGE_VALUE, FONT_SDF_PIXEL_DIST_SCALE, &chw, &chh, &chars[i].offsetX, &chars[i].offsetY);
else chars[i].image.data = NULL;
// Check if a glyph is available in the font
// WARNING: if (index == 0), glyph not found, it could fallback to default .notdef glyph (if defined in font)
int index = stbtt_FindGlyphIndex(&fontInfo, ch);
if (index > 0)
{
switch (type)
{
case FONT_DEFAULT:
case FONT_BITMAP: chars[i].image.data = stbtt_GetCodepointBitmap(&fontInfo, scaleFactor, scaleFactor, ch, &chw, &chh, &chars[i].offsetX, &chars[i].offsetY); break;
case FONT_SDF: if (ch != 32) chars[i].image.data = stbtt_GetCodepointSDF(&fontInfo, scaleFactor, ch, FONT_SDF_CHAR_PADDING, FONT_SDF_ON_EDGE_VALUE, FONT_SDF_PIXEL_DIST_SCALE, &chw, &chh, &chars[i].offsetX, &chars[i].offsetY); break;
default: break;
}
if (chars[i].image.data != NULL) // Glyph data has been found in the font
{
stbtt_GetCodepointHMetrics(&fontInfo, ch, &chars[i].advanceX, NULL);
chars[i].advanceX = (int)((float)chars[i].advanceX*scaleFactor);
@ -640,10 +686,15 @@ GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSiz
chars[i].image.format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE;
chars[i].offsetY += (int)((float)ascent*scaleFactor);
}
// NOTE: We create an empty image for space character, it could be further required for atlas packing
// NOTE: We create an empty image for space character,
// it could be further required for atlas packing
if (ch == 32)
{
stbtt_GetCodepointHMetrics(&fontInfo, ch, &chars[i].advanceX, NULL);
chars[i].advanceX = (int)((float)chars[i].advanceX*scaleFactor);
Image imSpace = {
.data = RL_CALLOC(chars[i].advanceX*fontSize, 2),
.width = chars[i].advanceX,
@ -665,15 +716,11 @@ GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSiz
else ((unsigned char *)chars[i].image.data)[p] = 255;
}
}
// Get bounding box for character (maybe offset to account for chars that dip above or below the line)
/*
int chX1, chY1, chX2, chY2;
stbtt_GetCodepointBitmapBox(&fontInfo, ch, scaleFactor, scaleFactor, &chX1, &chY1, &chX2, &chY2);
TRACELOGD("FONT: Character box measures: %i, %i, %i, %i", chX1, chY1, chX2 - chX1, chY2 - chY1);
TRACELOGD("FONT: Character offsetY: %i", (int)((float)ascent*scaleFactor) + chY1);
*/
}
else
{
// TODO: Use some fallback glyph for codepoints not found in the font
}
}
}
else TRACELOG(LOG_WARNING, "FONT: Failed to process TTF font data");
@ -687,7 +734,7 @@ GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSiz
// Generate image font atlas using chars info
// NOTE: Packing method: 0-Default, 1-Skyline
#if defined(SUPPORT_FILEFORMAT_TTF)
#if defined(SUPPORT_FILEFORMAT_TTF) || defined(SUPPORT_FILEFORMAT_BDF)
Image GenImageFontAtlas(const GlyphInfo *glyphs, Rectangle **glyphRecs, int glyphCount, int fontSize, int padding, int packMethod)
{
Image atlas = { 0 };
@ -713,7 +760,7 @@ Image GenImageFontAtlas(const GlyphInfo *glyphs, Rectangle **glyphRecs, int glyp
for (int i = 0; i < glyphCount; i++)
{
if (glyphs[i].image.width > maxGlyphWidth) maxGlyphWidth = glyphs[i].image.width;
totalWidth += glyphs[i].image.width + 4*padding;
totalWidth += glyphs[i].image.width + 2*padding;
}
//#define SUPPORT_FONT_ATLAS_SIZE_CONSERVATIVE
@ -731,8 +778,9 @@ Image GenImageFontAtlas(const GlyphInfo *glyphs, Rectangle **glyphRecs, int glyp
atlas.width = imageSize; // Atlas bitmap width
atlas.height = imageSize; // Atlas bitmap height
#else
int paddedFontSize = fontSize + 2*padding;
// No need for a so-conservative atlas generation
float totalArea = totalWidth*fontSize*1.2f;
float totalArea = totalWidth*paddedFontSize*1.2f;
float imageMinSize = sqrtf(totalArea);
int imageSize = (int)powf(2, ceilf(logf(imageMinSize)/logf(2)));
@ -778,7 +826,7 @@ Image GenImageFontAtlas(const GlyphInfo *glyphs, Rectangle **glyphRecs, int glyp
for (int j = i + 1; j < glyphCount; j++)
{
TRACELOG(LOG_WARNING, "FONT: Failed to package character (%i)", j);
// make sure remaining recs contain valid data
// Make sure remaining recs contain valid data
recs[j].x = 0;
recs[j].y = 0;
recs[j].width = 0;
@ -923,7 +971,7 @@ bool ExportFontAsCode(Font font, const char *fileName)
// Get file name from path
char fileNamePascal[256] = { 0 };
strcpy(fileNamePascal, TextToPascal(GetFileNameWithoutExt(fileName)));
strncpy(fileNamePascal, TextToPascal(GetFileNameWithoutExt(fileName)), 256 - 1);
// NOTE: Text data buffer size is estimated considering image data size in bytes
// and requiring 6 char bytes for every byte: "0x00, "
@ -937,7 +985,7 @@ bool ExportFontAsCode(Font font, const char *fileName)
byteCount += sprintf(txtData + byteCount, "// more info and bugs-report: github.com/raysan5/raylib //\n");
byteCount += sprintf(txtData + byteCount, "// feedback and support: ray[at]raylib.com //\n");
byteCount += sprintf(txtData + byteCount, "// //\n");
byteCount += sprintf(txtData + byteCount, "// Copyright (c) 2018-2023 Ramon Santamaria (@raysan5) //\n");
byteCount += sprintf(txtData + byteCount, "// Copyright (c) 2018-2024 Ramon Santamaria (@raysan5) //\n");
byteCount += sprintf(txtData + byteCount, "// //\n");
byteCount += sprintf(txtData + byteCount, "// ---------------------------------------------------------------------------------- //\n");
byteCount += sprintf(txtData + byteCount, "// //\n");
@ -1029,7 +1077,7 @@ bool ExportFontAsCode(Font font, const char *fileName)
#endif
// We have two possible mechanisms to assign font.recs and font.glyphs data,
// that data is already available as global arrays, we two options to assign that data:
// - 1. Data copy. This option consumes more memory and Font MUST be unloaded by user, requiring additional code.
// - 1. Data copy. This option consumes more memory and Font MUST be unloaded by user, requiring additional code
// - 2. Data assignment. This option consumes less memory and Font MUST NOT be unloaded by user because data is on protected DATA segment
//#define SUPPORT_FONT_DATA_COPY
#if defined(SUPPORT_FONT_DATA_COPY)
@ -1064,7 +1112,6 @@ bool ExportFontAsCode(Font font, const char *fileName)
return success;
}
// Draw current FPS
// NOTE: Uses default font
void DrawFPS(int posX, int posY)
@ -1104,7 +1151,7 @@ void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, f
int size = TextLength(text); // Total size in bytes of the text, scanned by codepoints in loop
int textOffsetY = 0; // Offset between lines (on linebreak '\n')
float textOffsetY = 0; // Offset between lines (on linebreak '\n')
float textOffsetX = 0.0f; // Offset X to next character to draw
float scaleFactor = fontSize/font.baseSize; // Character quad scaling factor
@ -1119,7 +1166,7 @@ void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, f
if (codepoint == '\n')
{
// NOTE: Line spacing is a global variable, use SetTextLineSpacing() to setup
textOffsetY += textLineSpacing;
textOffsetY += (fontSize + textLineSpacing);
textOffsetX = 0.0f;
}
else
@ -1178,7 +1225,7 @@ void DrawTextCodepoint(Font font, int codepoint, Vector2 position, float fontSiz
// Draw multiple character (codepoints)
void DrawTextCodepoints(Font font, const int *codepoints, int codepointCount, Vector2 position, float fontSize, float spacing, Color tint)
{
int textOffsetY = 0; // Offset between lines (on linebreak '\n')
float textOffsetY = 0; // Offset between lines (on linebreak '\n')
float textOffsetX = 0.0f; // Offset X to next character to draw
float scaleFactor = fontSize/font.baseSize; // Character quad scaling factor
@ -1190,7 +1237,7 @@ void DrawTextCodepoints(Font font, const int *codepoints, int codepointCount, Ve
if (codepoints[i] == '\n')
{
// NOTE: Line spacing is a global variable, use SetTextLineSpacing() to setup
textOffsetY += textLineSpacing;
textOffsetY += (fontSize + textLineSpacing);
textOffsetX = 0.0f;
}
else
@ -1235,7 +1282,7 @@ Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing
{
Vector2 textSize = { 0 };
if ((font.texture.id == 0) || (text == NULL)) return textSize;
if ((font.texture.id == 0) || (text == NULL)) return textSize; // Security check
int size = TextLength(text); // Get size in bytes of text
int tempByteCounter = 0; // Used to count longer text line num chars
@ -1244,7 +1291,7 @@ Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing
float textWidth = 0.0f;
float tempTextWidth = 0.0f; // Used to count longer text line width
float textHeight = (float)font.baseSize;
float textHeight = fontSize;
float scaleFactor = fontSize/(float)font.baseSize;
int letter = 0; // Current character
@ -1272,7 +1319,7 @@ Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing
textWidth = 0;
// NOTE: Line spacing is a global variable, use SetTextLineSpacing() to setup
textHeight += (float)textLineSpacing;
textHeight += (fontSize + textLineSpacing);
}
if (tempByteCounter < byteCounter) tempByteCounter = byteCounter;
@ -1281,7 +1328,7 @@ Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing
if (tempTextWidth < textWidth) tempTextWidth = textWidth;
textSize.x = tempTextWidth*scaleFactor + (float)((tempByteCounter - 1)*spacing);
textSize.y = textHeight*scaleFactor;
textSize.y = textHeight;
return textSize;
}
@ -1390,7 +1437,6 @@ const char *TextFormat(const char *text, ...)
return currentBuffer;
}
// Get integer value from text
// NOTE: This function replaces atoi() [stdlib.h]
int TextToInteger(const char *text)
@ -1409,6 +1455,37 @@ int TextToInteger(const char *text)
return value*sign;
}
// Get float value from text
// NOTE: This function replaces atof() [stdlib.h]
// WARNING: Only '.' character is understood as decimal point
float TextToFloat(const char *text)
{
float value = 0.0f;
float sign = 1.0f;
if ((text[0] == '+') || (text[0] == '-'))
{
if (text[0] == '-') sign = -1.0f;
text++;
}
int i = 0;
for (; ((text[i] >= '0') && (text[i] <= '9')); i++) value = value*10.0f + (float)(text[i] - '0');
if (text[i++] != '.') value *= sign;
else
{
float divisor = 10.0f;
for (; ((text[i] >= '0') && (text[i] <= '9')); i++)
{
value += ((float)(text[i] - '0'))/divisor;
divisor = divisor*10.0f;
}
}
return value;
}
#if defined(SUPPORT_TEXT_MANIPULATION)
// Copy one string to another, returns bytes copied
int TextCopy(char *dst, const char *src)
@ -1480,7 +1557,7 @@ const char *TextSubtext(const char *text, int position, int length)
// Replace text string
// REQUIRES: strlen(), strstr(), strncpy(), strcpy()
// WARNING: Allocated memory must be manually freed
char *TextReplace(char *text, const char *replace, const char *by)
char *TextReplace(const char *text, const char *replace, const char *by)
{
// Sanity checks and initialization
if (!text || !replace || !by) return NULL;
@ -1500,7 +1577,7 @@ char *TextReplace(char *text, const char *replace, const char *by)
byLen = TextLength(by);
// Count the number of replacements needed
insertPoint = text;
insertPoint = (char*)text;
for (count = 0; (temp = strstr(insertPoint, replace)); count++) insertPoint = temp + replaceLen;
// Allocate returning string and point temp to it
@ -1620,7 +1697,8 @@ const char **TextSplit(const char *text, char delimiter, int *count)
return result;
}
// Append text at specific position and move cursor!
// Append text at specific position and move cursor
// WARNING: It's up to the user to make sure appended text does not overflow the buffer!
// REQUIRES: strcpy()
void TextAppend(char *text, const char *append, int *position)
{
@ -2001,23 +2079,27 @@ int GetCodepointPrevious(const char *text, int *codepointSize)
//----------------------------------------------------------------------------------
// Module specific Functions Definition
//----------------------------------------------------------------------------------
#if defined(SUPPORT_FILEFORMAT_FNT)
#if defined(SUPPORT_FILEFORMAT_FNT) || defined(SUPPORT_FILEFORMAT_BDF)
// Read a line from memory
// REQUIRES: memcpy()
// NOTE: Returns the number of bytes read
static int GetLine(const char *origin, char *buffer, int maxLength)
{
int count = 0;
for (; count < maxLength; count++) if (origin[count] == '\n') break;
for (; count < maxLength - 1; count++) if (origin[count] == '\n') break;
memcpy(buffer, origin, count);
buffer[count] = '\0';
return count;
}
#endif
#if defined(SUPPORT_FILEFORMAT_FNT)
// Load a BMFont file (AngelCode font file)
// REQUIRES: strstr(), sscanf(), strrchr(), memcpy()
static Font LoadBMFont(const char *fileName)
{
#define MAX_BUFFER_SIZE 256
#define MAX_FONT_IMAGE_PAGES 8
Font font = { 0 };
@ -2029,7 +2111,8 @@ static Font LoadBMFont(const char *fileName)
int imWidth = 0;
int imHeight = 0;
char imFileName[129] = { 0 };
int pageCount = 1;
char imFileName[MAX_FONT_IMAGE_PAGES][129] = { 0 };
int base = 0; // Useless data
int readBytes = 0; // Data bytes read
@ -2048,17 +2131,26 @@ static Font LoadBMFont(const char *fileName)
// Read line data
readBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE);
searchPoint = strstr(buffer, "lineHeight");
readVars = sscanf(searchPoint, "lineHeight=%i base=%i scaleW=%i scaleH=%i", &fontSize, &base, &imWidth, &imHeight);
readVars = sscanf(searchPoint, "lineHeight=%i base=%i scaleW=%i scaleH=%i pages=%i", &fontSize, &base, &imWidth, &imHeight, &pageCount);
fileTextPtr += (readBytes + 1);
if (readVars < 4) { UnloadFileText(fileText); return font; } // Some data not available, file malformed
if (pageCount > MAX_FONT_IMAGE_PAGES)
{
TRACELOG(LOG_WARNING, "FONT: [%s] Font defines more pages than supported: %i/%i", fileName, pageCount, MAX_FONT_IMAGE_PAGES);
pageCount = MAX_FONT_IMAGE_PAGES;
}
for (int i = 0; i < pageCount; i++)
{
readBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE);
searchPoint = strstr(buffer, "file");
readVars = sscanf(searchPoint, "file=\"%128[^\"]\"", imFileName);
readVars = sscanf(searchPoint, "file=\"%128[^\"]\"", imFileName[i]);
fileTextPtr += (readBytes + 1);
if (readVars < 1) { UnloadFileText(fileText); return font; } // No fileName read
}
readBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE);
searchPoint = strstr(buffer, "count");
@ -2067,50 +2159,56 @@ static Font LoadBMFont(const char *fileName)
if (readVars < 1) { UnloadFileText(fileText); return font; } // No glyphCount read
// Compose correct path using route of .fnt file (fileName) and imFileName
char *imPath = NULL;
char *lastSlash = NULL;
// Load all required images for further compose
Image *imFonts = (Image *)RL_CALLOC(pageCount, sizeof(Image)); // Font atlases, multiple images
lastSlash = strrchr(fileName, '/');
if (lastSlash == NULL) lastSlash = strrchr(fileName, '\\');
if (lastSlash != NULL)
for (int i = 0; i < pageCount; i++)
{
// NOTE: We need some extra space to avoid memory corruption on next allocations!
imPath = (char *)RL_CALLOC(TextLength(fileName) - TextLength(lastSlash) + TextLength(imFileName) + 4, 1);
memcpy(imPath, fileName, TextLength(fileName) - TextLength(lastSlash) + 1);
memcpy(imPath + TextLength(fileName) - TextLength(lastSlash) + 1, imFileName, TextLength(imFileName));
}
else imPath = imFileName;
imFonts[i] = LoadImage(TextFormat("%s/%s", GetDirectoryPath(fileName), imFileName[i]));
TRACELOGD(" > Image loading path: %s", imPath);
Image imFont = LoadImage(imPath);
if (imFont.format == PIXELFORMAT_UNCOMPRESSED_GRAYSCALE)
if (imFonts[i].format == PIXELFORMAT_UNCOMPRESSED_GRAYSCALE)
{
// Convert image to GRAYSCALE + ALPHA, using the mask as the alpha channel
Image imFontAlpha = {
.data = RL_CALLOC(imFont.width*imFont.height, 2),
.width = imFont.width,
.height = imFont.height,
.data = RL_CALLOC(imFonts[i].width*imFonts[i].height, 2),
.width = imFonts[i].width,
.height = imFonts[i].height,
.mipmaps = 1,
.format = PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA
};
for (int p = 0, i = 0; p < (imFont.width*imFont.height*2); p += 2, i++)
for (int p = 0, pi = 0; p < (imFonts[i].width*imFonts[i].height*2); p += 2, pi++)
{
((unsigned char *)(imFontAlpha.data))[p] = 0xff;
((unsigned char *)(imFontAlpha.data))[p + 1] = ((unsigned char *)imFont.data)[i];
((unsigned char *)(imFontAlpha.data))[p + 1] = ((unsigned char *)imFonts[i].data)[pi];
}
UnloadImage(imFont);
imFont = imFontAlpha;
UnloadImage(imFonts[i]);
imFonts[i] = imFontAlpha;
}
}
font.texture = LoadTextureFromImage(imFont);
Image fullFont = imFonts[0];
for (int i = 1; i < pageCount; i++) UnloadImage(imFonts[i]);
if (lastSlash != NULL) RL_FREE(imPath);
// If multiple atlas, then merge atlas
// NOTE: WARNING: This process could be really slow!
if (pageCount > 1)
{
// Resize font atlas to draw additional images
ImageResizeCanvas(&fullFont, imWidth, imHeight*pageCount, 0, 0, BLACK);
for (int i = 1; i < pageCount; i++)
{
Rectangle srcRec = { 0.0f, 0.0f, (float)imWidth, (float)imHeight };
Rectangle destRec = { 0.0f, (float)imHeight*(float)i, (float)imWidth, (float)imHeight };
ImageDraw(&fullFont, imFonts[i], srcRec, destRec, WHITE);
}
}
RL_FREE(imFonts);
font.texture = LoadTextureFromImage(fullFont);
// Fill font characters info data
font.baseSize = fontSize;
@ -2119,19 +2217,19 @@ static Font LoadBMFont(const char *fileName)
font.glyphs = (GlyphInfo *)RL_MALLOC(glyphCount*sizeof(GlyphInfo));
font.recs = (Rectangle *)RL_MALLOC(glyphCount*sizeof(Rectangle));
int charId, charX, charY, charWidth, charHeight, charOffsetX, charOffsetY, charAdvanceX;
int charId, charX, charY, charWidth, charHeight, charOffsetX, charOffsetY, charAdvanceX, pageID;
for (int i = 0; i < glyphCount; i++)
{
readBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE);
readVars = sscanf(buffer, "char id=%i x=%i y=%i width=%i height=%i xoffset=%i yoffset=%i xadvance=%i",
&charId, &charX, &charY, &charWidth, &charHeight, &charOffsetX, &charOffsetY, &charAdvanceX);
readVars = sscanf(buffer, "char id=%i x=%i y=%i width=%i height=%i xoffset=%i yoffset=%i xadvance=%i page=%i",
&charId, &charX, &charY, &charWidth, &charHeight, &charOffsetX, &charOffsetY, &charAdvanceX, &pageID);
fileTextPtr += (readBytes + 1);
if (readVars == 8) // Make sure all char data has been properly read
if (readVars == 9) // Make sure all char data has been properly read
{
// Get character rectangle in the font atlas texture
font.recs[i] = (Rectangle){ (float)charX, (float)charY, (float)charWidth, (float)charHeight };
font.recs[i] = (Rectangle){ (float)charX, (float)charY + (float)imHeight*pageID, (float)charWidth, (float)charHeight };
// Save data properly in sprite font
font.glyphs[i].value = charId;
@ -2139,13 +2237,13 @@ static Font LoadBMFont(const char *fileName)
font.glyphs[i].offsetY = charOffsetY;
font.glyphs[i].advanceX = charAdvanceX;
// Fill character image data from imFont data
font.glyphs[i].image = ImageFromImage(imFont, font.recs[i]);
// Fill character image data from full font data
font.glyphs[i].image = ImageFromImage(fullFont, font.recs[i]);
}
else TRACELOG(LOG_WARNING, "FONT: [%s] Some characters data not correctly provided", fileName);
}
UnloadImage(imFont);
UnloadImage(fullFont);
UnloadFileText(fileText);
if (font.texture.id == 0)
@ -2158,6 +2256,258 @@ static Font LoadBMFont(const char *fileName)
return font;
}
#endif
#if defined(SUPPORT_FILEFORMAT_BDF)
// Convert hexadecimal to decimal (single digit)
static unsigned char HexToInt(char hex)
{
if (hex >= '0' && hex <= '9') return hex - '0';
else if (hex >= 'a' && hex <= 'f') return hex - 'a' + 10;
else if (hex >= 'A' && hex <= 'F') return hex - 'A' + 10;
else return 0;
}
// Load font data for further use
// NOTE: Requires BDF font memory data
static GlyphInfo *LoadFontDataBDF(const unsigned char *fileData, int dataSize, int *codepoints, int codepointCount, int *outFontSize)
{
#define MAX_BUFFER_SIZE 256
char buffer[MAX_BUFFER_SIZE] = { 0 };
GlyphInfo *glyphs = NULL;
bool genFontChars = false;
int totalReadBytes = 0; // Data bytes read (total)
int readBytes = 0; // Data bytes read (line)
int readVars = 0; // Variables filled by sscanf()
const char *fileText = (const char*)fileData;
const char *fileTextPtr = fileText;
bool fontMalformed = false; // Is the font malformed
bool fontStarted = false; // Has font started (STARTFONT)
int fontBBw = 0; // Font base character bounding box width
int fontBBh = 0; // Font base character bounding box height
int fontBBxoff0 = 0; // Font base character bounding box X0 offset
int fontBByoff0 = 0; // Font base character bounding box Y0 offset
int fontAscent = 0; // Font ascent
bool charStarted = false; // Has character started (STARTCHAR)
bool charBitmapStarted = false; // Has bitmap data started (BITMAP)
int charBitmapNextRow = 0; // Y position for the next row of bitmap data
int charEncoding = -1; // The unicode value of the character (-1 if not set)
int charBBw = 0; // Character bounding box width
int charBBh = 0; // Character bounding box height
int charBBxoff0 = 0; // Character bounding box X0 offset
int charBByoff0 = 0; // Character bounding box Y0 offset
int charDWidthX = 0; // Character advance X
int charDWidthY = 0; // Character advance Y (unused)
GlyphInfo *charGlyphInfo = NULL; // Pointer to output glyph info (NULL if not set)
if (fileData == NULL) return glyphs;
// In case no chars count provided, default to 95
codepointCount = (codepointCount > 0)? codepointCount : 95;
// Fill fontChars in case not provided externally
// NOTE: By default we fill glyphCount consecutively, starting at 32 (Space)
if (codepoints == NULL)
{
codepoints = (int *)RL_MALLOC(codepointCount*sizeof(int));
for (int i = 0; i < codepointCount; i++) codepoints[i] = i + 32;
genFontChars = true;
}
glyphs = (GlyphInfo *)RL_CALLOC(codepointCount, sizeof(GlyphInfo));
while (totalReadBytes <= dataSize)
{
readBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE);
totalReadBytes += (readBytes + 1);
fileTextPtr += (readBytes + 1);
// Line: COMMENT
if (strstr(buffer, "COMMENT") != NULL) continue; // Ignore line
if (charStarted)
{
// Line: ENDCHAR
if (strstr(buffer, "ENDCHAR") != NULL)
{
charStarted = false;
continue;
}
if (charBitmapStarted)
{
if (charGlyphInfo != NULL)
{
int pixelY = charBitmapNextRow++;
if (pixelY >= charGlyphInfo->image.height) break;
for (int x = 0; x < readBytes; x++)
{
unsigned char byte = HexToInt(buffer[x]);
for (int bitX = 0; bitX < 4; bitX++)
{
int pixelX = ((x*4) + bitX);
if (pixelX >= charGlyphInfo->image.width) break;
if ((byte & (8 >> bitX)) > 0) ((unsigned char *)charGlyphInfo->image.data)[(pixelY*charGlyphInfo->image.width) + pixelX] = 255;
}
}
}
continue;
}
// Line: ENCODING
if (strstr(buffer, "ENCODING") != NULL)
{
readVars = sscanf(buffer, "ENCODING %i", &charEncoding);
continue;
}
// Line: BBX
if (strstr(buffer, "BBX") != NULL)
{
readVars = sscanf(buffer, "BBX %i %i %i %i", &charBBw, &charBBh, &charBBxoff0, &charBByoff0);
continue;
}
// Line: DWIDTH
if (strstr(buffer, "DWIDTH") != NULL)
{
readVars = sscanf(buffer, "DWIDTH %i %i", &charDWidthX, &charDWidthY);
continue;
}
// Line: BITMAP
if (strstr(buffer, "BITMAP") != NULL)
{
// Search for glyph index in codepoints
charGlyphInfo = NULL;
for (int codepointIndex = 0; codepointIndex < codepointCount; codepointIndex++)
{
if (codepoints[codepointIndex] == charEncoding)
{
charGlyphInfo = &glyphs[codepointIndex];
break;
}
}
// Init glyph info
if (charGlyphInfo != NULL)
{
charGlyphInfo->value = charEncoding;
charGlyphInfo->offsetX = charBBxoff0 + fontBByoff0;
charGlyphInfo->offsetY = fontBBh - (charBBh + charBByoff0 + fontBByoff0 + fontAscent);
charGlyphInfo->advanceX = charDWidthX;
charGlyphInfo->image.data = RL_CALLOC(charBBw*charBBh, 1);
charGlyphInfo->image.width = charBBw;
charGlyphInfo->image.height = charBBh;
charGlyphInfo->image.mipmaps = 1;
charGlyphInfo->image.format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE;
}
charBitmapStarted = true;
charBitmapNextRow = 0;
continue;
}
}
else if (fontStarted)
{
// Line: ENDFONT
if (strstr(buffer, "ENDFONT") != NULL)
{
fontStarted = false;
break;
}
// Line: SIZE
if (strstr(buffer, "SIZE") != NULL)
{
if (outFontSize != NULL) readVars = sscanf(buffer, "SIZE %i", outFontSize);
continue;
}
// PIXEL_SIZE
if (strstr(buffer, "PIXEL_SIZE") != NULL)
{
if (outFontSize != NULL) readVars = sscanf(buffer, "PIXEL_SIZE %i", outFontSize);
continue;
}
// FONTBOUNDINGBOX
if (strstr(buffer, "FONTBOUNDINGBOX") != NULL)
{
readVars = sscanf(buffer, "FONTBOUNDINGBOX %i %i %i %i", &fontBBw, &fontBBh, &fontBBxoff0, &fontBByoff0);
continue;
}
// FONT_ASCENT
if (strstr(buffer, "FONT_ASCENT") != NULL)
{
readVars = sscanf(buffer, "FONT_ASCENT %i", &fontAscent);
continue;
}
// STARTCHAR
if (strstr(buffer, "STARTCHAR") != NULL)
{
charStarted = true;
charEncoding = -1;
charGlyphInfo = NULL;
charBBw = 0;
charBBh = 0;
charBBxoff0 = 0;
charBByoff0 = 0;
charDWidthX = 0;
charDWidthY = 0;
charGlyphInfo = NULL;
charBitmapStarted = false;
charBitmapNextRow = 0;
continue;
}
}
else
{
// STARTFONT
if (strstr(buffer, "STARTFONT") != NULL)
{
if (fontStarted)
{
fontMalformed = true;
break;
}
else
{
fontStarted = true;
continue;
}
}
}
}
if (genFontChars) RL_FREE(codepoints);
if (fontMalformed)
{
RL_FREE(glyphs);
glyphs = NULL;
}
return glyphs;
}
#endif // SUPPORT_FILEFORMAT_BDF
#endif // SUPPORT_MODULE_RTEXT

View file

@ -42,7 +42,7 @@
*
* LICENSE: zlib/libpng
*
* Copyright (c) 2013-2023 Ramon Santamaria (@raysan5)
* Copyright (c) 2013-2024 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.
@ -71,10 +71,10 @@
#if defined(SUPPORT_MODULE_RTEXTURES)
#include "utils.h" // Required for: TRACELOG()
#include "rlgl.h" // OpenGL abstraction layer to OpenGL 1.1, 3.3 or ES2
#include "rlgl.h" // OpenGL abstraction layer to multiple versions
#include <stdlib.h> // Required for: malloc(), free()
#include <string.h> // Required for: strlen() [Used in ImageTextEx()], strcmp() [Used in LoadImageFromMemory()]
#include <stdlib.h> // Required for: malloc(), calloc(), free()
#include <string.h> // Required for: strlen() [Used in ImageTextEx()], strcmp() [Used in LoadImageFromMemory()/LoadImageAnimFromMemory()/ExportImageToMemory()]
#include <math.h> // Required for: fabsf() [Used in DrawTextureRec()]
#include <stdio.h> // Required for: sprintf() [Used in ExportImageAsCode()]
@ -212,9 +212,19 @@
#define STBIR_MALLOC(size,c) ((void)(c), RL_MALLOC(size))
#define STBIR_FREE(ptr,c) ((void)(c), RL_FREE(ptr))
#if defined(__GNUC__) // GCC and Clang
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-function"
#endif
#define STB_IMAGE_RESIZE_IMPLEMENTATION
#include "external/stb_image_resize2.h" // Required for: stbir_resize_uint8_linear() [ImageResize()]
#if defined(__GNUC__) // GCC and Clang
#pragma GCC diagnostic pop
#endif
#if defined(SUPPORT_FILEFORMAT_SVG)
#define NANOSVG_IMPLEMENTATION // Expands implementation
#include "external/nanosvg.h"
@ -283,9 +293,12 @@ Image LoadImage(const char *fileName)
unsigned char *fileData = LoadFileData(fileName, &dataSize);
// Loading image from memory data
if (fileData != NULL) image = LoadImageFromMemory(GetFileExtension(fileName), fileData, dataSize);
if (fileData != NULL)
{
image = LoadImageFromMemory(GetFileExtension(fileName), fileData, dataSize);
RL_FREE(fileData);
UnloadFileData(fileData);
}
return image;
}
@ -301,9 +314,12 @@ Image LoadImageRaw(const char *fileName, int width, int height, int format, int
if (fileData != NULL)
{
unsigned char *dataPtr = fileData;
unsigned int size = GetPixelDataSize(width, height, format);
int size = GetPixelDataSize(width, height, format);
if (headerSize > 0) dataPtr += headerSize;
if (size <= dataSize) // Security check
{
// Offset file data to expected raw image by header size
if ((headerSize > 0) && ((headerSize + size) <= dataSize)) dataPtr += headerSize;
image.data = RL_MALLOC(size); // Allocate required memory in bytes
memcpy(image.data, dataPtr, size); // Copy required data to image
@ -311,8 +327,9 @@ Image LoadImageRaw(const char *fileName, int width, int height, int format, int
image.height = height;
image.mipmaps = 1;
image.format = format;
}
RL_FREE(fileData);
UnloadFileData(fileData);
}
return image;
@ -421,7 +438,7 @@ Image LoadImageAnim(const char *fileName, int *frames)
image.mipmaps = 1;
image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
RL_FREE(fileData);
UnloadFileData(fileData);
RL_FREE(delays); // NOTE: Frames delays are discarded
}
}
@ -438,12 +455,56 @@ Image LoadImageAnim(const char *fileName, int *frames)
return image;
}
// Load animated image data
// - Image.data buffer includes all frames: [image#0][image#1][image#2][...]
// - Number of frames is returned through 'frames' parameter
// - All frames are returned in RGBA format
// - Frames delay data is discarded
Image LoadImageAnimFromMemory(const char *fileType, const unsigned char *fileData, int dataSize, int *frames)
{
Image image = { 0 };
int frameCount = 0;
// Security check for input data
if ((fileType == NULL) || (fileData == NULL) || (dataSize == 0)) return image;
#if defined(SUPPORT_FILEFORMAT_GIF)
if ((strcmp(fileType, ".gif") == 0) || (strcmp(fileType, ".GIF") == 0))
{
if (fileData != NULL)
{
int comp = 0;
int *delays = NULL;
image.data = stbi_load_gif_from_memory(fileData, dataSize, &delays, &image.width, &image.height, &frameCount, &comp, 4);
image.mipmaps = 1;
image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
RL_FREE(delays); // NOTE: Frames delays are discarded
}
}
#else
if (false) { }
#endif
else
{
image = LoadImageFromMemory(fileType, fileData, dataSize);
frameCount = 1;
}
*frames = frameCount;
return image;
}
// Load image from memory buffer, fileType refers to extension: i.e. ".png"
// WARNING: File extension must be provided in lower-case
Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, int dataSize)
{
Image image = { 0 };
// Security check for input data
if ((fileType == NULL) || (fileData == NULL) || (dataSize == 0)) return image;
if ((false)
#if defined(SUPPORT_FILEFORMAT_PNG)
|| (strcmp(fileType, ".png") == 0) || (strcmp(fileType, ".PNG") == 0)
@ -633,10 +694,11 @@ Image LoadImageFromTexture(Texture2D texture)
// Load image from screen buffer and (screenshot)
Image LoadImageFromScreen(void)
{
Vector2 scale = GetWindowScaleDPI();
Image image = { 0 };
image.width = GetScreenWidth();
image.height = GetScreenHeight();
image.width = (int)(GetScreenWidth()*scale.x);
image.height = (int)(GetScreenHeight()*scale.y);
image.mipmaps = 1;
image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
image.data = rlReadScreenPixels(image.width, image.height);
@ -647,11 +709,15 @@ Image LoadImageFromScreen(void)
// Check if an image is ready
bool IsImageReady(Image image)
{
return ((image.data != NULL) && // Validate pixel data available
bool result = false;
if ((image.data != NULL) && // Validate pixel data available
(image.width > 0) &&
(image.height > 0) && // Validate image size
(image.format > 0) && // Validate image format
(image.mipmaps > 0)); // Validate image mipmaps (at least 1 for basic mipmap level)
(image.mipmaps > 0)) result = true; // Validate image mipmaps (at least 1 for basic mipmap level)
return result;
}
// Unload image from CPU memory (RAM)
@ -666,6 +732,7 @@ bool ExportImage(Image image, const char *fileName)
{
int result = 0;
// Security check for input data
if ((image.width == 0) || (image.height == 0) || (image.data == NULL)) return result;
#if defined(SUPPORT_IMAGE_EXPORT)
@ -753,6 +820,7 @@ unsigned char *ExportImageToMemory(Image image, const char *fileType, int *dataS
unsigned char *fileData = NULL;
*dataSize = 0;
// Security check for input data
if ((image.width == 0) || (image.height == 0) || (image.data == NULL)) return NULL;
#if defined(SUPPORT_IMAGE_EXPORT)
@ -800,7 +868,7 @@ bool ExportImageAsCode(Image image, const char *fileName)
byteCount += sprintf(txtData + byteCount, "// more info and bugs-report: github.com/raysan5/raylib //\n");
byteCount += sprintf(txtData + byteCount, "// feedback and support: ray[at]raylib.com //\n");
byteCount += sprintf(txtData + byteCount, "// //\n");
byteCount += sprintf(txtData + byteCount, "// Copyright (c) 2018-2023 Ramon Santamaria (@raysan5) //\n");
byteCount += sprintf(txtData + byteCount, "// Copyright (c) 2018-2024 Ramon Santamaria (@raysan5) //\n");
byteCount += sprintf(txtData + byteCount, "// //\n");
byteCount += sprintf(txtData + byteCount, "////////////////////////////////////////////////////////////////////////////////////////\n\n");
@ -856,8 +924,8 @@ Image GenImageColor(int width, int height, Color color)
#if defined(SUPPORT_IMAGE_GENERATION)
// Generate image: linear gradient
// The direction value specifies the direction of the gradient (in degrees)
// with 0 being vertical (from top to bottom), 90 being horizontal (from left to right).
// The gradient effectively rotates counter-clockwise by the specified amount.
// with 0 being vertical (from top to bottom), 90 being horizontal (from left to right)
// The gradient effectively rotates counter-clockwise by the specified amount
Image GenImageGradientLinear(int width, int height, int direction, Color start, Color end)
{
Color *pixels = (Color *)RL_MALLOC(width*height*sizeof(Color));
@ -1008,6 +1076,7 @@ Image GenImageChecked(int width, int height, int checksX, int checksY, Color col
}
// Generate image: white noise
// NOTE: It requires GetRandomValue(), defined in [rcore]
Image GenImageWhiteNoise(int width, int height, float factor)
{
Color *pixels = (Color *)RL_MALLOC(width*height*sizeof(Color));
@ -1600,7 +1669,6 @@ void ImageResizeNN(Image *image,int newWidth,int newHeight)
UnloadImageColors(pixels);
}
// Resize and image to new size
// NOTE: Uses stb default scaling filters (both bicubic):
// STBIR_DEFAULT_FILTER_UPSAMPLE STBIR_FILTER_CATMULLROM
@ -1693,8 +1761,22 @@ void ImageResizeCanvas(Image *image, int newWidth, int newHeight, int offsetX, i
int bytesPerPixel = GetPixelDataSize(1, 1, image->format);
unsigned char *resizedData = (unsigned char *)RL_CALLOC(newWidth*newHeight*bytesPerPixel, 1);
// TODO: Fill resized canvas with fill color (must be formatted to image->format)
// Fill resized canvas with fill color
// Set first pixel with image->format
SetPixelColor(resizedData, fill, image->format);
// Fill remaining bytes of first row
for (int x = 1; x < newWidth; x++)
{
memcpy(resizedData + x*bytesPerPixel, resizedData, bytesPerPixel);
}
// Copy the first row into the other rows
for (int y = 1; y < newHeight; y++)
{
memcpy(resizedData + y*newWidth*bytesPerPixel, resizedData, newWidth*bytesPerPixel);
}
// Copy old image to resized canvas
int dstOffsetSize = ((int)dstPos.y*newWidth + (int)dstPos.x)*bytesPerPixel;
for (int y = 0; y < (int)srcRec.height; y++)
@ -1930,8 +2012,9 @@ void ImageAlphaPremultiply(Image *image)
ImageFormat(image, format);
}
// Apply box blur
void ImageBlurGaussian(Image *image, int blurSize) {
// Apply box blur to image
void ImageBlurGaussian(Image *image, int blurSize)
{
// Security check to avoid program crash
if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
@ -1943,7 +2026,8 @@ void ImageBlurGaussian(Image *image, int blurSize) {
Vector4 *pixelsCopy1 = RL_MALLOC((image->height)*(image->width)*sizeof(Vector4));
Vector4 *pixelsCopy2 = RL_MALLOC((image->height)*(image->width)*sizeof(Vector4));
for (int i = 0; i < (image->height)*(image->width); i++) {
for (int i = 0; i < (image->height*image->width); i++)
{
pixelsCopy1[i].x = pixels[i].r;
pixelsCopy1[i].y = pixels[i].g;
pixelsCopy1[i].z = pixels[i].b;
@ -1951,7 +2035,8 @@ void ImageBlurGaussian(Image *image, int blurSize) {
}
// Repeated convolution of rectangular window signal by itself converges to a gaussian distribution
for (int j = 0; j < GAUSSIAN_BLUR_ITERATIONS; j++) {
for (int j = 0; j < GAUSSIAN_BLUR_ITERATIONS; j++)
{
// Horizontal motion blur
for (int row = 0; row < image->height; row++)
{
@ -1959,9 +2044,9 @@ void ImageBlurGaussian(Image *image, int blurSize) {
float avgG = 0.0f;
float avgB = 0.0f;
float avgAlpha = 0.0f;
int convolutionSize = blurSize+1;
int convolutionSize = blurSize;
for (int i = 0; i < blurSize+1; i++)
for (int i = 0; i < blurSize; i++)
{
avgR += pixelsCopy1[row*image->width + i].x;
avgG += pixelsCopy1[row*image->width + i].y;
@ -1969,19 +2054,14 @@ void ImageBlurGaussian(Image *image, int blurSize) {
avgAlpha += pixelsCopy1[row*image->width + i].w;
}
pixelsCopy2[row*image->width].x = avgR/convolutionSize;
pixelsCopy2[row*image->width].y = avgG/convolutionSize;
pixelsCopy2[row*image->width].z = avgB/convolutionSize;
pixelsCopy2[row*image->width].w = avgAlpha/convolutionSize;
for (int x = 1; x < image->width; x++)
for (int x = 0; x < image->width; x++)
{
if (x-blurSize >= 0)
if (x-blurSize-1 >= 0)
{
avgR -= pixelsCopy1[row*image->width + x-blurSize].x;
avgG -= pixelsCopy1[row*image->width + x-blurSize].y;
avgB -= pixelsCopy1[row*image->width + x-blurSize].z;
avgAlpha -= pixelsCopy1[row*image->width + x-blurSize].w;
avgR -= pixelsCopy1[row*image->width + x-blurSize-1].x;
avgG -= pixelsCopy1[row*image->width + x-blurSize-1].y;
avgB -= pixelsCopy1[row*image->width + x-blurSize-1].z;
avgAlpha -= pixelsCopy1[row*image->width + x-blurSize-1].w;
convolutionSize--;
}
@ -2008,9 +2088,9 @@ void ImageBlurGaussian(Image *image, int blurSize) {
float avgG = 0.0f;
float avgB = 0.0f;
float avgAlpha = 0.0f;
int convolutionSize = blurSize+1;
int convolutionSize = blurSize;
for (int i = 0; i < blurSize+1; i++)
for (int i = 0; i < blurSize; i++)
{
avgR += pixelsCopy2[i*image->width + col].x;
avgG += pixelsCopy2[i*image->width + col].y;
@ -2018,19 +2098,14 @@ void ImageBlurGaussian(Image *image, int blurSize) {
avgAlpha += pixelsCopy2[i*image->width + col].w;
}
pixelsCopy1[col].x = (unsigned char) (avgR/convolutionSize);
pixelsCopy1[col].y = (unsigned char) (avgG/convolutionSize);
pixelsCopy1[col].z = (unsigned char) (avgB/convolutionSize);
pixelsCopy1[col].w = (unsigned char) (avgAlpha/convolutionSize);
for (int y = 1; y < image->height; y++)
for (int y = 0; y < image->height; y++)
{
if (y-blurSize >= 0)
if (y-blurSize-1 >= 0)
{
avgR -= pixelsCopy2[(y-blurSize)*image->width + col].x;
avgG -= pixelsCopy2[(y-blurSize)*image->width + col].y;
avgB -= pixelsCopy2[(y-blurSize)*image->width + col].z;
avgAlpha -= pixelsCopy2[(y-blurSize)*image->width + col].w;
avgR -= pixelsCopy2[(y-blurSize-1)*image->width + col].x;
avgG -= pixelsCopy2[(y-blurSize-1)*image->width + col].y;
avgB -= pixelsCopy2[(y-blurSize-1)*image->width + col].z;
avgAlpha -= pixelsCopy2[(y-blurSize-1)*image->width + col].w;
convolutionSize--;
}
if (y+blurSize < image->height)
@ -2050,7 +2125,6 @@ void ImageBlurGaussian(Image *image, int blurSize) {
}
}
// Reverse premultiply
for (int i = 0; i < (image->width)*(image->height); i++)
{
@ -2082,6 +2156,135 @@ void ImageBlurGaussian(Image *image, int blurSize) {
ImageFormat(image, format);
}
// The kernel matrix is assumed to be square. Only supply the width of the kernel
void ImageKernelConvolution(Image *image, float* kernel, int kernelSize)
{
if ((image->data == NULL) || (image->width == 0) || (image->height == 0) || kernel == NULL) return;
int kernelWidth = (int)sqrtf((float)kernelSize);
if (kernelWidth*kernelWidth != kernelSize)
{
TRACELOG(LOG_WARNING, "IMAGE: Convolution kernel must be square to be applied");
return;
}
Color *pixels = LoadImageColors(*image);
Vector4 *imageCopy2 = RL_MALLOC((image->height)*(image->width)*sizeof(Vector4));
Vector4 *temp = RL_MALLOC(kernelSize*sizeof(Vector4));
for (int i = 0; i < kernelSize; i++)
{
temp[i].x = 0.0f;
temp[i].y = 0.0f;
temp[i].z = 0.0f;
temp[i].w = 0.0f;
}
float rRes = 0.0f;
float gRes = 0.0f;
float bRes = 0.0f;
float aRes = 0.0f;
int startRange = 0, endRange = 0;
if (kernelWidth%2 == 0)
{
startRange = -kernelWidth/2;
endRange = kernelWidth/2;
}
else
{
startRange = -kernelWidth/2;
endRange = kernelWidth/2 + 1;
}
for (int x = 0; x < image->height; x++)
{
for (int y = 0; y < image->width; y++)
{
for (int xk = startRange; xk < endRange; xk++)
{
for (int yk = startRange; yk < endRange; yk++)
{
int xkabs = xk + kernelWidth/2;
int ykabs = yk + kernelWidth/2;
unsigned int imgindex = image->width*(x + xk) + (y + yk);
if (imgindex >= (unsigned int)(image->width*image->height))
{
temp[kernelWidth*xkabs + ykabs].x = 0.0f;
temp[kernelWidth*xkabs + ykabs].y = 0.0f;
temp[kernelWidth*xkabs + ykabs].z = 0.0f;
temp[kernelWidth*xkabs + ykabs].w = 0.0f;
}
else
{
temp[kernelWidth*xkabs + ykabs].x = ((float)pixels[imgindex].r)/255.0f*kernel[kernelWidth*xkabs + ykabs];
temp[kernelWidth*xkabs + ykabs].y = ((float)pixels[imgindex].g)/255.0f*kernel[kernelWidth*xkabs + ykabs];
temp[kernelWidth*xkabs + ykabs].z = ((float)pixels[imgindex].b)/255.0f*kernel[kernelWidth*xkabs + ykabs];
temp[kernelWidth*xkabs + ykabs].w = ((float)pixels[imgindex].a)/255.0f*kernel[kernelWidth*xkabs + ykabs];
}
}
}
for (int i = 0; i < kernelSize; i++)
{
rRes += temp[i].x;
gRes += temp[i].y;
bRes += temp[i].z;
aRes += temp[i].w;
}
if (rRes < 0.0f) rRes = 0.0f;
if (gRes < 0.0f) gRes = 0.0f;
if (bRes < 0.0f) bRes = 0.0f;
if (rRes > 1.0f) rRes = 1.0f;
if (gRes > 1.0f) gRes = 1.0f;
if (bRes > 1.0f) bRes = 1.0f;
imageCopy2[image->width*x + y].x = rRes;
imageCopy2[image->width*x + y].y = gRes;
imageCopy2[image->width*x + y].z = bRes;
imageCopy2[image->width*x + y].w = aRes;
rRes = 0.0f;
gRes = 0.0f;
bRes = 0.0f;
aRes = 0.0f;
for (int i = 0; i < kernelSize; i++)
{
temp[i].x = 0.0f;
temp[i].y = 0.0f;
temp[i].z = 0.0f;
temp[i].w = 0.0f;
}
}
}
for (int i = 0; i < (image->width*image->height); i++)
{
float alpha = (float)imageCopy2[i].w;
pixels[i].r = (unsigned char)((imageCopy2[i].x)*255.0f);
pixels[i].g = (unsigned char)((imageCopy2[i].y)*255.0f);
pixels[i].b = (unsigned char)((imageCopy2[i].z)*255.0f);
pixels[i].a = (unsigned char)((alpha)*255.0f);
}
int format = image->format;
RL_FREE(image->data);
RL_FREE(imageCopy2);
RL_FREE(temp);
image->data = pixels;
image->format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
ImageFormat(image, format);
}
// Generate all mipmap levels for a provided image
// NOTE 1: Supports POT and NPOT images
// NOTE 2: image.data is scaled to include mipmap levels
@ -2469,21 +2672,17 @@ void ImageColorTint(Image *image, Color color)
float cB = (float)color.b/255;
float cA = (float)color.a/255;
for (int y = 0; y < image->height; y++)
for (int i = 0; i < image->width*image->height; i++)
{
for (int x = 0; x < image->width; x++)
{
int index = y*image->width + x;
unsigned char r = (unsigned char)(((float)pixels[index].r/255*cR)*255.0f);
unsigned char g = (unsigned char)(((float)pixels[index].g/255*cG)*255.0f);
unsigned char b = (unsigned char)(((float)pixels[index].b/255*cB)*255.0f);
unsigned char a = (unsigned char)(((float)pixels[index].a/255*cA)*255.0f);
unsigned char r = (unsigned char)(((float)pixels[i].r/255*cR)*255.0f);
unsigned char g = (unsigned char)(((float)pixels[i].g/255*cG)*255.0f);
unsigned char b = (unsigned char)(((float)pixels[i].b/255*cB)*255.0f);
unsigned char a = (unsigned char)(((float)pixels[i].a/255*cA)*255.0f);
pixels[index].r = r;
pixels[index].g = g;
pixels[index].b = b;
pixels[index].a = a;
}
pixels[i].r = r;
pixels[i].g = g;
pixels[i].b = b;
pixels[i].a = a;
}
int format = image->format;
@ -2503,14 +2702,11 @@ void ImageColorInvert(Image *image)
Color *pixels = LoadImageColors(*image);
for (int y = 0; y < image->height; y++)
for (int i = 0; i < image->width*image->height; i++)
{
for (int x = 0; x < image->width; x++)
{
pixels[y*image->width + x].r = 255 - pixels[y*image->width + x].r;
pixels[y*image->width + x].g = 255 - pixels[y*image->width + x].g;
pixels[y*image->width + x].b = 255 - pixels[y*image->width + x].b;
}
pixels[i].r = 255 - pixels[i].r;
pixels[i].g = 255 - pixels[i].g;
pixels[i].b = 255 - pixels[i].b;
}
int format = image->format;
@ -2543,11 +2739,9 @@ void ImageColorContrast(Image *image, float contrast)
Color *pixels = LoadImageColors(*image);
for (int y = 0; y < image->height; y++)
for (int i = 0; i < image->width*image->height; i++)
{
for (int x = 0; x < image->width; x++)
{
float pR = (float)pixels[y*image->width + x].r/255.0f;
float pR = (float)pixels[i].r/255.0f;
pR -= 0.5f;
pR *= contrast;
pR += 0.5f;
@ -2555,7 +2749,7 @@ void ImageColorContrast(Image *image, float contrast)
if (pR < 0) pR = 0;
if (pR > 255) pR = 255;
float pG = (float)pixels[y*image->width + x].g/255.0f;
float pG = (float)pixels[i].g/255.0f;
pG -= 0.5f;
pG *= contrast;
pG += 0.5f;
@ -2563,7 +2757,7 @@ void ImageColorContrast(Image *image, float contrast)
if (pG < 0) pG = 0;
if (pG > 255) pG = 255;
float pB = (float)pixels[y*image->width + x].b/255.0f;
float pB = (float)pixels[i].b/255.0f;
pB -= 0.5f;
pB *= contrast;
pB += 0.5f;
@ -2571,10 +2765,9 @@ void ImageColorContrast(Image *image, float contrast)
if (pB < 0) pB = 0;
if (pB > 255) pB = 255;
pixels[y*image->width + x].r = (unsigned char)pR;
pixels[y*image->width + x].g = (unsigned char)pG;
pixels[y*image->width + x].b = (unsigned char)pB;
}
pixels[i].r = (unsigned char)pR;
pixels[i].g = (unsigned char)pG;
pixels[i].b = (unsigned char)pB;
}
int format = image->format;
@ -2598,13 +2791,11 @@ void ImageColorBrightness(Image *image, int brightness)
Color *pixels = LoadImageColors(*image);
for (int y = 0; y < image->height; y++)
for (int i = 0; i < image->width*image->height; i++)
{
for (int x = 0; x < image->width; x++)
{
int cR = pixels[y*image->width + x].r + brightness;
int cG = pixels[y*image->width + x].g + brightness;
int cB = pixels[y*image->width + x].b + brightness;
int cR = pixels[i].r + brightness;
int cG = pixels[i].g + brightness;
int cB = pixels[i].b + brightness;
if (cR < 0) cR = 1;
if (cR > 255) cR = 255;
@ -2615,10 +2806,9 @@ void ImageColorBrightness(Image *image, int brightness)
if (cB < 0) cB = 1;
if (cB > 255) cB = 255;
pixels[y*image->width + x].r = (unsigned char)cR;
pixels[y*image->width + x].g = (unsigned char)cG;
pixels[y*image->width + x].b = (unsigned char)cB;
}
pixels[i].r = (unsigned char)cR;
pixels[i].g = (unsigned char)cG;
pixels[i].b = (unsigned char)cB;
}
int format = image->format;
@ -2638,20 +2828,17 @@ void ImageColorReplace(Image *image, Color color, Color replace)
Color *pixels = LoadImageColors(*image);
for (int y = 0; y < image->height; y++)
for (int i = 0; i < image->width*image->height; i++)
{
for (int x = 0; x < image->width; x++)
if ((pixels[i].r == color.r) &&
(pixels[i].g == color.g) &&
(pixels[i].b == color.b) &&
(pixels[i].a == color.a))
{
if ((pixels[y*image->width + x].r == color.r) &&
(pixels[y*image->width + x].g == color.g) &&
(pixels[y*image->width + x].b == color.b) &&
(pixels[y*image->width + x].a == color.a))
{
pixels[y*image->width + x].r = replace.r;
pixels[y*image->width + x].g = replace.g;
pixels[y*image->width + x].b = replace.b;
pixels[y*image->width + x].a = replace.a;
}
pixels[i].r = replace.r;
pixels[i].g = replace.g;
pixels[i].b = replace.b;
pixels[i].a = replace.a;
}
}
@ -3398,8 +3585,8 @@ void ImageDrawRectangleRec(Image *dst, Rectangle rec, Color color)
if ((dst->data == NULL) || (dst->width == 0) || (dst->height == 0)) return;
// Security check to avoid drawing out of bounds in case of bad user data
if (rec.x < 0) { rec.width -= rec.x; rec.x = 0; }
if (rec.y < 0) { rec.height -= rec.y; rec.y = 0; }
if (rec.x < 0) { rec.width += rec.x; rec.x = 0; }
if (rec.y < 0) { rec.height += rec.y; rec.y = 0; }
if (rec.width < 0) rec.width = 0;
if (rec.height < 0) rec.height = 0;
@ -3408,8 +3595,8 @@ void ImageDrawRectangleRec(Image *dst, Rectangle rec, Color color)
if ((rec.y + rec.height) >= dst->height) rec.height = dst->height - rec.y;
// Check if the rect is even inside the image
if ((rec.x > dst->width) || (rec.y > dst->height)) return;
if (((rec.x + rec.width) < 0) || (rec.y + rec.height < 0)) return;
if ((rec.x >= dst->width) || (rec.y >= dst->height)) return;
if (((rec.x + rec.width) <= 0) || (rec.y + rec.height <= 0)) return;
int sy = (int)rec.y;
int sx = (int)rec.x;
@ -3564,7 +3751,7 @@ void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec, Color
// Draw text (default font) within an image (destination)
void ImageDrawText(Image *dst, const char *text, int posX, int posY, int fontSize, Color color)
{
#if defined(SUPPORT_MODULE_RTEXT)
#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT)
// Make sure default font is loaded to be used on image text drawing
if (GetFontDefault().texture.id == 0) LoadFontDefault();
@ -3646,7 +3833,9 @@ TextureCubemap LoadTextureCubemap(Image image, int layout)
if ((image.height/6) == image.width) { layout = CUBEMAP_LAYOUT_LINE_VERTICAL; cubemap.width = image.height/6; }
else if ((image.width/3) == (image.height/4)) { layout = CUBEMAP_LAYOUT_CROSS_THREE_BY_FOUR; cubemap.width = image.width/3; }
}
} else {
}
else
{
if (layout == CUBEMAP_LAYOUT_LINE_VERTICAL) cubemap.width = image.height/6;
if (layout == CUBEMAP_LAYOUT_LINE_HORIZONTAL) cubemap.width = image.width/6;
if (layout == CUBEMAP_LAYOUT_CROSS_THREE_BY_FOUR) cubemap.width = image.width/3;
@ -3663,6 +3852,7 @@ TextureCubemap LoadTextureCubemap(Image image, int layout)
Image faces = { 0 }; // Vertical column image
Rectangle faceRecs[6] = { 0 }; // Face source rectangles
for (int i = 0; i < 6; i++) faceRecs[i] = (Rectangle){ 0, 0, (float)size, (float)size };
if (layout == CUBEMAP_LAYOUT_LINE_VERTICAL)
@ -3708,7 +3898,13 @@ TextureCubemap LoadTextureCubemap(Image image, int layout)
// NOTE: Cubemap data is expected to be provided as 6 images in a single data array,
// one after the other (that's a vertical image), following convention: +X, -X, +Y, -Y, +Z, -Z
cubemap.id = rlLoadTextureCubemap(faces.data, size, faces.format);
if (cubemap.id == 0) TRACELOG(LOG_WARNING, "IMAGE: Failed to load cubemap image");
if (cubemap.id != 0)
{
cubemap.format = faces.format;
cubemap.mipmaps = 1;
}
else TRACELOG(LOG_WARNING, "IMAGE: Failed to load cubemap image");
UnloadImage(faces);
}
@ -3723,7 +3919,7 @@ RenderTexture2D LoadRenderTexture(int width, int height)
{
RenderTexture2D target = { 0 };
target.id = rlLoadFramebuffer(width, height); // Load an empty framebuffer
target.id = rlLoadFramebuffer(); // Load an empty framebuffer
if (target.id > 0)
{
@ -3760,13 +3956,17 @@ RenderTexture2D LoadRenderTexture(int width, int height)
// Check if a texture is ready
bool IsTextureReady(Texture2D texture)
{
bool result = false;
// TODO: Validate maximum texture size supported by GPU?
return ((texture.id > 0) && // Validate OpenGL id
if ((texture.id > 0) && // Validate OpenGL id
(texture.width > 0) &&
(texture.height > 0) && // Validate texture size
(texture.format > 0) && // Validate texture pixel format
(texture.mipmaps > 0)); // Validate texture mipmaps (at least 1 for basic mipmap level)
(texture.mipmaps > 0)) result = true; // Validate texture mipmaps (at least 1 for basic mipmap level)
return result;
}
// Unload texture from GPU memory (VRAM)
@ -3783,9 +3983,13 @@ void UnloadTexture(Texture2D texture)
// Check if a render texture is ready
bool IsRenderTextureReady(RenderTexture2D target)
{
return ((target.id > 0) && // Validate OpenGL id
bool result = false;
if ((target.id > 0) && // Validate OpenGL id
IsTextureReady(target.depth) && // Validate FBO depth texture/renderbuffer
IsTextureReady(target.texture)); // Validate FBO texture
IsTextureReady(target.texture)) result = true; // Validate FBO texture
return result;
}
// Unload render texture from GPU memory (VRAM)
@ -4043,7 +4247,7 @@ void DrawTexturePro(Texture2D texture, Rectangle source, Rectangle dest, Vector2
// NOTE: Vertex position can be transformed using matrices
// but the process is way more costly than just calculating
// the vertex positions manually, like done above.
// the vertex positions manually, like done above
// I leave here the old implementation for educational purposes,
// just in case someone wants to do some performance test
/*
@ -4280,19 +4484,35 @@ void DrawTextureNPatch(Texture2D texture, NPatchInfo nPatchInfo, Rectangle dest,
}
}
// Check if two colors are equal
bool ColorIsEqual(Color col1, Color col2)
{
bool result = false;
if ((col1.r == col2.r) && (col1.g == col2.g) && (col1.b == col2.b) && (col1.a == col2.a)) result = true;
return result;
}
// Get color with alpha applied, alpha goes from 0.0f to 1.0f
Color Fade(Color color, float alpha)
{
Color result = color;
if (alpha < 0.0f) alpha = 0.0f;
else if (alpha > 1.0f) alpha = 1.0f;
return (Color){ color.r, color.g, color.b, (unsigned char)(255.0f*alpha) };
result.a = (unsigned char)(255.0f*alpha);
return result;
}
// Get hexadecimal value for a Color
int ColorToInt(Color color)
{
return (((int)color.r << 24) | ((int)color.g << 16) | ((int)color.b << 8) | (int)color.a);
int result = (((int)color.r << 24) | ((int)color.g << 16) | ((int)color.b << 8) | (int)color.a);
return result;
}
// Get color normalized as float [0..1]
@ -4511,10 +4731,14 @@ Color ColorContrast(Color color, float contrast)
// Get color with alpha applied, alpha goes from 0.0f to 1.0f
Color ColorAlpha(Color color, float alpha)
{
Color result = color;
if (alpha < 0.0f) alpha = 0.0f;
else if (alpha > 1.0f) alpha = 1.0f;
return (Color){color.r, color.g, color.b, (unsigned char)(255.0f*alpha)};
result.a = (unsigned char)(255.0f*alpha);
return result;
}
// Get src alpha-blended into dst color with tint
@ -4813,22 +5037,35 @@ int GetPixelDataSize(int width, int height, int format)
//----------------------------------------------------------------------------------
// Module specific Functions Definition
//----------------------------------------------------------------------------------
// From https://stackoverflow.com/questions/1659440/32-bit-to-16-bit-floating-point-conversion/60047308#60047308
// Convert half-float (stored as unsigned short) to float
// REF: https://stackoverflow.com/questions/1659440/32-bit-to-16-bit-floating-point-conversion/60047308#60047308
static float HalfToFloat(unsigned short x)
{
float result = 0.0f;
static float HalfToFloat(unsigned short x) {
const unsigned int e = (x&0x7C00)>>10; // exponent
const unsigned int m = (x&0x03FF)<<13; // mantissa
const unsigned int e = (x & 0x7C00) >> 10; // Exponent
const unsigned int m = (x & 0x03FF) << 13; // Mantissa
const float fm = (float)m;
const unsigned int v = (*(unsigned int*)&fm)>>23; // evil log2 bit hack to count leading zeros in denormalized format
const unsigned int v = (*(unsigned int*)&fm) >> 23; // Evil log2 bit hack to count leading zeros in denormalized format
const unsigned int r = (x & 0x8000) << 16 | (e != 0)*((e + 112) << 23 | m) | ((e == 0)&(m != 0))*((v - 37) << 23 | ((m << (150 - v)) & 0x007FE000)); // sign : normalized : denormalized
return *(float*)&r;
result = *(float *)&r;
return result;
}
static unsigned short FloatToHalf(float x) {
const unsigned int b = (*(unsigned int*)&x)+0x00001000; // round-to-nearest-even: add last bit after truncated mantissa
const unsigned int e = (b&0x7F800000)>>23; // exponent
const unsigned int m = b&0x007FFFFF; // mantissa; in line below: 0x007FF000 = 0x00800000-0x00001000 = decimal indicator flag - initial rounding
return (b&0x80000000)>>16 | (e>112)*((((e-112)<<10)&0x7C00)|m>>13) | ((e<113)&(e>101))*((((0x007FF000+m)>>(125-e))+1)>>1) | (e>143)*0x7FFF; // sign : normalized : denormalized : saturate
// Convert float to half-float (stored as unsigned short)
static unsigned short FloatToHalf(float x)
{
unsigned short result = 0;
const unsigned int b = (*(unsigned int*) & x) + 0x00001000; // Round-to-nearest-even: add last bit after truncated mantissa
const unsigned int e = (b & 0x7F800000) >> 23; // Exponent
const unsigned int m = b & 0x007FFFFF; // Mantissa; in line below: 0x007FF000 = 0x00800000-0x00001000 = decimal indicator flag - initial rounding
result = (b & 0x80000000) >> 16 | (e > 112)*((((e - 112) << 10) & 0x7C00) | m >> 13) | ((e < 113) & (e > 101))*((((0x007FF000 + m) >> (125 - e)) + 1) >> 1) | (e > 143)*0x7FFF; // sign : normalized : denormalized : saturate
return result;
}
// Get pixel data from image as Vector4 array (float normalized)

View file

@ -10,7 +10,7 @@
*
* LICENSE: zlib/libpng
*
* Copyright (c) 2014-2023 Ramon Santamaria (@raysan5)
* Copyright (c) 2014-2024 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.
@ -41,7 +41,7 @@
#if defined(PLATFORM_ANDROID)
#include <errno.h> // Required for: Android error types
#include <android/log.h> // Required for: Android log system: __android_log_vprint()
#include <android/asset_manager.h> // Required for: Android assets manager: AAsset, AAssetManager_open(), ...
#include <android/asset_manager.h> // Required for: Android assets manager: AAsset, AAssetManager_open()...
#endif
#include <stdlib.h> // Required for: exit()
@ -309,14 +309,20 @@ bool ExportDataAsCode(const unsigned char *data, int dataSize, const char *fileN
byteCount += sprintf(txtData + byteCount, "// more info and bugs-report: github.com/raysan5/raylib //\n");
byteCount += sprintf(txtData + byteCount, "// feedback and support: ray[at]raylib.com //\n");
byteCount += sprintf(txtData + byteCount, "// //\n");
byteCount += sprintf(txtData + byteCount, "// Copyright (c) 2022-2023 Ramon Santamaria (@raysan5) //\n");
byteCount += sprintf(txtData + byteCount, "// Copyright (c) 2022-2024 Ramon Santamaria (@raysan5) //\n");
byteCount += sprintf(txtData + byteCount, "// //\n");
byteCount += sprintf(txtData + byteCount, "////////////////////////////////////////////////////////////////////////////////////////\n\n");
// Get file name from path and convert variable name to uppercase
// Get file name from path
char varFileName[256] = { 0 };
strcpy(varFileName, GetFileNameWithoutExt(fileName));
for (int i = 0; varFileName[i] != '\0'; i++) if ((varFileName[i] >= 'a') && (varFileName[i] <= 'z')) { varFileName[i] = varFileName[i] - 32; }
for (int i = 0; varFileName[i] != '\0'; i++)
{
// Convert variable name to uppercase
if ((varFileName[i] >= 'a') && (varFileName[i] <= 'z')) { varFileName[i] = varFileName[i] - 32; }
// Replace non valid character for C identifier with '_'
else if (varFileName[i] == '.' || varFileName[i] == '-' || varFileName[i] == '?' || varFileName[i] == '!' || varFileName[i] == '+') { varFileName[i] = '_'; }
}
byteCount += sprintf(txtData + byteCount, "#define %s_DATA_SIZE %i\n\n", varFileName, dataSize);

View file

@ -5,7 +5,7 @@
*
* LICENSE: zlib/libpng
*
* Copyright (c) 2014-2023 Ramon Santamaria (@raysan5)
* Copyright (c) 2014-2024 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.