Compare commits

...

40 Commits

Author SHA1 Message Date
9f9191a9db Temp patch for text size bounds. Needs to be fixed properly.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 2m13s
2025-06-09 15:54:48 -05:00
2d536cd611 Merged several branches, fixed small error, updated dependency packages.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 5m14s
2025-06-06 13:02:29 -05:00
40412a300a Merge pull request 'Completed Shaders Task' (#47) from shaders_again into master
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 1m39s
Reviewed-on: #47
2025-06-06 13:44:47 -04:00
819539247e Merge pull request 'shader_preprocessor' (#43) from shader_preprocessor into shaders_again
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 26m34s
Reviewed-on: #43
2025-06-06 13:38:29 -04:00
ee90e7f95b Fix measure string.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 2m16s
2025-05-29 22:58:25 -04:00
ad3b451659 BatchFillOutlineRect
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 2m19s
2025-05-25 16:33:58 -04:00
74ab9d25db BatchFillRoundedRect
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 3m44s
2025-05-18 00:07:58 -04:00
a5424eb370 BatchFillCircle Instanced Rendering.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 2m51s
Added JGL::ClearScreen as-well to clear fbo 0 at any time.
2025-05-17 17:04:27 -04:00
6f99689add Performance optimization.
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Has been cancelled
2025-05-12 11:38:28 -04:00
7ea4b8696e cleanup & performance optimization.
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Has been cancelled
2025-05-11 13:49:07 -04:00
6d37cd93e3 BatchFillRect instanced rendering.
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Has been cancelled
during shader creation you can now also specify your attributes.
2025-05-09 16:41:29 -04:00
7875b777e5 Demonstrate GLSL custom preprocessor.
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Has been cancelled
2025-05-09 13:50:18 -05:00
ede11131fe Change gradient rect to accept 4x color4
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Has been cancelled
2025-05-06 11:55:01 -04:00
347b9cb278 Performance optimization
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Has been cancelled
2025-05-05 11:25:49 -04:00
1a2f7627d3 custom behavior per draw function
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Has been cancelled
Add support for doing custom behavior per draw function for JGL.

You must define uniform int J2D_RENDERING_ROUTINE in any shader to be used by JGL even if you're not doing per draw function behavior.
2025-05-04 23:19:49 -04:00
b3fb28be38 Package Maintenance - Update mcolor to 7.1
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 1m41s
2025-04-22 17:19:00 -04:00
d2497d64a2 Performance Optimization
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 2m35s
Draw points can accept multiple colors.

Draw points now uses vbos.

Allowed the user to set the usage hint for the VBO.

When changing data in a vbo, the data will be orphaned if the mode is not Fixed.
2025-04-20 04:30:29 -04:00
f7eff123be Fix alpha masking when using shader.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 5m11s
2025-04-18 12:00:51 -04:00
a1ca7ace77 Flip order of J2D::DrawString argument, so that we can default scale to 1, and add vector2 overload.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m59s
2025-04-17 13:57:03 -04:00
02370c6bfa work on the default shader.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 2m59s
2025-04-17 13:33:08 -04:00
2f13be9dd8 Merge branch 'shader_preprocessor' into shaders_again
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m56s
2025-04-16 01:44:27 -04:00
4c798ea76a shader with state stack.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 2m26s
2025-04-16 01:43:00 -04:00
af31b41782 Implement testing GLSL preprocessing and #include support.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 2m10s
2025-04-16 01:36:39 -04:00
4374b83464 Add Shader::OnCompilationErrorMessag
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 2m6s
2025-04-15 19:11:07 -05:00
147123b202 Working on the default shader
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 5m16s
( Need a system for accessing texture units & sampling etc )
2025-04-15 12:18:14 -04:00
2a2410e9bf Merge pull request 'Shader Implementation' (#42) from shaders_again into master
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 2m29s
Reviewed-on: #42
2025-04-15 01:21:42 -04:00
fdabbe866f Implement doxygen annotation for Shader class.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m55s
2025-04-14 22:12:28 -04:00
dac830fc7c Implement ineffectual shader for basis testing.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 6m44s
2025-04-14 21:45:42 -04:00
019b4aa5ae Implement Shader::UseDefault()
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Has been cancelled
2025-04-14 21:44:33 -04:00
3a293a2e0c Even Epicer
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m56s
2025-04-14 21:37:27 -04:00
73de143ec5 Epic Color Transition Fragment Shader
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Has been cancelled
2025-04-14 21:36:08 -04:00
5068b4660e Josh Attempts Shaders
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 3m24s
2025-04-14 21:28:06 -04:00
fd656cd543 Upgrade mcolor version.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 3m11s
2025-02-26 15:19:46 -05:00
291b3f3778 Fix the wireframe AABB not rendering correctly.
Some checks are pending
Run ReCI Build Test / Explore-Gitea-Actions (push) Waiting to run
2025-02-23 22:04:20 -05:00
4ee84d6442 Fix OpenGL 1280 on Windoze
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 2m2s
2025-02-19 21:06:38 -05:00
59df950e11 Add draw commands to support the texture atlas.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 2m33s
2025-02-18 14:10:59 -05:00
03a687179c Texture Atlas constructors. 2025-02-18 13:52:43 -05:00
ae84a68e11 Fix typo
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 2m27s
2025-02-13 21:49:32 -05:00
c5490cb321 Made a naming choice for will <3.
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Has been cancelled
2025-02-13 21:47:53 -05:00
04b7cc9544 Anisotropic filtering.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 2m19s
2025-02-13 03:18:23 -05:00
28 changed files with 1848 additions and 386 deletions

View File

@@ -17,7 +17,7 @@ include(cmake/CPM.cmake)
CPMAddPackage(
NAME mcolor
URL https://git.redacted.cc/maxine/mcolor/archive/Prerelease-5.zip
URL https://git.redacted.cc/maxine/mcolor/archive/Release-1.zip
)
CPMAddPackage(
@@ -27,17 +27,17 @@ CPMAddPackage(
CPMAddPackage(
NAME ReWindow
URL https://git.redacted.cc/Redacted/ReWindow/archive/Prerelease-32.zip
URL https://git.redacted.cc/Redacted/ReWindow/archive/Prerelease-34.zip
)
CPMAddPackage(
NAME GLAD
URL https://git.redacted.cc/Redacted/glad/archive/v2.1ext_fbo_depthtexture_shadow.zip
URL https://git.redacted.cc/Redacted/glad/archive/v2.1ext_fbo_depthtexture_shadow_anisotropic_instanced.zip
)
CPMAddPackage(
NAME jlog
URL https://git.redacted.cc/josh/jlog/Prerelease-16.zip
URL https://git.redacted.cc/josh/jlog/Prerelease-19.zip
)
if (WIN32)

View File

@@ -0,0 +1,30 @@
#ifndef include_shared
#define include_shared
#version 120
vec3 rgb2hsb( in vec3 c ){
vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
vec4 p = mix(vec4(c.bg, K.wz),
vec4(c.gb, K.xy),
step(c.b, c.g));
vec4 q = mix(vec4(p.xyw, c.r),
vec4(c.r, p.yzx),
step(p.x, c.r));
float d = q.x - min(q.w, q.y);
float e = 1.0e-10;
return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)),
d / (q.x + e),
q.x);
}
// Function from Iñigo Quiles
// https://www.shadertoy.com/view/MsS3Wc
vec3 hsb2rgb( in vec3 c ){
vec3 rgb = clamp(abs(mod(c.x*6.0+vec3(0.0,4.0,2.0),
6.0)-3.0)-1.0,
0.0,
1.0 );
rgb = rgb*rgb*(3.0-2.0*rgb);
return c.z * mix(vec3(1.0), rgb, c.y);
}
#endif

View File

@@ -1,5 +1,154 @@
#version 120
#define J2D_DrawPoint 1
#define J2D_DrawPoints 2
#define J2D_DrawLine 3
#define J2D_DrawLines 4
#define J2D_DrawDottedLine 5
#define J2D_DrawDashedLine 6
#define J2D_DrawGradientLine 7
#define J2D_OutlineRect 8
#define J2D_OutlineRoundedRect 9
#define J2D_OutlineChamferRect 10
#define J2D_FillRect 11
#define J2D_FillGradientRect 12
#define J2D_FillRoundedRect 13
#define J2D_FillChamferRect 14
#define J2D_DrawRenderTarget 15
#define J2D_DrawPartialRenderTarget 16
#define J2D_DrawSprite 17
#define J2D_DrawAlphaMaskSprite 18
#define J2D_DrawPartialSprite 19
#define J2D_DrawMirrorSprite 20
#define J2D_OutlineCircle 21
#define J2D_FillCircle 22
#define J2D_OutlineTriangle 23
#define J2D_FillTriangle 24
#define J2D_FillGradientTriangle 25
#define J2D_DrawCubicBezierCurve 26
#define J2D_OutlinePolygon 27
#define J2D_DrawString 28
#define J2D_DrawArc 29
uniform int JGL_RENDERING_ROUTINE;
uniform bool JGL_INSTANCED_RENDERING;
// The number of texture units that have been set.
uniform int TEXTURE_UNIT_SET_COUNT;
// The color manually set with glColor4f, glColor4ubv etc.
varying vec4 v_color;
// Texture unit 0 - 7 (8 - 31 will come later).
uniform sampler2D GL_TEXTURE0;
uniform sampler2D GL_TEXTURE1;
uniform sampler2D GL_TEXTURE2;
uniform sampler2D GL_TEXTURE3;
uniform sampler2D GL_TEXTURE4;
uniform sampler2D GL_TEXTURE5;
uniform sampler2D GL_TEXTURE6;
uniform sampler2D GL_TEXTURE7;
// Texture coordinates.
varying vec2 GL_TEXTURE0_COORD;
varying vec2 GL_TEXTURE1_COORD;
varying vec2 GL_TEXTURE2_COORD;
varying vec2 GL_TEXTURE3_COORD;
varying vec2 GL_TEXTURE4_COORD;
varying vec2 GL_TEXTURE5_COORD;
varying vec2 GL_TEXTURE6_COORD;
varying vec2 GL_TEXTURE7_COORD;
void DrawColorOnly() {
gl_FragColor = v_color;
}
void SampleTextureUnits() {
if (JGL_RENDERING_ROUTINE == J2D_DrawString)
gl_FragColor = vec4(v_color.rgb, v_color.a * texture2D(GL_TEXTURE0, GL_TEXTURE0_COORD).a);
// Draw sprite, partial sprite, mirror sprite, render target, partial render target.
else if (TEXTURE_UNIT_SET_COUNT == 1)
gl_FragColor = v_color * texture2D(GL_TEXTURE0, GL_TEXTURE0_COORD);
// Draw alpha masked sprite.
else if (TEXTURE_UNIT_SET_COUNT == 2) {
vec4 color_texture = texture2D(GL_TEXTURE0, GL_TEXTURE0_COORD);
float alpha_mask = texture2D(GL_TEXTURE1, GL_TEXTURE1_COORD).a;
gl_FragColor = vec4(v_color.rgb * color_texture.rgb, v_color.a * alpha_mask);
}
}
void Default() {
if (TEXTURE_UNIT_SET_COUNT == 0) {
DrawColorOnly(); return;
}
SampleTextureUnits();
}
void main() {
gl_FragColor = vec4(1, 1, 1, 1);
/* If you want behavior per JGL draw function, Or for specific ones. The order here matters because some JGL functions call others.
if (JGL_RENDERING_ROUTINE == J2D_DrawRenderTarget)
Default();
else if (JGL_RENDERING_ROUTINE == J2D_DrawPartialRenderTarget)
Default();
else if (JGL_RENDERING_ROUTINE == J2D_DrawAlphaMaskSprite)
Default();
else if (JGL_RENDERING_ROUTINE == J2D_DrawMirrorSprite)
Default();
else if (JGL_RENDERING_ROUTINE == J2D_DrawSprite)
Default();
else if (JGL_RENDERING_ROUTINE == J2D_DrawPartialSprite)
Default();
else if (JGL_RENDERING_ROUTINE == J2D_DrawPartialSprite)
Default();
else if (JGL_RENDERING_ROUTINE == J2D_DrawString)
Default();
else if (JGL_RENDERING_ROUTINE == J2D_DrawPoint)
Default();
else if (JGL_RENDERING_ROUTINE == J2D_DrawPoints)
Default();
else if (JGL_RENDERING_ROUTINE == J2D_DrawCubicBezierCurve)
Default();
else if (JGL_RENDERING_ROUTINE == J2D_DrawLine)
Default();
else if (JGL_RENDERING_ROUTINE == J2D_DrawLines)
Default();
else if (JGL_RENDERING_ROUTINE == J2D_DrawDottedLine)
Default();
else if (JGL_RENDERING_ROUTINE == J2D_DrawDashedLine)
Default();
else if (JGL_RENDERING_ROUTINE == J2D_DrawGradientLine)
Default();
else if (JGL_RENDERING_ROUTINE == J2D_OutlineRoundedRect)
Default();
else if (JGL_RENDERING_ROUTINE == J2D_FillChamferRect)
Default();
else if (JGL_RENDERING_ROUTINE == J2D_FillRoundedRect)
Default();
else if (JGL_RENDERING_ROUTINE == J2D_FillGradientRect)
Default();
else if (JGL_RENDERING_ROUTINE == J2D_OutlineRect)
Default();
else if (JGL_RENDERING_ROUTINE == J2D_FillRect)
Default();
else if (JGL_RENDERING_ROUTINE == J2D_OutlineCircle)
Default();
else if (JGL_RENDERING_ROUTINE == J2D_FillCircle)
Default();
else if (JGL_RENDERING_ROUTINE == J2D_OutlineTriangle)
Default();
else if (JGL_RENDERING_ROUTINE == J2D_FillTriangle)
Default();
else if (JGL_RENDERING_ROUTINE == J2D_FillGradientTriangle)
Default();
else if (JGL_RENDERING_ROUTINE == J2D_OutlinePolygon)
Default();
else if (JGL_RENDERING_ROUTINE == J2D_DrawArc)
Default();
else { Default(); }
*/
Default();
}

View File

@@ -1,7 +1,155 @@
#version 120
// TODO this will fail if we don't have the extension.
#extension GL_ARB_instanced_arrays : enable
attribute vec2 position;
#define J2D_DrawPoint 1
#define J2D_DrawPoints 2
#define J2D_DrawLine 3
#define J2D_DrawLines 4
#define J2D_DrawDottedLine 5
#define J2D_DrawDashedLine 6
#define J2D_DrawGradientLine 7
#define J2D_OutlineRect 8
#define J2D_OutlineRoundedRect 9
#define J2D_OutlineChamferRect 10
#define J2D_FillRect 11
#define J2D_FillGradientRect 12
#define J2D_FillRoundedRect 13
#define J2D_FillChamferRect 14
#define J2D_DrawRenderTarget 15
#define J2D_DrawPartialRenderTarget 16
#define J2D_DrawSprite 17
#define J2D_DrawAlphaMaskSprite 18
#define J2D_DrawPartialSprite 19
#define J2D_DrawMirrorSprite 20
#define J2D_OutlineCircle 21
#define J2D_FillCircle 22
#define J2D_OutlineTriangle 23
#define J2D_FillTriangle 24
#define J2D_FillGradientTriangle 25
#define J2D_DrawCubicBezierCurve 26
#define J2D_OutlinePolygon 27
#define J2D_DrawString 28
#define J2D_DrawArc 29
uniform int JGL_RENDERING_ROUTINE;
uniform bool JGL_INSTANCED_RENDERING;
// The color manually set with glColor4f, glColor4ubv etc etc.
varying vec4 v_color;
// Local space vertices for instanced rendering.
attribute vec2 a_vertex_position; // 0
// The position at which to render the instance.
attribute vec2 a_instance_position; // 1
// The scale of the instance.
attribute vec2 a_instance_size; // 2
// The color of the instance.
attribute vec4 a_instance_color; // 3
// The texture coordinates in each texture unit.
varying vec2 GL_TEXTURE0_COORD;
varying vec2 GL_TEXTURE1_COORD;
varying vec2 GL_TEXTURE2_COORD;
varying vec2 GL_TEXTURE3_COORD;
varying vec2 GL_TEXTURE4_COORD;
varying vec2 GL_TEXTURE5_COORD;
varying vec2 GL_TEXTURE6_COORD;
varying vec2 GL_TEXTURE7_COORD;
vec4 Default() {
v_color = gl_Color;
return gl_ModelViewProjectionMatrix * gl_Vertex;
}
vec4 DefaultInstanced() {
v_color = a_instance_color;
vec2 scaled = a_vertex_position * a_instance_size;
vec2 world_pos = scaled + a_instance_position;
return gl_ModelViewProjectionMatrix * vec4(world_pos, 0.0, 1.0);
}
#include "shared.glsl"
void main() {
gl_Position = vec4(position.x, position.y, 1.0, 1.0);
GL_TEXTURE0_COORD = gl_MultiTexCoord0.xy;
GL_TEXTURE1_COORD = gl_MultiTexCoord1.xy;
GL_TEXTURE2_COORD = gl_MultiTexCoord2.xy;
GL_TEXTURE3_COORD = gl_MultiTexCoord3.xy;
GL_TEXTURE4_COORD = gl_MultiTexCoord4.xy;
GL_TEXTURE5_COORD = gl_MultiTexCoord5.xy;
GL_TEXTURE6_COORD = gl_MultiTexCoord6.xy;
GL_TEXTURE7_COORD = gl_MultiTexCoord7.xy;
/* If you want behavior per JGL draw function, Or for specific ones. The order here matters because some JGL functions call others.
if (JGL_RENDERING_ROUTINE == J2D_DrawRenderTarget)
gl_Position = Default();
else if (JGL_RENDERING_ROUTINE == J2D_DrawPartialRenderTarget)
gl_Position = Default();
else if (JGL_RENDERING_ROUTINE == J2D_DrawAlphaMaskSprite)
gl_Position = Default();
else if (JGL_RENDERING_ROUTINE == J2D_DrawMirrorSprite)
gl_Position = Default();
else if (JGL_RENDERING_ROUTINE == J2D_DrawSprite)
gl_Position = Default();
else if (JGL_RENDERING_ROUTINE == J2D_DrawPartialSprite)
gl_Position = Default();
else if (JGL_RENDERING_ROUTINE == J2D_DrawPartialSprite)
gl_Position = Default();
else if (JGL_RENDERING_ROUTINE == J2D_DrawString)
gl_Position = Default();
else if (JGL_RENDERING_ROUTINE == J2D_DrawPoint)
gl_Position = Default();
else if (JGL_RENDERING_ROUTINE == J2D_DrawPoints)
gl_Position = Default();
else if (JGL_RENDERING_ROUTINE == J2D_DrawCubicBezierCurve)
gl_Position = Default();
else if (JGL_RENDERING_ROUTINE == J2D_DrawLine)
gl_Position = Default();
else if (JGL_RENDERING_ROUTINE == J2D_DrawLines)
gl_Position = Default();
else if (JGL_RENDERING_ROUTINE == J2D_DrawDottedLine)
gl_Position = Default();
else if (JGL_RENDERING_ROUTINE == J2D_DrawDashedLine)
gl_Position = Default();
else if (JGL_RENDERING_ROUTINE == J2D_DrawGradientLine)
gl_Position = Default();
else if (JGL_RENDERING_ROUTINE == J2D_OutlineRoundedRect)
gl_Position = Default();
else if (JGL_RENDERING_ROUTINE == J2D_FillChamferRect)
gl_Position = Default();
else if (JGL_RENDERING_ROUTINE == J2D_FillRoundedRect)
gl_Position = Default();
else if (JGL_RENDERING_ROUTINE == J2D_FillGradientRect)
gl_Position = Default();
else if (JGL_RENDERING_ROUTINE == J2D_OutlineRect)
gl_Position = Default();
else if (JGL_RENDERING_ROUTINE == J2D_FillRect)
gl_Position = Default();
else if (JGL_RENDERING_ROUTINE == J2D_OutlineCircle)
gl_Position = Default();
else if (JGL_RENDERING_ROUTINE == J2D_FillCircle)
gl_Position = Default();
else if (JGL_RENDERING_ROUTINE == J2D_OutlineTriangle)
gl_Position = Default();
else if (JGL_RENDERING_ROUTINE == J2D_FillTriangle)
gl_Position = Default();
else if (JGL_RENDERING_ROUTINE == J2D_FillGradientTriangle)
gl_Position = Default();
else if (JGL_RENDERING_ROUTINE == J2D_OutlinePolygon)
gl_Position = Default();
else if (JGL_RENDERING_ROUTINE == J2D_DrawArc)
gl_Position = Default();
else { gl_Position = Default(); }
*/
if (JGL_INSTANCED_RENDERING)
gl_Position = DefaultInstanced();
else
gl_Position = Default();
}

View File

@@ -16,6 +16,7 @@
#include <Color4.hpp>
#include <JGL/types/Texture.h>
#include <JGL/types/Enums.h>
#include <JGL/types/Instance.h>
#include <JGL/types/FontCache.h>
#include <JGL/types/Font.h>
#include <JGL/types/RenderTarget.h>
@@ -29,6 +30,8 @@
#include <JGL/types/Font.h>
#include <JGL/types/VRamList.h>
#include <JGL/types/VertexArray.h>
#include <JGL/types/TextureAtlas.h>
#include <JGL/types/Shader.h>
// Fonts that are included by default.
namespace JGL::Fonts {
@@ -45,6 +48,8 @@ namespace JGL::ShapeCache {
// Facing straight out.
inline VRamList* j2d_default_normal_data = nullptr;
inline VRamList* square_origin_topleft_vertex_data = nullptr;
inline VRamList* draw_points_positions = nullptr;
inline VRamList* draw_points_colors = nullptr;
void Init();
}
@@ -55,6 +60,8 @@ namespace JGL {
[[nodiscard]] bool Init(const Vector2i& window_size, float fovY, float far_plane);
void Update(const Vector2i& window_size);
/// Clear the default framebuffer for the OpenGL context (0).
void ClearScreen(const Color4& clear_color);
inline void PurgeFontCache() { JGL::fontCache.purgeCache(); }
@@ -72,7 +79,7 @@ namespace JGL::J2D {
/// This keeps our code from, say, clobbering the OpenGL rendering context driving 3D content in between our calls.
/// @param render_target
/// @param clear_buffers
void Begin(RenderTarget* render_target = nullptr, bool clear_buffers = false);
void Begin(RenderTarget* render_target = nullptr, Shader* shader = nullptr, bool clear_buffers = false);
/// Closes a 2-D rendering context with the underlying graphics system (In this case& by default OpenGL).
/// @see Begin().
@@ -94,7 +101,14 @@ namespace JGL::J2D {
/// Plots a series of pixel-points on the screen, in a batch.
/// @note This is more performant for multiple points than plotting them individually.
/// @param color A 3-or-4 channel color value. @see class Color3, class Color4
/// @param colors A set of 4 channel color values. @see class Color4.
/// @param points A set of x,y points to render.
/// @param radius The size of the point to plot. By default, a single pixel.
void DrawPoints(const Color4* color, const Vector2* points, int point_count, float radius = 1.f);
/// Plots a series of pixel-points on the screen, in a batch.
/// @note This is more performant for multiple points than plotting them individually.
/// @param color A 3-or-4 channel color value. @see class Color3, class Color4
/// @param points A set of x,y points to render.
/// @param radius The size of the point to plot. By default, a single pixel.
void DrawPoints(const Color4& color, const Vector2* points, int point_count, float radius = 1.f);
@@ -151,6 +165,8 @@ namespace JGL::J2D {
/// @param thickness The width at which to render the lines.
void OutlineRect(const Color4& color, const Vector2& pos, const Vector2& size, float thickness = 1);
void BatchOutlineRect(const Instance2D* instances, float thickness, const size_t& instance_count);
/// Draws an outline of a rectangle with rounded corners onto the screen.
/// @param color A 3-or-4 channel color value. @see class Color3, class Color4
/// @param pos The top-left corner of the rectangle.
@@ -175,12 +191,13 @@ namespace JGL::J2D {
void FillRect(const Color4& color, const Vector2& pos, const Vector2& size);
/// Draws a filled rectangle where the color transitions across it.
/// @param color1 A 3-or-4 channel color value. @see class Color3, class Color4
/// @param color2 A 3-or-4 channel color value. @see class Color3, class Color4
/// @param gradient See enum Direction
/// @param top_left_color A 3-or-4 channel color value. @see class Color3, class Color4
/// @param bottom_left_color A 3-or-4 channel color value. @see class Color3, class Color4
/// @param bottom_right_color A 3-or-4 channel color value. @see class Color3, class Color4
/// @param top_right_color A 3-or-4 channel color value. @see class Color3, class Color4
/// @param pos The top-left corner of the rectangle.
/// @param size The width and height of the rectangle.
void FillGradientRect(const Color4& color1, const Color4& color2, const Direction& gradient, const Vector2& pos, const Vector2& size);
void FillGradientRect(const Color4& top_left_color, const Color4& bottom_left_color, const Color4& bottom_right_color, const Color4& top_right_color, const Vector2& pos, const Vector2& size);
/// Draws a filled rectangle with rounded corners on the screen.
/// @param color A 3-or-4 channel color value. @see class Color3, class Color4
@@ -190,6 +207,8 @@ namespace JGL::J2D {
/// @param subdivisions The amount of sub-divisions (and calculations) to be performed per-arc rounding corner.
void FillRoundedRect(const Color4& color, const Vector2& pos, const Vector2& size, float radius = 5, unsigned int subdivisions = 8);
void BatchFillRoundedRect(const Color4* colors, const Vector2* positions, const Vector2* sizes, float radius, unsigned int subdivisions, const size_t& count);
/// Draws a filled rectangle with chamfered (beveled) corners on the screen.
/// @param color A 3-or-4 channel color value. @see class Color3, class Color4
/// @param pos The top-left corner of the rectangle.
@@ -230,6 +249,10 @@ namespace JGL::J2D {
void DrawSprite(const RenderTarget* render_target, const Vector2& position, float rad_rotation = 0, const Vector2& origin = Vector2(0 , 0),
const Vector2& scale = Vector2(1, 1), const Color4& color = Colors::White, Direction inversion = Direction::None);
void DrawSprite(const TextureAtlas* texture_atlas, const AtlasRegion& atlas_region, const Vector2& position, float rad_rotation, const Vector2& origin = Vector2(0, 0),
const Vector2& scale = Vector2(1, 1), const Color4& color = Colors::White);
void DrawSprite(const TextureAtlas& texture_atlas, const AtlasRegion& atlas_region, const Vector2& position, float rad_rotation, const Vector2& origin = Vector2(0, 0),
const Vector2& scale = Vector2(1, 1), const Color4& color = Colors::White);
/// Draws a sprite to the screen by passing a G̶L̶u̶i̶n̶t̶ JGL Texture that represents a handle to a loaded texture.
/// @param texture A texture instance to be displayed.
/// @param position The point at which to draw the sprite (from the top-left down).
@@ -325,7 +348,7 @@ namespace JGL::J2D {
/// @param color A 3-or-4 channel color value. @see class Color3, class Color4
/// @param tri The triangle defined by its vertices (A, B, and C).
void FillTriangle(const Color4& color, const Triangle2D& tri);
void FIllTriangle(const Color4& color, const Vector2& triA, const Vector2& triB, const Vector2& triC);
void FillTriangle(const Color4& color, const Vector2& triA, const Vector2& triB, const Vector2& triC);
/// Fills a triangle defined by the provided vertices with a gradient that transitions smoothly between the three specified colors at each corner.
/// @param a_color The color at vertex A of the triangle.
@@ -362,8 +385,9 @@ namespace JGL::J2D {
/// @param scale The value (in both axes) to scale the text by. Defaults to {1,1}.
/// @param size The point-size at which to render the font out. Re-using the same point-size allows efficient glyph caching.
/// @param font The font to use for rendering. @see Font.
void DrawString(const Color4& color, const std::string& text, float x, float y, float scale, u32 size, const Font& font = Fonts::Jupiteroid);
void DrawString(const Color4& color, const std::string& text, float x, float y, u32 size, float scale = 1.f, const Font& font = Fonts::Jupiteroid);
void DrawString(const Color4& color, const std::string& text, const Vector2& pos, u32 size, float scale = 1.f, const Font& font = Fonts::Jupiteroid);
/// Draws an Arc (section of a circle) to the screen.
/// @param color A 3-or-4 channel color value. @see class Color3, class Color4
@@ -382,9 +406,8 @@ namespace JGL::J2D {
void OutlineEllipse(const Color4& color, const Vector2& position, float radius_x, float radius_y, float thickness = 1, int subdivisions = 8);
void FillEllipse(const Color4& color, const Vector2& position, float radius_x, float radius_y, int subdivisions = 8);
void BatchFillRect(const Color4* colors, const Vector2* positions, const Vector2* sizes, const size_t& rect_count);
void BatchFillCircle(const Color4 *colors, const Vector2* positions, float* radii, unsigned int subdivisions, const size_t& circle_count);
void BatchFillRect(const Instance2D* instances, const size_t& instance_count);
void BatchFillCircle(const Instance2D* instances, float subdivisions, const size_t& instance_count);
}
/// Drawing functions for 3D objects.

View File

@@ -1,6 +1,9 @@
#pragma once
namespace JGL {
#define stringify(name) # name
enum class Direction : u8 {
None = 0,
Vertical = 1,
@@ -18,36 +21,45 @@ namespace JGL {
}
static std::string to_string(const JGL::Direction& direction) {
switch (direction) {
case JGL::Direction::None:
return "None";
case JGL::Direction::Vertical:
return "Vertical";
case JGL::Direction::Horizontal:
return "Horizontal";
case JGL::Direction::Diagonal_NWSE:
return "Diagonal_NWSE";
case JGL::Direction::Diagonal_SWNE:
return "Diagonal_SWNE";
default:
return "Unknown";
}
return stringify(direction);
}
enum class MSAA_SAMPLE_RATE : u8 {
MSAA_NONE = 0,
// TODO come up with a cleaner way to type out sample-rates before release.
// :shrug:, I'm not very creative. - Redacted.
/*enum class MSAA_SAMPLE_RATE : u8 {
NONE = 0,
MSAA_2X = 1,
MSAA_4X = 2,
MSAA_8X = 3
};
enum class ANISOTROPY_SAMPLE_RATE : u8 {
NONE = 0,
ANISOTROPY_2X = 2,
ANISOTROPY_4X = 4,
ANISOTROPY_8X = 8,
ANISOTROPY_16X = 16
};*/
/// This enumeration is used by both MSAA and Anisotropic Filtering.
/// However, higher rates may not be supported by one, or the other.
/// This additionally depends on what your graphics supports.
enum class SampleRate : u8 {
NONE = 0, X0 = 0,
X2 = 2,
X4 = 4,
X8 = 8,
X16 = 16,
};
enum class FilteringMode : u8 {
NEAREST = 0, // Fastest for 2D, Sometimes causes graphical issues.
BILINEAR = 1, // Fast and pretty, The best for 2D.
MIPMAP_NEAREST = 2, // Nearest with mipmaps. The fastest for 3D, Sometimes causes graphical issues. Uses more vram.
// Mipmapping uses approx 30% more v-ram.
MIPMAP_NEAREST = 2, // Nearest with mipmaps. The fastest for 3D, Sometimes causes graphical issues.
MIPMAP_BILINEAR = 3, // Bilinear with mipmaps, Fast and pretty. Uses more vram.
MIPMAP_TRILINEAR = 4 // The prettiest. Still decent speed. Uses more vram.
MIPMAP_TRILINEAR = 4 // Prettier, But still looks weird at very steep angles.
};
enum class WrappingMode : u8 {
@@ -57,34 +69,13 @@ namespace JGL {
CLAMP_TO_BORDER = 3 // Effectively the same as clamp_to_edge
};
enum class ColorFormat : bool { RGB = false, RGBA = true };
static std::string to_string(const JGL::MSAA_SAMPLE_RATE& sample_rate) {
switch (sample_rate) {
case MSAA_SAMPLE_RATE::MSAA_NONE:
return "No MSAA";
case MSAA_SAMPLE_RATE::MSAA_2X:
return "MSAA 2x";
case MSAA_SAMPLE_RATE::MSAA_4X:
return "MSAA 4x";
case MSAA_SAMPLE_RATE::MSAA_8X:
return "MSAA 8x";
default:
return "Unknown";
}
static std::string to_string(const JGL::SampleRate& sample_rate) {
return stringify(sample_rate);
}
static int to_int(const JGL::MSAA_SAMPLE_RATE& sample_rate) {
switch (sample_rate) {
case MSAA_SAMPLE_RATE::MSAA_NONE:
return 0;
case MSAA_SAMPLE_RATE::MSAA_2X:
return 2;
case MSAA_SAMPLE_RATE::MSAA_4X:
return 4;
case MSAA_SAMPLE_RATE::MSAA_8X:
return 8;
default:
return 0;
}
static int to_int(const JGL::SampleRate& sample_rate) {
return (int)sample_rate;
}
}

View File

@@ -22,11 +22,11 @@ private:
std::array<GLfloat, 12> texcoords;
public:
int x2offset = 0, y2offset = 0, w = 0, h = 0;
float advanceX = 0, advanceY = 0;
float advanceX = 0, advanceY = 0, ascent = 0, descent = 0;
//CachedGlyph(GLuint texture_id, char c);
CachedGlyph(char c, std::array<GLfloat, 12> texcoords, float x2o, float y2o, float w, float h, float advX, float advY);
char getCharacter() const;
CachedGlyph(char c, std::array<GLfloat, 12> texcoords, float x2o, float y2o, float w, float h, float advX, float advY, float asc, float desc);
[[nodiscard]] char getCharacter() const;
[[nodiscard]] std::array<GLfloat, 12> getTexCoords() const;
};

View File

@@ -0,0 +1,49 @@
#pragma once
#include <Colors.hpp>
#include <J3ML/LinearAlgebra/Vector2.hpp>
#include <J3ML/LinearAlgebra/Matrix4x4.hpp>
namespace JGL {
struct Instance2D;
struct Instance3D;
}
/// Information provided to JGL for each instance of an object you're rendering in a batch.
/// @note This is non-negotiable. This information is interleaved in *the same buffer* in v-ram for performance reasons - Redacted.
struct JGL::Instance2D {
public:
Color4 color;
Vector2 position;
Vector2 size;
Vector2 scale;
float rotation;
public:
/// @param position The position of the instance in world space.
/// @param size The size of the instance.
/// @param scale A multiplier to be applied to the size.
/// @param rotation The rotation in radians.
/// @param color A 3-or-4 channel color value. @see class Color3, class Color4.
/// @note If the thing you're drawing is textured you probably want Colors::White
Instance2D(const Color4& color, const Vector2& position, const Vector2& size, const Vector2& scale = Vector2::One, float rotation = 0.0f) :
color(color), position(position), size(size), scale(scale), rotation(rotation) {};
Instance2D() = default;
~Instance2D() = default;
};
struct JGL::Instance3D {
public:
Matrix4x4 instance_matrix;
Color4 color;
public:
/// @param instance_matrix A matrix containing rotation matrix, position, and scale.
/// @param color A 3-or-4 channel color value. @see class Color3, class Color4.
/// @note If the thing you're drawing is textured you probably want Colors::White
Instance3D(const Matrix4x4& instance_matrix, const Color4& color) : instance_matrix(instance_matrix), color(color) {};
Instance3D() = default;
~Instance3D() = default;
};

View File

@@ -12,6 +12,8 @@ namespace JGL {
}
using J3ML::LinearAlgebra::Vector2i;
/// A
class JGL::RenderTarget {
private:
Color4 clear_color{0,0,0,0};
@@ -21,28 +23,28 @@ private:
GLuint framebuffer_object = 0;
GLuint depth_buffer = 0;
const Texture* texture = nullptr;
MSAA_SAMPLE_RATE msaa_sample_rate = MSAA_SAMPLE_RATE::MSAA_NONE;
SampleRate msaa_sample_rate = SampleRate::NONE;
GLuint msaa_framebuffer_object = 0;
GLuint msaa_depth_buffer = 0;
GLuint msaa_render_buffer = 0;
void Erase();
public:
/// @returns The Render Target currently in use by OpenGL.
/// @note Zero is the screen.
/// @returns A handle to the RenderTarget currently in use by OpenGL.
/// @note zero is returned when no RenderTarget is in use.
static GLuint GetActiveGLFramebufferHandle();
/// Changes the Render Target that OpenGL will draw on.
/// @param render_target The new Render Target for OpenGL to draw on.
/// Changes the RenderTarget that OpenGL will draw on.
/// @param render_target The new RenderTarget for OpenGL to draw on.
static void SetActiveGLRenderTarget(const RenderTarget& render_target);
/// Changes the size of the renderable area of this Render Target.
/// Changes the size of the render-able area of this RenderTarget.
/// @param new_size new width & height in pixels.
/// @note The data stored in this Render Target will be lost.
void Resize(const Vector2i& new_size);
/// Sets the MSAA mode for this Render Target.
/// @returns false if the mode isn't available, true for success.
[[nodiscard]] bool SetMSAAEnabled(MSAA_SAMPLE_RATE sample_rate);
[[nodiscard]] bool SetMSAAEnabled(SampleRate sample_rate);
/// If you're using MSAA and not using J2D || J3D Begin & End you must do this.
void MSAABlit() const;
@@ -71,7 +73,7 @@ public:
[[nodiscard]] Vector2i GetDimensions() const;
/// @returns The currently selected MSAA Sample Rate.
[[nodiscard]] MSAA_SAMPLE_RATE GetMSAASampleRate() const;
[[nodiscard]] SampleRate GetMSAASampleRate() const;
/// @returns Whether or not this Render Target is using MSAA.
[[nodiscard]] bool MSAAEnabled() const;
@@ -96,7 +98,7 @@ public:
/// @note The CPU thread this is called from & the GPU cannot do anything while this takes place. It's very slow.
[[nodiscard]] std::vector<GLfloat> GetPixels() const;
[[nodiscard]] static Vector2i MaximumSize();
[[nodiscard]] static Vector2i MaxSize();
public:
/// Create a Render Target from a Render Target that already exists.
/** @note Render Targets that are copies of another will copy the Texture.
@@ -116,7 +118,7 @@ public:
/// @param use_depth Whether or not this Render Target will have depth information.
/// @param sample_rate The MSAA sample rate this Render Target will use.
explicit RenderTarget(const Vector2i& size, const Color4& clear_color = Colors::Transparent, bool use_depth = false,
MSAA_SAMPLE_RATE sample_rate = MSAA_SAMPLE_RATE::MSAA_NONE, FilteringMode filteirng_mode = FilteringMode::NEAREST);
SampleRate sample_rate = SampleRate::NONE, FilteringMode filteirng_mode = FilteringMode::NEAREST);
/// Deletes this Render Target.
/** @note If this Render Target was made with a Texture that already existed

121
include/JGL/types/Shader.h Normal file
View File

@@ -0,0 +1,121 @@
#pragma once
#include <filesystem>
#include <J3ML/LinearAlgebra.hpp>
#include <fstream>
#include <iostream>
#include "glad/glad.h"
#include <Event.h>
// LearnOpenGL::Shader
// OpenGL Shader Class Wrapper
// Updated by dawsh
// https://learnopengl.com/code_viewer_gh.php?code=includes/learnopengl/shader.h
namespace JGL {
using namespace J3ML::LinearAlgebra;
class Shader;
}
/// TODO: Eventually support Hot-reloading shaders like in shadertoy. Should help tremendously with debugging.
class JGL::Shader {
public:
static inline Event<std::string, std::string> OnCompilationErrorMessage;
static bool HasFile(const std::filesystem::path& path)
{
return std::filesystem::exists(path);
}
static std::string ReadFile(const std::filesystem::path& path);
/// The default constructor does not initialize any member values.
Shader() = default;
/// Creates a shader by compiling a vertex and fragment program from the sources in the respective filesystem paths.
Shader(const std::filesystem::path& vertex_source_path, const std::filesystem::path& fragment_source_path, const std::vector<std::pair<std::string, GLint>>& attribute_bindings = {});
/// Creates a shader by compiling a vertex and fragment program from the respective GLSL source-strings.
Shader(const std::string& vertex_code, const std::string& fragment_code, const std::vector<std::pair<std::string, GLint>>& attribute_bindings = {});
/// @return True if the shader program successfully loaded and compiled.
[[nodiscard]] bool Loaded() const;
/// @return The integer handle that OpenGL links to this shader program.
[[nodiscard]] unsigned int Handle() const;
/// Enable this shader. All rendering performed thereafter will be affected by this shader code.
/// @see UseDefault.
// TODO: Implement for hot-reloading.
void Reload();
// TODO: Implement for hot-reloading.
void Unload();
/// @return The Uniform variable linked to the specified name.
/// A Uniform is global a variable that is unique per shader program object, and can be accessed from any shader at any stage in the shader program.
GLint Uniform(const std::string& name) const;
/// @return The Attribute variable linked to the specified name.
/// Attributes differ from Uniforms in that their value is different for each instance of the shader-program as it is running.
[[nodiscard]] GLint Attribute(const std::string& name) const;
/// Sets a `uniform bool name = value` in the shader program.
void SetBool (const std::string& name, bool value) const;
/// Sets a `uniform int name = value` in the shader program.
void SetInt (const std::string& name, int value) const;
/// Sets a `uniform float name = value` in the shader program.
void SetFloat(const std::string& name, float value) const;
/// Sets a `uniform vec2 name = value` in the shader program.
/// @note GLSL has builtin vec2, while we implement a Vector2 type. Please be aware there may be differences in implementation between them!
/// @see class J3ML::LinearAlgebra::Vector2.
void SetVector2(const std::string& name, const Vector2& value) const;
/// Sets a `uniform vec2 name = value` in the shader program.
/// @note GLSL has builtin vec2, while we implement a Vector2 type. Please be aware there may be differences in implementation between them!
/// @see class J3ML::LinearAlgebra::Vector2.
void SetVector2(const std::string& name, float x, float y) const;
/// Sets a `uniform vec3 name = value` in the shader program.
/// @note GLSL has builtin vec3, while we implement a Vector3 type. Please be aware there may be differences in implementation between them!
/// @see class J3ML::LinearAlgebra::Vector3.
void SetVector3(const std::string& name, const Vector3& value) const;
/// Sets a `uniform vec3 name = value` in the shader program.
/// @note GLSL has builtin vec3, while we implement a Vector3 type. Please be aware there may be differences in implementation between them!
/// @see class J3ML::LinearAlgebra::Vector3.
void SetVector3(const std::string& name, float x, float y, float z) const;
/// Sets a `uniform vec4 name = value` in the shader program.
/// @note GLSL has builtin vec4, while we implement aVector4 type. Please be aware there may be differences in implementation between them!
/// @see class J3ML::LinearAlgebra::Vector4.
void SetVector4(const std::string& name, const Vector4& value) const;
/// Sets a `uniform vec4 name = value` in the shader program.
/// @note GLSL has builtin vec4, while we implement a Vector4 type. Please be aware there may be differences in implementation between them!
/// @see class J3ML::LinearAlgebra::Vector4.
void SetVector4(const std::string& name, float x, float y, float z, float w) const;
/// Sets a `uniform mat2 name = value` in the shader program.
/// @note GLSL has builtin mat2, while we implement a Matrix2x2 type. Please be aware there may be differences in implementation between them!
/// @see class J3ML::LinearAlgebra::Matrix2x2
void SetMatrix2x2(const std::string& name, const Matrix2x2& value) const;
/// Sets a `uniform mat3 name = value` in the shader program.
/// @note GLSL has builtin mat3, while we implement a Matrix3x3 type. Please be aware there may be differences in implementation between them!
/// @see class J3ML::LinearAlgebra::Matrix3x3
void SetMatrix3x3(const std::string& name, const Matrix3x3& value) const;
/// Sets a `uniform mat4 name = value` in the shader program.
/// @note GLSL has builtin mat4, while we implement a Matrix4x4 type. Please be aware there may be differences in implementation between them!
/// @see class J3ML::LinearAlgebra::Matrix4x4
void SetMatrix4x4(const std::string& name, const Matrix4x4& value) const;
// TODO: Implement Uniform-Setters for GLSL types that do not have a J3ML corollary (dmat3x4, dvec4, etc).
protected:
private:
unsigned int id = 0;
std::string vertexPath;
std::string vertexSource;
std::string fragmentSource;
std::string fragmentPath;
mutable std::unordered_map<std::string, GLint> uniform_location_cache;
static void checkCompileErrors(GLuint shader, const std::string& type);
};

View File

@@ -20,6 +20,7 @@ protected:
ColorFormat format = ColorFormat::RGBA;
FilteringMode filtering_mode;
WrappingMode wrapping_mode;
SampleRate anisotropy;
void load(const unsigned char* pixels);
std::vector<unsigned char> png(const std::filesystem::path& file);
std::vector<unsigned char> bmp(const std::filesystem::path& file);
@@ -33,6 +34,8 @@ public:
[[nodiscard]] FilteringMode GetFilteringMode() const;
/// @returns The way this texture behaves when used on geometry of different sizes.
[[nodiscard]] WrappingMode GetWrappingMode() const;
/// @returns The current level of anisotropic filtering for this texture.
[[nodiscard]] SampleRate GetAnisotropySampleRate() const;
/// @returns The orientation of this texture in v-ram.
/// @note true is right-side-up because OpenGL defaults to upside-down.
[[nodiscard]] bool Inverted() const;
@@ -40,23 +43,27 @@ public:
/// @returns The raw pixels this texture is made up of.
/// @note This will read-back from the GPU. Slow.
[[nodiscard]] std::vector<Color4> GetPixelData() const;
/// @returns The biggest size for a texture on this system.
/// @note on modern systems this is *usually* ridiculous.
[[nodiscard]] static Vector2i MaximumSize();
public:
/// Load a texture from a file,
explicit Texture(const std::filesystem::path& file, FilteringMode filtering_mode = FilteringMode::BILINEAR, WrappingMode wrapping_mode = WrappingMode::CLAMP_TO_EDGE, bool invert_y = true);
explicit Texture(const std::filesystem::path& file, FilteringMode filtering_mode = FilteringMode::BILINEAR,
SampleRate anisotropy = SampleRate::NONE, WrappingMode wrapping_mode = WrappingMode::CLAMP_TO_EDGE, bool invert_y = true);
/// Load a texture from raw pixels.
Texture(const Color4* pixels, const Vector2i& size, const ColorFormat& format, FilteringMode filtering_mode, WrappingMode wrapping_mode);
Texture(const Color4* pixels, const Vector2i& size, FilteringMode filtering_mode = FilteringMode::BILINEAR,
SampleRate anisotropy = SampleRate::NONE, WrappingMode wrapping_mode = WrappingMode::CLAMP_TO_EDGE);
/// Load a texture from raw pixels.
Texture(const Color3* pixels, const Vector2i& size, const ColorFormat& format, FilteringMode filtering_mode, WrappingMode wrapping_mode);
/// Construct a Texture Atlas from many different textures.
/// @note THIS IS UNFINISHED.
Texture(const Texture* textures, const size_t& texture_count);
Texture(const Color3* pixels, const Vector2i& size, FilteringMode filtering_mode = FilteringMode::BILINEAR,
SampleRate anisotropy = SampleRate::NONE, WrappingMode wrapping_mode = WrappingMode::CLAMP_TO_EDGE);
/// Initialize a texture filled with trash data.
/// @see RenderTarget
explicit Texture(const Vector2i& size, FilteringMode filtering_mode = FilteringMode::NEAREST);
Texture(const Texture& rhs);
Texture() = default;
~Texture();
public:
/// @returns True if this system supports anisotropy.
// TODO add a similar mechanism for MSAA so the extension isn't required.
static enum SampleRate MaxAnisotropySampleRate();
/// @returns The biggest size for a texture on this system.
/// @note on modern systems this is *usually* ridiculous.
[[nodiscard]] static Vector2i MaxSize();
};

View File

@@ -0,0 +1,35 @@
#pragma once
#include <JGL/types/Texture.h>
namespace JGL {
struct AtlasRegion {
Vector2i position = { 0, 0 };
Vector2i size = { 0, 0 };
};
class TextureAtlas;
}
// A texture which actually contains multiple such that you can pass it in to draw sprite or draw partial sprite.
// It also makes for more efficient sprite batch because you don't have to swap textures between.
class JGL::TextureAtlas : public Texture {
protected:
std::vector<AtlasRegion> regions;
public:
unsigned int RegionCount() { return regions.size(); }
[[nodiscard]] AtlasRegion GetRegion(unsigned int region) const;
public:
/// Create a texture atlas from multiple separate images.
/// @param pixels The array(s) of pixels.
/// @param sizes The size of each image.
/// @param texture_count The number of textures this atlas will contain.
TextureAtlas(const Color4** pixels, const Vector2i** sizes, unsigned int texture_count, FilteringMode filtering_mode = FilteringMode::NEAREST);
/// Create a texture atlas from a single image
/// @param pixels The array of pixels.
/// @param size The size of the image.
/// @param regions The individual regions that will make up your atlas.
/// @param region_count The number of regions there are.
TextureAtlas(const Color4* pixels, const Vector2i& size, AtlasRegion** regions, unsigned int region_count, FilteringMode filtering_mode = FilteringMode::NEAREST, SampleRate anisotropy = SampleRate::NONE);
~TextureAtlas() = default;
};

View File

@@ -5,52 +5,68 @@
#include <J3ML/LinearAlgebra/Vector2i.hpp>
#include <J3ML/LinearAlgebra/Vector3.hpp>
#include <J3ML/LinearAlgebra/Vector4.hpp>
#include <Color4.hpp>
namespace JGL {
class VRamList;
/// A hint for OpenGL on how the VBO is to be used.
enum VRamUsageHint : GLenum {
// Never updated after creation.
Fixed = GL_STATIC_DRAW,
// Modified occasionally.
Dynamic = GL_DYNAMIC_DRAW,
// Constantly modified and used one or a few times.
Stream = GL_STREAM_DRAW
};
}
/// A wrapped for "Vertex Buffer Object" In OpenGL, Store things in VRam.
/// A wrapped for "Vertex Buffer Object" In OpenGL, Store things in V-ram.
class JGL::VRamList {
private:
VRamUsageHint usage_hint = Fixed;
GLuint list_handle = 0;
long num_elements = 0;
long byte_count = 0;
bool element_array_buffer = false;
/// "Spin Locking" fix for multi-threading.
bool spin_lock = false;
// TODO mutex lock.
void load(const GLfloat* data, const long& size);
void load(const GLuint* data, const long& size);
void SetData(void* data, const long& count);
void UpdateData(void* data, const long& offset, const long& count);
void Erase();
public:
VRamList(const GLuint* data, const long& count);
VRamList(const GLfloat* data, const long& count);
VRamList(const Vector2* data, const long& count);
VRamList(const Vector3* data, const long& count);
VRamList(const Vector4* data, const long& count);
VRamList(const GLuint* data, const long& count, VRamUsageHint hint = Fixed);
VRamList(const GLfloat* data, const long& count, VRamUsageHint hint = Fixed);
VRamList(const Vector2* data, const long& count, VRamUsageHint hint = Fixed);
VRamList(const Vector3* data, const long& count, VRamUsageHint hint = Fixed);
VRamList(const Vector4* data, const long& count, VRamUsageHint hint = Fixed);
VRamList(const Color4* data, const long& count, VRamUsageHint hint = Fixed);
/// Allocate an empty VBO
/// @param byte_count the size of the buffer in bytes.
/// @param element_array_buffer if applicable, whether the buffer is to be used for GLuint indices.
/// @param hint A hint to the graphics driver for what the buffer is to be used for.
VRamList(const size_t& byte_count, bool element_array_buffer, VRamUsageHint hint = Fixed);
~VRamList();
/** Copying around the VBO data to a new VBO like this is slow.
* Pass to function by const reference or pointer always. */
VRamList(const VRamList& rhs);
VRamList() : list_handle(0), num_elements(0), element_array_buffer(false), spin_lock(false) {}
VRamList() : list_handle(0), byte_count(0), element_array_buffer(false) {}
public:
[[nodiscard]] GLuint GetHandle() const;
[[nodiscard]] GLuint Handle() const;
/// Returns the number of elements in the list.
[[nodiscard]] long GetLength() const;
[[nodiscard]] long Length() const;
/// Returns the size of the data in bytes.
[[nodiscard]] size_t GetDataSize() const;
[[nodiscard]] size_t Size() const;
/** Get VBO data back from the GPU. This is *bad* because the CPU is going to wait
* for the transfer to finish. Has limited use other than testing. */
[[nodiscard]] std::vector<GLfloat> GetDataF() const;
[[nodiscard]] std::vector<GLuint> GetDataUI() const;
[[nodiscard]] bool IsFloatArray() const;
/** Replace the data of an existing VBO in it's entirety. Must be same type. */
void SetData(const GLfloat* data, const long& count);
void SetData(const Vector2* data, const long& count);
void SetData(const Vector3* data, const long& count);
void SetData(const Vector4* data, const long& count);
void SetData(const Color4* data, const long& count);
void SetData(const GLuint* data, const long& count);
void SetData(const Vector2i* data, const long& count);
@@ -58,11 +74,17 @@ public:
/** Update only a portion of the data in a VBO. Must be same type.
* "offset" refers the number of Typename T into the buffer the data you want to change is.
* For ex, offset 0 and length of 1 overwrites the first value. Offset 1 the second etc */
// TODO provide a bool to specify whether the current buffer should be orphaned.
void UpdateData(const GLfloat* data, const long& offset, const long& count);
void UpdateData(const Vector2* data, const long& offset, const long& count);
void UpdateData(const Vector3* data, const long& offset, const long& count);
void UpdateData(const Vector4* data, const long& offset, const long& count);
void UpdateData(const Color4* data, const long& offset, const long& count);
void UpdateData(const GLuint* data, const long& offset, const long& count);
void UpdateData(const Vector2i* data, const long& offset, const long& count);
// Update only a portion of the data in a VBO using bytes.
// TODO This version of the function has no protection for out of bounds writes.
void UpdateData(const uint8_t* data, const long& offset, const long& count);
};

View File

@@ -7,6 +7,7 @@
#include <J3ML/Geometry/AABB.hpp>
#include <ReWindow/Logger.h>
#include <JGL/types/VertexArray.h>
#include <JGL/types/Shader.h>
using J3ML::LinearAlgebra::Vector2;
using namespace JGL::Fonts;
@@ -14,7 +15,7 @@ using namespace JGL;
using JGL::Font;
float fps = 0.0f;
std::vector<Instance2D> rect_instances;
/// A draggable 2D point that highlights when moused over and when clicked.
class Gizmo
{
@@ -111,6 +112,8 @@ JGL::Font FreeSans;
Texture* image;
Texture* image_mask;
RenderTarget* j2d_render_target;
Shader* shader;
Vector2 result;
class JGLDemoWindow : public ReWindow::OpenGLWindow
{
@@ -128,23 +131,30 @@ public:
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);
glDepthMask(GL_TRUE);
image = new Texture("assets/sprites/Re3D.png", FilteringMode::BILINEAR);
image = new Texture("assets/sprites/Re3D.png", FilteringMode::MIPMAP_NEAREST);
image_mask = new Texture("assets/sprites/alpha_mask_2.png");
j2d_render_target = new RenderTarget({540, 500}, {0,0,0,0}, false,
MSAA_SAMPLE_RATE::MSAA_8X, FilteringMode::MIPMAP_TRILINEAR);
SampleRate::NONE, FilteringMode::MIPMAP_NEAREST);
//Texture::MultiplyByAlphaMask(*image, *image_mask);
shader = new Shader(std::filesystem::path("assets/shader_programs/test_vertex.glsl"), std::filesystem::path("assets/shader_programs/test_fragment.glsl"),
{{"a_vertex_position", 0}, {"a_instance_position", 1}, {"a_instance_size", 2}, {"a_instance_color", 3}});
result = Jupiteroid.MeasureString("The quick black fox jumps over the lazy dog.", 16);
for (unsigned int i = 0; i < 100; i++)
rect_instances.emplace_back(Colors::Red, Vector2(420, 420), Vector2(20, 20));
}
EulerAngleXYZ textAngle = {0,0,0};
float fov = 90;
u8 pulse = 0;
float pulse = 0;
float sprite_radians = 0;
bool fov_increasing = true;
int blit_pos = 0;
void display() {
pulse++;
pulse += 20 * delta_time;
float dt = GetDeltaTime();
JGL::Update({ GetSize().x, GetSize().y });
@@ -166,38 +176,40 @@ public:
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
camera->render();
// All 3D elements of the scene and JGL elements *must* be rendered before the 2D stuff
/* if rendering to screen space directly. */
auto test_light = PointLight({2,1,2}, {pulse,pulse,pulse, 255}, {pulse, pulse, pulse, 255}, {0,0,0}, 1, 0.1, 0.01);
auto test_light = PointLight({2,1,2}, {(u8) pulse,(u8) pulse,(u8) pulse, 255}, {(u8) pulse, (u8) pulse, (u8) pulse, 255}, {0,0,0}, 1, 0.1, 0.01);
// If a 3D object has transparency. The things you'd like to see through it must be drawn before.
J3D::Begin();
J3D::DrawLine(Colors::Red, {-0.33,-0.125,1}, {-1,-0.125,1});
J3D::DrawLine(Colors::Red, {-0.33,-0.125,1}, {-0.33,0.25,1});
J3D::DrawString(Colors::Red, "JGL Sample Text", {-0.33, -0.1, 1.0f}, 1.f, 32, FreeSans, textAngle, true);
//J3D::WireframeSphere(Colors::Green, {0,0,0.5f}, 0.25f, 1, 128, 128);
Sphere sphere = {{0,0, 0.5f}, 0.2125};
Sphere sphere = {{1,0, 0.5f}, 0.2125};
J3D::BatchWireframeRevoSphere(Colors::Green, &sphere, 1, 1, 8, 8, true);
J3D::RequiredLight(&test_light);
J3D::FillAABB(Colors::Whites::AliceBlue, {0,0,0.5f}, {0.05f, 0.05f, 0.05f});
J3D::WireframeAABB(Colors::Gray, {0,0,0.5f}, {0.11f, 0.06f, 0.11f});
AABB boxes[1] = {{Vector3(-0.2125, -0.2125,0.28750), Vector3(0.2125,0.2125,0.7125)}};
J3D::BatchWireframeAABB(Colors::Yellow, boxes, 1, 1);
J3D::WireframeAABB(Colors::Yellow, {0.5, 0, 0.5}, {0.125, 0.125, 0.125}, 1);
J3D::End();
J2D::Begin(j2d_render_target, true);
//JGL::ClearScreen(Colors::Red);
J2D::Begin(j2d_render_target, shader, true);
J2D::FillRect(Colors::Blue, {0,52}, {100,100});
J2D::BatchFillRect(rect_instances.data(), rect_instances.size());
J2D::DrawSprite(image, {300, 400}, sprite_radians * 0.10f, {0.5,0.5}, {1, 1}, Colors::White);
J2D::DrawMirrorSprite(image, {400, 300}, Direction::Horizontal | Direction::Vertical, sprite_radians, {0.5,0.5}, {1, 1}, Colors::White);
J2D::DrawPartialSprite(image, Vector2(225, 300), Vector2(image->GetDimensions()) * 0.25, Vector2(image->GetDimensions()) * 0.75, sprite_radians, {0.5, 0.5}, {1,1}, Colors::White);
J2D::FillRect(Colors::Pinks::HotPink, {68, 120}, {32, 32});
J2D::FillGradientRect(Colors::Red, Colors::Blue, Direction::Diagonal_SWNE, {100,52}, {100,100});
J2D::FillGradientRect(Colors::Red, Colors::Green, Colors::Blue, Colors::White, {100,52}, {100,100});
J2D::FillRoundedRect(Colors::Red, {200, 52}, {100, 100}, 8, 8);
J2D::FillRoundedRect(Colors::Purples::BlueViolet, {300, 52}, {100, 100}, 8, 4);
J2D::FillCircle(Colors::White, {52, 204}, 50, 24);
J2D::OutlineCircle(Colors::White, {153, 204}, 50, 24);
auto box = JGL::Fonts::Jupiteroid.MeasureString("Hello g", 16);
J2D::FillChamferRect(Colors::Reds::LightSalmon, {150, 400}, {64, 64}, 5);
J2D::OutlineRoundedRect(Colors::Reds::LightCoral, {250, 350}, {128, 128}, 10, 2);
@@ -205,12 +217,13 @@ public:
J2D::FillGradientTriangle(Color4(Colors::Red), Color4(Colors::Green), Color4(Colors::Blue), {{0, 275}, {0, 375}, {100, 375}});
J2D::OutlineTriangle(Colors::Blue, {{100, 275}, {0, 275}, {100, 375}});
J2D::DrawGradientLine(Colors::Red, Colors::Blue, {105, 375}, {200, 275}, 2);
auto result = Jupiteroid.MeasureString("Jupiteroid Font", 16);
J2D::DrawString(Colors::Green, "The quick black fox jumps over the lazy dog.", 0, 20, 1, 16);
J2D::OutlineRect(Colors::Red, {0, 20}, result, 1);
J2D::DrawString(Colors::Green, "Jupteroid Font", 0.f, 0, 1.f, 16, Jupiteroid);
J2D::DrawString(Colors::White, "Position: " + std::to_string(camera->position.x) + " " + std::to_string(camera->position.y) + " " + std::to_string(camera->position.z), 0, 16, 1,16, Jupiteroid);
J2D::DrawString(Colors::White, "ViewAngle: " + std::to_string(camera->angle.x) + " " + std::to_string(camera->angle.y) + " " + std::to_string(camera->angle.z), 0, 33, 1,16, Jupiteroid);
J2D::DrawString(Colors::White, "Framerate: " + std::to_string((int) fps), 0, 48, 1, 16, Jupiteroid);
//J2D::DrawString(Colors::Green, "Jupteroid Font", 0.f, 0, 1.f, 16, Jupiteroid);
//J2D::DrawString(Colors::White, "Position: " + std::to_string(camera->position.x) + " " + std::to_string(camera->position.y) + " " + std::to_string(camera->position.z), 0, 16, 1,16, Jupiteroid);
//J2D::DrawString(Colors::White, "ViewAngle: " + std::to_string(camera->angle.x) + " " + std::to_string(camera->angle.y) + " " + std::to_string(camera->angle.z), 0, 33, 1,16, Jupiteroid);
//J2D::DrawString(Colors::White, "Framerate: " + std::to_string((int) fps), 0, 48, 1, 16, Jupiteroid);
std::array<Vector2, 5> polygon = {Vector2(200, 400), {220, 420}, {220, 430}, {230, 410}, {200, 400}};
J2D::OutlinePolygon(Colors::White, polygon.data(), polygon.size());
J2D::DrawCubicBezierCurve(Colors::Blues::CornflowerBlue,
@@ -224,17 +237,17 @@ public:
b.Draw();
c.Draw();
d.Draw();
J2D::End();
J2D::Begin();
J2D::Begin(nullptr, nullptr, true);
J2D::DrawRenderTarget(j2d_render_target, {0, 0});
J2D::DrawSprite(image, image_mask, {0, 0}, 0.25, {0.5, 0.5}, {1,1});
//J2D::DrawSprite(image, image_mask, {0, 0}, 0.25, {0.5, 0.5}, {1,1});
J2D::End();
}
void OnRefresh(float elapsed) override {
fps = GetRefreshRate();
if (IsKeyDown(Keys::RightArrow))
@@ -309,6 +322,10 @@ int main(int argc, char** argv) {
if (!file.is_open())
return -1;
Shader::OnCompilationErrorMessage += [] (std::string type, std::string info) {
std::cout << type << ", " << info << std::endl;
};
/*
std::stringstream buffer;
buffer << file.rdbuf();

View File

@@ -43,6 +43,10 @@ namespace JGL {
return false;
if (!GLAD_GL_ARB_shadow)
return false;
if (!GLAD_GL_ARB_draw_instanced)
supports_instanced = false;
if (!GLAD_GL_ARB_instanced_arrays)
supports_instanced = false;
return true;
}
}

View File

@@ -47,11 +47,22 @@ void JGL::ShapeCache::Init() {
cube_normal_data = new VRamList(vertex_normals.data(), vertex_normals.size());
if (!square_origin_topleft_vertex_data) {
std::array<Vector2, 4> square_vertices = { Vector2(0, 0), {1, 0}, {1, -1}, {0, -1} };
//std::array<Vector2, 4> square_vertices = { Vector2(0, 0), {1, 0}, {1, -1}, {0, -1} };
std::array<Vector2, 4> square_vertices = { Vector2(0, 0), {0, 1}, {1, 1}, {1, 0} };
square_origin_topleft_vertex_data = new VRamList(square_vertices.data(), square_vertices.size());
}
if (!j2d_default_normal_data) {
std::array<GLfloat, 3> normal {0, 0, 1};
j2d_default_normal_data = new VRamList(normal.data(), normal.size());
}
if (!draw_points_colors) {
std::array<Color4, 1> color = { Colors::Transparent };
draw_points_colors = new VRamList(color.data(), color.size(), Stream);
}
if (!draw_points_positions) {
std::array<Vector2, 1> position = { Vector2::Zero };
draw_points_positions = new VRamList(position.data(), position.size(), Stream);
}
}

View File

@@ -1,12 +1,48 @@
#include <JGL/JGL.h>
#include <JGL/logger/logger.h>
#include <JGL/types/Instance.h>
#include <J3ML/Algorithm/Bezier.hpp>
#include "internals/internals.h"
void J2D::Begin(RenderTarget* render_target, bool clear_buffers) {
void JGL::ClearScreen(const Color4& clear_color) {
GLuint current_framebuffer = 0;
GLint current_viewport[4] = {0, 0, 0, 0};
GLfloat current_clear_color[4];
glGetIntegerv(GL_VIEWPORT, current_viewport);
glGetFloatv(GL_COLOR_CLEAR_VALUE, current_clear_color);
current_framebuffer = RenderTarget::GetActiveGLFramebufferHandle();
if (current_framebuffer)
glBindFramebuffer(GL_FRAMEBUFFER, 0);
bool changed_viewport = false;
if (current_viewport[2] != window_size.x || current_viewport[3] != window_size.y)
glViewport(0, 0, window_size.x, window_size.y),
changed_viewport = true;
GLint has_depth = 0;
glGetIntegerv(GL_DEPTH_BITS, &has_depth);
glClearColor(clear_color.RN(), clear_color.GN(), clear_color.BN(), clear_color.AN());
if (has_depth)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
else
glClear(GL_COLOR_BUFFER_BIT);
glClearColor(current_clear_color[0], current_clear_color[1], current_clear_color[2], current_clear_color[3]);
if (current_framebuffer)
glBindFramebuffer(GL_FRAMEBUFFER, current_framebuffer);
if (changed_viewport)
glViewport(current_viewport[0], current_viewport[1], current_viewport[2], current_viewport[3]);
}
void J2D::Begin(RenderTarget* render_target, Shader* shader, bool clear_buffers) {
State new_state = default_state;
state_stack.Push(State::SaveState());
state_stack.Push(State::SaveState(current_state));
glMatrixMode(GL_PROJECTION);
glPushMatrix();
@@ -31,12 +67,16 @@ void J2D::Begin(RenderTarget* render_target, bool clear_buffers) {
new_state.viewport[3] = window_size.y;
}
if (shader) {
new_state.current_shader = shader;
new_state.current_shader_handle = shader->Handle();
}
State::RestoreState(new_state);
current_state = new_state;
if (current_state.current_render_target)
JGL::RenderTarget::SetActiveGLRenderTarget(*current_state.current_render_target);
RenderTarget::SetActiveGLRenderTarget(*current_state.current_render_target);
if (render_target != nullptr && clear_buffers) {
glClearColor(render_target->GetClearColor().RedChannelNormalized(), render_target->GetClearColor().GreenChannelNormalized(), render_target->GetClearColor().BlueChannelNormalized(), render_target->GetClearColor().AlphaChannelNormalized());
@@ -86,7 +126,15 @@ void J2D::DrawPoint(const Color4& color, const Vector2& coordinates, float radiu
glPointSize(radius);
glColor4ubv(color.ptr());
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), coordinates.ptr());
if (current_state.current_shader)
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_DrawPoint);
glDrawArrays(GL_POINTS, 0, 1);
if (current_state.current_shader)
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0);
glColor4fv(default_state.draw_color);
}
@@ -102,7 +150,15 @@ void J2D::DrawLine(const Color4& color, const Vector2& A, const Vector2& B, floa
glLineWidth(thickness);
glColor4ubv(color.ptr());
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices);
if (current_state.current_shader)
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_DrawLine);
glDrawArrays(GL_LINES, 0, 2);
if (current_state.current_shader)
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0);
glColor4fv(default_state.draw_color);
}
@@ -117,7 +173,15 @@ void J2D::DrawLines(const Color4& color, const Vector2* points, const size_t& po
glLineWidth(thickness);
glColor3ubv(color.ptr());
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), points);
if (current_state.current_shader)
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_DrawLines);
glDrawArrays(GL_LINE_STRIP, 0, point_count);
if (current_state.current_shader)
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0);
glColor4fv(default_state.draw_color);
}
@@ -134,7 +198,13 @@ void J2D::DrawDottedLine(const Color4& color, const Vector2& A, const Vector2& B
for (unsigned int i = 0; i < point_count; ++i)
points[i] = A + direction * (i * spacing);
return J2D::DrawPoints(color, points.data(), points.size(), thickness);
if (current_state.current_shader)
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_DrawDottedLine);
J2D::DrawPoints(color, points.data(), points.size(), thickness);
if (current_state.current_shader)
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0);
}
void J2D::DrawDottedLine(const Color4& color, float x1, float y1, float x2, float y2, float spacing, float thickness) {
@@ -151,6 +221,10 @@ void J2D::DrawDashedLine(const Color4& color, const Vector2& A, const Vector2& B
Logger::Error("Drawing a dashed line that would have no dashes?");
Vector2 A_current, B_current;
if (current_state.current_shader)
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_DrawDashedLine);
for (unsigned int i = 0; i < dash_count; i++) {
A_current = A + direction * (i * length_of_dash_and_gap);
B_current = A_current + (direction * dash_length);
@@ -164,6 +238,9 @@ void J2D::DrawDashedLine(const Color4& color, const Vector2& A, const Vector2& B
B_current = A_current + direction * std::min(dash_length, distance_left);
J2D::DrawLine(color, A_current, B_current, thickness);
}
if (current_state.current_shader)
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0);
}
void J2D::DrawDashedLine(const Color4& color, float x1, float y1, float x2, float y2, float spacing, float dash_length, float thickness) {
@@ -182,7 +259,15 @@ void J2D::DrawGradientLine(const Color4& color1, const Color4& color2, const Vec
glLineWidth(thickness);
glColorPointer(4,GL_FLOAT,sizeof(Color4), colors);
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices);
if (current_state.current_shader)
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_DrawGradientLine);
glDrawArrays(GL_LINES, 0, 2);
if (current_state.current_shader)
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0);
glDisableClientState(GL_COLOR_ARRAY);
glColor4fv(default_state.draw_color);
}
@@ -192,90 +277,204 @@ void DrawGradientLine(const Color4& color1, const Color4& color2, float x, float
}
void J2D::OutlineRect(const Color4& color, const Vector2& pos, const Vector2& size, float thickness) {
Instance2D rect(color, pos, size);
J2D::BatchOutlineRect(&rect, thickness, 1);
}
void J2D::BatchOutlineRect(const Instance2D* instances, float thickness, const size_t& instance_count) {
if (!state_stack.Size())
Logger::Error("Drawing J2D element before J2D begin.");
Vector2 vertices[] = {{pos.x, pos.y}, {pos.x, pos.y + size.y}, {pos.x + size.x, pos.y + size.y}, {pos.x + size.x, pos.y}};
if (instance_count <= 0)
return;
if (current_state.current_shader)
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_OutlineRect);
glBindBuffer(GL_ARRAY_BUFFER, ShapeCache::square_origin_topleft_vertex_data->Handle());
glLineWidth(thickness);
glColor4ubv(color.ptr());
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices);
glDrawArrays(GL_LINE_LOOP, 0, 4);
if (instance_count == 1 || !supports_instanced || !current_state.current_shader) {
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), nullptr);
for (size_t i = 0; i < instance_count; i++) {
glPushMatrix();
glColor4ubv(instances[i].color.ptr());
glTranslatef(instances[i].position.x, instances[i].position.y, 0);
glScalef(instances[i].size.x, instances[i].size.y, 1);
glDrawArrays(GL_LINE_LOOP, 0, 4);
glPopMatrix();
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
else {
current_state.current_shader->SetBool("JGL_INSTANCED_RENDERING", true);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(Vector2), nullptr);
glVertexAttribDivisorARB(0, 0);
VRamList instance_buffer((GLfloat*) instances, instance_count * (sizeof(Instance2D) / sizeof(GLfloat)), Stream);
glBindBuffer(GL_ARRAY_BUFFER, instance_buffer.Handle());
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Instance2D), (GLvoid*) offsetof(Instance2D, position));
glVertexAttribDivisorARB(1, 1);
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Instance2D), (GLvoid*) offsetof(Instance2D, size));
glVertexAttribDivisorARB(2, 1);
glEnableVertexAttribArray(3);
glVertexAttribPointer(3, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Instance2D), (GLvoid*) offsetof(Instance2D, color));
glVertexAttribDivisorARB(3, 1);
glDrawArraysInstancedARB(GL_LINE_LOOP, 0, 4, instance_count);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glDisableVertexAttribArray(2);
glDisableVertexAttribArray(3);
glBindBuffer(GL_ARRAY_BUFFER, 0);
current_state.current_shader->SetBool("JGL_INSTANCED_RENDERING", false);
}
if (current_state.current_shader)
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0);
glColor4fv(default_state.draw_color);
}
void J2D::FillRect(const Color4& color, const Vector2& pos, const Vector2& size) {
BatchFillRect(&color, &pos, &size, 1);
Instance2D rect(color, pos, size);
BatchFillRect(&rect, 1);
}
void J2D::BatchFillRect(const Color4* colors, const Vector2* positions, const Vector2* sizes, const size_t& rect_count) {
void J2D::BatchFillRect(const Instance2D* instances, const size_t& instance_count) {
if (!state_stack.Size())
Logger::Error("Drawing J2D element before J2D begin.");
if (rect_count <= 0)
if (instance_count <= 0)
return;
glBindBuffer(GL_ARRAY_BUFFER, ShapeCache::square_origin_topleft_vertex_data->GetHandle());
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), nullptr);
if (current_state.current_shader)
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_FillRect);
for (size_t i = 0; i < rect_count; i++) {
glPushMatrix();
glColor4ubv(colors[i].ptr());
glTranslatef(positions[i].x, positions[i].y + sizes[i].y, 0);
glScalef(sizes[i].x, sizes[i].y, 1);
glDrawArrays(GL_QUADS, 0, 4);
glPopMatrix();
glBindBuffer(GL_ARRAY_BUFFER, ShapeCache::square_origin_topleft_vertex_data->Handle());
if (instance_count == 1 || !supports_instanced || !current_state.current_shader) {
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), nullptr);
for (size_t i = 0; i < instance_count; i++) {
glPushMatrix();
glColor4ubv(instances[i].color.ptr());
glTranslatef(instances[i].position.x, instances[i].position.y, 0);
glScalef(instances[i].size.x, instances[i].size.y, 1);
glDrawArrays(GL_QUADS, 0, 4);
glPopMatrix();
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
else {
current_state.current_shader->SetBool("JGL_INSTANCED_RENDERING", true);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(Vector2), nullptr);
glVertexAttribDivisorARB(0, 0);
VRamList instance_buffer((GLfloat*) instances, instance_count * (sizeof(Instance2D) / sizeof(GLfloat)), Stream);
glBindBuffer(GL_ARRAY_BUFFER, instance_buffer.Handle());
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Instance2D), (GLvoid*) offsetof(Instance2D, position));
glVertexAttribDivisorARB(1, 1);
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Instance2D), (GLvoid*) offsetof(Instance2D, size));
glVertexAttribDivisorARB(2, 1);
glEnableVertexAttribArray(3);
glVertexAttribPointer(3, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Instance2D), (GLvoid*) offsetof(Instance2D, color));
glVertexAttribDivisorARB(3, 1);
glDrawArraysInstancedARB(GL_QUADS, 0, 4, instance_count);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glDisableVertexAttribArray(2);
glDisableVertexAttribArray(3);
glBindBuffer(GL_ARRAY_BUFFER, 0);
current_state.current_shader->SetBool("JGL_INSTANCED_RENDERING", false);
}
if (current_state.current_shader)
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0);
glColor4fv(default_state.draw_color);
}
void J2D::FillGradientRect(const Color4& color1, const Color4& color2, const Direction& gradient, const Vector2& pos, const Vector2& size) {
void J2D::FillGradientRect(const Color4& top_left_color, const Color4& bottom_left_color, const Color4& bottom_right_color, const Color4& top_right_color, const Vector2& pos, const Vector2& size) {
if (!state_stack.Size())
Logger::Error("Drawing J2D element before J2D begin.");
Vector2 vertices[] = {{pos.x, pos.y}, {pos.x, pos.y + size.y}, {pos.x + size.x, pos.y + size.y}, {pos.x + size.x, pos.y}};
std::vector<GLfloat> colors{};
if (gradient == Direction::Horizontal)
colors = {color1.RedChannelNormalized(), color1.GreenChannelNormalized(), color1.BlueChannelNormalized(), color1.AlphaChannelNormalized(), color1.RedChannelNormalized(), color1.GreenChannelNormalized(), color1.BlueChannelNormalized(), color1.AlphaChannelNormalized(),
color2.RedChannelNormalized(), color2.GreenChannelNormalized(), color2.BlueChannelNormalized(), color2.AlphaChannelNormalized(), color2.RedChannelNormalized(), color2.GreenChannelNormalized(), color2.BlueChannelNormalized(), color2.AlphaChannelNormalized()};
else if (gradient == Direction::Vertical)
colors = {color1.RedChannelNormalized(), color1.GreenChannelNormalized(), color1.BlueChannelNormalized(), color1.AlphaChannelNormalized(), color2.RedChannelNormalized(), color2.GreenChannelNormalized(), color2.BlueChannelNormalized(), color2.AlphaChannelNormalized(),
color2.RedChannelNormalized(), color2.GreenChannelNormalized(), color2.BlueChannelNormalized(), color2.AlphaChannelNormalized(), color1.RedChannelNormalized(), color1.GreenChannelNormalized(), color1.BlueChannelNormalized(), color1.AlphaChannelNormalized()};
else if (gradient == Direction::Diagonal_SWNE)
colors = {(color1.RedChannelNormalized() + color2.RedChannelNormalized()) / 2.f, (color1.GreenChannelNormalized() + color2.GreenChannelNormalized()) / 2.f, (color1.BlueChannelNormalized() + color2.BlueChannelNormalized()) / 2.f, (color1.AlphaChannelNormalized() + color2.AlphaChannelNormalized()) / 2.f,
color1.RedChannelNormalized(), color1.GreenChannelNormalized(), color1.BlueChannelNormalized(), color1.AlphaChannelNormalized(), (color1.RedChannelNormalized() + color2.RedChannelNormalized()) / 2.f, (color1.GreenChannelNormalized() + color2.GreenChannelNormalized()) / 2.f,
(color1.BlueChannelNormalized() + color2.BlueChannelNormalized()) / 2.f, (color1.AlphaChannelNormalized() + color2.AlphaChannelNormalized()) / 2.f, color2.RedChannelNormalized(), color2.GreenChannelNormalized(), color2.BlueChannelNormalized(), color2.AlphaChannelNormalized()};
else if (gradient == Direction::Diagonal_NWSE)
colors = {color1.RedChannelNormalized(), color1.GreenChannelNormalized(), color1.BlueChannelNormalized(), color1.AlphaChannelNormalized(),(color1.RedChannelNormalized() + color2.RedChannelNormalized()) / 2.f, (color1.GreenChannelNormalized() + color2.GreenChannelNormalized()) / 2.f,
(color1.BlueChannelNormalized() + color2.BlueChannelNormalized()) / 2.f, (color1.AlphaChannelNormalized() + color2.AlphaChannelNormalized()) / 2.f, color2.RedChannelNormalized(), color2.GreenChannelNormalized(), color2.BlueChannelNormalized(), color2.AlphaChannelNormalized(),
(color1.RedChannelNormalized() + color2.RedChannelNormalized()) / 2.f, (color1.GreenChannelNormalized() + color2.GreenChannelNormalized()) / 2.f, (color1.BlueChannelNormalized() + color2.BlueChannelNormalized()) / 2.f,(color1.AlphaChannelNormalized() + color2.AlphaChannelNormalized()) / 2.f};
std::vector<GLfloat> colors {
top_left_color.RN(), top_left_color.GN(), top_left_color.BN(), top_left_color.AN(),
bottom_left_color.RN(), bottom_left_color.GN(), bottom_left_color.BN(), bottom_left_color.AN(),
bottom_right_color.RN(), bottom_right_color.GN(), bottom_right_color.BN(), bottom_right_color.AN(),
top_right_color.RN(), top_right_color.GN(), top_right_color.BN(), top_right_color.AN()
};
glEnableClientState(GL_COLOR_ARRAY);
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices);
glColorPointer(4, GL_FLOAT, 0, colors.data());
if (current_state.current_shader)
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_FillGradientRect);
glDrawArrays(GL_QUADS, 0, 4);
if (current_state.current_shader)
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0);
glDisableClientState(GL_COLOR_ARRAY);
glColor4fv(default_state.draw_color);
}
void J2D::FillRoundedRect(const Color4& color, const Vector2& pos, const Vector2& size, float radius, unsigned int subdivisions) {
std::array<Color4, 4> colors = { color, color, color, color };
std::array<Vector2, 2> rect_positions = { Vector2(pos.x + radius, pos.y), {pos.x, pos.y + radius} };
std::array<Vector2, 2> rect_sizes = { Vector2(size.x - 2 * radius, size.y), {size.x, size.y - 2 * radius} };
std::array<float, 4> circle_radii = { radius, radius, radius, radius };
std::array<Vector2, 4> circle_positions
{
Vector2(pos.x + radius, pos.y + radius), {pos.x + size.x - radius, pos.y + radius},
{pos.x + radius, pos.y + size.y - radius}, {pos.x + size.x - radius, pos.y + size.y - radius}
};
void J2D::BatchFillRoundedRect(const Color4* colors, const Vector2* positions, const Vector2* sizes, float radius, unsigned int subdivisions, const size_t& count) {
std::vector<Instance2D> rect_instances(count * 2);
std::vector<Instance2D> circle_instances(count * 4);
J2D::BatchFillRect(colors.data(), rect_positions.data(), rect_sizes.data(), 2);
J2D::BatchFillCircle(colors.data(), circle_positions.data(), circle_radii.data(), subdivisions, 4);
if (current_state.current_shader)
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_FillRoundedRect);
for (unsigned int i = 0; i < count; i++) {
unsigned int rect_i = i * 2;
unsigned int circle_i = i * 4;
rect_instances[rect_i] = Instance2D(colors[i], Vector2(positions[i].x + radius, positions[i].y), Vector2(sizes[i].x - 2 * radius, sizes[i].y));
rect_instances[rect_i + 1] = Instance2D(colors[i], Vector2(positions[i].x, positions[i].y + radius), Vector2(sizes[i].x, sizes[i].y - 2 * radius));
circle_instances[circle_i] = Instance2D(colors[i], Vector2(positions[i].x + radius, positions[i].y + radius), Vector2::One, Vector2(radius, radius));
circle_instances[circle_i + 1] = Instance2D(colors[i], Vector2(positions[i].x + sizes[i].x - radius, positions[i].y + radius), Vector2::One, Vector2(radius, radius));
circle_instances[circle_i + 2] = Instance2D(colors[i], Vector2(positions[i].x + radius, positions[i].y + sizes[i].y - radius), Vector2::One, Vector2(radius, radius));
circle_instances[circle_i + 3] = Instance2D(colors[i], Vector2(positions[i].x + sizes[i].x - radius, positions[i].y + sizes[i].y - radius), Vector2::One, Vector2(radius, radius));
}
J2D::BatchFillRect(rect_instances.data(), rect_instances.size());
J2D::BatchFillCircle(circle_instances.data(), subdivisions, circle_instances.size());
if (current_state.current_shader)
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0);
}
void J2D::FillRoundedRect(const Color4& color, const Vector2& pos, const Vector2& size, float radius, unsigned int subdivisions) {
J2D::BatchFillRoundedRect(&color, &pos, &size, radius, subdivisions, 1);
}
void J2D::DrawSprite(const Texture* texture, float positionX, float positionY, float rad_rotation,
@@ -347,8 +546,15 @@ void J2D::DrawSprite(const RenderTarget& rt, const Vector2& position, float rad_
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
auto r_position = Vector2(Math::Floor(position.x), Math::Floor(position.y));
if (current_state.current_shader)
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_DrawRenderTarget);
J2D::DrawPartialSprite(*rt.GetTexture(), r_position, {0, 0}, Vector2(rt.GetDimensions()), rad_rotation, origin, scale, color, d);
if (current_state.current_shader)
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0);
if (rt.OwnsTexture())
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
@@ -423,9 +629,23 @@ void J2D::DrawSprite(const Texture& texture, const Texture& alpha_mask, const Ve
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices.data());
glTexCoordPointer(2, GL_FLOAT, sizeof(Vector2), textureCoordinates.data());
if (current_state.current_shader)
current_state.current_shader->SetInt("TEXTURE_UNIT_SET_COUNT", 2),
current_state.current_shader->SetInt("GL_TEXTURE1", 1);
// Draw.
if (current_state.current_shader)
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_DrawAlphaMaskSprite);
glDrawArrays(GL_QUADS, 0, 4);
if (current_state.current_shader)
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0);
if (current_state.current_shader)
current_state.current_shader->SetInt("TEXTURE_UNIT_SET_COUNT", 0),
current_state.current_shader->SetInt("GL_TEXTURE1", 0);
// Reset Texture 1.
glBindTexture(GL_TEXTURE_2D, 0);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
@@ -482,8 +702,14 @@ void J2D::DrawPartialRenderTarget(const RenderTarget& rt, const Vector2& positio
auto r_position = Vector2(Math::Floor(position.x), Math::Floor(position.y));
auto r_sub_texture_position = Vector2(Math::Floor(sub_texture_position.x), Math::Floor(sub_texture_position.y));
if (current_state.current_shader)
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_DrawPartialRenderTarget);
J2D::DrawPartialSprite(*rt.GetTexture(), r_position, r_sub_texture_position, sub_texture_size, rad_rotation, origin, scale, color, d);
if (current_state.current_shader)
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0);
if (rt.OwnsTexture())
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
@@ -507,9 +733,6 @@ void J2D::DrawSprite(const Texture& texture, const Vector2& pos, float rad_rotat
else
textureCoordinates = {Vector2(0, 0), Vector2(0, 1), Vector2(1, 1), Vector2(1, 0)};
// TODO: Kind of a mess, refactor to be more sensible later.
// Factors in scaling and origin correctly.
// i.e. to render at 2x size, from the center, at coords XY, use {2, 2} scale, and {0.5, 0.5} offset.
const Vector2 offset = origin * size;
Vector2 pos2 = pos;
Vector2 scaled_size = scale * size;
@@ -547,7 +770,17 @@ void J2D::DrawSprite(const Texture& texture, const Vector2& pos, float rad_rotat
glBindTexture(GL_TEXTURE_2D, texture.GetHandle());
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices.data());
glTexCoordPointer(2, GL_FLOAT, sizeof(Vector2), textureCoordinates.data());
if (current_state.current_shader)
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_DrawSprite),
current_state.current_shader->SetInt("TEXTURE_UNIT_SET_COUNT", 1);
glDrawArrays(GL_QUADS, 0, 4);
if (current_state.current_shader)
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0),
current_state.current_shader->SetInt("TEXTURE_UNIT_SET_COUNT", 0);
glBindTexture(GL_TEXTURE_2D, 0);
glColor4fv(default_state.draw_color);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
@@ -636,7 +869,17 @@ void J2D::DrawPartialSprite(const Texture& texture, const Vector2& position, con
glBindTexture(GL_TEXTURE_2D, texture.GetHandle());
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices.data());
glTexCoordPointer(2, GL_FLOAT, 0, textureCoordinates.data());
if (current_state.current_shader)
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_DrawPartialSprite),
current_state.current_shader->SetInt("TEXTURE_UNIT_SET_COUNT", 1);
glDrawArrays(GL_QUADS, 0, 4);
if (current_state.current_shader)
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0),
current_state.current_shader->SetInt("TEXTURE_UNIT_SET_COUNT", 0);
glBindTexture(GL_TEXTURE_2D, 0);
glColor4fv(default_state.draw_color);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
@@ -718,8 +961,17 @@ void J2D::DrawMirrorSprite(const Texture& texture, const Vector2& position, Dire
glColor4ubv(color.ptr());
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices.data());
glTexCoordPointer(2, GL_FLOAT, sizeof(Vector2), textureCoordinates.data());
if (current_state.current_shader)
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_DrawMirrorSprite),
current_state.current_shader->SetInt("TEXTURE_UNIT_SET_COUNT", 1);
glDrawArrays(GL_QUADS, 0, 4);
if (current_state.current_shader)
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0),
current_state.current_shader->SetInt("TEXTURE_UNIT_SET_COUNT", 0);
//Reset the wrapping mode.
if (texture.GetWrappingMode() == WrappingMode::CLAMP_TO_EDGE)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE),
@@ -760,19 +1012,28 @@ void J2D::OutlineCircle(const Color4& color, const Vector2& center, float radius
glLineWidth(thickness);
glColor4ubv(color.ptr());
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices.data());
if (current_state.current_shader)
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_OutlineCircle);
glDrawArrays(GL_LINE_LOOP, 0, (int) vertices.size());
if (current_state.current_shader)
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0);
glColor4fv(default_state.draw_color);
}
void J2D::FillCircle(const Color4& color, const Vector2& center, float radius, unsigned int subdivisions) {
BatchFillCircle(&color, &center, &radius, subdivisions, 1);
Instance2D circle(color, center, Vector2(1, 1), Vector2(radius, radius));
BatchFillCircle(&circle, subdivisions, 1);
}
void J2D::BatchFillCircle(const Color4* colors, const Vector2* positions, float* radii, unsigned int subdivisions, const size_t& circle_count) {
void J2D::BatchFillCircle(const JGL::Instance2D* instances, float subdivisions, const size_t &instance_count) {
if (!state_stack.Size())
Logger::Error("Drawing J2D element before J2D begin.");
if (circle_count <= 0)
if (instance_count <= 0)
return;
GLfloat angle, x, y;
@@ -793,15 +1054,64 @@ void J2D::BatchFillCircle(const Color4* colors, const Vector2* positions, float*
i++;
}
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices.data());
for (size_t j = 0; j < circle_count; j++) {
glPushMatrix();
glColor4ubv(colors[j].ptr());
glTranslatef(positions[j].x, positions[j].y, 0);
glScalef(radii[j], radii[j], 0);
auto vertex_buffer = VRamList(vertices.data(), vertices.size(), Stream);
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer.Handle());
if (current_state.current_shader)
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_FillCircle);
if (instance_count == 1 || !supports_instanced || !current_state.current_shader) {
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), nullptr);
for (size_t j = 0; j < instance_count; j++) {
glPushMatrix();
glColor4ubv(instances[j].color.ptr());
glTranslatef(instances[j].position.x, instances[j].position.y, 0);
glScalef(instances[j].scale.x, instances[j].scale.y, 0);
glDrawArrays(GL_TRIANGLE_FAN, 0, (int) vertices.size());
glPopMatrix();
glPopMatrix();
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
else {
current_state.current_shader->SetBool("JGL_INSTANCED_RENDERING", true);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(Vector2), nullptr);
glVertexAttribDivisorARB(0, 0);
VRamList instance_buffer((GLfloat*) instances, instance_count * (sizeof(Instance2D) / sizeof(GLfloat)), Stream);
glBindBuffer(GL_ARRAY_BUFFER, instance_buffer.Handle());
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Instance2D), (GLvoid*) offsetof(Instance2D, position));
glVertexAttribDivisorARB(1, 1);
glEnableVertexAttribArray(2);
// Swapped scale with size in this path because we always render a unit circle scaled up to the given size.
// TODO implement scaling attribute in vertex shader.
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Instance2D), (GLvoid*) offsetof(Instance2D, scale));
glVertexAttribDivisorARB(2, 1);
glEnableVertexAttribArray(3);
glVertexAttribPointer(3, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Instance2D), (GLvoid*) offsetof(Instance2D, color));
glVertexAttribDivisorARB(3, 1);
glDrawArraysInstancedARB(GL_TRIANGLE_FAN, 0, vertices.size(), instance_count);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glDisableVertexAttribArray(2);
glDisableVertexAttribArray(3);
glBindBuffer(GL_ARRAY_BUFFER, 0);
current_state.current_shader->SetBool("JGL_INSTANCED_RENDERING", false);
}
if (current_state.current_shader)
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0);
glColor4fv(default_state.draw_color);
}
@@ -814,7 +1124,15 @@ void J2D::OutlineTriangle(const Color4& color, const Triangle2D& tri, float thic
glLineWidth(thickness);
glColor4ubv(color.ptr());
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices);
if (current_state.current_shader)
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_OutlineTriangle);
glDrawArrays(GL_LINE_LOOP, 0, 3);
if (current_state.current_shader)
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0);
glColor4fv(default_state.draw_color);
}
@@ -826,7 +1144,15 @@ void J2D::FillTriangle(const Color4& color, const Triangle2D& tri) {
glColor4ubv(color.ptr());
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices);
if (current_state.current_shader)
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_FillTriangle);
glDrawArrays(GL_TRIANGLES, 0, 3);
if (current_state.current_shader)
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0);
glColor4fv(default_state.draw_color);
}
@@ -842,7 +1168,15 @@ void J2D::FillGradientTriangle(const Color4& a_color, const Color4& b_color, con
glEnableClientState(GL_COLOR_ARRAY);
glColorPointer(4, GL_FLOAT, sizeof(Color4), colors);
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices);
if (current_state.current_shader)
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_FillGradientTriangle);
glDrawArrays(GL_TRIANGLES, 0, 3);
if (current_state.current_shader)
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0);
glDisableClientState(GL_COLOR_ARRAY);
glColor4fv(default_state.draw_color);
}
@@ -864,7 +1198,14 @@ void J2D::DrawCubicBezierCurve(const Color4& color, const Vector2& controlA, con
}
vertices[2 * subdivisions] = last;
vertices[2 * subdivisions + 1] = first;
if (current_state.current_shader)
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_DrawCubicBezierCurve);
DrawLines(color, vertices.data(), vertices.size(), thickness);
if (current_state.current_shader)
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0);
}
void J2D::OutlinePolygon(const Color4& color, const Vector2* points, int points_size, float thickness) {
@@ -877,7 +1218,15 @@ void J2D::OutlinePolygon(const Color4& color, const Vector2* points, int points_
glLineWidth(thickness);
glColor4ubv(color.ptr());
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), points);
if (current_state.current_shader)
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_OutlinePolygon);
glDrawArrays(GL_LINE_LOOP, 0, points_size);
if (current_state.current_shader)
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0);
glColor4fv(default_state.draw_color);
}
@@ -915,7 +1264,15 @@ void J2D::DrawArc(const Color4& color, const Vector2& center, float radius, floa
glLineWidth(thickness);
glColor4ubv(color.ptr());
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices.data());
if (current_state.current_shader)
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_DrawArc);
glDrawArrays(GL_LINE_STRIP, 0, (int) vertices.size());
if (current_state.current_shader)
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0);
glColor4fv(default_state.draw_color);
}
@@ -969,6 +1326,9 @@ void J2D::OutlineRoundedRect(const Color4& color, const Vector2& pos, const Vect
unsigned int subdivisions = 9;
if (current_state.current_shader)
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_OutlineRoundedRect);
J2D::DrawArc(color, anchor_tl, radius, Math::Pi, 3.01f*Math::Pi/2.f, subdivisions, thickness);
J2D::DrawLine(color, anchor_topleft_top, anchor_topright_top, thickness);
J2D::DrawArc(color, anchor_tr, radius, 3.f*Math::Pi/2.f, 2.02*Math::Pi, subdivisions, thickness);
@@ -977,13 +1337,22 @@ void J2D::OutlineRoundedRect(const Color4& color, const Vector2& pos, const Vect
J2D::DrawLine(color, anchor_bottomright_bottom, anchor_bottomleft_bottom, thickness);
J2D::DrawArc(color, anchor_bl, radius, Math::Pi/2, Math::Pi*1.01f, subdivisions, thickness);
J2D::DrawLine(color, anchor_bottomleft_left, anchor_topleft_left, thickness);
if (current_state.current_shader)
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0);
//J2D::End();
}
void J2D::FillChamferRect(const Color4& color, const Vector2& pos, const Vector2& size, float radius) {
if (current_state.current_shader)
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_FillChamferRect);
FillRoundedRect(color, pos, size, radius, 4);
if (current_state.current_shader)
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0);
}
void J2D::OutlineChamferRect(const Color4& color, const Vector2& pos, const Vector2& size, float radius, float thickness) {
@@ -1000,19 +1369,63 @@ void J2D::OutlineChamferRect(const Color4& color, const Vector2& pos, const Vect
glLineWidth(thickness);
glColor4ubv(color.ptr());
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices);
if (current_state.current_shader)
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_OutlineChamferRect);
glDrawArrays(GL_LINE_LOOP, 0, 8);
if (current_state.current_shader)
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0);
glColor4fv(default_state.draw_color);
}
void J2D::DrawPoints(const Color4& color, const Vector2* points, int num_points, float radius) {
void J2D::DrawPoints(const Color4* colors, const Vector2* points, int point_count, float radius) {
ShapeCache::draw_points_colors->SetData(colors, point_count);
ShapeCache::draw_points_positions->SetData(points, point_count);
glPointSize(radius);
glEnableClientState(GL_COLOR_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER, ShapeCache::draw_points_colors->Handle());
glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Color4), nullptr);
glBindBuffer(GL_ARRAY_BUFFER, ShapeCache::draw_points_positions->Handle());
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), nullptr);
if (current_state.current_shader)
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_DrawPoints);
glDrawArrays(GL_POINTS, 0, point_count);
if (current_state.current_shader)
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0);
glDisableClientState(GL_COLOR_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glColor4fv(default_state.draw_color);
}
void J2D::DrawPoints(const Color4& color, const Vector2* points, int point_count, float radius) {
ShapeCache::draw_points_positions->SetData(points, point_count);
glPointSize(radius);
glColor4ubv(color.ptr());
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), points);
glDrawArrays(GL_POINTS, 0, num_points);
glBindBuffer(GL_ARRAY_BUFFER, ShapeCache::draw_points_positions->Handle());
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), nullptr);
if (current_state.current_shader)
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_DrawPoints);
glDrawArrays(GL_POINTS, 0, point_count);
if (current_state.current_shader)
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glColor4fv(default_state.draw_color);
}
void J2D::FIllTriangle(const Color4& color, const Vector2& triA, const Vector2& triB, const Vector2& triC) {
void J2D::FillTriangle(const Color4& color, const Vector2& triA, const Vector2& triB, const Vector2& triC) {
FillTriangle(color, {triA, triB, triC});
}
@@ -1044,7 +1457,19 @@ void J2D::OutlineEllipse(const Color4& color, const Vector2& position, float rad
glLineWidth(thickness);
glColor4ubv(color.ptr());
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices.data());
/*
if (current_state.current_shader)
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_OutlineElipse);
*/
glDrawArrays(GL_LINE_LOOP, 0, (int) vertices.size());
/*
if (current_state.current_shader)
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0);
*/
glColor4fv(default_state.draw_color);
}
@@ -1072,6 +1497,30 @@ void J2D::FillEllipse(const Color4& color, const Vector2& position, float radius
glColor4ubv(color.ptr());
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices.data());
/*
if (current_state.current_shader)
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_OutlineElipse);
*/
glDrawArrays(GL_TRIANGLE_FAN, 0, (int) vertices.size());
/*
if (current_state.current_shader)
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0);
*/
glColor4fv(default_state.draw_color);
}
void J2D::DrawSprite(const TextureAtlas* texture_atlas, const AtlasRegion& atlas_region, const Vector2& position, float rad_rotation, const Vector2& origin, const Vector2& scale, const Color4& color) {
J2D::DrawPartialSprite(texture_atlas, position, Vector2(atlas_region.position), Vector2(atlas_region.size), rad_rotation, origin, scale, color);
}
void J2D::DrawSprite(const TextureAtlas& texture_atlas, const AtlasRegion& atlas_region, const Vector2& position, float rad_rotation, const Vector2& origin, const Vector2& scale, const Color4& color) {
J2D::DrawSprite(&texture_atlas, atlas_region, position, rad_rotation, origin, scale, color);
}
void J2D::DrawString(const Color4 &color, const std::string &text, const Vector2 &pos, u32 size, float scale,
const Font &font) {
DrawString(color, text, pos.x, pos.y, size, scale, font);
}

View File

@@ -48,7 +48,7 @@ void JGL::J3D::Begin(bool two_pass) {
auto aspect = (float) window_size.x / (float) window_size.y;
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glMultMatrixf(OpenGLPerspectiveProjectionRH(fov, aspect, 0.001, far_plane).data());
glLoadMatrixf(OpenGLPerspectiveProjectionRH(fov, aspect, 0.001, far_plane).data());
glMatrixMode(GL_MODELVIEW);
//Get what the draw color was before we did anything.
@@ -236,8 +236,9 @@ void JGL::J3D::BatchWireframeRevoSphere(const Color4& color, const Sphere* spher
glLineWidth(thickness);
glColor4ubv(color.ptr());
// TODO allocate once.
VRamList vertex_data(vertices.data(), vertices.size());
glBindBuffer(GL_ARRAY_BUFFER, vertex_data.GetHandle());
glBindBuffer(GL_ARRAY_BUFFER, vertex_data.Handle());
glVertexPointer(3, GL_FLOAT, sizeof(Vector3), nullptr);
// Render each sphere in the batch at their given position and radius.
@@ -245,10 +246,10 @@ void JGL::J3D::BatchWireframeRevoSphere(const Color4& color, const Sphere* spher
glPushMatrix();
glTranslatef(spheres[i].Position.x, spheres[i].Position.y, spheres[i].Position.z);
glScalef(spheres[i].Radius, spheres[i].Radius, spheres[i].Radius);
glDrawArrays(GL_LINE_LOOP, 0, vertex_data.GetLength());
glDrawArrays(GL_LINE_LOOP, 0, vertex_data.Length());
if (draw_stacks)
glRotatef(90, 0, 1, 0),
glDrawArrays(GL_LINE_LOOP, 0, vertex_data.GetLength());
glDrawArrays(GL_LINE_LOOP, 0, vertex_data.Length());
glPopMatrix();
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
@@ -371,8 +372,9 @@ void JGL::J3D::BatchWireframeAABB(const Color4& color, const AABB* boxes, const
glColor4ubv(color.ptr());
glLineWidth(thickness);
glBindBuffer(GL_ARRAY_BUFFER, ShapeCache::cube_vertex_data->GetHandle());
glBindBuffer(GL_ARRAY_BUFFER, ShapeCache::cube_vertex_data->Handle());
glVertexPointer(3, GL_FLOAT, sizeof(Vector3), nullptr);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ShapeCache::cube_index_data->Handle());
for (size_t i = 0; i < box_count; i++) {
Vector3 delta = (boxes[i].maxPoint - boxes[i].minPoint) / 2;
@@ -380,7 +382,7 @@ void JGL::J3D::BatchWireframeAABB(const Color4& color, const AABB* boxes, const
glPushMatrix();
glTranslatef(center.x, center.y, center.z);
glScalef(delta.x, delta.y, delta.z);
glDrawArrays(GL_LINES, 0, ShapeCache::cube_vertex_data->GetLength());
glDrawElements(GL_LINE_LOOP, ShapeCache::cube_index_data->Length(), GL_UNSIGNED_INT, nullptr);
glPopMatrix();
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
@@ -395,9 +397,9 @@ void JGL::J3D::WireframeAABB(const Color4& color, const Vector3& pos, const Vect
void JGL::J3D::BatchFillAABB(const Color4& color, const AABB* boxes, const size_t& box_count) {
glColor4ubv(color.ptr());
glBindBuffer(GL_ARRAY_BUFFER, ShapeCache::cube_vertex_data->GetHandle());
glBindBuffer(GL_ARRAY_BUFFER, ShapeCache::cube_vertex_data->Handle());
glVertexPointer(3, GL_FLOAT, sizeof(Vector3), nullptr);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ShapeCache::cube_index_data->GetHandle());
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ShapeCache::cube_index_data->Handle());
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
@@ -406,7 +408,7 @@ void JGL::J3D::BatchFillAABB(const Color4& color, const AABB* boxes, const size_
if (UsingLighting()) {
using_lights = true;
glEnableClientState(GL_NORMAL_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER, ShapeCache::cube_normal_data->GetHandle());
glBindBuffer(GL_ARRAY_BUFFER, ShapeCache::cube_normal_data->Handle());
glNormalPointer(GL_FLOAT, sizeof(float), nullptr);
}
@@ -485,8 +487,8 @@ void JGL::J3D::BatchFillSphere(const Color4& color, const Sphere* spheres, const
VRamList index_data(indices.data(), indices.size());
glColor4ubv(color.ptr());
glBindBuffer(GL_ARRAY_BUFFER, vertex_data.GetHandle());
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_data.GetHandle());
glBindBuffer(GL_ARRAY_BUFFER, vertex_data.Handle());
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_data.Handle());
glVertexPointer(3, GL_FLOAT, sizeof(Vector3), nullptr);
for (size_t i = 0; i < sphere_count; i++) {
@@ -494,7 +496,7 @@ void JGL::J3D::BatchFillSphere(const Color4& color, const Sphere* spheres, const
glPushMatrix();
glTranslatef(position.x, position.y, position.z);
glScalef(spheres[i].Radius, spheres[i].Radius, spheres[i].Radius);
glDrawElements(GL_TRIANGLES, index_data.GetLength(), GL_UNSIGNED_INT, nullptr);
glDrawElements(GL_TRIANGLES, index_data.Length(), GL_UNSIGNED_INT, nullptr);
glPopMatrix();
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
@@ -565,13 +567,13 @@ void JGL::J3D::DrawVertexArray(const Color4& color, const VertexArray& vertex_ar
glColor4ubv(color.ptr());
glEnableClientState(GL_VERTEX_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER, vertex_array.GetVertices()->GetHandle());
glBindBuffer(GL_ARRAY_BUFFER, vertex_array.GetVertices()->Handle());
glVertexPointer(3, GL_FLOAT, 0, nullptr);
glPushMatrix();
glTranslatef(position.x, position.y, position.z);
//glScalef(1,1,1);
glDrawArrays(GL_TRIANGLES, 0, vertex_array.GetVertices()->GetLength());
glDrawArrays(GL_TRIANGLES, 0, vertex_array.GetVertices()->Length());
glPopMatrix();
//glDrawElements(GL_LINES, vertex_array.GetIndices()->GetLength(), GL_UNSIGNED_INT, nullptr);

View File

@@ -86,7 +86,10 @@ namespace JGL {
u1, v0
};
cachedFont->appendGlyph(new CachedGlyph((char)charcode, texcoords, g->bitmap_left, g->bitmap_top, g->bitmap.width, g->bitmap.rows, (g->advance.x >> 6), (g->advance.y >> 6)));
float ascent = font.face->size->metrics.ascender / 64.0f;
float descent = -font.face->size->metrics.descender / 64.0f;
cachedFont->appendGlyph(new CachedGlyph((char) charcode, texcoords, g->bitmap_left, g->bitmap_top, g->bitmap.width, g->bitmap.rows, (g->advance.x >> 6), (g->advance.y >> 6), ascent, descent));
xoffset += g->bitmap.width;
charcode = FT_Get_Next_Char(font.face, charcode, &gindex);
@@ -95,7 +98,7 @@ namespace JGL {
return cachedFont;
}
void J2D::DrawString(const Color4& color, const std::string& text, float x, float y, float scale, u32 size, const Font& font) {
void J2D::DrawString(const Color4& color, const std::string& text, float x, float y, u32 size, float scale, const Font& font) {
// Offset by height to render at "correct" location.
y += size;
@@ -134,7 +137,7 @@ namespace JGL {
continue;
x2 = x + glyph->x2offset * scale;
y2 = y - glyph->y2offset * scale; // Adjust y-coordinate
y2 = y - glyph->y2offset * scale;
w = glyph->w * scale;
h = glyph->h * scale;
x += glyph->advanceX * scale;
@@ -156,7 +159,17 @@ namespace JGL {
}
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices.data());
glTexCoordPointer(2, GL_FLOAT, sizeof(Vector2), texcoords.data());
if (current_state.current_shader)
current_state.current_shader->SetInt("TEXTURE_UNIT_SET_COUNT", 1),
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_DrawString);
glDrawArrays(GL_TRIANGLES, 0, (int) vertices.size() * 6);
if (current_state.current_shader)
current_state.current_shader->SetInt("TEXTURE_UNIT_SET_COUNT", 0),
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0);
glBindTexture(GL_TEXTURE_2D, 0);
glColor4fv(default_state.draw_color);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
@@ -238,8 +251,18 @@ namespace JGL {
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
/*
if (current_state.current_shader)
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_DrawString);
*/
glDrawArrays(GL_TRIANGLES, 0, (int) vertices.size() * 6);
/*
if (current_state.current_shader)
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0);
*/
if (!draw_back_face)
glDisable(GL_CULL_FACE);

View File

@@ -133,7 +133,7 @@ JGL::State* JGL::StateStack::PreviousState() {
return &states.back();
}
JGL::State JGL::State::SaveState() {
JGL::State JGL::State::SaveState(const State& state) {
State result;
result.depth_test = glIsEnabled(GL_DEPTH_TEST);
@@ -152,6 +152,8 @@ JGL::State JGL::State::SaveState() {
glGetFloatv(GL_CURRENT_COLOR, result.draw_color);
glGetIntegerv(GL_VIEWPORT, result.viewport);
glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&result.current_fbo);
glGetIntegerv(GL_CURRENT_PROGRAM, &result.current_shader_handle);
result.current_shader = state.current_shader;
glGetIntegerv(GL_BLEND_SRC, &result.blend_func[0]);
glGetIntegerv(GL_BLEND_DST, &result.blend_func[1]);
@@ -198,9 +200,14 @@ void JGL::State::RestoreState(const State& state) {
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
if (state.color_array)
glEnable(GL_COLOR_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
else
glDisable(GL_COLOR_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
if (state.current_shader)
glUseProgram(state.current_shader->Handle());
else
glUseProgram(0);
glBindFramebuffer(GL_FRAMEBUFFER, state.current_fbo);
glViewport(state.viewport[0], state.viewport[1], state.viewport[2], state.viewport[3]);

View File

@@ -6,21 +6,59 @@
#include <J3ML/LinearAlgebra/Vector2.hpp>
#include <JGL/types/RenderTarget.h>
#include <JGL/types/Light.h>
#include <JGL/types/Shader.h>
#include <JGL/logger/logger.h>
namespace JGL {
class State;
class StateStack;
enum RENDERING_ROUTINE_ID : int32_t;
}
enum JGL::RENDERING_ROUTINE_ID : int32_t {
J2D_DrawPoint = 1,
J2D_DrawPoints = 2,
J2D_DrawLine = 3,
J2D_DrawLines = 4,
J2D_DrawDottedLine = 5,
J2D_DrawDashedLine = 6,
J2D_DrawGradientLine = 7,
J2D_OutlineRect = 8,
J2D_OutlineRoundedRect = 9,
J2D_OutlineChamferRect = 10,
J2D_FillRect = 11,
J2D_FillGradientRect = 12,
J2D_FillRoundedRect = 13,
J2D_FillChamferRect = 14,
J2D_DrawRenderTarget = 15,
J2D_DrawPartialRenderTarget = 16,
J2D_DrawSprite = 17,
J2D_DrawAlphaMaskSprite = 18,
J2D_DrawPartialSprite = 19,
J2D_DrawMirrorSprite = 20,
J2D_OutlineCircle = 21,
J2D_FillCircle = 22,
J2D_OutlineTriangle = 23,
J2D_FillTriangle = 24,
J2D_FillGradientTriangle = 25,
J2D_DrawCubicBezierCurve = 26,
J2D_OutlinePolygon = 27,
J2D_DrawString = 28,
J2D_DrawArc = 29,
};
class JGL::State {
public:
//Matrix4x4 transformation = Matrix4x4::Identity;
GLfloat clear_color[4] = {0, 0, 0, 1};
GLfloat draw_color[4] = {1, 1, 1, 1};
GLint viewport[4] = {0, 0, 0, 0};
GLint blend_func[2];
GLuint current_fbo = 0;
RenderTarget* current_render_target = nullptr;
GLint current_shader_handle = 0;
Shader* current_shader = nullptr;
bool texture_2D = false;
bool texture_coordinate_array = false;
@@ -36,7 +74,7 @@ public:
// List of all lights in the scene.
std::vector<const LightBase*> optional_lights{};
public:
static State SaveState();
static State SaveState(const State& state);
static void RestoreState(const State& state);
};
@@ -58,16 +96,18 @@ namespace JGL {
std::vector<Vector3> TriangleMeshVertexNormals(const Vector3* vertices, const size_t& vertex_count, const unsigned int* indices, const size_t& index_count);
inline StateStack state_stack;
inline Vector2i window_size;
inline bool supports_instanced = true;
}
namespace JGL::J2D {
// On windoze this can't be constexpr?
inline State default_state
{
{0, 0, 0, 1},
{1, 1, 1, 1},
{0, 0, 0, 0},
{GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA},
0, nullptr, false, false,
0, nullptr, 0, nullptr, false, false,
false, false, true, true,
true, false, 0,
{}, {}

View File

@@ -108,61 +108,36 @@ namespace JGL {
return Font(path);
}
Vector2 Font::MeasureString(const std::string &text, unsigned int ptSize) {
Vector2 extents = Vector2(0,0);
bool font_of_size_in_cache = false;
Vector2 Font::MeasureString(const std::string& text, unsigned int ptSize) {
Vector2 extents = Vector2::Zero;
for(const auto& f : fontCache.getFonts()) {
if (f->getFontSize() == ptSize) {
font_of_size_in_cache = true;
break;
for(auto& f : fontCache.getFonts()) {
// This edit technically "works" by solving the immediate problem of text-bounds returning incorrectly,
// But I am **sure** this is not how it should be implemented, I will leave that to Will to fix.
if (f->getFontIndex() == this->index && f->getFontSize() == ptSize) {
for (const char &c: text) {
auto glyph = f->getGlyph(c);
extents.x += glyph->advanceX;
extents.y = glyph->ascent + glyph->descent;
}
return extents;
}
}
if (font_of_size_in_cache) {
CachedFont* font;
for (auto* f: fontCache.getFonts())
if (f->getFontSize() == ptSize)
font = f;
for (const char& c : text)
extents.x += font->getGlyph(c)->advanceX;
extents.y = ptSize;
return extents;
}
jlog::Warning("Measuring a font size that is not cached, Defaulting to Jupiteroid.");
FT_Set_Pixel_Sizes(Fonts::Jupiteroid.face, ptSize, ptSize);
// No cache
FT_Set_Pixel_Sizes(this->face, ptSize, ptSize);
for (const char& c : text) {
// TODO: Fix segfault
//FT_GlyphSlot slot = Fonts::Jupiteroid.face->glyph;
//auto glyph_index = FT_Get_Char_Index(Fonts::Jupiteroid.face, c);
FT_GlyphSlot slot = face->glyph;
auto glyph_index = FT_Get_Char_Index(this->face, c);
//auto error = FT_Load_Glyph(Fonts::Jupiteroid.face, glyph_index, FT_LOAD_DEFAULT);
auto error = FT_Load_Glyph(this->face, glyph_index, FT_LOAD_DEFAULT);
if (error)
continue;
Vector2 advance = {static_cast<float>(slot->advance.x >> 6),
static_cast<float>(slot->advance.y >> 6)};
extents += advance;
// Gives smaller results than we'd want.
if (extents.y < slot->metrics.height / 64)
extents.y = slot->metrics.height / 64;
// Just fucking hardcode it, we know the glyph height is roughly always the ptSize anyway.
if (extents.y < ptSize)
extents.y = ptSize;
extents.x += static_cast<float>(slot->advance.x >> 6);
extents.y = (face->size->metrics.ascender / 64.0f) + (-face->size->metrics.descender / 64.0f);
}
return extents;
}

View File

@@ -10,7 +10,7 @@ std::array<GLfloat, 12> CachedGlyph::getTexCoords() const {
return texcoords;
}
CachedGlyph::CachedGlyph(char c, std::array<GLfloat, 12> texcoords, float x2offset, float y2offset, float w, float h, float advanceX, float advanceY) {
CachedGlyph::CachedGlyph(char c, std::array<GLfloat, 12> texcoords, float x2offset, float y2offset, float w, float h, float advanceX, float advanceY, float asc, float desc) {
character = c;
this->x2offset = x2offset;
this->y2offset = y2offset;
@@ -19,6 +19,8 @@ CachedGlyph::CachedGlyph(char c, std::array<GLfloat, 12> texcoords, float x2offs
this->advanceX = advanceX;
this->advanceY = advanceY;
this->texcoords = texcoords;
this->ascent = asc;
this->descent = desc;
}
void JGL::CachedFont::appendGlyph(JGL::CachedGlyph* glyph) {

View File

@@ -44,7 +44,7 @@ void JGL::RenderTarget::Erase() {
glDeleteFramebuffers(1, &framebuffer_object);
if (MSAAEnabled())
SetMSAAEnabled(MSAA_SAMPLE_RATE::MSAA_NONE);
SetMSAAEnabled(SampleRate::NONE);
}
Color4 JGL::RenderTarget::GetClearColor() const {
@@ -76,7 +76,7 @@ JGL::RenderTarget::RenderTarget(const JGL::Texture* texture, const Color4& clear
texture_created_by_us = false;
}
JGL::RenderTarget::RenderTarget(const Vector2i& size, const Color4& clear_color, bool use_depth, MSAA_SAMPLE_RATE sample_rate, FilteringMode filtering_mode) {
JGL::RenderTarget::RenderTarget(const Vector2i& size, const Color4& clear_color, bool use_depth, SampleRate sample_rate, FilteringMode filtering_mode) {
if (size.x < 1 || size.y < 1)
Logger::Fatal("Creating a render target where the color attachment is empty?");
@@ -116,7 +116,7 @@ JGL::RenderTarget::RenderTarget(const Vector2i& size, const Color4& clear_color,
this->size = size;
texture_created_by_us = true;
if (sample_rate != MSAA_SAMPLE_RATE::MSAA_NONE)
if (sample_rate != SampleRate::NONE)
SetMSAAEnabled(sample_rate);
}
@@ -161,8 +161,8 @@ void JGL::RenderTarget::Resize(const Vector2i& new_size) {
//Disable & Re-enable MSAA so the msaa buffer is remade with the correct dimensions.
if (MSAAEnabled()) {
MSAA_SAMPLE_RATE current_sample_rate = msaa_sample_rate;
SetMSAAEnabled(MSAA_SAMPLE_RATE::MSAA_NONE);
SampleRate current_sample_rate = msaa_sample_rate;
SetMSAAEnabled(SampleRate::NONE);
SetMSAAEnabled(current_sample_rate);
}
}
@@ -177,15 +177,15 @@ bool JGL::RenderTarget::OwnsTexture() const {
return texture_created_by_us;
}
JGL::MSAA_SAMPLE_RATE JGL::RenderTarget::GetMSAASampleRate() const {
JGL::SampleRate JGL::RenderTarget::GetMSAASampleRate() const {
return msaa_sample_rate;
}
bool JGL::RenderTarget::MSAAEnabled() const {
return msaa_sample_rate != MSAA_SAMPLE_RATE::MSAA_NONE;
return msaa_sample_rate != SampleRate::NONE;
}
bool JGL::RenderTarget::SetMSAAEnabled(JGL::MSAA_SAMPLE_RATE sample_rate) {
bool JGL::RenderTarget::SetMSAAEnabled(JGL::SampleRate sample_rate) {
// If we'd be setting the same sample_rate we already have.
if (sample_rate == msaa_sample_rate)
return false;
@@ -195,7 +195,7 @@ bool JGL::RenderTarget::SetMSAAEnabled(JGL::MSAA_SAMPLE_RATE sample_rate) {
return false;
// Remove it if they request no msaa or if what they requested is different than what they already have.
if (sample_rate == MSAA_SAMPLE_RATE::MSAA_NONE || msaa_sample_rate != MSAA_SAMPLE_RATE::MSAA_NONE) {
if (sample_rate == SampleRate::NONE || msaa_sample_rate != SampleRate::NONE) {
if(using_depth)
glDeleteRenderbuffers(1, &msaa_depth_buffer);
@@ -205,10 +205,10 @@ bool JGL::RenderTarget::SetMSAAEnabled(JGL::MSAA_SAMPLE_RATE sample_rate) {
msaa_framebuffer_object = 0;
msaa_depth_buffer = 0;
msaa_render_buffer = 0;
msaa_sample_rate = MSAA_SAMPLE_RATE::MSAA_NONE;
msaa_sample_rate = SampleRate::NONE;
// Only return here if they specifically requested no MSAA. else continue to change mode.
if (sample_rate == MSAA_SAMPLE_RATE::MSAA_NONE)
if (sample_rate == SampleRate::NONE)
return true;
}
@@ -239,7 +239,7 @@ bool JGL::RenderTarget::SetMSAAEnabled(JGL::MSAA_SAMPLE_RATE sample_rate) {
msaa_sample_rate = sample_rate;
if (failure)
SetMSAAEnabled(MSAA_SAMPLE_RATE::MSAA_NONE);
SetMSAAEnabled(SampleRate::NONE);
return failure;
}
@@ -288,7 +288,7 @@ void JGL::RenderTarget::Blit(const JGL::RenderTarget& source, JGL::RenderTarget*
void JGL::RenderTarget::Blit(const Texture* source, RenderTarget* destination, const Vector2i& position) {
auto temp = new RenderTarget(source);
Blit(*temp, destination);
Blit(*temp, destination, position);
delete temp;
}
@@ -348,8 +348,8 @@ JGL::RenderTarget::RenderTarget(const JGL::RenderTarget& rhs) {
operator delete(this_render_target);
}
Vector2i JGL::RenderTarget::MaximumSize() {
return Texture::MaximumSize();
Vector2i JGL::RenderTarget::MaxSize() {
return Texture::MaxSize();
}
void JGL::RenderTarget::RegenerateMipMaps() {

269
src/types/Shader.cpp Normal file
View File

@@ -0,0 +1,269 @@
#include <JGL/types/Shader.h>
#include <glad/glad.h>
namespace JGL {
class ShaderPreprocessor {
public:
std::string include_keyword = "#include";
ShaderPreprocessor() {}
std::string LoadShaderFile(const std::filesystem::path& filepath)
{
// if size is 0, it indicates, that we are at the top of the recursive load stack
bool stack_top = (already_included.size() == 0);
if (stack_top) {
already_included.emplace_back(filepath.string());
}
std::string ret_data = "";
std::ifstream file(filepath);
if (!file.good())
{
std::cout << "ERROR [ ShaderLoader::load_shader ]: Failed to start fstream, check if '" << filepath << "' exists\n";
return ret_data;
}
if (file.is_open()) {
std::string line;
while (getline(file, line)) {
if (line.find(include_keyword) == 0)
{
// Get path between double quotes
std::string rel_include_path = extract_first_between(line.substr(include_keyword.length()), '"', '"');
// Modify path according to os
// TODO: Use std::filesystem dummy...
// later, let me get this stolen code to work first.
#ifdef _WIN32
std::replace(rel_include_path.begin(), rel_include_path.end(), '/', '\\');
#elif __linux__
std::replace(rel_include_path.begin(), rel_include_path.end(), '\\', '/');
#endif
std::string full_include_path = extract_path(filepath.string()) + rel_include_path;
// Avoid including self
if (filepath == full_include_path) {
std::cout << "WARNING [ ShaderLoader::load_shader ]: '"<< filepath <<"' tried to include itself\n";
continue;
} else {
bool include_flag = true;
// check if the current file is already included
for (const auto& file_to_check : already_included) {
// Avoid duplicate includes
if (file_to_check == full_include_path) {
include_flag = false;
break;
}
}
// If not yet included, push path to include vector and replace line with file contents
if (include_flag) {
already_included.push_back(full_include_path);
// Repeat recursively.
ret_data += LoadShaderFile(full_include_path) + '\n';
}
}
} else {
ret_data += line + "\n";
}
}
file.close();
} else {
std::cout << "ERROR [ ShaderLoader::load_shader ]: Unable to open file '" << filepath << "'\n";
}
// We are back to the first call
if (stack_top) {
already_included.clear();
}
return ret_data;
}
std::string PreprocessShaderSource(const std::string& source);
private:
std::vector<std::string> already_included;
/// Helper function that strips filename from a path, basically getting the path to the directory containing the file.
std::string extract_path(const std::string& path)
{
// find the position of the last directory separator.
std::size_t pos = path.find_last_of("\\/");
// strip fname from path
if (pos != std::string::npos) {
return path.substr(0, pos+1);
}
return "";
}
std::string extract_first_between(const std::string& input, char start_symbol, char end_symbol)
{
size_t start_index = input.find(start_symbol);
size_t end_index = input.find(end_symbol, start_index+1);
std::string extracted = "";
if (start_index != std::string::npos && end_index != std::string::npos) {
extracted = input.substr(start_index + 1, end_index - start_index - 1);
} else {
std::cout << "ERROR [ ShaderLoader::extract_first_between ]: Start '" << start_symbol << "' or end symbol '" << end_symbol << "' not found" << std::endl;
}
return extracted;
}
};
Shader::Shader(const std::filesystem::path& vertex_source_path, const std::filesystem::path& fragment_source_path, const std::vector<std::pair<std::string, GLint>>& attribute_bindings) :
Shader(ReadFile(vertex_source_path), ReadFile(fragment_source_path), attribute_bindings) {
vertexPath = vertex_source_path.string();
fragmentPath = fragment_source_path.string();
}
void Shader::checkCompileErrors(GLuint shader, const std::string& type) {
GLint success;
GLchar infoLog[1024];
if (type != "PROGRAM") {
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
if (!success) {
glGetShaderInfoLog(shader, 1024, nullptr, infoLog);
OnCompilationErrorMessage.Invoke( std::format("COMPILATION_ERROR: {}", type), infoLog);
}
} else {
glGetProgramiv(shader, GL_LINK_STATUS, &success);
if (!success) {
glGetProgramInfoLog(shader, 1024, nullptr, infoLog);
OnCompilationErrorMessage.Invoke(std::format("COMPILATION_ERROR: {}", type), infoLog);
}
}
if (success)
std::cout << "Shader compiled successfully" << std::endl;
}
GLint Shader::Uniform(const std::string &name) const {
auto it = uniform_location_cache.find(name);
if (it == uniform_location_cache.end()) {
auto location = glGetUniformLocation(id, name.c_str());
uniform_location_cache[name] = location;
return location;
}
return it->second;
}
GLint Shader::Attribute(const std::string &name) const {
return glGetAttribLocation(id, name.c_str());
}
Shader::Shader(const std::string &vertex_code, const std::string &fragment_code, const std::vector<std::pair<std::string, GLint>>& attribute_bindings) {
vertexSource = vertex_code;
fragmentSource = fragment_code;
const char* vShaderCode = vertex_code.c_str();
const char* fShaderCode = fragment_code.c_str();
// 2. Compile shaders.
unsigned int vertex, fragment;
// vertex shader.
vertex = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex, 1, &vShaderCode, nullptr);
glCompileShader(vertex);
checkCompileErrors(vertex, "VERTEX");
// fragment shader
fragment = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment, 1, &fShaderCode, nullptr);
glCompileShader(fragment);
checkCompileErrors(fragment, "FRAGMENT");
// shader Program
id = glCreateProgram();
// bind attribute locations.
for (auto& a: attribute_bindings)
glBindAttribLocation(id, a.second, a.first.c_str());
glAttachShader(id, vertex);
glAttachShader(id, fragment);
glLinkProgram(id);
checkCompileErrors(id, "PROGRAM");
// delete the shaders as they're linked into our program now and are no longer necessary
glDeleteShader(vertex);
glDeleteShader(fragment);
}
bool Shader::Loaded() const { return id != 0; }
unsigned int Shader::Handle() const { return id;}
void Shader::SetBool(const std::string &name, bool value) const { glUniform1i(Uniform(name), (int)value); }
void Shader::SetInt(const std::string &name, int value) const { glUniform1i(Uniform(name), value); }
void Shader::SetFloat(const std::string &name, float value) const { glUniform1f(Uniform(name), value); }
void Shader::SetVector2(const std::string &name, const Vector2 &value) const { glUniform2f(Uniform(name), value.x, value.y); }
void Shader::SetVector2(const std::string &name, float x, float y) const { glUniform2f(Uniform(name), x, y); }
void Shader::SetVector3(const std::string &name, const Vector3 &value) const { glUniform3f(Uniform(name), value.x, value.y, value.z); }
void Shader::SetVector3(const std::string &name, float x, float y, float z) const { glUniform3f(Uniform(name), x, y, z); }
void Shader::SetVector4(const std::string &name, const Vector4 &value) const { glUniform4f(Uniform(name), value.x, value.y, value.z, value.w); }
void Shader::SetVector4(const std::string &name, float x, float y, float z, float w) const { glUniform4f(Uniform(name), x, y, z, w); }
void Shader::SetMatrix2x2(const std::string &name, const Matrix2x2 &value) const {
/// TODO: Verify if glsl expects row-major or col-major!!
bool transpose = false;
glUniformMatrix2fv(Uniform(name), 4, transpose, value.ptr());
}
void Shader::SetMatrix3x3(const std::string &name, const Matrix3x3 &value) const {
/// TODO: Verify if glsl expects row-major or col-major!!
bool transpose = false;
glUniformMatrix3fv(Uniform(name), 9, transpose, value.ptr());
}
void Shader::SetMatrix4x4(const std::string &name, const Matrix4x4 &value) const {
/// TODO: Verify if glsl expects row-major or col-major!!
bool transpose = false;
glUniformMatrix4fv(Uniform(name), 16, transpose, value.ptr());
}
std::string Shader::ReadFile(const std::filesystem::path &path) {
/*std::string contents;
std::ifstream file;
file.exceptions(std::ifstream::failbit | std::ifstream::badbit);
try {
// Open Files
file.open(path);
std::stringstream stream;
// Read file's buffer contents into streams
stream << file.rdbuf();
// close file handlers
file.close();
// convert stream into string
contents = stream.str();
return contents;
} catch (std::ifstream::failure& e) {
std::cout << "ERROR::FILE_READ_FAILURE: " << e.what() << std::endl;
return "";
}*/
ShaderPreprocessor processor;
return processor.LoadShaderFile(path);
}
}

View File

@@ -9,8 +9,8 @@
using namespace JGL;
Texture::Texture(const std::filesystem::path& file, FilteringMode filtering_mode, WrappingMode wrapping_mode, bool invert_y) :
invert_y(invert_y), filtering_mode(filtering_mode), wrapping_mode(wrapping_mode) {
Texture::Texture(const std::filesystem::path& file, FilteringMode filtering_mode, SampleRate anisotropy, WrappingMode wrapping_mode, bool invert_y) :
invert_y(invert_y), filtering_mode(filtering_mode), anisotropy(anisotropy), wrapping_mode(wrapping_mode) {
std::vector<unsigned char> pixels{};
std::ifstream ifStream(file, std::ios::in | std::ios::binary);
@@ -30,7 +30,7 @@ invert_y(invert_y), filtering_mode(filtering_mode), wrapping_mode(wrapping_mode)
if (invert_y) {
unsigned int row_size = size.x * 4;;
unsigned int row_size = size.x * 4;
if (format == ColorFormat::RGB)
row_size = size.x * 3;
@@ -51,7 +51,7 @@ std::vector<unsigned char> Texture::png(const std::filesystem::path& file) {
std::vector<unsigned char> result{};
int channels;
unsigned char* image_data = stbi_load(file.c_str(), &size.x, &size.y, &channels, 0);
unsigned char* image_data = stbi_load(file.string().c_str(), &size.x, &size.y, &channels, 0);
if (!image_data)
Logger::Fatal("Trying to load a texture from: " + file.string() + "but we couldn't read the file.");
@@ -100,7 +100,7 @@ std::vector<unsigned char> Texture::bmp(const std::filesystem::path& file) {
Texture::Texture(const Vector2i& size, FilteringMode filtering_mode) : invert_y(true), format(ColorFormat::RGBA), size(size), filtering_mode(filtering_mode), wrapping_mode(WrappingMode::CLAMP_TO_EDGE) {
if (SizeExceedsMaximum(size))
Logger::Error("Creating a texture where the size is bigger than the maximum for this system.");
Logger::Error("Creating a texture where the size is bigger than the maximum for this system, use Texture::MaximumSize() to check for this beforehand.");
GLuint previous_texture;
glGetIntegerv(GL_TEXTURE_BINDING_2D, (GLint*) &previous_texture);
@@ -138,6 +138,7 @@ Texture::Texture(const Vector2i& size, FilteringMode filtering_mode) : invert_y(
glBindTexture(GL_TEXTURE_2D, previous_texture);
}
void Texture::load(const unsigned char* pixels) {
// TODO a static member function to query for this.
if (SizeExceedsMaximum(size))
Logger::Error("Creating a texture where the size is bigger than the maximum for this system.");
@@ -169,17 +170,9 @@ void Texture::load(const unsigned char* pixels) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER),
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
if (filtering_mode == FilteringMode::NEAREST)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST),
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
else if (filtering_mode == FilteringMode::BILINEAR)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR),
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
else if (filtering_mode == FilteringMode::MIPMAP_NEAREST ||
filtering_mode == FilteringMode::MIPMAP_BILINEAR ||
filtering_mode == FilteringMode::MIPMAP_TRILINEAR) {
if (filtering_mode == FilteringMode::MIPMAP_NEAREST ||
filtering_mode == FilteringMode::MIPMAP_BILINEAR ||
filtering_mode == FilteringMode::MIPMAP_TRILINEAR) {
glGenerateMipmap(GL_TEXTURE_2D);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
@@ -195,6 +188,28 @@ void Texture::load(const unsigned char* pixels) {
else if (filtering_mode == FilteringMode::MIPMAP_TRILINEAR)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR),
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
if (anisotropy != SampleRate::NONE) {
if (anisotropy > MaxAnisotropySampleRate())
Logger::Error("Anisotropy set higher than the maximum value for this system, disabled, use Texture::MaxAnisotropy()."),
anisotropy = SampleRate::NONE;
else
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY, (int) anisotropy);
}
}
else if (filtering_mode == FilteringMode::NEAREST || filtering_mode == FilteringMode::BILINEAR) {
if (filtering_mode == FilteringMode::NEAREST)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST),
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
else if (filtering_mode == FilteringMode::BILINEAR)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR),
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
if (anisotropy != SampleRate::NONE)
Logger::Error("Anisotropy only applies when using mipmaps with a 3D perspective, disabled."),
anisotropy = SampleRate::NONE;
}
glBindTexture(GL_TEXTURE_2D, previous_texture);
}
@@ -246,13 +261,13 @@ WrappingMode Texture::GetWrappingMode() const {
return wrapping_mode;
}
Texture::Texture(const Color4* pixels, const Vector2i& size, const ColorFormat& format, FilteringMode filtering_mode, WrappingMode wrapping_mode) :
size(size), format(format), filtering_mode(filtering_mode), wrapping_mode(wrapping_mode) {
Texture::Texture(const Color4* pixels, const Vector2i& size, FilteringMode filtering_mode, SampleRate anisotropy, WrappingMode wrapping_mode) :
size(size), format(ColorFormat::RGBA), filtering_mode(filtering_mode), anisotropy(anisotropy), wrapping_mode(wrapping_mode) {
load((unsigned char*) pixels);
}
Texture::Texture(const Color3* pixels, const Vector2i& size, const ColorFormat& format, FilteringMode filtering_mode, WrappingMode wrapping_mode) :
size(size), format(format), filtering_mode(filtering_mode), wrapping_mode(wrapping_mode) {
Texture::Texture(const Color3* pixels, const Vector2i& size, FilteringMode filtering_mode, SampleRate anisotropy, WrappingMode wrapping_mode) :
size(size), format(ColorFormat::RGB), filtering_mode(filtering_mode), anisotropy(anisotropy), wrapping_mode(wrapping_mode) {
load((unsigned char*) pixels);
}
@@ -278,35 +293,36 @@ Texture::Texture(const Texture& rhs) {
operator delete(this_texture);
}
Texture::Texture(const Texture* textures, const size_t& texture_count) {
int length = 0;
int biggest_y = 0;
for (int i = 0; i < texture_count; i++) {
if (biggest_y < textures[i].size.y)
biggest_y = textures[i].size.y;
length += textures[i].size.x;
}
auto* result = new Texture(Vector2i(length, biggest_y));
auto render_target = RenderTarget(result);
int next_x = 0;
for (int i = 0; i < texture_count; i++) {
RenderTarget::Blit(&textures[i], &render_target, Vector2i(next_x, 0));
next_x += textures[i].GetDimensions().x;
}
}
bool Texture::SizeExceedsMaximum(const Vector2i& s) {
auto max_size = Texture::MaximumSize();
auto max_size = Texture::MaxSize();
return s.x > max_size.x || s.y > max_size.y;
}
Vector2i Texture::MaximumSize() {
Vector2i Texture::MaxSize() {
GLint max_size;
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_size);
return { max_size, max_size };
}
SampleRate Texture::MaxAnisotropySampleRate() {
if (!GLAD_GL_ARB_texture_filter_anisotropic)
return SampleRate::NONE;
float anisotropy;
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY, &anisotropy);
if (anisotropy == 2)
return SampleRate::X2;
else if (anisotropy == 4)
return SampleRate::X4;
else if (anisotropy == 8)
return SampleRate::X8;
else
return SampleRate::X16;
}
SampleRate Texture::GetAnisotropySampleRate() const {
return anisotropy;
}

View File

@@ -0,0 +1,63 @@
#include <JGL/types/TextureAtlas.h>
#include <JGL/types/RenderTarget.h>
#include <JGL/logger/logger.h>
using namespace JGL;
AtlasRegion TextureAtlas::GetRegion(unsigned int region) const {
if (region >= regions.size()) {
Logger::Error("Requesting region " + std::to_string(region) + "but it's outside of the texture atlas?");
return { {0, 0},{0, 0} };
}
return regions[region];
}
TextureAtlas::TextureAtlas(const Color4** pixels, const Vector2i** sizes, unsigned int texture_count, FilteringMode filtering_mode) {
std::vector<Texture> textures(texture_count);
int x_length = 0; int largest_height = 0;
for (unsigned int i = 0; i < texture_count; i++) {
textures[i] = Texture(pixels[i], *sizes[i], FilteringMode::NEAREST);
x_length += sizes[i]->x;
if (sizes[i]->y > largest_height)
largest_height = sizes[i]->y;
}
int rows = 1;
while (x_length > Texture::MaxSize().x)
x_length /= 2, rows += 1;
auto* result = new Texture(Vector2i(x_length, largest_height * rows), filtering_mode);
auto* render_target = new RenderTarget(result);
int current_x = 0; int current_y = 0;
for (unsigned int i = 0; i < texture_count; i++) {
if (current_x + sizes[i]->x > x_length)
current_x = 0, current_y += largest_height;
RenderTarget::Blit(&textures[i], render_target, Vector2i(current_x, current_y));
regions.emplace_back(AtlasRegion({ current_x, current_y }, { sizes[i]->x, sizes[i]->y }));
current_x += sizes[i]->x;
}
render_target->RegenerateMipMaps();
delete render_target;
this->texture_handle = result->GetHandle();
this->size = result->GetDimensions();
this->invert_y = result->Inverted();
this->filtering_mode = result->GetFilteringMode();
this->wrapping_mode = result->GetWrappingMode();
this->anisotropy = result->GetAnisotropySampleRate();
this->format = result->GetFormat();
// Don't call destructor so the v-ram isn't cleared.
operator delete(result);
}
TextureAtlas::TextureAtlas(const Color4* pixels, const Vector2i& size, AtlasRegion** regions, unsigned int region_count, FilteringMode filtering_mode, SampleRate anisotropy) : Texture(pixels, size, filtering_mode, anisotropy) {
this->regions.resize(region_count);
for (unsigned int i = 0; i < region_count; ++i)
this->regions[i] = *regions[i];
}

View File

@@ -2,44 +2,39 @@
#include <JGL/logger/logger.h>
#include <cstring>
// TODO combine the two load functions.
void JGL::VRamList::load(const GLfloat* data, const long& size) {
while (spin_lock) {}
spin_lock = true;
GLint current_array_buffer = 0;
glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &current_array_buffer);
glGenBuffers(1, &list_handle);
glBindBuffer(GL_ARRAY_BUFFER, list_handle);
glBufferData(GL_ARRAY_BUFFER, size, data, GL_STATIC_DRAW);
glBufferData(GL_ARRAY_BUFFER, size, data, usage_hint);
glBindBuffer(GL_ARRAY_BUFFER, current_array_buffer);
element_array_buffer = false;
num_elements = size / sizeof(GLfloat);
byte_count = size;
spin_lock = false;
}
void JGL::VRamList::load(const GLuint* data, const long& size) {
while (spin_lock) {}
spin_lock = true;
GLint current_element_array_buffer = 0;
glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &current_element_array_buffer);
glGenBuffers(1, &list_handle);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, list_handle);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, data, GL_STATIC_DRAW);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, data, usage_hint);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, current_element_array_buffer);
element_array_buffer = true;
num_elements = size / sizeof(GLuint);
byte_count = size;
spin_lock = false;
}
void JGL::VRamList::Erase() {
if (list_handle == 0)
return;
while (spin_lock) {}
spin_lock = true;
GLint current_element_array_buffer = 0;
glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &current_element_array_buffer);
@@ -55,36 +50,27 @@ void JGL::VRamList::Erase() {
glDeleteBuffers(1, &list_handle);
list_handle = 0;
spin_lock = false;
}
GLuint JGL::VRamList::GetHandle() const {
GLuint JGL::VRamList::Handle() const {
return list_handle;
}
bool JGL::VRamList::IsFloatArray() const {
return !element_array_buffer;
long JGL::VRamList::Length() const {
// sizeof(GLfloat) & sizeof(GLuint) are both 4 - Redacted.
return byte_count / 4;
}
long JGL::VRamList::GetLength() const {
return num_elements;
}
size_t JGL::VRamList::GetDataSize() const {
if (element_array_buffer)
return sizeof(GLuint) * num_elements;
return sizeof(GLfloat) * num_elements;
size_t JGL::VRamList::Size() const {
return byte_count;
}
void JGL::VRamList::SetData(void* data, const long& length) {
while (spin_lock) {}
spin_lock = true;
bool should_resize = (this->num_elements != length);
bool should_resize = (this->byte_count != length * 4);
if (should_resize) {
glDeleteBuffers(1, &list_handle);
list_handle = 0;
spin_lock = false;
element_array_buffer ? load((GLuint*) data, sizeof(GLuint) * length) : load((GLfloat*) data, sizeof(GLfloat) * length);
return;
}
@@ -100,33 +86,22 @@ void JGL::VRamList::SetData(void* data, const long& length) {
glGetIntegerv(buffer_binding, &current_buffer);
glBindBuffer(buffer_type, list_handle);
void* vram = glMapBuffer(buffer_type, GL_WRITE_ONLY);
if (!vram)
JGL::Logger::Fatal("Mapping in a VBO that doesn't exist?");
memcpy(vram, data, (element_array_buffer ? sizeof(GLuint) : sizeof(GLfloat)) * length);
if (!glUnmapBuffer(buffer_type))
JGL::Logger::Fatal("We couldn't unmap the buffer?");
size_t element_size = element_array_buffer ? sizeof(GLuint) : sizeof(GLfloat);
glBufferSubData(buffer_type, 0, length * element_size, data);
glBindBuffer(buffer_type, current_buffer);
spin_lock = false;
}
void JGL::VRamList::UpdateData(void* data, const long& offset, const long& length) {
while (spin_lock) {}
spin_lock = true;
if (offset + length > num_elements) {
JGL::Logger::Warning("Using UpdateData to out-of-bounds write the VRamList? I'll resize it for you, But this is slow.");
unsigned long oob_delta = (offset + length) - num_elements;
if (offset + length > Length()) {
unsigned long oob_delta = (offset + length) - Length();
if (element_array_buffer) {
auto list_data = GetDataUI();
list_data.resize(list_data.size() + oob_delta);
memcpy(list_data.data() + (offset * sizeof(GLuint)), data, length * sizeof(GLuint));
spin_lock = false; // This is going unlock and relock really fast, But this code fixes something considered wrong anyway - Redacted.
// This is going unlock and relock really fast, But this code fixes something considered wrong anyway - Redacted.
return SetData(list_data.data(), list_data.size());
}
@@ -134,7 +109,6 @@ void JGL::VRamList::UpdateData(void* data, const long& offset, const long& lengt
auto list_data = GetDataF();
list_data.resize(list_data.size() + oob_delta);
memcpy(list_data.data() + (offset * sizeof(GLfloat)), data, length * sizeof(GLfloat));
spin_lock = false;
return SetData(list_data.data(), list_data.size());
}
@@ -149,23 +123,13 @@ void JGL::VRamList::UpdateData(void* data, const long& offset, const long& lengt
glGetIntegerv(buffer_binding, &current_buffer);
glBindBuffer(buffer_type, list_handle);
void* vram = glMapBuffer(buffer_type, GL_WRITE_ONLY);
if (!vram)
JGL::Logger::Fatal("Mapping in a VBO that doesn't exist?");
size_t element_size = element_array_buffer ? sizeof(GLuint) : sizeof(GLfloat);
memcpy(reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(vram) + (offset * element_size)), data, length * element_size);
if (!glUnmapBuffer(buffer_type))
JGL::Logger::Fatal("We couldn't unmap the buffer?");
glBufferSubData(buffer_type, offset * element_size, length * element_size, data);
glBindBuffer(buffer_type, current_buffer);
spin_lock = false;
}
std::vector<GLfloat> JGL::VRamList::GetDataF() const {
while (spin_lock) {}
if (element_array_buffer)
JGL::Logger::Warning("Getting data as GLfloat but the buffer data is GLuint?");
@@ -178,12 +142,12 @@ std::vector<GLfloat> JGL::VRamList::GetDataF() const {
glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &current_buffer);
glBindBuffer(GL_ARRAY_BUFFER, list_handle);
std::vector<GLfloat> data(num_elements);
std::vector<GLfloat> data(byte_count / 4);
void* vram = glMapBuffer(GL_ARRAY_BUFFER, GL_READ_ONLY);
if (vram == nullptr)
JGL::Logger::Fatal("Mapping in a VBO that doesn't exist?");
memcpy(data.data(), vram, num_elements * sizeof(GLfloat));
memcpy(data.data(), vram, (byte_count / 4) * sizeof(GLfloat));
glUnmapBuffer(GL_ARRAY_BUFFER);
glBindBuffer(GL_ARRAY_BUFFER, current_buffer);
@@ -194,7 +158,6 @@ std::vector<GLfloat> JGL::VRamList::GetDataF() const {
}
std::vector<GLuint> JGL::VRamList::GetDataUI() const {
while (spin_lock) {}
if (!element_array_buffer)
JGL::Logger::Warning("Getting data as GLuint but the buffer data is GLfloat?");
@@ -203,13 +166,13 @@ std::vector<GLuint> JGL::VRamList::GetDataUI() const {
glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &current_buffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, list_handle);
std::vector<GLuint> data(num_elements);
std::vector<GLuint> data(byte_count / 4);
void* vram = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_READ_ONLY);
if (vram == nullptr)
JGL::Logger::Fatal("Mapping in a VBO that doesn't exist?");
memcpy(data.data(), vram, num_elements * sizeof(GLuint));
memcpy(data.data(), vram, (byte_count / 4) * sizeof(GLuint));
glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, current_buffer);
@@ -217,26 +180,35 @@ std::vector<GLuint> JGL::VRamList::GetDataUI() const {
return data;
}
JGL::VRamList::VRamList(const GLfloat* data, const long& length) {
JGL::VRamList::VRamList(const GLfloat* data, const long& length, VRamUsageHint hint) {
usage_hint = hint;
load(data, (long) sizeof(GLfloat) * length);
}
JGL::VRamList::VRamList(const GLuint* data, const long& length) {
JGL::VRamList::VRamList(const GLuint* data, const long& length, VRamUsageHint hint) {
usage_hint = hint;
load(data, (long) sizeof(GLuint) * length);
}
JGL::VRamList::VRamList(const Vector2* data, const long& length) {
JGL::VRamList::VRamList(const Vector2* data, const long& length, VRamUsageHint hint) {
usage_hint = hint;
load(reinterpret_cast<const GLfloat*>(data), (long) sizeof(Vector2) * length);
}
JGL::VRamList::VRamList(const Vector3* data, const long& length) {
JGL::VRamList::VRamList(const Vector3* data, const long& length, VRamUsageHint hint) {
usage_hint = hint;
load(reinterpret_cast<const GLfloat*>(data), (long) sizeof(Vector3) * length);
}
JGL::VRamList::VRamList(const Vector4* data, const long& length) {
JGL::VRamList::VRamList(const Vector4* data, const long& length, VRamUsageHint hint) {
usage_hint = hint;
load(reinterpret_cast<const GLfloat*>(data), (long) sizeof(Vector4) * length);
}
JGL::VRamList::VRamList(const Color4* data, const long& count, VRamUsageHint hint) {
usage_hint = hint;
load(reinterpret_cast<const GLfloat *>(data), (long) sizeof(GLuint) * count);
}
void JGL::VRamList::SetData(const GLfloat* data, const long& length) {
SetData((void*) data, length);
}
@@ -261,6 +233,10 @@ void JGL::VRamList::SetData(const Vector2i* data, const long& length) {
SetData((void*) data, length * 2);
}
void JGL::VRamList::SetData(const Color4* data, const long& count) {
SetData((void*) data, count);
}
void JGL::VRamList::UpdateData(const GLfloat* data, const long& offset, const long& length) {
UpdateData((void*) data, offset, length);
}
@@ -285,13 +261,15 @@ void JGL::VRamList::UpdateData(const Vector2i* data, const long& offset, const l
UpdateData((void*) data, offset, length * 2);
}
void JGL::VRamList::UpdateData(const Color4* data, const long &offset, const long &count) {
UpdateData((void*) data, offset, count);
}
JGL::VRamList::~VRamList() {
Erase();
}
JGL::VRamList::VRamList(const JGL::VRamList& rhs) {
while (rhs.spin_lock) {}
if (rhs.element_array_buffer) {
auto data_array = rhs.GetDataUI();
this->load(data_array.data(), data_array.size());
@@ -300,3 +278,32 @@ JGL::VRamList::VRamList(const JGL::VRamList& rhs) {
auto data_array = rhs.GetDataF();
this->load(data_array.data(), data_array.size());
}
JGL::VRamList::VRamList(const size_t& byte_count, bool element_array_buffer, VRamUsageHint hint) {
this->element_array_buffer = element_array_buffer;
this->byte_count = byte_count;
GLint current_buffer = 0;
GLenum buffer = element_array_buffer ? GL_ELEMENT_ARRAY_BUFFER : GL_ARRAY_BUFFER;
GLenum buffer_binding = element_array_buffer ? GL_ELEMENT_ARRAY_BUFFER_BINDING : GL_ARRAY_BUFFER_BINDING;
glGetIntegerv(buffer_binding, &current_buffer);
glGenBuffers(1, &list_handle);
glBindBuffer(buffer, list_handle);
glBufferData(buffer, byte_count, nullptr, hint);
glBindBuffer(buffer, current_buffer);
}
void JGL::VRamList::UpdateData(const uint8_t* data, const long& offset, const long& count) {
GLint current_buffer = 0;
GLenum buffer_type = element_array_buffer ? GL_ELEMENT_ARRAY_BUFFER : GL_ARRAY_BUFFER;
GLenum buffer_binding = element_array_buffer ? GL_ELEMENT_ARRAY_BUFFER_BINDING : GL_ARRAY_BUFFER_BINDING;
glGetIntegerv(buffer_binding, &current_buffer);
glBindBuffer(buffer_type, list_handle);
glBufferSubData(buffer_type, offset, count, data);
glBindBuffer(buffer_type, current_buffer);
}