This commit is contained in:
lesleyrs 2023-12-16 05:03:54 +01:00
commit 53caff1e05
27 changed files with 1610 additions and 118 deletions

View File

@ -7,6 +7,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to
| name | raylib version | language | license | repo |
|:------------------:|:---------------:|:---------:|:----------:|-----------------------------------------------------------|
| raylib | **5.0** | [C/C++](https://en.wikipedia.org/wiki/C_(programming_language)) | Zlib | https://github.com/raysan5/raylib |
| raylib-beef | **5.0** | [Beef](https://www.beeflang.org/) | MIT | https://github.com/Starpelly/raylib-beef |
| raylib-boo | 3.7 | [Boo](http://boo-language.github.io/)| MIT | https://github.com/Rabios/raylib-boo |
| Raylib-cs | **4.5** | [C#](https://en.wikipedia.org/wiki/C_Sharp_(programming_language)) | Zlib | https://github.com/ChrisDill/Raylib-cs |
| Raylib-CsLo | 4.2 | [C#](https://en.wikipedia.org/wiki/C_Sharp_(programming_language)) | MPL-2.0 | https://github.com/NotNotTech/Raylib-CsLo |

View File

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.0)
cmake_minimum_required(VERSION 3.5)
project(raylib)
# Avoid excessive expansion of variables in conditionals. In particular, if

View File

@ -2,7 +2,7 @@
*
* raylib [audio] example - Music stream processing effects
*
* Example originally created with raylib 4.2, last time updated with raylib 4.2
* Example originally created with raylib 4.2, last time updated with raylib 5.0
*
* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
* BSD-like license that allows static linking with closed source software
@ -13,7 +13,7 @@
#include "raylib.h"
#include <stdlib.h> // Required for: NULL
#include <stdlib.h> // Required for: NULL
// Required delay effect variables
static float *delayBuffer = NULL;
@ -149,13 +149,17 @@ static void AudioProcessEffectLPF(void *buffer, unsigned int frames)
static const float cutoff = 70.0f / 44100.0f; // 70 Hz lowpass filter
const float k = cutoff / (cutoff + 0.1591549431f); // RC filter formula
// Converts the buffer data before using it
float *bufferData = (float *)buffer;
for (unsigned int i = 0; i < frames*2; i += 2)
{
float l = ((float *)buffer)[i], r = ((float *)buffer)[i + 1];
const float l = bufferData[i];
const float r = bufferData[i + 1];
low[0] += k * (l - low[0]);
low[1] += k * (r - low[1]);
((float *)buffer)[i] = low[0];
((float *)buffer)[i + 1] = low[1];
bufferData[i] = low[0];
bufferData[i + 1] = low[1];
}
}

View File

@ -39,7 +39,6 @@ int main(void)
.vResolution = 1200, // Vertical resolution in pixels
.hScreenSize = 0.133793f, // Horizontal size in meters
.vScreenSize = 0.0669f, // Vertical size in meters
.vScreenCenter = 0.04678f, // Screen center in meters
.eyeToScreenDistance = 0.041f, // Distance between eye and display in meters
.lensSeparationDistance = 0.07f, // Lens separation distance in meters
.interpupillaryDistance = 0.07f, // IPD (distance between pupils) in meters

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 623 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 657 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 645 KiB

View File

@ -0,0 +1,156 @@
#version 100
precision mediump float;
#define MAX_LIGHTS 4
#define LIGHT_DIRECTIONAL 0
#define LIGHT_POINT 1
#define PI 3.14159265358979323846
struct Light {
int enabled;
int type;
vec3 position;
vec3 target;
vec4 color;
float intensity;
};
// Input vertex attributes (from vertex shader)
varying in vec3 fragPosition;
varying in vec2 fragTexCoord;
varying in vec4 fragColor;
varying in vec3 fragNormal;
varying in vec4 shadowPos;
varying in mat3 TBN;
// Input uniform values
uniform int numOfLights;
uniform sampler2D albedoMap;
uniform sampler2D mraMap;
uniform sampler2D normalMap;
uniform sampler2D emissiveMap; // r: Hight g:emissive
uniform vec2 tiling;
uniform vec2 offset;
uniform int useTexAlbedo;
uniform int useTexNormal;
uniform int useTexMRA;
uniform int useTexEmissive;
uniform vec4 albedoColor;
uniform vec4 emissiveColor;
uniform float normalValue;
uniform float metallicValue;
uniform float roughnessValue;
uniform float aoValue;
uniform float emissivePower;
// Input lighting values
uniform Light lights[MAX_LIGHTS];
uniform vec3 viewPos;
uniform vec3 ambientColor;
uniform float ambient;
// refl in range 0 to 1
// returns base reflectivity to 1
// incrase reflectivity when surface view at larger angle
vec3 schlickFresnel(float hDotV,vec3 refl)
{
return refl + (1.0 - refl) * pow(1.0 - hDotV,5.0);
}
float ggxDistribution(float nDotH,float roughness)
{
float a = roughness * roughness * roughness * roughness;
float d = nDotH * nDotH * (a - 1.0) + 1.0;
d = PI * d * d;
return a / max(d,0.0000001);
}
float geomSmith(float nDotV,float nDotL,float roughness)
{
float r = roughness + 1.0;
float k = r * r / 8.0;
float ik = 1.0 - k;
float ggx1 = nDotV / (nDotV * ik + k);
float ggx2 = nDotL / (nDotL * ik + k);
return ggx1 * ggx2;
}
vec3 pbr(){
vec3 albedo = texture2D(albedoMap,vec2(fragTexCoord.x*tiling.x+offset.x,fragTexCoord.y*tiling.y+offset.y)).rgb;
albedo = vec3(albedoColor.x*albedo.x,albedoColor.y*albedo.y,albedoColor.z*albedo.z);
float metallic = clamp(metallicValue,0.0,1.0);
float roughness = clamp(roughnessValue,0.0,1.0);
float ao = clamp(aoValue,0.0,1.0);
if(useTexMRA == 1) {
vec4 mra = texture2D(mraMap, vec2(fragTexCoord.x * tiling.x + offset.x, fragTexCoord.y * tiling.y + offset.y));
metallic = clamp(mra.r+metallicValue,0.04,1.0);
roughness = clamp(mra.g+roughnessValue,0.04,1.0);
ao = (mra.b+aoValue)*0.5;
}
vec3 N = normalize(fragNormal);
if(useTexNormal == 1) {
N = texture2D(normalMap, vec2(fragTexCoord.x * tiling.x + offset.y, fragTexCoord.y * tiling.y + offset.y)).rgb;
N = normalize(N * 2.0 - 1.0);
N = normalize(N * TBN);
}
vec3 V = normalize(viewPos - fragPosition);
vec3 e = vec3(0);
e = (texture2D(emissiveMap, vec2(fragTexCoord.x*tiling.x+offset.x, fragTexCoord.y*tiling.y+offset.y)).rgb).g * emissiveColor.rgb*emissivePower * float(useTexEmissive);
//return N;//vec3(metallic,metallic,metallic);
//if dia-electric use base reflectivity of 0.04 otherwise ut is a metal use albedo as base reflectivity
vec3 baseRefl = mix(vec3(0.04),albedo.rgb,metallic);
vec3 Lo = vec3(0.0); // acumulate lighting lum
for(int i=0;i<numOfLights;++i){
vec3 L = normalize(lights[i].position - fragPosition); // calc light vector
vec3 H = normalize(V + L); // calc halfway bisecting vector
float dist = length(lights[i].position - fragPosition); // calc distance to light
float attenuation = 1.0 / (dist * dist * 0.23); // calc attenuation
vec3 radiance = lights[i].color.rgb * lights[i].intensity * attenuation; // calc input radiance,light energy comming in
//Cook-Torrance BRDF distribution function
float nDotV = max(dot(N,V),0.0000001);
float nDotL = max(dot(N,L),0.0000001);
float hDotV = max(dot(H,V),0.0);
float nDotH = max(dot(N,H),0.0);
float D = ggxDistribution(nDotH,roughness); // larger the more micro-facets aligned to H
float G = geomSmith(nDotV,nDotL,roughness); // smaller the more micro-facets shadow
vec3 F = schlickFresnel(hDotV, baseRefl); // fresnel proportion of specular reflectance
vec3 spec = (D * G * F) / (4.0 * nDotV * nDotL);
// difuse and spec light can't be above 1.0
// kD = 1.0 - kS diffuse component is equal 1.0 - spec comonent
vec3 kD = vec3(1.0) - F;
//mult kD by the inverse of metallnes , only non-metals should have diffuse light
kD *= 1.0 - metallic;
Lo += ((kD * albedo.rgb / PI + spec) * radiance * nDotL)*float(lights[i].enabled); // angle of light has impact on result
}
vec3 ambient_final = (ambientColor + albedo)* ambient * 0.5;
return ambient_final+Lo*ao+e;
}
void main()
{
vec3 color = pbr();
//HDR tonemapping
color = pow(color,color + vec3(1.0));
//gamma correction
color = pow(color,vec3(1.0/2.2));
gl_FragColor = vec4(color,1.0);
}

View File

@ -0,0 +1,75 @@
#version 100
// Input vertex attributes
attribute vec3 vertexPosition;
attribute vec2 vertexTexCoord;
attribute vec3 vertexNormal;
attribute vec3 vertexTangent;
attribute vec4 vertexColor;
// Input uniform values
uniform mat4 mvp;
uniform mat4 matModel;
uniform mat4 matNormal;
uniform vec3 lightPos;
uniform vec4 difColor;
// Output vertex attributes (to fragment shader)
varying vec3 fragPosition;
varying vec2 fragTexCoord;
varying vec4 fragColor;
varying vec3 fragNormal;
varying mat3 TBN;
const float normalOffset = 0.1;
// https://github.com/glslify/glsl-inverse
mat3 inverse(mat3 m)
{
float a00 = m[0][0], a01 = m[0][1], a02 = m[0][2];
float a10 = m[1][0], a11 = m[1][1], a12 = m[1][2];
float a20 = m[2][0], a21 = m[2][1], a22 = m[2][2];
float b01 = a22*a11 - a12*a21;
float b11 = -a22*a10 + a12*a20;
float b21 = a21*a10 - a11*a20;
float det = a00*b01 + a01*b11 + a02*b21;
return mat3(b01, (-a22*a01 + a02*a21), (a12*a01 - a02*a11),
b11, (a22*a00 - a02*a20), (-a12*a00 + a02*a10),
b21, (-a21*a00 + a01*a20), (a11*a00 - a01*a10))/det;
}
// https://github.com/glslify/glsl-transpose
mat3 transpose(mat3 m)
{
return mat3(m[0][0], m[1][0], m[2][0],
m[0][1], m[1][1], m[2][1],
m[0][2], m[1][2], m[2][2]);
}
void main()
{
// calc binormal from vertex normal and tangent
vec3 vertexBinormal = cross(vertexNormal, vertexTangent);
// calc fragment normal based on normal transformations
mat3 normalMatrix = transpose(inverse(mat3(matModel)));
// calc fragment position based on model transformations
fragPosition = vec3(matModel*vec4(vertexPosition, 1.0));
fragTexCoord = vertexTexCoord*2.0;
fragNormal = normalize(normalMatrix*vertexNormal);
vec3 fragTangent = normalize(normalMatrix*vertexTangent);
fragTangent = normalize(fragTangent - dot(fragTangent, fragNormal)*fragNormal);
vec3 fragBinormal = normalize(normalMatrix*vertexBinormal);
fragBinormal = cross(fragNormal, fragTangent);
TBN = transpose(mat3(fragTangent, fragBinormal, fragNormal));
// Calculate final vertex position
gl_Position = mvp * vec4(vertexPosition, 1.0);
}

View File

@ -0,0 +1,156 @@
#version 120
precision mediump float;
#define MAX_LIGHTS 4
#define LIGHT_DIRECTIONAL 0
#define LIGHT_POINT 1
#define PI 3.14159265358979323846
struct Light {
int enabled;
int type;
vec3 position;
vec3 target;
vec4 color;
float intensity;
};
// Input vertex attributes (from vertex shader)
varying in vec3 fragPosition;
varying in vec2 fragTexCoord;
varying in vec4 fragColor;
varying in vec3 fragNormal;
varying in vec4 shadowPos;
varying in mat3 TBN;
// Input uniform values
uniform int numOfLights;
uniform sampler2D albedoMap;
uniform sampler2D mraMap;
uniform sampler2D normalMap;
uniform sampler2D emissiveMap; // r: Hight g:emissive
uniform vec2 tiling;
uniform vec2 offset;
uniform int useTexAlbedo;
uniform int useTexNormal;
uniform int useTexMRA;
uniform int useTexEmissive;
uniform vec4 albedoColor;
uniform vec4 emissiveColor;
uniform float normalValue;
uniform float metallicValue;
uniform float roughnessValue;
uniform float aoValue;
uniform float emissivePower;
// Input lighting values
uniform Light lights[MAX_LIGHTS];
uniform vec3 viewPos;
uniform vec3 ambientColor;
uniform float ambient;
// refl in range 0 to 1
// returns base reflectivity to 1
// incrase reflectivity when surface view at larger angle
vec3 schlickFresnel(float hDotV,vec3 refl)
{
return refl + (1.0 - refl) * pow(1.0 - hDotV,5.0);
}
float ggxDistribution(float nDotH,float roughness)
{
float a = roughness * roughness * roughness * roughness;
float d = nDotH * nDotH * (a - 1.0) + 1.0;
d = PI * d * d;
return a / max(d,0.0000001);
}
float geomSmith(float nDotV,float nDotL,float roughness)
{
float r = roughness + 1.0;
float k = r * r / 8.0;
float ik = 1.0 - k;
float ggx1 = nDotV / (nDotV * ik + k);
float ggx2 = nDotL / (nDotL * ik + k);
return ggx1 * ggx2;
}
vec3 pbr(){
vec3 albedo = texture2D(albedoMap,vec2(fragTexCoord.x*tiling.x+offset.x,fragTexCoord.y*tiling.y+offset.y)).rgb;
albedo = vec3(albedoColor.x*albedo.x,albedoColor.y*albedo.y,albedoColor.z*albedo.z);
float metallic = clamp(metallicValue,0.0,1.0);
float roughness = clamp(roughnessValue,0.0,1.0);
float ao = clamp(aoValue,0.0,1.0);
if(useTexMRA == 1) {
vec4 mra = texture2D(mraMap, vec2(fragTexCoord.x * tiling.x + offset.x, fragTexCoord.y * tiling.y + offset.y));
metallic = clamp(mra.r+metallicValue,0.04,1.0);
roughness = clamp(mra.g+roughnessValue,0.04,1.0);
ao = (mra.b+aoValue)*0.5;
}
vec3 N = normalize(fragNormal);
if(useTexNormal == 1) {
N = texture2D(normalMap, vec2(fragTexCoord.x * tiling.x + offset.y, fragTexCoord.y * tiling.y + offset.y)).rgb;
N = normalize(N * 2.0 - 1.0);
N = normalize(N * TBN);
}
vec3 V = normalize(viewPos - fragPosition);
vec3 e = vec3(0);
e = (texture2D(emissiveMap, vec2(fragTexCoord.x*tiling.x+offset.x, fragTexCoord.y*tiling.y+offset.y)).rgb).g * emissiveColor.rgb*emissivePower * float(useTexEmissive);
//return N;//vec3(metallic,metallic,metallic);
//if dia-electric use base reflectivity of 0.04 otherwise ut is a metal use albedo as base reflectivity
vec3 baseRefl = mix(vec3(0.04),albedo.rgb,metallic);
vec3 Lo = vec3(0.0); // acumulate lighting lum
for(int i=0;i<numOfLights;++i){
vec3 L = normalize(lights[i].position - fragPosition); // calc light vector
vec3 H = normalize(V + L); // calc halfway bisecting vector
float dist = length(lights[i].position - fragPosition); // calc distance to light
float attenuation = 1.0 / (dist * dist * 0.23); // calc attenuation
vec3 radiance = lights[i].color.rgb * lights[i].intensity * attenuation; // calc input radiance,light energy comming in
//Cook-Torrance BRDF distribution function
float nDotV = max(dot(N,V),0.0000001);
float nDotL = max(dot(N,L),0.0000001);
float hDotV = max(dot(H,V),0.0);
float nDotH = max(dot(N,H),0.0);
float D = ggxDistribution(nDotH,roughness); // larger the more micro-facets aligned to H
float G = geomSmith(nDotV,nDotL,roughness); // smaller the more micro-facets shadow
vec3 F = schlickFresnel(hDotV, baseRefl); // fresnel proportion of specular reflectance
vec3 spec = (D * G * F) / (4.0 * nDotV * nDotL);
// difuse and spec light can't be above 1.0
// kD = 1.0 - kS diffuse component is equal 1.0 - spec comonent
vec3 kD = vec3(1.0) - F;
//mult kD by the inverse of metallnes , only non-metals should have diffuse light
kD *= 1.0 - metallic;
Lo += ((kD * albedo.rgb / PI + spec) * radiance * nDotL)*float(lights[i].enabled); // angle of light has impact on result
}
vec3 ambient_final = (ambientColor + albedo)* ambient * 0.5;
return ambient_final+Lo*ao+e;
}
void main()
{
vec3 color = pbr();
//HDR tonemapping
color = pow(color,color + vec3(1.0));
//gamma correction
color = pow(color,vec3(1.0/2.2));
gl_FragColor = vec4(color,1.0);
}

View File

@ -0,0 +1,75 @@
#version 120
// Input vertex attributes
attribute vec3 vertexPosition;
attribute vec2 vertexTexCoord;
attribute vec3 vertexNormal;
attribute vec3 vertexTangent;
attribute vec4 vertexColor;
// Input uniform values
uniform mat4 mvp;
uniform mat4 matModel;
uniform mat4 matNormal;
uniform vec3 lightPos;
uniform vec4 difColor;
// Output vertex attributes (to fragment shader)
varying vec3 fragPosition;
varying vec2 fragTexCoord;
varying vec4 fragColor;
varying vec3 fragNormal;
varying mat3 TBN;
const float normalOffset = 0.1;
// https://github.com/glslify/glsl-inverse
mat3 inverse(mat3 m)
{
float a00 = m[0][0], a01 = m[0][1], a02 = m[0][2];
float a10 = m[1][0], a11 = m[1][1], a12 = m[1][2];
float a20 = m[2][0], a21 = m[2][1], a22 = m[2][2];
float b01 = a22*a11 - a12*a21;
float b11 = -a22*a10 + a12*a20;
float b21 = a21*a10 - a11*a20;
float det = a00*b01 + a01*b11 + a02*b21;
return mat3(b01, (-a22*a01 + a02*a21), (a12*a01 - a02*a11),
b11, (a22*a00 - a02*a20), (-a12*a00 + a02*a10),
b21, (-a21*a00 + a01*a20), (a11*a00 - a01*a10))/det;
}
// https://github.com/glslify/glsl-transpose
mat3 transpose(mat3 m)
{
return mat3(m[0][0], m[1][0], m[2][0],
m[0][1], m[1][1], m[2][1],
m[0][2], m[1][2], m[2][2]);
}
void main()
{
// calc binormal from vertex normal and tangent
vec3 vertexBinormal = cross(vertexNormal, vertexTangent);
// calc fragment normal based on normal transformations
mat3 normalMatrix = transpose(inverse(mat3(matModel)));
// calc fragment position based on model transformations
fragPosition = vec3(matModel*vec4(vertexPosition, 1.0));
fragTexCoord = vertexTexCoord*2.0;
fragNormal = normalize(normalMatrix*vertexNormal);
vec3 fragTangent = normalize(normalMatrix*vertexTangent);
fragTangent = normalize(fragTangent - dot(fragTangent, fragNormal)*fragNormal);
vec3 fragBinormal = normalize(normalMatrix*vertexBinormal);
fragBinormal = cross(fragNormal, fragTangent);
TBN = transpose(mat3(fragTangent, fragBinormal, fragNormal));
// Calculate final vertex position
gl_Position = mvp * vec4(vertexPosition, 1.0);
}

View File

@ -0,0 +1,159 @@
#version 330
#define MAX_LIGHTS 4
#define LIGHT_DIRECTIONAL 0
#define LIGHT_POINT 1
#define PI 3.14159265358979323846
struct Light {
int enabled;
int type;
vec3 position;
vec3 target;
vec4 color;
float intensity;
};
// Input vertex attributes (from vertex shader)
in vec3 fragPosition;
in vec2 fragTexCoord;
in vec4 fragColor;
in vec3 fragNormal;
in vec4 shadowPos;
in mat3 TBN;
// Output fragment color
out vec4 finalColor;
// Input uniform values
uniform int numOfLights;
uniform sampler2D albedoMap;
uniform sampler2D mraMap;
uniform sampler2D normalMap;
uniform sampler2D emissiveMap; // r: Hight g:emissive
uniform vec2 tiling;
uniform vec2 offset;
uniform int useTexAlbedo;
uniform int useTexNormal;
uniform int useTexMRA;
uniform int useTexEmissive;
uniform vec4 albedoColor;
uniform vec4 emissiveColor;
uniform float normalValue;
uniform float metallicValue;
uniform float roughnessValue;
uniform float aoValue;
uniform float emissivePower;
// Input lighting values
uniform Light lights[MAX_LIGHTS];
uniform vec3 viewPos;
uniform vec3 ambientColor;
uniform float ambient;
// refl in range 0 to 1
// returns base reflectivity to 1
// incrase reflectivity when surface view at larger angle
vec3 schlickFresnel(float hDotV,vec3 refl)
{
return refl + (1.0 - refl) * pow(1.0 - hDotV,5.0);
}
float ggxDistribution(float nDotH,float roughness)
{
float a = roughness * roughness * roughness * roughness;
float d = nDotH * nDotH * (a - 1.0) + 1.0;
d = PI * d * d;
return a / max(d,0.0000001);
}
float geomSmith(float nDotV,float nDotL,float roughness)
{
float r = roughness + 1.0;
float k = r * r / 8.0;
float ik = 1.0 - k;
float ggx1 = nDotV / (nDotV * ik + k);
float ggx2 = nDotL / (nDotL * ik + k);
return ggx1 * ggx2;
}
vec3 pbr(){
vec3 albedo = texture(albedoMap,vec2(fragTexCoord.x*tiling.x+offset.x,fragTexCoord.y*tiling.y+offset.y)).rgb;
albedo = vec3(albedoColor.x*albedo.x,albedoColor.y*albedo.y,albedoColor.z*albedo.z);
float metallic = clamp(metallicValue,0.0,1.0);
float roughness = clamp(roughnessValue,0.0,1.0);
float ao = clamp(aoValue,0.0,1.0);
if(useTexMRA == 1) {
vec4 mra = texture(mraMap, vec2(fragTexCoord.x * tiling.x + offset.x, fragTexCoord.y * tiling.y + offset.y)) * useTexMRA;
metallic = clamp(mra.r+metallicValue,0.04,1.0);
roughness = clamp(mra.g+roughnessValue,0.04,1.0);
ao = (mra.b+aoValue)*0.5;
}
vec3 N = normalize(fragNormal);
if(useTexNormal == 1) {
N = texture(normalMap, vec2(fragTexCoord.x * tiling.x + offset.y, fragTexCoord.y * tiling.y + offset.y)).rgb;
N = normalize(N * 2.0 - 1.0);
N = normalize(N * TBN);
}
vec3 V = normalize(viewPos - fragPosition);
vec3 e = vec3(0);
e = (texture(emissiveMap, vec2(fragTexCoord.x*tiling.x+offset.x, fragTexCoord.y*tiling.y+offset.y)).rgb).g * emissiveColor.rgb*emissivePower * useTexEmissive;
//return N;//vec3(metallic,metallic,metallic);
//if dia-electric use base reflectivity of 0.04 otherwise ut is a metal use albedo as base reflectivity
vec3 baseRefl = mix(vec3(0.04),albedo.rgb,metallic);
vec3 Lo = vec3(0.0); // acumulate lighting lum
for(int i=0;i<numOfLights;++i){
vec3 L = normalize(lights[i].position - fragPosition); // calc light vector
vec3 H = normalize(V + L); // calc halfway bisecting vector
float dist = length(lights[i].position - fragPosition); // calc distance to light
float attenuation = 1.0 / (dist * dist * 0.23); // calc attenuation
vec3 radiance = lights[i].color.rgb * lights[i].intensity * attenuation; // calc input radiance,light energy comming in
//Cook-Torrance BRDF distribution function
float nDotV = max(dot(N,V),0.0000001);
float nDotL = max(dot(N,L),0.0000001);
float hDotV = max(dot(H,V),0.0);
float nDotH = max(dot(N,H),0.0);
float D = ggxDistribution(nDotH,roughness); // larger the more micro-facets aligned to H
float G = geomSmith(nDotV,nDotL,roughness); // smaller the more micro-facets shadow
vec3 F = schlickFresnel(hDotV, baseRefl); // fresnel proportion of specular reflectance
vec3 spec = (D * G * F) / (4.0 * nDotV * nDotL);
// difuse and spec light can't be above 1.0
// kD = 1.0 - kS diffuse component is equal 1.0 - spec comonent
vec3 kD = vec3(1.0) - F;
//mult kD by the inverse of metallnes , only non-metals should have diffuse light
kD *= 1.0 - metallic;
Lo += ((kD * albedo.rgb / PI + spec) * radiance * nDotL)*lights[i].enabled; // angle of light has impact on result
}
vec3 ambient_final = (ambientColor + albedo)* ambient * 0.5;
return ambient_final+Lo*ao+e;
}
void main()
{
vec3 color = pbr();
//HDR tonemapping
color = pow(color,color + vec3(1.0));
//gamma correction
color = pow(color,vec3(1.0/2.2));
finalColor = vec4(color,1.0);
}

View File

@ -0,0 +1,49 @@
#version 330
// Input vertex attributes
in vec3 vertexPosition;
in vec2 vertexTexCoord;
in vec3 vertexNormal;
in vec3 vertexTangent;
in vec4 vertexColor;
// Input uniform values
uniform mat4 mvp;
uniform mat4 matModel;
uniform mat4 matNormal;
uniform vec3 lightPos;
uniform vec4 difColor;
// Output vertex attributes (to fragment shader)
out vec3 fragPosition;
out vec2 fragTexCoord;
out vec4 fragColor;
out vec3 fragNormal;
out mat3 TBN;
const float normalOffset = 0.1;
void main()
{
// calc binormal from vertex normal and tangent
vec3 vertexBinormal = cross(vertexNormal, vertexTangent);
// calc fragment normal based on normal transformations
mat3 normalMatrix = transpose(inverse(mat3(matModel)));
// calc fragment position based on model transformations
fragPosition = vec3(matModel*vec4(vertexPosition, 1.0f));
fragTexCoord = vertexTexCoord*2.0;
fragNormal = normalize(normalMatrix*vertexNormal);
vec3 fragTangent = normalize(normalMatrix*vertexTangent);
fragTangent = normalize(fragTangent - dot(fragTangent, fragNormal)*fragNormal);
vec3 fragBinormal = normalize(normalMatrix*vertexBinormal);
fragBinormal = cross(fragNormal, fragTangent);
TBN = transpose(mat3(fragTangent, fragBinormal, fragNormal));
// Calculate final vertex position
gl_Position = mvp * vec4(vertexPosition, 1.0);
}

466
examples/shaders/rpbr.h Normal file
View File

@ -0,0 +1,466 @@
/**********************************************************************************************
*
* raylib.pbr - Some useful functions to deal with pbr materials and lights
*
* CONFIGURATION:
*
* #define RPBR_IMPLEMENTATION
* Generates the implementation of the library into the included file.
* If not defined, the library is in header only mode and can be included in other headers
* or source files without problems. But only ONE file should hold the implementation.
*
* LICENSE: zlib/libpng
*
* Copyright (c) 2023-2024 Afan OLOVCIC (@_DevDad) 2017-2020 Victor Fisac(@victorfisac),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.
*
* Permission is granted to anyone to use this software for any purpose, including commercial
* applications, and to alter it and redistribute it freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not claim that you
* wrote the original software. If you use this software in a product, an acknowledgment
* in the product documentation would be appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be misrepresented
* as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*
**********************************************************************************************/
#ifndef RPBR_H
#define RPBR_H
#include "raylib.h"
//----------------------------------------------------------------------------------
// Defines and Macros
//----------------------------------------------------------------------------------
#define MAX_LIGHTS 4 // Max dynamic lights supported by shader
#define SHADER_LOC_MAP_MRA SHADER_LOC_MAP_METALNESS //METALLIC, ROUGHNESS and AO
#define SHADER_LOC_MAP_EMISSIVE SHADER_LOC_MAP_HEIGHT //EMISSIVE
#define MATERIAL_MAP_MRA MATERIAL_MAP_METALNESS
#define MATERIAL_MAP_EMISSIVE MATERIAL_MAP_HEIGHT
#define NULL 0
#define COLOR_TO_ARRAY(c)
typedef struct {
int enabled;
int type;
Vector3 position;
Vector3 target;
float color[4];
float intensity;
int enabledLoc;
int typeLoc;
int positionLoc;
int targetLoc;
int colorLoc;
int intensityLoc;
} PBRLight;
typedef enum {
LIGHT_DIRECTIONAL = 0,
LIGHT_POINT,
LIGHT_SPOT
} PBRLightType;
typedef struct{
Shader pbrShader;
Shader skyShader;
unsigned int cubemap;
unsigned int irradiance;
unsigned int prefilter;
unsigned int brdf;
int modelMatrixLoc;
int pbrViewLoc;
int skyViewLoc;
int skyResolutionLoc;
} PBREnvironment;
typedef enum{
PBR_COLOR_ALBEDO = 0,
PBR_COLOR_EMISSIVE
}PBRColorType;
typedef enum{
PBR_VEC2_TILING = 0,
PBR_VEC2_OFFSET
}PBRVec2Type;
typedef enum{
PBR_PARAM_NORMAL =0,
PBR_PARAM_METALLIC,
PBR_PARAM_ROUGHNESS,
PBR_PARAM_EMISSIVE,
PBR_PARAM_AO
}PBRFloatType;
typedef enum{
PBR_TEXTURE_ALBEDO = 0,
PBR_TEXTURE_NORMAL,
PBR_TEXTURE_MRA,
PBR_TEXTURE_EMISSIVE
}PBRTexType;
// Textures are moved to material from params to pack better and use less textures on the end
// texture MRAE 4Channel R: Metallic G: Roughness B: A: Ambient Occlusion
// texEmissive use just one channel, so we have 3 channels still to use if we need
typedef struct {
Shader pbrShader;
float albedo[4];
float normal;
float metallic;
float roughness;
float ao;
float emissive[4];
float ambient[3];
float emissivePower;
Texture2D texAlbedo;
Texture2D texNormal;
Texture2D texMRA;//r: Metallic g: Roughness b: AO a:Empty
Texture2D texEmissive; //Emissive Texture
// Using float4 to store tilling at 1st and 2nd position and offset at 3rd and 4th
float texTiling[2];
float texOffset[2];
int useTexAlbedo;
int useTexNormal;
int useTexMRA;
int useTexEmissive;
int albedoLoc;
int normalLoc;
int metallicLoc;
int roughnessLoc;
int aoLoc;
int emissiveColorLoc;
int emissivePowerLoc;
int texTilingLoc;
int texOffsetLoc;
int useTexAlbedoLoc;
int useTexNormalLoc;
int useTexMRAELoc;
int useTexEmissiveLoc;
} PBRMaterial;
typedef struct{
Model model;
PBRMaterial pbrMat;
}PBRModel;
#ifdef __cplusplus
extern "C" {
#endif
//----------------------------------------------------------------------------------
// Module Functions Declaration
//----------------------------------------------------------------------------------
// Create a light and get shader locations
PBRLight PBRLightCreate(int type, Vector3 position, Vector3 target, Color color,float intensity, Shader shader);
// Send light properties to shader
void PBRLightUpdate(Shader shader, PBRLight light);
//For now until we do real skylight
void PBRSetAmbient(Shader shader, Color color, float intensity);
PBRModel PBRModelLoad(const char *fileName);
PBRModel PBRModelLoadFromMesh(Mesh mesh);
void PBRLoadTextures(PBRMaterial *pbrMat,PBRTexType pbrTexType,const char *fileName);
void UnloadPBRMaterial(PBRMaterial pbrMat);
void PBRSetColor(PBRMaterial *pbrMat,PBRColorType pbrColorType,Color color);
void PBRSetVec2(PBRMaterial *pbrMat,PBRVec2Type type,Vector2 value);
void PBRSetFloat(PBRMaterial *pbrMat, PBRFloatType pbrParamType, float value);
void PBRMaterialSetup( PBRMaterial *pbrMat,Shader pbrShader, PBREnvironment* environment);
void PBRSetMaterial(PBRModel* model,PBRMaterial* pbrMat,int matIndex);
void PBRDrawModel(PBRModel pbrModel, Vector3 position, float scale);
#ifdef __cplusplus
}
#endif
#endif //RPBR_H
/***********************************************************************************
*
* RPBR IMPLEMENTATION
*
************************************************************************************/
#if defined(RPBR_IMPLEMENTATION)
//----------------------------------------------------------------------------------
// Global Variables Definition
//----------------------------------------------------------------------------------
static int lightsCount = 0; // Current amount of created lights
// Create a light and get shader locations
PBRLight PBRLightCreate(int type, Vector3 position, Vector3 target, Color color,float intensity, Shader shader)
{
PBRLight light = { 0 };
if (lightsCount < MAX_LIGHTS)
{
light.enabled = 1;
light.type = type;
light.position = position;
light.target = target;
light.color[0] = (float)color.r/(float)255;
light.color[1] = (float)color.g/(float)255;
light.color[2] = (float)color.b/(float)255;
light.color[3] = (float)color.a/(float)255;
light.intensity = intensity;
// NOTE: Lighting shader naming must be the provided ones
light.enabledLoc = GetShaderLocation(shader, TextFormat("lights[%i].enabled", lightsCount));
light.typeLoc = GetShaderLocation(shader, TextFormat("lights[%i].type", lightsCount));
light.positionLoc = GetShaderLocation(shader, TextFormat("lights[%i].position", lightsCount));
light.targetLoc = GetShaderLocation(shader, TextFormat("lights[%i].target", lightsCount));
light.colorLoc = GetShaderLocation(shader, TextFormat("lights[%i].color", lightsCount));
light.intensityLoc = GetShaderLocation(shader, TextFormat("lights[%i].intensity", lightsCount));
PBRLightUpdate(shader, light);
lightsCount++;
}
return light;
}
// Send light properties to shader
// NOTE: Light shader locations should be available
void PBRLightUpdate(Shader shader, PBRLight light)
{
SetShaderValue(shader, light.enabledLoc, &light.enabled, SHADER_UNIFORM_INT);
SetShaderValue(shader, light.typeLoc, &light.type, SHADER_UNIFORM_INT);
// Send to shader light position values
float position[3] = { light.position.x, light.position.y, light.position.z };
SetShaderValue(shader, light.positionLoc, position, SHADER_UNIFORM_VEC3);
// Send to shader light target position values
float target[3] = { light.target.x, light.target.y, light.target.z };
SetShaderValue(shader, light.targetLoc, target, SHADER_UNIFORM_VEC3);
SetShaderValue(shader, light.colorLoc, light.color, SHADER_UNIFORM_VEC4);
SetShaderValue(shader, light.intensityLoc, &light.intensity, SHADER_UNIFORM_FLOAT);
}
void PBRSetAmbient(Shader shader, Color color, float intensity){
float col[3] = {color.r/255,color.g/255,color.b/255};
SetShaderValue(shader, GetShaderLocation(shader, "ambientColor"), col, SHADER_UNIFORM_VEC3);
SetShaderValue(shader, GetShaderLocation(shader, "ambient"), &intensity, SHADER_UNIFORM_FLOAT);
}
void PBRMaterialSetup(PBRMaterial *pbrMat, Shader pbrShader, PBREnvironment* environment){
pbrMat->pbrShader = pbrShader;
pbrMat->texAlbedo = (Texture2D){0};
pbrMat->texNormal = (Texture2D){0};
pbrMat->texMRA = (Texture2D){0};
pbrMat->texEmissive = (Texture2D){0};
//PBRParam
pbrMat->albedo[0] = 1.0;
pbrMat->albedo[1] = 1.0;
pbrMat->albedo[2] = 1.0;
pbrMat->albedo[3] = 1.0;
pbrMat->metallic = 0;
pbrMat->roughness = 0;
pbrMat->ao = 1.0;
pbrMat->normal = 1;
pbrMat->emissive[0] = 0;
pbrMat->emissive[1] = 0;
pbrMat->emissive[2] = 0;
pbrMat->emissive[3] = 0;
pbrMat->texTiling[0] = 1.0;
pbrMat->texTiling[1] = 1.0;
pbrMat->texOffset[0] = 0.0;
pbrMat->texOffset[1] = 0.0;
pbrMat->emissivePower = 1.0;
// Set up PBR shader material locations
pbrMat->albedoLoc = GetShaderLocation(pbrMat->pbrShader, "albedoColor");
pbrMat->normalLoc = GetShaderLocation(pbrMat->pbrShader, "normalValue");
pbrMat->metallicLoc = GetShaderLocation(pbrMat->pbrShader, "metallicValue");
pbrMat->roughnessLoc = GetShaderLocation(pbrMat->pbrShader, "roughnessValue");
pbrMat->aoLoc = GetShaderLocation(pbrMat->pbrShader, "aoValue");
pbrMat->emissiveColorLoc = GetShaderLocation(pbrMat->pbrShader, "emissiveColor");
pbrMat->emissivePowerLoc = GetShaderLocation(pbrMat->pbrShader, "emissivePower");
pbrMat->texTilingLoc = GetShaderLocation(pbrMat->pbrShader, "tiling");
pbrMat->texOffsetLoc = GetShaderLocation(pbrMat->pbrShader, "offset");
pbrMat->useTexAlbedoLoc = GetShaderLocation(pbrMat->pbrShader, "useTexAlbedo");
pbrMat->useTexNormalLoc = GetShaderLocation(pbrMat->pbrShader, "useTexNormal");
pbrMat->useTexMRAELoc = GetShaderLocation(pbrMat->pbrShader, "useTexMRA");
pbrMat->useTexEmissiveLoc = GetShaderLocation(pbrMat->pbrShader, "useTexEmissive");
SetShaderValue(pbrMat->pbrShader,pbrMat->albedoLoc,pbrMat->albedo,SHADER_UNIFORM_VEC4);
SetShaderValue(pbrMat->pbrShader, pbrMat->emissiveColorLoc, pbrMat->emissive, SHADER_UNIFORM_VEC4);
SetShaderValue(pbrMat->pbrShader, pbrMat->emissivePowerLoc, &pbrMat->emissivePower, SHADER_UNIFORM_FLOAT);
SetShaderValue(pbrMat->pbrShader,pbrMat->metallicLoc,&pbrMat->metallic,SHADER_UNIFORM_FLOAT);
SetShaderValue(pbrMat->pbrShader,pbrMat->roughnessLoc,&pbrMat->roughness,SHADER_UNIFORM_FLOAT);
SetShaderValue(pbrMat->pbrShader,pbrMat->aoLoc,&pbrMat->ao,SHADER_UNIFORM_FLOAT);
SetShaderValue(pbrMat->pbrShader,pbrMat->normalLoc,&pbrMat->normal,SHADER_UNIFORM_FLOAT);
SetShaderValue(pbrMat->pbrShader,pbrMat->texTilingLoc,pbrMat->texTiling,SHADER_UNIFORM_VEC2);
SetShaderValue(pbrMat->pbrShader,pbrMat->texOffsetLoc,pbrMat->texOffset,SHADER_UNIFORM_VEC2);
}
void PBRLoadTextures(PBRMaterial *pbrMat,PBRTexType pbrTexType,const char *fileName){
if(pbrMat == NULL) return;
switch(pbrTexType){
case PBR_TEXTURE_ALBEDO:
pbrMat->texAlbedo = LoadTexture(fileName);
pbrMat->useTexAlbedo = 1;
break;
case PBR_TEXTURE_MRA:
pbrMat->texMRA = LoadTexture(fileName);
pbrMat->useTexMRA = 1;
break;
case PBR_TEXTURE_NORMAL:
pbrMat->texNormal = LoadTexture(fileName);
pbrMat->useTexNormal = 1;
break;
case PBR_TEXTURE_EMISSIVE:
pbrMat->texEmissive = LoadTexture(fileName);
pbrMat->useTexEmissive = 1;
break;
}
}
void UnloadPBRMaterial(PBRMaterial pbrMat){
if(pbrMat.useTexAlbedo == 1) UnloadTexture(pbrMat.texAlbedo);
if(pbrMat.useTexNormal == 1) UnloadTexture(pbrMat.texNormal);
if(pbrMat.useTexMRA == 1) UnloadTexture(pbrMat.texMRA);
if(pbrMat.useTexEmissive == 1) UnloadTexture(pbrMat.texEmissive);
}
void PBRSetColor(PBRMaterial *pbrMat,PBRColorType pbrColorType,Color color){
if(pbrMat == NULL) return;
switch(pbrColorType){
case PBR_COLOR_ALBEDO:
pbrMat->albedo[0] = (float) color.r / 255;
pbrMat->albedo[1] = (float) color.g / 255;
pbrMat->albedo[2] = (float) color.b / 255;
pbrMat->albedo[3] = (float) color.a / 255;
SetShaderValue(pbrMat->pbrShader,pbrMat->albedoLoc,pbrMat->albedo,SHADER_UNIFORM_VEC4);
break;
case PBR_COLOR_EMISSIVE:
pbrMat->emissive[0] = (float) color.r / 255;
pbrMat->emissive[1] = (float) color.g / 255;
pbrMat->emissive[2] = (float) color.b / 255;
pbrMat->emissive[3] = (float) color.a / 255;
SetShaderValue(pbrMat->pbrShader, pbrMat->emissiveColorLoc, pbrMat->emissive, SHADER_UNIFORM_VEC4);
break;
}
}
void PBRSetFloat(PBRMaterial *pbrMat, PBRFloatType pbrParamType, float value){
if(pbrMat == NULL) return;
switch(pbrParamType){
case PBR_PARAM_METALLIC:
pbrMat->metallic = value;
SetShaderValue(pbrMat->pbrShader,pbrMat->metallicLoc,&pbrMat->metallic,SHADER_UNIFORM_FLOAT);
break;
case PBR_PARAM_ROUGHNESS:
pbrMat->roughness = value;
SetShaderValue(pbrMat->pbrShader,pbrMat->roughnessLoc,&pbrMat->roughness,SHADER_UNIFORM_FLOAT);
break;
case PBR_PARAM_NORMAL:
pbrMat->normal = value;
SetShaderValue(pbrMat->pbrShader,pbrMat->normalLoc,&pbrMat->normal,SHADER_UNIFORM_FLOAT);
break;
case PBR_PARAM_AO:
pbrMat->ao = value;
SetShaderValue(pbrMat->pbrShader,pbrMat->aoLoc,&pbrMat->ao,SHADER_UNIFORM_FLOAT);
break;
case PBR_PARAM_EMISSIVE:
pbrMat->emissivePower = value;
SetShaderValue(pbrMat->pbrShader,pbrMat->emissivePowerLoc,&pbrMat->emissivePower,SHADER_UNIFORM_FLOAT);
break;
}
}
void PBRSetVec2(PBRMaterial *pbrMat,PBRVec2Type type,Vector2 value){
switch(type){
case PBR_VEC2_TILING:
pbrMat->texTiling[0] = value.x;
pbrMat->texTiling[1] = value.y;
SetShaderValue(pbrMat->pbrShader,pbrMat->texTilingLoc,&pbrMat->texTiling,SHADER_UNIFORM_VEC2);
break;
case PBR_VEC2_OFFSET:
pbrMat->texOffset[0] = value.x;
pbrMat->texOffset[1] = value.y;
SetShaderValue(pbrMat->pbrShader,pbrMat->texOffsetLoc,&pbrMat->texOffset,SHADER_UNIFORM_VEC2);
break;
}
}
void PBRSetMaterial(PBRModel* model,PBRMaterial* pbrMat,int matIndex){
model->pbrMat = *pbrMat;
model->model.materials[matIndex].shader = model->pbrMat.pbrShader;
pbrMat->pbrShader.locs[SHADER_LOC_MAP_MRA] = GetShaderLocation(pbrMat->pbrShader, "mraMap");
pbrMat->pbrShader.locs[SHADER_LOC_MAP_EMISSIVE] = GetShaderLocation(pbrMat->pbrShader, "emissiveMap");
pbrMat->pbrShader.locs[SHADER_LOC_MAP_NORMAL] = GetShaderLocation(pbrMat->pbrShader, "normalMap");
if(pbrMat->useTexAlbedo) {
model->model.materials[matIndex].maps[MATERIAL_MAP_ALBEDO].texture = pbrMat->texAlbedo;
}
if(pbrMat->useTexMRA) {
model->model.materials[matIndex].maps[MATERIAL_MAP_MRA].texture = pbrMat->texMRA;
}
if(pbrMat->useTexNormal) {
model->model.materials[matIndex].maps[MATERIAL_MAP_NORMAL].texture = pbrMat->texNormal;
}
if(pbrMat->useTexEmissive) {
model->model.materials[matIndex].maps[MATERIAL_MAP_EMISSIVE].texture = pbrMat->texEmissive;
}
SetShaderValue(pbrMat->pbrShader,pbrMat->useTexAlbedoLoc,&pbrMat->useTexAlbedo,SHADER_UNIFORM_INT);
SetShaderValue(pbrMat->pbrShader,pbrMat->useTexNormalLoc,&pbrMat->useTexNormal,SHADER_UNIFORM_INT);
SetShaderValue(pbrMat->pbrShader, pbrMat->useTexMRAELoc, &pbrMat->useTexMRA, SHADER_UNIFORM_INT);
SetShaderValue(pbrMat->pbrShader, pbrMat->useTexEmissiveLoc, &pbrMat->useTexEmissive, SHADER_UNIFORM_INT);
}
void PBRDrawModel(PBRModel pbrModel, Vector3 position, float scale){
PBRMaterial *pbrMat = &pbrModel.pbrMat;
SetShaderValue(pbrMat->pbrShader,pbrMat->albedoLoc,pbrMat->albedo,SHADER_UNIFORM_VEC4);
SetShaderValue(pbrMat->pbrShader, pbrMat->emissiveColorLoc, pbrMat->emissive, SHADER_UNIFORM_VEC4);
SetShaderValue(pbrMat->pbrShader,pbrMat->metallicLoc,&pbrMat->metallic,SHADER_UNIFORM_FLOAT);
SetShaderValue(pbrMat->pbrShader,pbrMat->roughnessLoc,&pbrMat->roughness,SHADER_UNIFORM_FLOAT);
SetShaderValue(pbrMat->pbrShader,pbrMat->aoLoc,&pbrMat->ao,SHADER_UNIFORM_FLOAT);
SetShaderValue(pbrMat->pbrShader,pbrMat->normalLoc,&pbrMat->normal,SHADER_UNIFORM_FLOAT);
SetShaderValue(pbrMat->pbrShader,pbrMat->texTilingLoc,pbrMat->texTiling,SHADER_UNIFORM_VEC2);
SetShaderValue(pbrMat->pbrShader,pbrMat->texOffsetLoc,pbrMat->texOffset,SHADER_UNIFORM_VEC2);
SetShaderValue(pbrMat->pbrShader,pbrMat->useTexAlbedoLoc,&pbrMat->useTexAlbedo,SHADER_UNIFORM_INT);
SetShaderValue(pbrMat->pbrShader,pbrMat->useTexNormalLoc,&pbrMat->useTexNormal,SHADER_UNIFORM_INT);
SetShaderValue(pbrMat->pbrShader, pbrMat->useTexMRAELoc, &pbrMat->useTexMRA, SHADER_UNIFORM_INT);
SetShaderValue(pbrMat->pbrShader, pbrMat->useTexEmissiveLoc, &pbrMat->useTexEmissive, SHADER_UNIFORM_INT);
DrawModel(pbrModel.model,position,scale,WHITE);
}
PBRModel PBRModelLoad(const char *fileName){
PBRModel pbrModel = (PBRModel){0};
pbrModel.model = LoadModel(fileName);
return pbrModel;
}
PBRModel PBRModelLoadFromMesh(Mesh mesh){
PBRModel pbrModel = (PBRModel){0};
pbrModel.model = LoadModelFromMesh(mesh);
return pbrModel;
}
#endif // RPBR_IMPLEMENTATION

View File

@ -0,0 +1,171 @@
/*******************************************************************************************
*
* raylib [core] example - Model Defuse Normal Shader (adapted for HTML5 platform)
*
* This example is prepared to compile for PLATFORM_WEB and PLATFORM_DESKTOP
* As you will notice, code structure is slightly different to the other examples...
* To compile it for PLATFORM_WEB just uncomment #define PLATFORM_WEB at beginning
*
* This example has been created using raylib 5.0 (www.raylib.com)
* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
*
* Copyright (c) 2023-2024 Afan OLOVCIC (@_DevDad) 2015 Ramon Santamaria (@raysan5)
* Model: "Old Rusty Car" (https://skfb.ly/LxRy) by Renafox is licensed under Creative Commons Attribution-NonCommercial (http://creativecommons.org/licenses/by-nc/4.0/).
********************************************************************************************/
#include "raylib.h"
#if defined(PLATFORM_WEB)
#include <emscripten/emscripten.h>
#endif
#define RPBR_IMPLEMENTATION
#include "rpbr.h"
#if defined(PLATFORM_DESKTOP)
#define GLSL_VERSION 330
#else // PLATFORM_ANDROID, PLATFORM_WEB
#define GLSL_VERSION 120
#endif
//----------------------------------------------------------------------------------
// Main Entry Point
//----------------------------------------------------------------------------------
int main()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
SetConfigFlags(FLAG_MSAA_4X_HINT);
InitWindow(screenWidth, screenHeight, "raylib [shaders] example - basic pbr");
// Define the camera to look into our 3d world
Camera camera = { 0 };
camera.position = (Vector3){ 2.0f, 2.0f, 6.0f }; // Camera position
camera.target = (Vector3){ 0.0f, 0.5f, 0.0f }; // Camera looking at point
camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera up vector (rotation towards target)
camera.fovy = 45.0f; // Camera field-of-view Y
camera.projection = CAMERA_PERSPECTIVE; // Camera projection type
Shader shader = LoadShader(TextFormat("resources/shaders/glsl%i/pbr.vs",GLSL_VERSION),
TextFormat("resources/shaders/glsl%i/pbr.fs",GLSL_VERSION));
PBRModel model = PBRModelLoad("resources/models/old_car_new.glb");
//if we use obj file formator if model doesn't have tangents we have to calculate MeshTangents
//by using raylib function GenMeshTangents(mesh) for example: obj file doesn't support tangents
//GenMeshTangents(&model.model.meshes[0]);
PBRMaterial model_mat = (PBRMaterial){0};
PBRMaterialSetup(&model_mat, shader, NULL); //environment = NULL for now
PBRLoadTextures(&model_mat, PBR_TEXTURE_ALBEDO, "resources/old_car_d.png");
PBRLoadTextures(&model_mat, PBR_TEXTURE_MRA, "resources/old_car_mra.png");
PBRLoadTextures(&model_mat, PBR_TEXTURE_NORMAL, "resources/old_car_n.png");
PBRLoadTextures(&model_mat, PBR_TEXTURE_EMISSIVE, "resources/old_car_e.png");
PBRSetColor(&model_mat,PBR_COLOR_EMISSIVE, (Color){255,162,0,255});
PBRSetVec2(&model_mat, PBR_VEC2_TILING,(Vector2){0.5,0.5});
PBRSetMaterial(&model,&model_mat,0);
PBRModel floor = PBRModelLoad("resources/models/plane.glb");
PBRMaterial floor_mat = (PBRMaterial){0};
PBRMaterialSetup(&floor_mat, shader, NULL);
PBRLoadTextures(&floor_mat, PBR_TEXTURE_ALBEDO, "resources/road_a.png");
PBRLoadTextures(&floor_mat, PBR_TEXTURE_MRA, "resources/road_mra.png");
PBRLoadTextures(&floor_mat, PBR_TEXTURE_NORMAL, "resources/road_n.png");
PBRSetVec2(&floor_mat, PBR_VEC2_TILING,(Vector2){0.5,0.5});
PBRSetMaterial(&floor,&floor_mat,0);
shader.locs[SHADER_LOC_VECTOR_VIEW] = GetShaderLocation(shader, "viewPos");
int numOfLightsLoc = GetShaderLocation(shader, "numOfLights");
int numOfLights = 4;
SetShaderValue(shader, numOfLightsLoc, &numOfLights, SHADER_UNIFORM_INT);
Color ambCol = (Color){26,32,135,255};
float ambIntens = 0.02;
int albedoLoc = GetShaderLocation(shader, "albedo");
PBRSetAmbient(shader,ambCol,ambIntens);
// Create lights
PBRLight lights[MAX_LIGHTS] = { 0 };
lights[0] = PBRLightCreate(LIGHT_POINT, (Vector3){ -1, 1, -2 }, (Vector3){0,0,0}, YELLOW,4, shader);
lights[1] = PBRLightCreate(LIGHT_POINT, (Vector3){ 2, 1, 1 }, (Vector3){0,0,0}, GREEN,3.3, shader);
lights[2] = PBRLightCreate(LIGHT_POINT, (Vector3){ -2, 1, 1 }, (Vector3){0,0,0}, RED,8.3, shader);
lights[3] = PBRLightCreate(LIGHT_POINT, (Vector3){ 1, 1, -2 }, (Vector3){0,0,0}, BLUE,2, shader);
SetShaderValueV(shader, GetShaderLocation(shader, "lights"), lights, SHADER_UNIFORM_FLOAT, numOfLights);
SetTargetFPS(60); // Set our game to run at 60 frames-per-second-------------------------------------------------------------
int emissiveCnt = 0;
// Main game loop
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(&camera, CAMERA_ORBITAL);
// Update the shader with the camera view vector (points towards { 0.0f, 0.0f, 0.0f })
float cameraPos[3] = {camera.position.x, camera.position.y, camera.position.z};
SetShaderValue(shader, shader.locs[SHADER_LOC_VECTOR_VIEW], cameraPos, SHADER_UNIFORM_VEC3);
// Check key inputs to enable/disable lights
if (IsKeyPressed(KEY_Y)) { lights[0].enabled = !lights[0].enabled; }
if (IsKeyPressed(KEY_G)) { lights[1].enabled = !lights[1].enabled; }
if (IsKeyPressed(KEY_R)) { lights[2].enabled = !lights[2].enabled; }
if (IsKeyPressed(KEY_B)) { lights[3].enabled = !lights[3].enabled; }
// Update light values (actually, only enable/disable them)
for (int i = 0; i < MAX_LIGHTS; i++) PBRLightUpdate(shader, lights[i]);
emissiveCnt--;
if(emissiveCnt<=0){
emissiveCnt = GetRandomValue(0,20);
PBRSetFloat(&model_mat,PBR_PARAM_EMISSIVE,(float)GetRandomValue(0,100)/100);
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(BLACK);
BeginMode3D(camera);
PBRDrawModel(floor, (Vector3){0,0,0}, 5.0f);
PBRDrawModel(model, (Vector3) {0, 0.0, 0}, 0.005);
// Draw spheres to show where the lights are
for (int i = 0; i < MAX_LIGHTS; i++) {
Color col = (Color) {lights[i].color[0] * 255, lights[i].color[1] * 255, lights[i].color[2] * 255,
lights[i].color[3] * 255};
if (lights[i].enabled) DrawSphereEx(lights[i].position, 0.2f, 8, 8, col);
else DrawSphereWires(lights[i].position, 0.2f, 8, 8, ColorAlpha(col, 0.3f));
}
EndMode3D();
DrawText("(c) Old Rusty Car model by Renafox (https://skfb.ly/LxRy)", screenWidth - 320, screenHeight - 20, 10, GRAY);
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
}
//--------------------------------------------------------------------------------------
// De-Initialization
//--------------------------------------------------------------------------------------
UnloadModel(floor.model); // Unload model
UnloadModel(model.model); // Unload model
UnloadShader(shader); // Unload Shader
UnloadPBRMaterial(floor_mat); // Unload PBRMaterial
UnloadPBRMaterial(model_mat); // Unload PBRMaterial
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}

View File

@ -73,6 +73,7 @@
//----------------------------------------------------------------------------------
typedef struct {
GLFWwindow *handle; // GLFW window handle (graphic device)
bool ourFullscreen; // Internal var to filter our handling of fullscreen vs the user handling of fullscreen
} PlatformData;
//----------------------------------------------------------------------------------
@ -140,6 +141,37 @@ bool WindowShouldClose(void)
// Toggle fullscreen mode
void ToggleFullscreen(void)
{
platform.ourFullscreen = true;
bool enterFullscreen = false;
const bool wasFullscreen = EM_ASM_INT( { if (document.fullscreenElement) return 1; }, 0);
if (wasFullscreen)
{
EM_ASM(document.exitFullscreen(););
if (CORE.Window.flags & FLAG_FULLSCREEN_MODE) enterFullscreen = false;
else enterFullscreen = true;
CORE.Window.fullscreen = false;
CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE;
CORE.Window.flags &= ~FLAG_BORDERLESS_WINDOWED_MODE;
}
else enterFullscreen = true;
if (enterFullscreen)
{
// NOTE: The setTimeouts handle the browser mode change delay
EM_ASM(
setTimeout(function()
{
Module.requestFullscreen(false, false);
}, 100);
);
CORE.Window.fullscreen = true;
CORE.Window.flags |= FLAG_FULLSCREEN_MODE;
}
// NOTE: Old notes below:
/*
EM_ASM
(
@ -204,40 +236,44 @@ void ToggleFullscreen(void)
CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE;
}
*/
CORE.Window.fullscreen = !CORE.Window.fullscreen; // Toggle fullscreen flag
}
// Toggle borderless windowed mode
void ToggleBorderlessWindowed(void)
{
platform.ourFullscreen = true;
bool enterBorderless = false;
const bool wasFullscreen = EM_ASM_INT( { if (document.fullscreenElement) return 1; }, 0);
if (wasFullscreen)
{
EM_ASM(document.exitFullscreen(););
if (CORE.Window.flags & FLAG_BORDERLESS_WINDOWED_MODE) enterBorderless = false;
else enterBorderless = true;
CORE.Window.fullscreen = false;
CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE;
CORE.Window.flags &= ~FLAG_BORDERLESS_WINDOWED_MODE;
}
else enterBorderless = true;
if (!IsWindowState(FLAG_BORDERLESS_WINDOWED_MODE))
if (enterBorderless)
{
// NOTE: 1. The setTimeouts handle the browser mode change delay
// 2. The style unset handles the possibility of a width="100%" like on the default shell.html file
// 2. The style unset handles the possibility of a width="value%" like on the default shell.html file
EM_ASM(
setTimeout(function()
{
Module.requestFullscreen(true, true);
Module.requestFullscreen(false, true);
setTimeout(function()
{
canvas.style.width="unset";
}, 100);
}, 100);
);
CORE.Window.flags |= FLAG_BORDERLESS_WINDOWED_MODE;
}
else CORE.Window.flags &= ~FLAG_BORDERLESS_WINDOWED_MODE;
}
// Set window state: maximized, if resizable
@ -277,9 +313,9 @@ void SetWindowState(unsigned int flags)
}
// State change: FLAG_FULLSCREEN_MODE
if ((flags & FLAG_FULLSCREEN_MODE) > 0)
if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) != (flags & FLAG_FULLSCREEN_MODE))
{
TRACELOG(LOG_WARNING, "SetWindowState(FLAG_FULLSCREEN_MODE) not available yet on target platform");
ToggleFullscreen(); // NOTE: Window state flag updated inside function
}
// State change: FLAG_WINDOW_RESIZABLE
@ -384,9 +420,9 @@ void ClearWindowState(unsigned int flags)
}
// State change: FLAG_FULLSCREEN_MODE
if ((flags & FLAG_FULLSCREEN_MODE) > 0)
if (((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0) && ((flags & FLAG_FULLSCREEN_MODE) > 0))
{
TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_FULLSCREEN_MODE) not available yet on target platform");
ToggleFullscreen(); // NOTE: Window state flag updated inside function
}
// State change: FLAG_WINDOW_RESIZABLE
@ -572,15 +608,19 @@ Vector2 GetMonitorPosition(int monitor)
// Get selected monitor width (currently used by monitor)
int GetMonitorWidth(int monitor)
{
TRACELOG(LOG_WARNING, "GetMonitorWidth() not implemented on target platform");
return 0;
// NOTE: Returned value is limited to the current monitor where the browser window is located
int width = 0;
width = EM_ASM_INT( { return screen.width; }, 0);
return width;
}
// Get selected monitor height (currently used by monitor)
int GetMonitorHeight(int monitor)
{
TRACELOG(LOG_WARNING, "GetMonitorHeight() not implemented on target platform");
return 0;
// NOTE: Returned value is limited to the current monitor where the browser window is located
int height = 0;
height = EM_ASM_INT( { return screen.height; }, 0);
return height;
}
// Get selected monitor physical width in millimetres
@ -614,8 +654,11 @@ const char *GetMonitorName(int monitor)
// Get window position XY on monitor
Vector2 GetWindowPosition(void)
{
TRACELOG(LOG_WARNING, "GetWindowPosition() not implemented on target platform");
return (Vector2){ 0, 0 };
// NOTE: Returned position is relative to the current monitor where the browser window is located
Vector2 position = { 0, 0 };
position.x = (float)EM_ASM_INT( { return window.screenX; }, 0);
position.y = (float)EM_ASM_INT( { return window.screenY; }, 0);
return position;
}
// Get window scale DPI factor for current monitor
@ -944,8 +987,13 @@ int InitPlatform(void)
if ((CORE.Window.flags & FLAG_WINDOW_TOPMOST) > 0) glfwWindowHint(GLFW_FLOATING, GLFW_TRUE);
else glfwWindowHint(GLFW_FLOATING, GLFW_FALSE);
// NOTE: Some GLFW flags are not supported on HTML5
// e.g.: GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_SCALE_TO_MONITOR, GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_MOUSE_PASSTHROUGH
// NOTE: Some GLFW flags are not supported on HTML5
// e.g.: GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_MOUSE_PASSTHROUGH
// Scale content area based on the monitor content scale where window is placed on
// NOTE: This feature requires emscripten 3.1.51
//if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_TRUE);
//else glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_FALSE);
if (CORE.Window.flags & FLAG_MSAA_4X_HINT)
{
@ -1003,6 +1051,9 @@ int InitPlatform(void)
CORE.Window.display.width = CORE.Window.screen.width;
CORE.Window.display.height = CORE.Window.screen.height;
// Init fullscreen toggle required var:
platform.ourFullscreen = false;
if (CORE.Window.fullscreen)
{
// remember center for switchinging from fullscreen to window
@ -1136,7 +1187,7 @@ int InitPlatform(void)
// Initialize input events callbacks
//----------------------------------------------------------------------------
// Setup callback functions for the DOM events
emscripten_set_fullscreenchange_callback("#canvas", NULL, 1, EmscriptenFullscreenChangeCallback);
emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenFullscreenChangeCallback);
// WARNING: Below resize code was breaking fullscreen mode for sample games and examples, it needs review
// Check fullscreen change events(note this is done on the window since most browsers don't support this on #canvas)
@ -1400,7 +1451,19 @@ static void CursorEnterCallback(GLFWwindow *window, int enter)
// Register fullscreen change events
static EM_BOOL EmscriptenFullscreenChangeCallback(int eventType, const EmscriptenFullscreenChangeEvent *event, void *userData)
{
// TODO: Implement EmscriptenFullscreenChangeCallback()?
// NOTE: 1. Reset the fullscreen flags if the user left fullscreen manually by pressing the Escape key
// 2. Which is a necessary safeguard because that case will bypass the toggles CORE.Window.flags resets
if (platform.ourFullscreen) platform.ourFullscreen = false;
else
{
const bool wasFullscreen = EM_ASM_INT( { if (document.fullscreenElement) return 1; }, 0);
if (!wasFullscreen)
{
CORE.Window.fullscreen = false;
CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE;
CORE.Window.flags &= ~FLAG_BORDERLESS_WINDOWED_MODE;
}
}
return 1; // The event was consumed by the callback handler
}

View File

@ -484,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
@ -1481,6 +1480,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)

View File

@ -1194,8 +1194,8 @@ VrStereoConfig LoadVrStereoConfig(VrDeviceInfo device)
// 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);
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
/*

View File

@ -1546,6 +1546,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 +1653,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
}

View File

@ -1421,6 +1421,28 @@ int TextToInteger(const char *text)
return value*sign;
}
float TextToFloat(const char *text)
{
float value = 0.0f;
float sign = 1.0f;
if ((text[0] == '+') || (text[0] == '-'))
{
if (text[0] == '-') sign = -1;
text++;
}
int i = 0;
for (; ((text[i] >= '0') && (text[i] <= '9')); ++i) value = value*10.0f + (float)(text[i] - '0');
if (text[i++] != '.') return value*sign;
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)
@ -2029,7 +2051,8 @@ static int GetLine(const char *origin, char *buffer, int maxLength)
// REQUIRES: strstr(), sscanf(), strrchr(), memcpy()
static Font LoadBMFont(const char *fileName)
{
#define MAX_BUFFER_SIZE 256
#define MAX_BUFFER_SIZE 256
#define MAX_FONT_IMAGE_PAGES 8
Font font = { 0 };
@ -2041,7 +2064,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
@ -2060,17 +2084,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
readBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE);
searchPoint = strstr(buffer, "file");
readVars = sscanf(searchPoint, "file=\"%128[^\"]\"", imFileName);
fileTextPtr += (readBytes + 1);
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;
}
if (readVars < 1) { UnloadFileText(fileText); return font; } // No fileName read
for (int i = 0; i < pageCount; i++)
{
readBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE);
searchPoint = strstr(buffer, "file");
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");
@ -2079,50 +2112,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)
{
// 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,
.mipmaps = 1,
.format = PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA
};
for (int p = 0, i = 0; p < (imFont.width*imFont.height*2); p += 2, i++)
if (imFonts[i].format == PIXELFORMAT_UNCOMPRESSED_GRAYSCALE)
{
((unsigned char *)(imFontAlpha.data))[p] = 0xff;
((unsigned char *)(imFontAlpha.data))[p + 1] = ((unsigned char *)imFont.data)[i];
}
// Convert image to GRAYSCALE + ALPHA, using the mask as the alpha channel
Image imFontAlpha = {
.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
};
UnloadImage(imFont);
imFont = imFontAlpha;
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 *)imFonts[i].data)[pi];
}
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;
@ -2131,19 +2170,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;
@ -2151,13 +2190,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)
@ -2170,6 +2209,7 @@ static Font LoadBMFont(const char *fileName)
return font;
}
#endif
#endif // SUPPORT_MODULE_RTEXT

View File

@ -213,14 +213,14 @@
#define STBIR_MALLOC(size,c) ((void)(c), RL_MALLOC(size))
#define STBIR_FREE(ptr,c) ((void)(c), RL_FREE(ptr))
#define STB_IMAGE_RESIZE_IMPLEMENTATION
#include "external/stb_image_resize2.h" // Required for: stbir_resize_uint8_linear() [ImageResize()]
#include "external/stb_image_resize2.h" // Required for: stbir_resize_uint8_linear() [ImageResize()]
#if defined(SUPPORT_FILEFORMAT_SVG)
#define NANOSVG_IMPLEMENTATION // Expands implementation
#include "external/nanosvg.h"
#define NANOSVG_IMPLEMENTATION // Expands implementation
#include "external/nanosvg.h"
#define NANOSVGRAST_IMPLEMENTATION
#include "external/nanosvgrast.h"
#define NANOSVGRAST_IMPLEMENTATION
#include "external/nanosvgrast.h"
#endif
//----------------------------------------------------------------------------------
@ -1959,29 +1959,24 @@ 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;
avgB += pixelsCopy1[row*image->width + i].z;
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--;
}
@ -1999,7 +1994,7 @@ void ImageBlurGaussian(Image *image, int blurSize) {
pixelsCopy2[row*image->width + x].z = avgB/convolutionSize;
pixelsCopy2[row*image->width + x].w = avgAlpha/convolutionSize;
}
}
}
// Vertical motion blur
for (int col = 0; col < image->width; col++)
@ -2008,9 +2003,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 +2013,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)