Merge branch 'master' of https://github.com/raysan5/raylib
|
|
@ -7,6 +7,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to
|
||||||
| name | raylib version | language | license | repo |
|
| 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 | **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-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-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 |
|
| Raylib-CsLo | 4.2 | [C#](https://en.wikipedia.org/wiki/C_Sharp_(programming_language)) | MPL-2.0 | https://github.com/NotNotTech/Raylib-CsLo |
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
cmake_minimum_required(VERSION 3.0)
|
cmake_minimum_required(VERSION 3.5)
|
||||||
project(raylib)
|
project(raylib)
|
||||||
|
|
||||||
# Avoid excessive expansion of variables in conditionals. In particular, if
|
# Avoid excessive expansion of variables in conditionals. In particular, if
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
*
|
*
|
||||||
* raylib [audio] example - Music stream processing effects
|
* 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,
|
* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
|
||||||
* BSD-like license that allows static linking with closed source software
|
* BSD-like license that allows static linking with closed source software
|
||||||
|
|
@ -13,7 +13,7 @@
|
||||||
|
|
||||||
#include "raylib.h"
|
#include "raylib.h"
|
||||||
|
|
||||||
#include <stdlib.h> // Required for: NULL
|
#include <stdlib.h> // Required for: NULL
|
||||||
|
|
||||||
// Required delay effect variables
|
// Required delay effect variables
|
||||||
static float *delayBuffer = NULL;
|
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
|
static const float cutoff = 70.0f / 44100.0f; // 70 Hz lowpass filter
|
||||||
const float k = cutoff / (cutoff + 0.1591549431f); // RC filter formula
|
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)
|
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[0] += k * (l - low[0]);
|
||||||
low[1] += k * (r - low[1]);
|
low[1] += k * (r - low[1]);
|
||||||
((float *)buffer)[i] = low[0];
|
bufferData[i] = low[0];
|
||||||
((float *)buffer)[i + 1] = low[1];
|
bufferData[i + 1] = low[1];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,6 @@ int main(void)
|
||||||
.vResolution = 1200, // Vertical resolution in pixels
|
.vResolution = 1200, // Vertical resolution in pixels
|
||||||
.hScreenSize = 0.133793f, // Horizontal size in meters
|
.hScreenSize = 0.133793f, // Horizontal size in meters
|
||||||
.vScreenSize = 0.0669f, // Vertical 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
|
.eyeToScreenDistance = 0.041f, // Distance between eye and display in meters
|
||||||
.lensSeparationDistance = 0.07f, // Lens separation distance in meters
|
.lensSeparationDistance = 0.07f, // Lens separation distance in meters
|
||||||
.interpupillaryDistance = 0.07f, // IPD (distance between pupils) in meters
|
.interpupillaryDistance = 0.07f, // IPD (distance between pupils) in meters
|
||||||
|
|
|
||||||
BIN
examples/shaders/resources/models/old_car_new.glb
Normal file
BIN
examples/shaders/resources/models/plane.glb
Normal file
BIN
examples/shaders/resources/old_car_d.png
Normal file
|
After Width: | Height: | Size: 1.6 MiB |
BIN
examples/shaders/resources/old_car_e.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
examples/shaders/resources/old_car_mra.png
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
examples/shaders/resources/old_car_n.png
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
BIN
examples/shaders/resources/road_a.png
Normal file
|
After Width: | Height: | Size: 623 KiB |
BIN
examples/shaders/resources/road_mra.png
Normal file
|
After Width: | Height: | Size: 657 KiB |
BIN
examples/shaders/resources/road_n.png
Normal file
|
After Width: | Height: | Size: 645 KiB |
156
examples/shaders/resources/shaders/glsl100/pbr.fs
Normal 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);
|
||||||
|
|
||||||
|
}
|
||||||
75
examples/shaders/resources/shaders/glsl100/pbr.vs
Normal 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);
|
||||||
|
}
|
||||||
156
examples/shaders/resources/shaders/glsl120/pbr.fs
Normal 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);
|
||||||
|
|
||||||
|
}
|
||||||
75
examples/shaders/resources/shaders/glsl120/pbr.vs
Normal 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);
|
||||||
|
}
|
||||||
159
examples/shaders/resources/shaders/glsl330/pbr.fs
Normal 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);
|
||||||
|
}
|
||||||
49
examples/shaders/resources/shaders/glsl330/pbr.vs
Normal 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
|
|
@ -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
|
||||||
171
examples/shaders/shaders_basic_pbr.c
Normal 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;
|
||||||
|
}
|
||||||
|
|
@ -73,6 +73,7 @@
|
||||||
//----------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------
|
||||||
typedef struct {
|
typedef struct {
|
||||||
GLFWwindow *handle; // GLFW window handle (graphic device)
|
GLFWwindow *handle; // GLFW window handle (graphic device)
|
||||||
|
bool ourFullscreen; // Internal var to filter our handling of fullscreen vs the user handling of fullscreen
|
||||||
} PlatformData;
|
} PlatformData;
|
||||||
|
|
||||||
//----------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------
|
||||||
|
|
@ -140,6 +141,37 @@ bool WindowShouldClose(void)
|
||||||
// Toggle fullscreen mode
|
// Toggle fullscreen mode
|
||||||
void ToggleFullscreen(void)
|
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
|
EM_ASM
|
||||||
(
|
(
|
||||||
|
|
@ -204,40 +236,44 @@ void ToggleFullscreen(void)
|
||||||
CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE;
|
CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE;
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
CORE.Window.fullscreen = !CORE.Window.fullscreen; // Toggle fullscreen flag
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Toggle borderless windowed mode
|
// Toggle borderless windowed mode
|
||||||
void ToggleBorderlessWindowed(void)
|
void ToggleBorderlessWindowed(void)
|
||||||
{
|
{
|
||||||
|
platform.ourFullscreen = true;
|
||||||
|
bool enterBorderless = false;
|
||||||
|
|
||||||
const bool wasFullscreen = EM_ASM_INT( { if (document.fullscreenElement) return 1; }, 0);
|
const bool wasFullscreen = EM_ASM_INT( { if (document.fullscreenElement) return 1; }, 0);
|
||||||
if (wasFullscreen)
|
if (wasFullscreen)
|
||||||
{
|
{
|
||||||
EM_ASM(document.exitFullscreen(););
|
EM_ASM(document.exitFullscreen(););
|
||||||
|
|
||||||
|
if (CORE.Window.flags & FLAG_BORDERLESS_WINDOWED_MODE) enterBorderless = false;
|
||||||
|
else enterBorderless = true;
|
||||||
|
|
||||||
CORE.Window.fullscreen = false;
|
CORE.Window.fullscreen = false;
|
||||||
CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE;
|
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
|
// 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(
|
EM_ASM(
|
||||||
setTimeout(function()
|
setTimeout(function()
|
||||||
{
|
{
|
||||||
Module.requestFullscreen(true, true);
|
Module.requestFullscreen(false, true);
|
||||||
setTimeout(function()
|
setTimeout(function()
|
||||||
{
|
{
|
||||||
canvas.style.width="unset";
|
canvas.style.width="unset";
|
||||||
}, 100);
|
}, 100);
|
||||||
}, 100);
|
}, 100);
|
||||||
);
|
);
|
||||||
|
|
||||||
CORE.Window.flags |= FLAG_BORDERLESS_WINDOWED_MODE;
|
CORE.Window.flags |= FLAG_BORDERLESS_WINDOWED_MODE;
|
||||||
}
|
}
|
||||||
else CORE.Window.flags &= ~FLAG_BORDERLESS_WINDOWED_MODE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set window state: maximized, if resizable
|
// Set window state: maximized, if resizable
|
||||||
|
|
@ -277,9 +313,9 @@ void SetWindowState(unsigned int flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
// State change: FLAG_FULLSCREEN_MODE
|
// 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
|
// State change: FLAG_WINDOW_RESIZABLE
|
||||||
|
|
@ -384,9 +420,9 @@ void ClearWindowState(unsigned int flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
// State change: FLAG_FULLSCREEN_MODE
|
// 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
|
// State change: FLAG_WINDOW_RESIZABLE
|
||||||
|
|
@ -572,15 +608,19 @@ Vector2 GetMonitorPosition(int monitor)
|
||||||
// Get selected monitor width (currently used by monitor)
|
// Get selected monitor width (currently used by monitor)
|
||||||
int GetMonitorWidth(int monitor)
|
int GetMonitorWidth(int monitor)
|
||||||
{
|
{
|
||||||
TRACELOG(LOG_WARNING, "GetMonitorWidth() not implemented on target platform");
|
// NOTE: Returned value is limited to the current monitor where the browser window is located
|
||||||
return 0;
|
int width = 0;
|
||||||
|
width = EM_ASM_INT( { return screen.width; }, 0);
|
||||||
|
return width;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get selected monitor height (currently used by monitor)
|
// Get selected monitor height (currently used by monitor)
|
||||||
int GetMonitorHeight(int monitor)
|
int GetMonitorHeight(int monitor)
|
||||||
{
|
{
|
||||||
TRACELOG(LOG_WARNING, "GetMonitorHeight() not implemented on target platform");
|
// NOTE: Returned value is limited to the current monitor where the browser window is located
|
||||||
return 0;
|
int height = 0;
|
||||||
|
height = EM_ASM_INT( { return screen.height; }, 0);
|
||||||
|
return height;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get selected monitor physical width in millimetres
|
// Get selected monitor physical width in millimetres
|
||||||
|
|
@ -614,8 +654,11 @@ const char *GetMonitorName(int monitor)
|
||||||
// Get window position XY on monitor
|
// Get window position XY on monitor
|
||||||
Vector2 GetWindowPosition(void)
|
Vector2 GetWindowPosition(void)
|
||||||
{
|
{
|
||||||
TRACELOG(LOG_WARNING, "GetWindowPosition() not implemented on target platform");
|
// NOTE: Returned position is relative to the current monitor where the browser window is located
|
||||||
return (Vector2){ 0, 0 };
|
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
|
// 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);
|
if ((CORE.Window.flags & FLAG_WINDOW_TOPMOST) > 0) glfwWindowHint(GLFW_FLOATING, GLFW_TRUE);
|
||||||
else glfwWindowHint(GLFW_FLOATING, GLFW_FALSE);
|
else glfwWindowHint(GLFW_FLOATING, GLFW_FALSE);
|
||||||
|
|
||||||
// NOTE: Some GLFW flags are not supported on HTML5
|
// 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
|
// 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)
|
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.width = CORE.Window.screen.width;
|
||||||
CORE.Window.display.height = CORE.Window.screen.height;
|
CORE.Window.display.height = CORE.Window.screen.height;
|
||||||
|
|
||||||
|
// Init fullscreen toggle required var:
|
||||||
|
platform.ourFullscreen = false;
|
||||||
|
|
||||||
if (CORE.Window.fullscreen)
|
if (CORE.Window.fullscreen)
|
||||||
{
|
{
|
||||||
// remember center for switchinging from fullscreen to window
|
// remember center for switchinging from fullscreen to window
|
||||||
|
|
@ -1136,7 +1187,7 @@ int InitPlatform(void)
|
||||||
// Initialize input events callbacks
|
// Initialize input events callbacks
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
// Setup callback functions for the DOM events
|
// 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
|
// 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)
|
// 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
|
// Register fullscreen change events
|
||||||
static EM_BOOL EmscriptenFullscreenChangeCallback(int eventType, const EmscriptenFullscreenChangeEvent *event, void *userData)
|
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
|
return 1; // The event was consumed by the callback handler
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -484,7 +484,6 @@ typedef struct VrDeviceInfo {
|
||||||
int vResolution; // Vertical resolution in pixels
|
int vResolution; // Vertical resolution in pixels
|
||||||
float hScreenSize; // Horizontal size in meters
|
float hScreenSize; // Horizontal size in meters
|
||||||
float vScreenSize; // Vertical 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 eyeToScreenDistance; // Distance between eye and display in meters
|
||||||
float lensSeparationDistance; // Lens separation distance in meters
|
float lensSeparationDistance; // Lens separation distance in meters
|
||||||
float interpupillaryDistance; // IPD (distance between pupils) 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 *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 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 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)
|
// Basic 3d Shapes Drawing Functions (Module: models)
|
||||||
|
|
|
||||||
|
|
@ -1194,8 +1194,8 @@ VrStereoConfig LoadVrStereoConfig(VrDeviceInfo device)
|
||||||
// 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
|
// 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.
|
// 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[0] = MatrixTranslate(device.interpupillaryDistance*0.5f, 0.075f, 0.045f);
|
||||||
config.viewOffset[1] = MatrixTranslate(device.interpupillaryDistance*0.5f, 0.075f, 0.045f);
|
config.viewOffset[1] = MatrixTranslate(-device.interpupillaryDistance*0.5f, 0.075f, 0.045f);
|
||||||
|
|
||||||
// Compute eyes Viewports
|
// Compute eyes Viewports
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
|
|
@ -1546,6 +1546,92 @@ void DrawPolyLinesEx(Vector2 center, int sides, float radius, float rotation, fl
|
||||||
// Draw spline: linear, minimum 2 points
|
// Draw spline: linear, minimum 2 points
|
||||||
void DrawSplineLinear(Vector2 *points, int pointCount, float thick, Color color)
|
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 };
|
Vector2 delta = { 0 };
|
||||||
float length = 0.0f;
|
float length = 0.0f;
|
||||||
float scale = 0.0f;
|
float scale = 0.0f;
|
||||||
|
|
@ -1567,8 +1653,10 @@ void DrawSplineLinear(Vector2 *points, int pointCount, float thick, Color color)
|
||||||
|
|
||||||
DrawTriangleStrip(strip, 4, 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
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
144
src/rtext.c
|
|
@ -1421,6 +1421,28 @@ int TextToInteger(const char *text)
|
||||||
return value*sign;
|
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)
|
#if defined(SUPPORT_TEXT_MANIPULATION)
|
||||||
// Copy one string to another, returns bytes copied
|
// Copy one string to another, returns bytes copied
|
||||||
int TextCopy(char *dst, const char *src)
|
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()
|
// REQUIRES: strstr(), sscanf(), strrchr(), memcpy()
|
||||||
static Font LoadBMFont(const char *fileName)
|
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 };
|
Font font = { 0 };
|
||||||
|
|
||||||
|
|
@ -2041,7 +2064,8 @@ static Font LoadBMFont(const char *fileName)
|
||||||
|
|
||||||
int imWidth = 0;
|
int imWidth = 0;
|
||||||
int imHeight = 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 base = 0; // Useless data
|
||||||
int readBytes = 0; // Data bytes read
|
int readBytes = 0; // Data bytes read
|
||||||
|
|
@ -2060,17 +2084,26 @@ static Font LoadBMFont(const char *fileName)
|
||||||
// Read line data
|
// Read line data
|
||||||
readBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE);
|
readBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE);
|
||||||
searchPoint = strstr(buffer, "lineHeight");
|
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);
|
fileTextPtr += (readBytes + 1);
|
||||||
|
|
||||||
if (readVars < 4) { UnloadFileText(fileText); return font; } // Some data not available, file malformed
|
if (readVars < 4) { UnloadFileText(fileText); return font; } // Some data not available, file malformed
|
||||||
|
|
||||||
readBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE);
|
if (pageCount > MAX_FONT_IMAGE_PAGES)
|
||||||
searchPoint = strstr(buffer, "file");
|
{
|
||||||
readVars = sscanf(searchPoint, "file=\"%128[^\"]\"", imFileName);
|
TRACELOG(LOG_WARNING, "FONT: [%s] Font defines more pages than supported: %i/%i", fileName, pageCount, MAX_FONT_IMAGE_PAGES);
|
||||||
fileTextPtr += (readBytes + 1);
|
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);
|
readBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE);
|
||||||
searchPoint = strstr(buffer, "count");
|
searchPoint = strstr(buffer, "count");
|
||||||
|
|
@ -2079,50 +2112,56 @@ static Font LoadBMFont(const char *fileName)
|
||||||
|
|
||||||
if (readVars < 1) { UnloadFileText(fileText); return font; } // No glyphCount read
|
if (readVars < 1) { UnloadFileText(fileText); return font; } // No glyphCount read
|
||||||
|
|
||||||
// Compose correct path using route of .fnt file (fileName) and imFileName
|
// Load all required images for further compose
|
||||||
char *imPath = NULL;
|
Image *imFonts = (Image *)RL_CALLOC(pageCount, sizeof(Image)); // Font atlases, multiple images
|
||||||
char *lastSlash = NULL;
|
|
||||||
|
|
||||||
lastSlash = strrchr(fileName, '/');
|
for (int i = 0; i < pageCount; i++)
|
||||||
if (lastSlash == NULL) lastSlash = strrchr(fileName, '\\');
|
|
||||||
|
|
||||||
if (lastSlash != NULL)
|
|
||||||
{
|
{
|
||||||
// NOTE: We need some extra space to avoid memory corruption on next allocations!
|
imFonts[i] = LoadImage(TextFormat("%s/%s", GetDirectoryPath(fileName), imFileName[i]));
|
||||||
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;
|
|
||||||
|
|
||||||
TRACELOGD(" > Image loading path: %s", imPath);
|
if (imFonts[i].format == PIXELFORMAT_UNCOMPRESSED_GRAYSCALE)
|
||||||
|
|
||||||
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++)
|
|
||||||
{
|
{
|
||||||
((unsigned char *)(imFontAlpha.data))[p] = 0xff;
|
// Convert image to GRAYSCALE + ALPHA, using the mask as the alpha channel
|
||||||
((unsigned char *)(imFontAlpha.data))[p + 1] = ((unsigned char *)imFont.data)[i];
|
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);
|
for (int p = 0, pi = 0; p < (imFonts[i].width*imFonts[i].height*2); p += 2, pi++)
|
||||||
imFont = imFontAlpha;
|
{
|
||||||
|
((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
|
// Fill font characters info data
|
||||||
font.baseSize = fontSize;
|
font.baseSize = fontSize;
|
||||||
|
|
@ -2131,19 +2170,19 @@ static Font LoadBMFont(const char *fileName)
|
||||||
font.glyphs = (GlyphInfo *)RL_MALLOC(glyphCount*sizeof(GlyphInfo));
|
font.glyphs = (GlyphInfo *)RL_MALLOC(glyphCount*sizeof(GlyphInfo));
|
||||||
font.recs = (Rectangle *)RL_MALLOC(glyphCount*sizeof(Rectangle));
|
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++)
|
for (int i = 0; i < glyphCount; i++)
|
||||||
{
|
{
|
||||||
readBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE);
|
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",
|
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);
|
&charId, &charX, &charY, &charWidth, &charHeight, &charOffsetX, &charOffsetY, &charAdvanceX, &pageID);
|
||||||
fileTextPtr += (readBytes + 1);
|
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
|
// 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
|
// Save data properly in sprite font
|
||||||
font.glyphs[i].value = charId;
|
font.glyphs[i].value = charId;
|
||||||
|
|
@ -2151,13 +2190,13 @@ static Font LoadBMFont(const char *fileName)
|
||||||
font.glyphs[i].offsetY = charOffsetY;
|
font.glyphs[i].offsetY = charOffsetY;
|
||||||
font.glyphs[i].advanceX = charAdvanceX;
|
font.glyphs[i].advanceX = charAdvanceX;
|
||||||
|
|
||||||
// Fill character image data from imFont data
|
// Fill character image data from full font data
|
||||||
font.glyphs[i].image = ImageFromImage(imFont, font.recs[i]);
|
font.glyphs[i].image = ImageFromImage(fullFont, font.recs[i]);
|
||||||
}
|
}
|
||||||
else TRACELOG(LOG_WARNING, "FONT: [%s] Some characters data not correctly provided", fileName);
|
else TRACELOG(LOG_WARNING, "FONT: [%s] Some characters data not correctly provided", fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
UnloadImage(imFont);
|
UnloadImage(fullFont);
|
||||||
UnloadFileText(fileText);
|
UnloadFileText(fileText);
|
||||||
|
|
||||||
if (font.texture.id == 0)
|
if (font.texture.id == 0)
|
||||||
|
|
@ -2170,6 +2209,7 @@ static Font LoadBMFont(const char *fileName)
|
||||||
|
|
||||||
return font;
|
return font;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif // SUPPORT_MODULE_RTEXT
|
#endif // SUPPORT_MODULE_RTEXT
|
||||||
|
|
|
||||||
|
|
@ -213,14 +213,14 @@
|
||||||
#define STBIR_MALLOC(size,c) ((void)(c), RL_MALLOC(size))
|
#define STBIR_MALLOC(size,c) ((void)(c), RL_MALLOC(size))
|
||||||
#define STBIR_FREE(ptr,c) ((void)(c), RL_FREE(ptr))
|
#define STBIR_FREE(ptr,c) ((void)(c), RL_FREE(ptr))
|
||||||
#define STB_IMAGE_RESIZE_IMPLEMENTATION
|
#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)
|
#if defined(SUPPORT_FILEFORMAT_SVG)
|
||||||
#define NANOSVG_IMPLEMENTATION // Expands implementation
|
#define NANOSVG_IMPLEMENTATION // Expands implementation
|
||||||
#include "external/nanosvg.h"
|
#include "external/nanosvg.h"
|
||||||
|
|
||||||
#define NANOSVGRAST_IMPLEMENTATION
|
#define NANOSVGRAST_IMPLEMENTATION
|
||||||
#include "external/nanosvgrast.h"
|
#include "external/nanosvgrast.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
//----------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------
|
||||||
|
|
@ -1959,29 +1959,24 @@ void ImageBlurGaussian(Image *image, int blurSize) {
|
||||||
float avgG = 0.0f;
|
float avgG = 0.0f;
|
||||||
float avgB = 0.0f;
|
float avgB = 0.0f;
|
||||||
float avgAlpha = 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;
|
avgR += pixelsCopy1[row*image->width + i].x;
|
||||||
avgG += pixelsCopy1[row*image->width + i].y;
|
avgG += pixelsCopy1[row*image->width + i].y;
|
||||||
avgB += pixelsCopy1[row*image->width + i].z;
|
avgB += pixelsCopy1[row*image->width + i].z;
|
||||||
avgAlpha += pixelsCopy1[row*image->width + i].w;
|
avgAlpha += pixelsCopy1[row*image->width + i].w;
|
||||||
}
|
}
|
||||||
|
|
||||||
pixelsCopy2[row*image->width].x = avgR/convolutionSize;
|
for (int x = 0; x < image->width; x++)
|
||||||
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++)
|
|
||||||
{
|
{
|
||||||
if (x-blurSize >= 0)
|
if (x-blurSize-1 >= 0)
|
||||||
{
|
{
|
||||||
avgR -= pixelsCopy1[row*image->width + x-blurSize].x;
|
avgR -= pixelsCopy1[row*image->width + x-blurSize-1].x;
|
||||||
avgG -= pixelsCopy1[row*image->width + x-blurSize].y;
|
avgG -= pixelsCopy1[row*image->width + x-blurSize-1].y;
|
||||||
avgB -= pixelsCopy1[row*image->width + x-blurSize].z;
|
avgB -= pixelsCopy1[row*image->width + x-blurSize-1].z;
|
||||||
avgAlpha -= pixelsCopy1[row*image->width + x-blurSize].w;
|
avgAlpha -= pixelsCopy1[row*image->width + x-blurSize-1].w;
|
||||||
convolutionSize--;
|
convolutionSize--;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1999,7 +1994,7 @@ void ImageBlurGaussian(Image *image, int blurSize) {
|
||||||
pixelsCopy2[row*image->width + x].z = avgB/convolutionSize;
|
pixelsCopy2[row*image->width + x].z = avgB/convolutionSize;
|
||||||
pixelsCopy2[row*image->width + x].w = avgAlpha/convolutionSize;
|
pixelsCopy2[row*image->width + x].w = avgAlpha/convolutionSize;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Vertical motion blur
|
// Vertical motion blur
|
||||||
for (int col = 0; col < image->width; col++)
|
for (int col = 0; col < image->width; col++)
|
||||||
|
|
@ -2008,9 +2003,9 @@ void ImageBlurGaussian(Image *image, int blurSize) {
|
||||||
float avgG = 0.0f;
|
float avgG = 0.0f;
|
||||||
float avgB = 0.0f;
|
float avgB = 0.0f;
|
||||||
float avgAlpha = 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;
|
avgR += pixelsCopy2[i*image->width + col].x;
|
||||||
avgG += pixelsCopy2[i*image->width + col].y;
|
avgG += pixelsCopy2[i*image->width + col].y;
|
||||||
|
|
@ -2018,19 +2013,14 @@ void ImageBlurGaussian(Image *image, int blurSize) {
|
||||||
avgAlpha += pixelsCopy2[i*image->width + col].w;
|
avgAlpha += pixelsCopy2[i*image->width + col].w;
|
||||||
}
|
}
|
||||||
|
|
||||||
pixelsCopy1[col].x = (unsigned char) (avgR/convolutionSize);
|
for (int y = 0; y < image->height; y++)
|
||||||
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++)
|
|
||||||
{
|
{
|
||||||
if (y-blurSize >= 0)
|
if (y-blurSize-1 >= 0)
|
||||||
{
|
{
|
||||||
avgR -= pixelsCopy2[(y-blurSize)*image->width + col].x;
|
avgR -= pixelsCopy2[(y-blurSize-1)*image->width + col].x;
|
||||||
avgG -= pixelsCopy2[(y-blurSize)*image->width + col].y;
|
avgG -= pixelsCopy2[(y-blurSize-1)*image->width + col].y;
|
||||||
avgB -= pixelsCopy2[(y-blurSize)*image->width + col].z;
|
avgB -= pixelsCopy2[(y-blurSize-1)*image->width + col].z;
|
||||||
avgAlpha -= pixelsCopy2[(y-blurSize)*image->width + col].w;
|
avgAlpha -= pixelsCopy2[(y-blurSize-1)*image->width + col].w;
|
||||||
convolutionSize--;
|
convolutionSize--;
|
||||||
}
|
}
|
||||||
if (y+blurSize < image->height)
|
if (y+blurSize < image->height)
|
||||||
|
|
|
||||||