Compare commits

...

17 Commits

Author SHA1 Message Date
a22a83d2f8 Fix out-of-date ReWindow API usage.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 2m0s
2024-12-06 12:05:29 -05:00
76cd48c9b7 Update Font.cpp
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 6m31s
Fixed several cases where the font index would be incorrect.
2024-12-06 11:34:38 -05:00
38bb1b22ce Include Jupiteroid as a default font
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m41s
2024-12-04 16:22:14 -05:00
122644a013 Update repositories
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 6m3s
2024-12-04 14:25:48 -05:00
Redacted
641f2de8d0 Update CMakeLists.txt
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 5m22s
2024-12-04 11:16:20 -05:00
6618aa5e6b just pushing what I have
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 2m20s
2024-12-01 11:00:48 -05:00
3970aa2718 Update GLAD
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 2m22s
2024-11-25 16:53:20 -05:00
fb5ca55fda Alpha masked sprite.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 7m44s
2024-11-22 07:46:48 -05:00
bcca6285af J2D DottedLine & DashedLine
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 6m41s
2024-11-20 18:04:21 -05:00
f8395726cd Improve performance of single-pixel blit.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 6m5s
2024-11-20 09:23:09 -05:00
ca7abb3044 DrawPartialRenderTarget
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m57s
2024-11-19 19:55:08 -05:00
14c45ab0f1 Merge branch 'master' of https://git.redacted.cc/Josh/JGL
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 5m34s
2024-11-14 18:19:42 -05:00
1ca5e5a694 Fix for Windows. 2024-11-14 18:22:28 -05:00
25fc3f8698 Fix for Windows.
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Has been cancelled
2024-11-14 18:20:57 -05:00
a836fc7b32 More batching & performance optimization.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 5m56s
2024-11-14 16:14:18 -05:00
e6dcc9d61e Reformatting
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 6m56s
2024-11-14 10:57:00 -05:00
6dff2f97c1 BatchWireframeAABB
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 5m54s
2024-11-13 21:07:21 -05:00
18 changed files with 9950 additions and 819 deletions

View File

@@ -22,17 +22,17 @@ CPMAddPackage(
CPMAddPackage(
NAME J3ML
URL https://git.redacted.cc/josh/j3ml/archive/Release-3.4.zip
URL https://git.redacted.cc/josh/j3ml/archive/3.4.3.zip
)
CPMAddPackage(
NAME ReWindow
URL https://git.redacted.cc/Redacted/ReWindow/archive/Prerelease-21.zip
URL https://git.redacted.cc/Redacted/ReWindow/archive/Prerelease-26.zip
)
CPMAddPackage(
NAME GLAD
URL https://git.redacted.cc/Redacted/glad/archive/v2.1ext_fboV2.zip
URL https://git.redacted.cc/Redacted/glad/archive/v2.1ext_fbo_depthtexture_shadow.zip
)
CPMAddPackage(

View File

@@ -53,7 +53,7 @@ JGL::J2D::End();
```
## Requirements
An OpenGL 2.1 or newer accelerator that supports the `GL_ARB_framebuffer_object` extension or
An OpenGL 2.1 or newer accelerator with at-least two texture mappers that supports the `GL_ARB_framebuffer_object` extension or
an implementation that can provide those features through alternative means (common on ArmSoC and Risc-V).
## Compatability

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 942 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

File diff suppressed because it is too large Load Diff

View File

@@ -13,8 +13,6 @@ extern "C" typedef struct FT_LibraryRec_* FT_Library;
namespace JGL
{
//bool Init();
bool InitTextEngine();
/// A Font class implementation.
@@ -23,7 +21,8 @@ namespace JGL
public:
/// Default constructor does not initialize any members
Font() = default;
Font(const std::filesystem::path& path);
explicit Font(const std::filesystem::path& path);
Font(const unsigned char* data, const size_t& size);
/// Destructor handles freeing of the underlying asset handle.
~Font();
static Font LoadTTF(const std::filesystem::path& filepath);
@@ -35,6 +34,6 @@ namespace JGL
Vector2 MeasureString(const std::string& text, unsigned int ptSize);
public:
int index = 0;
FT_Face face;
FT_Face face = nullptr;
};
}

View File

@@ -1,30 +1,65 @@
#pragma once
#include <J3ML/LinearAlgebra/Vector4.hpp>
#include <J3ML/LinearAlgebra/Vector3.hpp>
#include <J3ML/LinearAlgebra/DirectionVector.hpp>
#include <J3ML/Geometry/Frustum.hpp>
#include <Color4.hpp>
namespace JGL {
class Light;
class OmnidirectionalLight2D;
class PointLight2D;
class LightBase;
class PointLight;
class SpotLight;
}
class JGL::Light {
private:
/// W in position seems to determine whether or not the light is omni-directional. 1 = omni 0 = point.
/// Position is un-normalized screen space. For ex 500, 500, 1 for a light coming from where you're sitting.
class JGL::LightBase {
protected:
Vector4 position = {0, 0, 0, 1};
Color4 ambient = {0, 0, 0, 0};
Color4 diffuse = {0, 0, 0, 0};
Color4 specular = {0, 0, 0, 0};
float constant_attenuation;
float linear_attenuation;
float quadratic_attenuation;
public:
Light(const Vector3& position, const Color4& ambient, const Color4& diffuse, const Color4& specular);
Vector3 GetNormalizedSceenSpaceCoordinates() const;
[[nodiscard]] Vector3 GetPosition() const;
[[nodiscard]] Color4 GetAmbient() const;
[[nodiscard]] Color4 GetDiffuse() const;
[[nodiscard]] Color4 GetSpecular() const;
[[nodiscard]] float GetConstantAttenuation() const;
[[nodiscard]] float GetLinearAttenuation() const;
[[nodiscard]] float GetQuadraticAttenuation() const;
public:
/// Runs a calculation to determine the lights influence on a given point in 3D space.
/// @note 0 would be no impact, 1 would be the light is at the same position.
[[nodiscard]] virtual float GetAttenuationAtPosition(const Vector3& pos) const { return 0; }
public:
virtual ~LightBase() = default;
};
class JGL::OmnidirectionalLight2D {
private:
/// Omni-directional lights.
class JGL::PointLight : public LightBase {
public:
OmnidirectionalLight2D(const Vector3& position, const Color4& ambient, const Color4& diffuse, const Color4& specular);
[[nodiscard]] float GetAttenuationAtPosition(const Vector3& pos) const override;
public:
PointLight(const Vector3& position, const Color4& ambient, const Color4& diffuse, const Color4& specular, float constant_attenuation = 1, float linear_attenuation = 0, float quadratic_attenuation = 0);
};
/// Lights which only effect things in a given cone.
class JGL::SpotLight : public LightBase {
protected:
Matrix3x3 orientation;
float exponent;
float cut;
public:
/// Create a spotlight in 3D space.
/// @param position The position of the light in 3D space.
/// @param ro_mat Orientation of the light in 3D space.
/// @param cone_size_degrees The size of the cone.
/// @param exponent How focused the beam should be, Higher is more focused, Lower is less.
/// @param ambient How much this light should effect the ambient light of the scene.
/// @param diffuse
/// @param specular How much this light should effect specular highlights of objects being influenced by it.
SpotLight(const Vector3& position, const Matrix3x3& ro_mat, float cone_size_degrees, float exponent, const Color4& ambient, const Color4& diffuse, const Color4& specular, float constant_attenuation = 1, float linear_attenuation = 0, float quadratic_attenuation = 0);
};

View File

@@ -0,0 +1,18 @@
#pragma once
#include <JGL/types/RenderTarget.h>
#include <JGL/types/Light.h>
namespace JGL {
class ShadowMap;
}
/// You render your scene with all the static objects from the perspective of each static light to a ShadowMap.
/// Then, for shadow casters which move. Or lights that move. You only redraw that object from the perspective of each light.
/// Some of the approaches I saw for this were disgusting - Redacted.
class JGL::ShadowMap {
private:
RenderTarget shadow_map;
private:
void Create(const LightBase* Light);
};

View File

@@ -20,15 +20,15 @@ private:
bool spin_lock = false;
void load(const GLfloat* data, const long& size);
void load(const GLuint* data, const long& size);
void SetData(void* data, const long& length);
void UpdateData(void* data, const long& offset, const long& length);
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& length);
VRamList(const GLfloat* data, const long& length);
VRamList(const Vector2* data, const long& length);
VRamList(const Vector3* data, const long& length);
VRamList(const Vector4* data, const long& length);
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();
/** Copying around the VBO data to a new VBO like this is slow.
@@ -46,25 +46,23 @@ public:
[[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.
* "length" refers to the number of elements in data. Not the number of bytes. */
void SetData(const GLfloat* data, const long& length);
void SetData(const Vector2* data, const long& length);
void SetData(const Vector3* data, const long& length);
void SetData(const Vector4* data, const long& length);
/** 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 GLuint* data, const long& length);
void SetData(const Vector2i* data, const long& length);
void SetData(const GLuint* data, const long& count);
void SetData(const Vector2i* data, const long& count);
/** Update only a portion of the data in a VBO. Must be same type.
* "length" refers to the number of elements in data. Not the number of bytes.
* "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 */
void UpdateData(const GLfloat* data, const long& offset, const long& length);
void UpdateData(const Vector2* data, const long& offset, const long& length);
void UpdateData(const Vector3* data, const long& offset, const long& length);
void UpdateData(const Vector4* data, const long& offset, const long& length);
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 GLuint* data, const long& offset, const long& length);
void UpdateData(const Vector2i* data, const long& offset, const long& length);
void UpdateData(const GLuint* data, const long& offset, const long& count);
void UpdateData(const Vector2i* data, const long& offset, const long& count);
};

105
main.cpp
View File

@@ -4,12 +4,13 @@
#include <chrono>
#include <J3ML/LinearAlgebra/Vector2.hpp>
#include <JGL/logger/logger.h>
#include <J3ML/Geometry/AABB.hpp>
using J3ML::LinearAlgebra::Vector2;
using namespace JGL::Fonts;
using namespace JGL;
JGL::Font FreeSans;
JGL::Font Jupiteroid;
float fps = 0.0f;
class Gizmo
@@ -97,9 +98,8 @@ Gizmo c({350, 300});
Gizmo d({450, 250});
Texture* image;
Texture* image2;
Texture* image_mask;
RenderTarget* j2d_render_target;
RenderTarget* image2_render_target;
class JGLDemoWindow : public ReWindow::RWindow
{
@@ -107,28 +107,23 @@ public:
void initGL() {
camera = new Camera;
if (!JGL::Init(getSize(), 75, 100))
if (!JGL::Init(GetSize(), 75, 100))
Logger::Fatal("Initialization failed.");
FreeSans = JGL::Font("assets/fonts/FreeSans.ttf");
Jupiteroid = JGL::Font("assets/fonts/Jupiteroid.ttf");
glClearColor(0.f, 0.f, 0.f, 0.f);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);
glDepthMask(GL_TRUE);
image = new Texture("assets/sprites/Re3D.png", TextureFilteringMode::BILINEAR);
image_mask = new Texture("assets/sprites/alpha_mask_2.png");
j2d_render_target = new RenderTarget({540, 540}, {0,0,0,0}, false, MSAA_SAMPLE_RATE::MSAA_NONE);
image2 = image;
image2_render_target = new RenderTarget(image2);
J2D::Begin(image2_render_target);
J2D::DrawString(Colors::Red, "TEST", 0, 16, 1, 16, FreeSans);
J2D::FillRect(Colors::Blue, {0,0}, {4,4});
J2D::End();
//Texture::MultiplyByAlphaMask(*image, *image_mask);
}
Vector3 textAngle = {0,0,0};
EulerAngleXYZ textAngle = {0,0,0};
float fov = 90;
float sprite_radians = 0;
bool fov_increasing = true;
@@ -136,8 +131,9 @@ public:
void display() {
float dt = 1.f / fps;
JGL::Update(getSize());
float dt = GetDeltaTime();
JGL::Update(GetSize());
if (fov_increasing)
fov += 0.025;
@@ -151,7 +147,7 @@ public:
//J3D::ChangeFOV(fov);
sprite_radians += 0.005;
textAngle.y += (5.f * delta_time);
textAngle.yaw += 1;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
@@ -159,17 +155,22 @@ public:
camera->render();
// All 3D elements of the scene and JGL elements *must* be rendered before the 2D stuff
/* if rendering to screen space directly. */
// 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 * 100, true);
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[5] = {{{0,0, 0.5f}, 0.2125}, {{0,0, 0.5f}, 0.025},{{0,0, 0.5f}, 0.05},{{0,0, 0.5f}, 0.075},{{0,0, 0.5f}, 0.1}};
J3D::BatchWireframeRevoSphere(Colors::Green, sphere, 1, 1, 16, 16, true);
//J3D::FillAABB(Colors::Whites::AliceBlue, {0,0,0.5f}, {0.1f, 0.05f, 0.1f});
//J3D::WireframeAABB(Colors::Gray, {0,0,0.5f}, {0.11f, 0.06f, 0.11f});
Sphere sphere = {{0,0, 0.5f}, 0.2125};
J3D::BatchWireframeRevoSphere(Colors::Green, &sphere, 1, 1, 16, 16, true);
J3D::FillAABB(Colors::Whites::AliceBlue, {0,0,0.5f}, {0.1f, 0.1f, 0.1f});
J3D::WireframeAABB(Colors::Gray, {0,0,0.5f}, {0.11f, 0.06f, 0.11f});
J3D::WireframeOBB(Colors::Red, {0, 0, 1.5f}, {0.40f, 0.10f, 0.10f}, {0,textAngle.y, 0});
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::WireframeOBB(Colors::Red, {0, 0, 1.5f}, {0.40f, 0.10f, 0.10f}, {0,textAngle.y, 0});
//J3D::FillSphere({0,255,0,120}, sphere);
//J3D::DrawCubicBezierCurve(Colors::Blue, {0,0,0.3}, {0,0,0.5}, {0.2,0,0.3}, {0.2, 0.3, 0.1}, 30);
@@ -178,7 +179,7 @@ public:
J2D::Begin(j2d_render_target, true);
J2D::FillRect(Colors::Blue, {0,52}, {100,100});
J2D::DrawSprite(image2, {300, 400}, sprite_radians * 0.10f, {0.5,0.5}, {1, 1}, Colors::White);
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, {225, 300}, image->GetDimensions() * 0.25, image->GetDimensions() * 0.75, sprite_radians, {0.5, 0.5}, {1,1}, Colors::White);
J2D::FillRect(Colors::Pinks::HotPink, {68, 120}, {32, 32});
@@ -221,26 +222,43 @@ public:
//Draw the Render Target that we just drew all that stuff onto.
J2D::Begin();
J2D::DrawSprite(j2d_render_target, {0, 0}, 0, {0.5, 0.5}, {1,1}, Colors::White);
J2D::DrawSprite(image2_render_target, {300, 500}, 0, {0.5, 0.5}, {1,1}, Colors::White);
J2D::DrawPartialRenderTarget(j2d_render_target, {0, 0}, {0,0}, {512, 512});
J2D::DrawSprite(image, image_mask, {0, 0}, 0.25, {0.5, 0.5}, {1,1});
//J2D::DrawSprite(, {0, 0}, 0, {0.5, 0.5}, {1,1}, Colors::White);
//J2D::DrawSprite( {0, 0}, 0, {0.5, 0.5}, {1,1}, Colors::White);
J2D::End();
}
void OnRefresh(float elapsed) override {
if (isKeyDown(Keys::RightArrow))
fps = GetRefreshRate();
if (IsKeyDown(Keys::RightArrow))
camera->angle.y += 45.f * elapsed;
if (isKeyDown(Keys::LeftArrow))
if (IsKeyDown(Keys::LeftArrow))
camera->angle.y -= 45.f * elapsed;
if (isKeyDown(Keys::UpArrow))
if (IsKeyDown(Keys::UpArrow))
camera->angle.x -= 45.f * elapsed;
if (isKeyDown(Keys::DownArrow))
if (IsKeyDown(Keys::DownArrow))
camera->angle.x += 45.f * elapsed;
if (isKeyDown(Keys::Space))
if (IsKeyDown(Keys::Space))
camera->position.y += 1.f * elapsed;
if (isKeyDown(Keys::LeftShift))
if (IsKeyDown(Keys::LeftShift))
camera->position.y -= 1.f * elapsed;
//This is wrong of course. Just for testing purposes.
if (IsKeyDown(Keys::W))
camera->position.z += 1.f * elapsed;
if (IsKeyDown(Keys::S))
camera->position.z -= 1.f * elapsed;
if (IsKeyDown(Keys::A))
camera->position.x += 1.f * elapsed;
if (IsKeyDown(Keys::D))
camera->position.x -= 1.f * elapsed;
auto mouse = GetMouseCoordinates();
a.Update(mouse);
b.Update(mouse);
@@ -250,11 +268,11 @@ public:
int glError = glGetError();
if (glError != GL_NO_ERROR)
std::cout << glError << std::endl;
glSwapBuffers();
GLSwapBuffers();
}
void OnMouseButtonDown(const ReWindow::WindowEvents::MouseButtonDownEvent & ev) override
void OnMouseButtonDown(const ReWindow::MouseButtonDownEvent & ev) override
{
RWindow::OnMouseButtonDown(ev);
a.Grab();
@@ -263,7 +281,7 @@ public:
d.Grab();
}
void OnMouseButtonUp(const ReWindow::WindowEvents::MouseButtonUpEvent & ev) override
void OnMouseButtonUp(const ReWindow::MouseButtonUpEvent & ev) override
{
RWindow::OnMouseButtonUp(ev);
a.Release();
@@ -279,19 +297,20 @@ public:
int main(int argc, char** argv) {
auto* window = new JGLDemoWindow("JGL Demo Window", 1280, 720);
window->setRenderer(RenderingAPI::OPENGL);
window->SetRenderer(RenderingAPI::OPENGL);
window->Open();
window->initGL();
window->setResizable(true);
window->setVsyncEnabled(false);
window->SetResizable(true);
window->SetVsyncEnabled(false);
while (window->isAlive()) {
std::chrono::high_resolution_clock::time_point start = std::chrono::high_resolution_clock::now();
window->pollEvents();
window->refresh();
std::chrono::high_resolution_clock::time_point stop = std::chrono::high_resolution_clock::now();
std::chrono::duration<float> frame_time = stop - start;
fps = 1.0f / frame_time.count();
while (window->IsAlive()) {
window->ManagedRefresh();
//std::chrono::high_resolution_clock::time_point start = std::chrono::high_resolution_clock::now();
//window->PollEvents();
//window->Refresh();
//std::chrono::high_resolution_clock::time_point stop = std::chrono::high_resolution_clock::now();
//std::chrono::duration<float> frame_time = stop - start;
//fps = 1.0f / frame_time.count();
}
return 0;
}

8671
src/Fonts.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -31,6 +31,9 @@ bool wasBlendEnabled = false;
bool wasColorArrayEnabled = false;
GLint activeTextureUnit = 0;
std::array<const JGL::LightBase*, 8> J3D_Empty_Light_Array{nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr};
std::array<const JGL::LightBase*, 8> J3D_Required_Lights{};
std::vector<const JGL::LightBase*> J3D_Light_Array{};
float j3d_far_plane = 0;
float j3d_fov = 0;
Vector2 wS;
@@ -46,6 +49,8 @@ namespace JGL {
}
InitTextEngine();
Fonts::Init();
ShapeCache::Init();
wS = window_size;
j3d_fov = fovY;
j3d_far_plane = far_plane;
@@ -62,6 +67,10 @@ namespace JGL {
return false;
if (!GLAD_GL_ARB_framebuffer_object)
return false;
if (!GLAD_GL_ARB_depth_texture)
return false;
if (!GLAD_GL_ARB_shadow)
return false;
return true;
}
@@ -99,8 +108,10 @@ namespace JGL {
glGetIntegerv(GL_ACTIVE_TEXTURE, &activeTextureUnit);
activeTextureUnit = activeTextureUnit - GL_TEXTURE0;
if (activeTextureUnit != 0)
if (activeTextureUnit != 0) {
glActiveTexture(GL_TEXTURE0);
glClientActiveTexture(GL_TEXTURE0);
}
if (glIsEnabled(GL_DEPTH_TEST))
wasDepthTestEnabled = true,
@@ -171,6 +182,10 @@ namespace JGL {
if (!wasBlendEnabled)
glDisable(GL_BLEND);
//Select whatever texture mapper was selected before.
glActiveTexture(GL_TEXTURE0 + activeTextureUnit);
glClientActiveTexture(GL_TEXTURE0 + activeTextureUnit);
if (wasTexture2DEnabled)
glEnable(GL_TEXTURE_2D);
@@ -180,9 +195,6 @@ namespace JGL {
if (wasColorArrayEnabled)
glEnableClientState(GL_COLOR_ARRAY);
//Select whatever texture_handle unit was selected before.
glActiveTexture(GL_TEXTURE0 + activeTextureUnit);
//Put the draw color back how it was before.
glColor4fv(oldColor);
@@ -226,6 +238,55 @@ namespace JGL {
J2D::DrawLine(color, {x, y}, {w, h}, thickness);
}
void J2D::DrawDottedLine(const Color4& color, const Vector2& A, const Vector2& B, float spacing, float thickness) {
float distance = Vector2::Distance(A, B);
Vector2 direction = (B - A).Normalized();
unsigned int point_count = distance / spacing;
std::vector<Vector2> points(point_count);
if (spacing > distance)
Logger::Error("Drawing a dotted line that would have no dots?");
for (unsigned int i = 0; i < point_count; ++i)
points[i] = A + direction * (i * spacing);
return J2D::DrawPoints(color, points.data(), points.size(), thickness);
}
void J2D::DrawDottedLine(const Color4& color, float x1, float y1, float x2, float y2, float spacing, float thickness) {
return J2D::DrawDottedLine(color, {x1, y1}, {x2, y2}, spacing, thickness);
}
void J2D::DrawDashedLine(const Color4& color, const Vector2& A, const Vector2& B, float spacing, float dash_length, float thickness) {
float distance = Vector2::Distance(A, B);
Vector2 direction = (B - A).Normalized();
float length_of_dash_and_gap = dash_length + spacing;
unsigned int dash_count = distance / length_of_dash_and_gap;
if (spacing > distance)
Logger::Error("Drawing a dashed line that would have no dashes?");
Vector2 A_current, B_current;
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);
J2D::DrawLine(color, A_current, B_current, thickness);
}
// For the little piece at the end.
float distance_left = distance - (dash_count * length_of_dash_and_gap);
if (distance_left > 0) {
A_current = A + direction * (dash_count * length_of_dash_and_gap);
B_current = A_current + direction * std::min(dash_length, distance_left);
J2D::DrawLine(color, A_current, B_current, thickness);
}
}
void J2D::DrawDashedLine(const Color4& color, float x1, float y1, float x2, float y2, float spacing, float dash_length, float thickness) {
return J2D::DrawDashedLine(color, {x1, y1}, {x2, y2}, spacing, dash_length, thickness);
}
void J2D::DrawGradientLine(const Color4& color1, const Color4& color2, const Vector2& A, const Vector2& B, float thickness) {
if (!inJ2D)
Logger::Error("Drawing J2D element before J2D begin.");
@@ -390,6 +451,147 @@ namespace JGL {
if (rt.TextureCreatedByRenderTarget())
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
void J2D::DrawSprite(const Texture& texture, const Texture &alpha_mask, const Vector2& position, float rad_rotation,
const Vector2& origin, const Vector2& scale,const Color4& color, JGL::Direction inversion) {
if (!inJ2D)
Logger::Error("Drawing J2D element before J2D begin.");
if (texture.GetDimensions() != alpha_mask.GetDimensions())
Logger::Warning("The alpha mask and the texture are not the same size.");
const Vector2 size = texture.GetDimensions();
std::array<Vector2, 4> textureCoordinates{};
if (texture.GetFlags() & INVERT_Y)
textureCoordinates = {Vector2(0, 1), Vector2(0, 0), Vector2(1, 0), Vector2(1, 1)};
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 = position;
Vector2 scaled_size = scale * size;
Vector2 size2 = scaled_size;
float cos_theta = std::cos(rad_rotation);
float sin_theta = std::sin(rad_rotation);
std::array<Vector2, 4> vertices =
{
pos2, // Top-left vertex
{pos2.x, pos2.y + size2.y}, // Bottom-left
{pos2.x + size2.x, pos2.y + size2.y}, // Bottom-right
{pos2.x + size2.x, pos2.y} // Top-right
};
//Rotate the vertices about the origin by float rad_rotation.
if (rad_rotation != 0)
for (auto& v: vertices)
v = {(v.x - pos2.x - offset.x * scale.x) * cos_theta - (v.y - pos2.y - offset.y * scale.y) * sin_theta +
pos2.x + offset.x * scale.x,
(v.x - pos2.x - offset.x * scale.x) * sin_theta + (v.y - pos2.y - offset.y * scale.y) * cos_theta +
pos2.y + offset.y * scale.y
};
if (inversion == Direction::Vertical)
std::swap(textureCoordinates[0], textureCoordinates[1]),
std::swap(textureCoordinates[3], textureCoordinates[2]);
if (inversion == Direction::Horizontal)
std::swap(textureCoordinates[0], textureCoordinates[3]),
std::swap(textureCoordinates[1], textureCoordinates[2]);
glColor4ubv(color.ptr());
// Texture 0.
glEnable(GL_TEXTURE_2D);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glBindTexture(GL_TEXTURE_2D, texture.GetGLTextureHandle());
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices.data());
glTexCoordPointer(2, GL_FLOAT, sizeof(Vector2), textureCoordinates.data());
// Texture 1.
glActiveTexture(GL_TEXTURE1);
glClientActiveTexture(GL_TEXTURE1);
glEnable(GL_TEXTURE_2D);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnableClientState(GL_VERTEX_ARRAY);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glBindTexture(GL_TEXTURE_2D, alpha_mask.GetGLTextureHandle());
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices.data());
glTexCoordPointer(2, GL_FLOAT, sizeof(Vector2), textureCoordinates.data());
// Draw.
glDrawArrays(GL_QUADS, 0, 4);
// Reset Texture 1.
glBindTexture(GL_TEXTURE_2D, 0);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
glDisable(GL_TEXTURE_2D);
// Reset Texture 0.
glActiveTexture(GL_TEXTURE0);
glClientActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, 0);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisable(GL_TEXTURE_2D);
glColor4fv(baseColor);
}
void J2D::DrawSprite(const Texture* texture, const Texture* alpha_mask, const Vector2& position, float rad_rotation,
const Vector2& origin, const Vector2& scale,const Color4& color, Direction inversion) {
DrawSprite(*texture, *alpha_mask, position, rad_rotation, origin, scale, color, inversion);
}
void J2D::DrawSprite(const Texture& texture, const Texture& alpha_mask, float positionX, float positionY, float rad_rotation,
float originX, float originY,float scaleX, float scaleY,const Color4& color, Direction inversion) {
DrawSprite(texture, alpha_mask, {positionX, positionY}, rad_rotation, {originX, originY}, {scaleX, scaleY}, color, inversion);
}
void J2D::DrawSprite(const Texture* texture, const Texture* alpha_mask, float positionX, float positionY, float rad_rotation,
float originX, float originY,float scaleX, float scaleY,const Color4& color, Direction inversion) {
DrawSprite(*texture, *alpha_mask, {positionX, positionY}, rad_rotation, {originX, originY}, {scaleX, scaleY}, color, inversion);
}
void J2D::DrawPartialRenderTarget(const JGL::RenderTarget& rt, const Vector2& position, const Vector2& sub_texture_position, const Vector2& sub_texture_size,
float rad_rotation, const Vector2& origin, const Vector2& scale, const Color4& color, JGL::Direction inversion) {
//Correct for the render-target being upside-down.
Direction d{};
if (inversion == Direction::None && !(rt.GetJGLTexture()->GetFlags() & INVERT_Y))
d = Direction::Vertical;
else if (inversion == Direction::Horizontal) {
d = Direction::Horizontal;
if (!(rt.GetJGLTexture()->GetFlags() & INVERT_Y))
d = Direction::Horizontal | Direction::Vertical;
}
else if (inversion& Direction::Horizontal && inversion& Direction::Vertical) {
d = Direction::Horizontal;
if (!(rt.GetJGLTexture()->GetFlags() & INVERT_Y))
d = Direction::Horizontal | Direction::Vertical;
}
//Change the blending mode such that the alpha doesn't get multiplied again.
if (rt.TextureCreatedByRenderTarget())
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
J2D::DrawPartialSprite(*rt.GetJGLTexture(), position, sub_texture_position, sub_texture_size, rad_rotation, origin, scale, color, d);
if (rt.TextureCreatedByRenderTarget())
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
void J2D::DrawPartialRenderTarget(const JGL::RenderTarget* rt, const Vector2& position,const Vector2& sub_texture_position,const Vector2& sub_texture_size, float rad_rotation,
const Vector2& origin, const Vector2& scale, const Color4& color, Direction inversion) {
DrawPartialRenderTarget(*rt, position, sub_texture_position, sub_texture_size, rad_rotation, origin, scale, color, inversion);
}
void J2D::DrawSprite(const Texture& texture, const Vector2& pos, float rad_rotation, const Vector2& origin,
const Vector2& scale, const Color4& color, Direction inversion) {
if (!inJ2D)
@@ -462,7 +664,7 @@ namespace JGL {
color, inversion);
}
void J2D::DrawPartialSprite(const Texture& texture, const Vector2 &position, const Vector2& sub_texture_position,
void J2D::DrawPartialSprite(const Texture& texture, const Vector2& position, const Vector2& sub_texture_position,
const Vector2& sub_texture_size, float rad_rotation, const Vector2& origin,
const Vector2& scale, const Color4& color, Direction inversion) {
if (!inJ2D)
@@ -731,7 +933,7 @@ namespace JGL {
}
//TODO render all in once pass with GL_LINE_LOOP instead of separate lines.
void J2D::DrawCubicBezierCurve(const Color4 &color, const Vector2& controlA, const Vector2& pointA, const Vector2& pointB, const Vector2& controlB,
void J2D::DrawCubicBezierCurve(const Color4& color, const Vector2& controlA, const Vector2& pointA, const Vector2& pointB, const Vector2& controlB,
int subdivisions, float thickness) {
@@ -763,7 +965,7 @@ namespace JGL {
glColor4fv(baseColor);
}
void J2D::DrawGradientLine(const Color4 &color_a, const Color4 &color_b, float x, float y, float w, float h,
void J2D::DrawGradientLine(const Color4& color_a, const Color4& color_b, float x, float y, float w, float h,
float thickness) {
DrawGradientLine(color_a, color_b, {x, y}, {w, h}, thickness);
}
@@ -801,7 +1003,7 @@ namespace JGL {
glColor4fv(baseColor);
}
void J2D::OutlineRoundedRect(const Color4 &color, const Vector2 &pos, const Vector2 &size, float radius, float thickness)
void J2D::OutlineRoundedRect(const Color4& color, const Vector2& pos, const Vector2& size, float radius, float thickness)
{
// A rounded rectangle of size 2a x 2b with rounding radius r is given by
// f(x; a, r) + f(y; b, r) = 1
@@ -865,7 +1067,7 @@ namespace JGL {
}
void J2D::FillChamferRect(const Color4 &color, const Vector2 &pos, const Vector2 &size, float radius) {
void J2D::FillChamferRect(const Color4& color, const Vector2& pos, const Vector2& size, float radius) {
FillRoundedRect(color, pos, size, radius, 4);
}
@@ -900,14 +1102,12 @@ namespace JGL {
FillTriangle(color, {triA, triB, triC});
}
void
J2D::FillGradientTriangle(const Color4 &a_color, const Color4 &b_color, const Color4 &c_color, const Vector2 &tri_a,
const Vector2 &tri_b, const Vector2 &tri_c) {
void J2D::FillGradientTriangle(const Color4& a_color, const Color4& b_color, const Color4& c_color, const Vector2& tri_a,
const Vector2& tri_b, const Vector2& tri_c) {
FillGradientTriangle(a_color, b_color, c_color, {tri_a, tri_b, tri_c});
}
void
J2D::OutlineEllipse(const Color4 &color, const Vector2 &position, float radius_x, float radius_y, float thickness,
void J2D::OutlineEllipse(const Color4& color, const Vector2& position, float radius_x, float radius_y, float thickness,
int subdivisions) {
if (!inJ2D)
Logger::Error("Drawing J2D element before J2D begin.");
@@ -934,8 +1134,7 @@ namespace JGL {
glColor4fv(baseColor);
}
void
J2D::FillEllipse(const Color4 &color, const Vector2 &position, float radius_x, float radius_y, int subdivisions) {
void J2D::FillEllipse(const Color4& color, const Vector2& position, float radius_x, float radius_y, int subdivisions) {
if (!inJ2D)
Logger::Error("Drawing J2D element before J2D begin.");
@@ -965,7 +1164,18 @@ namespace JGL {
#pragma endregion
// TODO We're going to need two-pass rendering for occlusion queries and for determining what lights to use for what object.
// We'll do a pre-render pass where we render a *very* simple scene where each object is replaced by an AABB.
// Then, For each object we get a result on whether it was actually visible or not.
// For lights, We can get the closest point on the AABB to each light and see how much it would influence that point.
// Finally, we decide what lights to have enabled for the objects which were not occluded based on influence and distance from the camera.
// For objects that are *extremely* big, Like base level geometry, I'll have to use its collision map for this to look right. - Redacted.
#pragma region J3D
#pragma region internal_drawables
#pragma endregion
std::array<GLfloat, 16> OpenGLPerspectiveProjectionRH(float fovY, float aspect, float z_near, float z_far) {
std::array<GLfloat, 16> result{};
GLfloat f = 1.0f / std::tan(fovY * 0.5f * Math::Pi / 180.0f);
@@ -977,6 +1187,14 @@ namespace JGL {
return result;
}
float EffectOfLightOnPointIn3DSpace(const PointLight* light, const Vector3& position) {
Vector3 light_pos = light->GetPosition();
Vector3 vector_to_position = position - light_pos;
float distance = vector_to_position.Length();
return 1.0f / (light->GetConstantAttenuation() + light->GetLinearAttenuation() * distance + light->GetQuadraticAttenuation() * distance * distance);
}
void J3D::ChangeFOV(float fov) {
j3d_fov = fov;
}
@@ -985,6 +1203,17 @@ namespace JGL {
j3d_far_plane = far_plane;
}
void J3D::RequiredLight(const JGL::LightBase* light) {
for (auto* i : J3D_Required_Lights)
if (i == nullptr)
i = light;
}
void J3D::LightArray(const JGL::LightBase** lights, const size_t& light_count) {
for (size_t i = 0; i < light_count; i++)
J3D_Light_Array.push_back(lights[i]);
}
void J3D::Begin() {
auto aspect = (float) wS.x / (float) wS.y;
@@ -997,10 +1226,11 @@ namespace JGL {
glGetFloatv(GL_CURRENT_COLOR, oldColor);
glColor4fv(baseColor);
wasDepthTestEnabled = false;
if (glIsEnabled(GL_DEPTH_TEST))
wasDepthTestEnabled = true,
glDisable(GL_DEPTH_TEST);
if (!glIsEnabled(GL_DEPTH_TEST))
wasDepthTestEnabled = false,
glEnable(GL_DEPTH_TEST);
else
wasDepthTestEnabled = true;
wasVertexArraysEnabled = false;
if (!glIsEnabled(GL_VERTEX_ARRAY))
@@ -1027,6 +1257,12 @@ namespace JGL {
else
wasBlendEnabled = true;
// Reset the lights.
J3D_Required_Lights = J3D_Empty_Light_Array;
J3D_Light_Array = {};
glDisable(GL_LIGHTING);
if (!inJ2D)
inJ3D = true;
else
@@ -1034,8 +1270,8 @@ namespace JGL {
}
void J3D::End() {
if (wasDepthTestEnabled)
glEnable(GL_DEPTH_TEST);
if (!wasDepthTestEnabled)
glDisable(GL_DEPTH_TEST);
if (!wasVertexArraysEnabled)
glDisableClientState(GL_VERTEX_ARRAY);
@@ -1052,6 +1288,21 @@ namespace JGL {
if (wasTextureCoordArrayEnabled)
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
if (glIsEnabled(GL_LIGHTING)) {
glDisable(GL_LIGHTING);
glDisable(GL_LIGHT0);
glDisable(GL_LIGHT1);
glDisable(GL_LIGHT2);
glDisable(GL_LIGHT3);
glDisable(GL_LIGHT4);
glDisable(GL_LIGHT5);
glDisable(GL_LIGHT6);
glDisable(GL_LIGHT7);
}
J3D_Required_Lights = J3D_Empty_Light_Array;
J3D_Light_Array = {};
//Put the draw color back how it was before.
glColor4fv(oldColor);
inJ3D = false;
@@ -1070,16 +1321,16 @@ namespace JGL {
glColor4fv(baseColor);
}
void J3D::WireframeSphere(const Color4& color, const Vector3& position, float radius, float thickness, uint sectors, uint stacks) {
void J3D::WireframeSphere(const Color4& color, const Vector3& position, float radius, float thickness, unsigned int sectors, unsigned int stacks) {
Sphere sphere = {position, radius};
BatchWireframeSphere(color, &sphere, 1, thickness, sectors, stacks);
BatchWireframeSphere(color,& sphere, 1, thickness, sectors, stacks);
}
void J3D::WireframeSphere(const Color4& color, const Sphere& sphere, float thickness, uint sectors, uint stacks) {
BatchWireframeSphere(color, &sphere, 1, thickness, sectors, stacks);
void J3D::WireframeSphere(const Color4& color, const Sphere& sphere, float thickness, unsigned int sectors, unsigned int stacks) {
BatchWireframeSphere(color,& sphere, 1, thickness, sectors, stacks);
}
void J3D::BatchWireframeSphere(const Color4& color, const Sphere* spheres, const size_t& sphere_count, float thickness, uint sectors, uint stacks) {
void J3D::BatchWireframeSphere(const Color4& color, const Sphere* spheres, const size_t& sphere_count, float thickness, unsigned int sectors, unsigned int stacks) {
if (!inJ3D)
Logger::Error("Drawing J3D element before J3D begin.");
@@ -1089,7 +1340,7 @@ namespace JGL {
int index = 0;
for (int i = 0; i <= sectors; i++) {
float lat = M_PI * (-0.5 + (float) i / sectors);
float lat = J3ML::Math::Pi * (-0.5 + (float) i / sectors);
float z = J3ML::Math::Sin(lat);
float zr = J3ML::Math::Cos(lat);
@@ -1121,23 +1372,23 @@ namespace JGL {
glColor4fv(baseColor);
}
void J3D::WireframeRevoSphere(const Color4& color, const Vector3& position, float radius, float thickness, uint sectors, uint revolutions, bool draw_stacks) {
void J3D::WireframeRevoSphere(const Color4& color, const Vector3& position, float radius, float thickness, unsigned int sectors, unsigned int revolutions, bool draw_stacks) {
Sphere sphere = {position, radius};
BatchWireframeRevoSphere(color, &sphere, 1, thickness, sectors, revolutions, draw_stacks);
BatchWireframeRevoSphere(color,& sphere, 1, thickness, sectors, revolutions, draw_stacks);
}
void J3D::WireframeRevoSphere(const Color4& color, const Sphere& sphere, float thickness, uint sectors, uint revolutions, bool draw_stacks) {
BatchWireframeRevoSphere(color, &sphere, 1, thickness, sectors, revolutions, draw_stacks);
void J3D::WireframeRevoSphere(const Color4& color, const Sphere& sphere, float thickness, unsigned int sectors, unsigned int revolutions, bool draw_stacks) {
BatchWireframeRevoSphere(color,& sphere, 1, thickness, sectors, revolutions, draw_stacks);
}
void J3D::BatchWireframeRevoSphere(const Color4& color, const Sphere* spheres, const size_t& sphere_count, float thickness, uint sectors, uint revolutions, bool draw_stacks) {
void J3D::BatchWireframeRevoSphere(const Color4& color, const Sphere* spheres, const size_t& sphere_count, float thickness, unsigned int sectors, unsigned int revolutions, bool draw_stacks) {
float r = 1;
std::vector<Vector3> vertices;
vertices.reserve((sectors + 1) * (revolutions + 1));
std::vector<Vector3> cross_section(sectors + 1);
for (int i = 0; i <= sectors; i++) {
float lat = M_PI * (-0.5 + (float)i / sectors);
float lat = J3ML::Math::Pi * (-0.5 + (float)i / sectors);
float z = J3ML::Math::Sin(lat);
float zr = J3ML::Math::Cos(lat);
cross_section[i] = Vector3(0, zr * r, z * r);
@@ -1145,7 +1396,7 @@ namespace JGL {
// Revolve
for (int j = 0; j <= revolutions; j++) {
float lng = 2 * M_PI * (float)j / revolutions;
float lng = 2 * J3ML::Math::Pi * (float)j / revolutions;
float cosLng = J3ML::Math::Cos(lng);
float sinLng = J3ML::Math::Sin(lng);
@@ -1160,19 +1411,23 @@ namespace JGL {
glLineWidth(thickness);
glColor4ubv(color.ptr());
glVertexPointer(3, GL_FLOAT, sizeof(Vector3), vertices.data());
VRamList vertex_data(vertices.data(), vertices.size());
glBindBuffer(GL_ARRAY_BUFFER, vertex_data.GetHandle());
glVertexPointer(3, GL_FLOAT, sizeof(Vector3), nullptr);
// Render each sphere in the batch at their given position and radius.
for(size_t i = 0; i < sphere_count; i++) {
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, vertices.size());
if (draw_stacks)
glRotatef(90, 0, 1, 0),
glDrawArrays(GL_LINE_LOOP, 0, vertices.size());
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());
if (draw_stacks)
glRotatef(90, 0, 1, 0),
glDrawArrays(GL_LINE_LOOP, 0, vertex_data.GetLength());
glPopMatrix();
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
glColor4fv(baseColor);
}
@@ -1250,8 +1505,7 @@ namespace JGL {
}
}
void J3D::WireframeIcosahedron(const Color4 &color, const Vector3 &position, float radius, float thickness) {
void J3D::WireframeIcosahedron(const Color4& color, const Vector3& position, float radius, float thickness) {
if (!inJ3D)
Logger::Error("Drawing J3D element before J3D begin.");
@@ -1296,220 +1550,170 @@ namespace JGL {
glColor4fv(baseColor);
}
void J3D::WireframeAABB(const Color4& color, const Vector3& pos, const Vector3& radii, float thickness) {
if (!inJ3D)
Logger::Error("Drawing J3D element before J3D begin.");
// Top of cube
Vector3 top_right_top = pos + Vector3(radii.x, radii.y, -radii.z);
Vector3 top_left_top = pos + Vector3(-radii.x, radii.y, -radii.z);
Vector3 bottom_left_top = pos - radii;
Vector3 bottom_right_top = pos + Vector3(radii.x, radii.y, radii.z);
// Bottom of cube
Vector3 top_right_bottom = pos + Vector3(radii.x, -radii.y, radii.z);
Vector3 top_left_bottom = pos + Vector3(-radii.x, -radii.y, radii.z);
Vector3 bottom_left_bottom = pos - radii;
Vector3 bottom_right_bottom = pos + Vector3(radii.x, -radii.y, -radii.z);
// front of cube
Vector3 top_right_front = pos + radii;
Vector3 top_left_front = pos + Vector3(-radii.x, radii.y, radii.z);
Vector3 bottom_left_front = pos + Vector3(-radii.x, -radii.y, radii.z);
Vector3 bottom_right_front = pos + Vector3(radii.x, -radii.y, radii.z);
// back of cube
Vector3 top_right_back = pos + Vector3(radii.x, -radii.y, -radii.z);
Vector3 top_left_back = pos - radii;
Vector3 bottom_left_back = pos + Vector3(-radii.x, radii.y, -radii.z);
Vector3 bottom_right_back = pos + Vector3(radii.x, radii.y, -radii.z);
// left of cube
Vector3 top_right_left = pos + Vector3(-radii.x, radii.y, radii.z);
Vector3 top_left_left = pos + Vector3(-radii.x, radii.y, -radii.z);
Vector3 bottom_left_left = pos - radii;
Vector3 bottom_right_left = pos + Vector3(-radii.x, -radii.y, radii.z);
// right of cube
Vector3 top_right_right = pos + Vector3(radii.x, radii.y, -radii.z);
Vector3 top_left_right = pos + radii;
Vector3 bottom_left_right = pos + Vector3(radii.x, -radii.y, radii.z);
Vector3 bottom_right_right = pos + Vector3(radii.x, -radii.y, -radii.z);
std::vector<Vector3> vertices
{
top_right_top, top_left_top, bottom_left_top, bottom_right_top,
top_right_bottom, top_left_bottom, bottom_left_bottom, bottom_right_bottom,
top_right_front, top_left_front, bottom_left_front, bottom_right_front,
top_right_back, top_left_back, bottom_left_back, bottom_right_back,
top_right_left, top_left_left, bottom_left_left, bottom_right_left,
top_right_right, top_left_right, bottom_left_right, bottom_right_right
};
//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
void J3D::BatchWireframeAABB(const Color4& color, const AABB* boxes, const size_t& box_count, float thickness) {
glColor4ubv(color.ptr());
glLineWidth(thickness);
glColor4ubv(color.ptr());
//glBindBuffer(GL_ARRAY_BUFFER, vertices.size());
glVertexPointer(3, GL_FLOAT, sizeof(Vector3), vertices.data());
glDrawArrays(GL_LINE_LOOP, 0, vertices.size());
glBindBuffer(GL_ARRAY_BUFFER, ShapeCache::cube_vertex_data->GetHandle());
glVertexPointer(3, GL_FLOAT, sizeof(Vector3), nullptr);
for (size_t i = 0; i < box_count; i++) {
Vector3 delta = (boxes[i].maxPoint - boxes[i].minPoint) / 2;
Vector3 center = boxes[i].Centroid();
glPushMatrix();
glTranslatef(center.x, center.y, center.z);
glScalef(delta.x, delta.y, delta.z);
glDrawArrays(GL_LINES, 0, ShapeCache::cube_vertex_data->GetLength());
glPopMatrix();
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
glColor4fv(baseColor);
}
void J3D::FillAABB(const Color4 &color, const Vector3 &pos, const Vector3 &radii) {
void J3D::WireframeAABB(const Color4& color, const Vector3& pos, const Vector3& radii, float thickness) {
AABB aabb = {Vector3(pos.x - radii.x, pos.y - radii.y, pos.z - radii.z), Vector3(pos.x + radii.x, pos.y + radii.y, pos.z + radii.z)};
BatchWireframeAABB(color, &aabb, 1, thickness);
}
void J3D::BatchFillAABB(const Color4& color, const AABB* boxes, const size_t& box_count) {
if (!inJ3D)
Logger::Error("Drawing J3D element before J3D begin.");
// Top of cube
Vector3 top_right_top = pos + Vector3(radii.x, radii.y, -radii.z);
Vector3 bottom_right_top = pos + Vector3(radii.x, radii.y, radii.z);
Vector3 bottom_left_top = pos + Vector3(-radii.x, radii.y, radii.z);
Vector3 top_left_top = pos + Vector3(-radii.x, radii.y, -radii.z);
// Bottom of cube
Vector3 top_right_bottom = pos + Vector3(radii.x, -radii.y, radii.z);
Vector3 bottom_right_bottom = pos + Vector3(radii.x, -radii.y, -radii.z);
Vector3 bottom_left_bottom = pos + Vector3(-radii.x, -radii.y, -radii.z);
Vector3 top_left_bottom = pos + Vector3(-radii.x, -radii.y, radii.z);
// front of cube
Vector3 top_right_front = pos + Vector3(radii.x, radii.y, radii.z);
Vector3 bottom_right_front = pos + Vector3(radii.x, -radii.y, radii.z);
Vector3 bottom_left_front = pos + Vector3(-radii.x, -radii.y, radii.z);
Vector3 top_left_front = pos + Vector3(-radii.x, radii.y, radii.z);
// back of cube
Vector3 top_right_back = pos + Vector3(radii.x, radii.y, -radii.z);
Vector3 bottom_right_back = pos + Vector3(radii.x, -radii.y, -radii.z);
Vector3 bottom_left_back = pos + Vector3(-radii.x, -radii.y, -radii.z);
Vector3 top_left_back = pos + Vector3(-radii.x, radii.y, -radii.z);
// left of cube
Vector3 top_right_left = pos + Vector3(-radii.x, radii.y, radii.z);
Vector3 bottom_right_left = pos + Vector3(-radii.x, -radii.y, radii.z);
Vector3 bottom_left_left = pos + Vector3(-radii.x, -radii.y, -radii.z);
Vector3 top_left_left = pos + Vector3(-radii.x, radii.y, -radii.z);
// right of cube
Vector3 top_right_right = pos + Vector3(radii.x, radii.y, -radii.z);
Vector3 bottom_right_right = pos + Vector3(radii.x, -radii.y, -radii.z);
Vector3 bottom_left_right = pos + Vector3(radii.x, -radii.y, radii.z);
Vector3 top_left_right = pos + Vector3(radii.x, radii.y, radii.z);
std::vector<Vector3> vertices
{
top_right_top, bottom_right_top, bottom_left_top, top_left_top,
top_right_bottom, bottom_right_bottom, bottom_left_bottom, top_left_bottom,
top_right_front, bottom_right_front, bottom_left_front, top_left_front,
top_right_back, bottom_right_back, bottom_left_back, top_left_back,
top_right_left, bottom_right_left, bottom_left_left, top_left_left,
top_right_right, bottom_right_right, bottom_left_right, top_left_right
};
//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glColor4ubv(color.ptr());
//glBindBuffer(GL_ARRAY_BUFFER, vertices.size());
glVertexPointer(3, GL_FLOAT, sizeof(Vector3), vertices.data());
glDrawArrays(GL_QUADS, 0, vertices.size());
glColor4fv(baseColor);
glBindBuffer(GL_ARRAY_BUFFER, ShapeCache::cube_vertex_data->GetHandle());
glVertexPointer(3, GL_FLOAT, sizeof(Vector3), nullptr);
for (size_t i = 0; i < box_count; i++) {
Vector3 delta = (boxes[i].maxPoint - boxes[i].minPoint) / 2;
Vector3 center = boxes[i].Centroid();
glPushMatrix();
glTranslatef(center.x, center.y, center.z);
glScalef(delta.x, delta.y, delta.z);
glDrawArrays(GL_QUAD_STRIP, 0, ShapeCache::cube_vertex_data->GetLength());
glPopMatrix();
}
glColor4fv(oldColor);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
void J3D::FillAABB(const Color4& color, const Vector3& pos, const Vector3& radii) {
AABB box = {pos - radii, pos + radii};
BatchFillAABB(color, &box, 1);
}
void J3D::FillAABB(const Color4 &color, const AABB &aabb) {
FillAABB(color, aabb.Centroid(), aabb.Size());
void J3D::FillAABB(const Color4& color, const AABB& aabb) {
BatchFillAABB(color, &aabb, 1);
}
void J3D::FillSphere(const Color4 &color, const Vector3& position, float radius, uint sectors, uint stacks) {
void J3D::FillSphere(const Color4& color, const Sphere& sphere, unsigned int sectors, unsigned int stacks) {
BatchFillSphere(color, &sphere, 1, sectors, stacks);
}
void J3D::BatchFillSphere(const Color4& color, const Sphere* spheres, const size_t& sphere_count, unsigned int sectors, unsigned int stacks) {
if (!inJ3D)
Logger::Error("Drawing J3D element before J3D begin.");
float r = 1;
std::vector<Vector3> vertices((sectors + 1) * (stacks + 1));
std::vector<unsigned int> indices; indices.reserve(sectors * stacks * 6);
glColor4ubv(color.ptr());
unsigned int lats = sectors;
unsigned int longs = stacks;
float r = radius;
std::vector<Vector3> vertices((lats + 1) * (longs + 1));
float two_pi = 2 * J3ML::Math::Pi;
int index = 0;
for (int i = 0; i <= lats; i++) {
float lat = M_PI * (-0.5 + (float) i / lats);
for (int i = 0; i <= sectors; i++) {
float lat = J3ML::Math::Pi * (-0.5 + (float) i / sectors);
float z = J3ML::Math::Sin(lat);
float zr = J3ML::Math::Cos(lat);
for (int j = 0; j <= longs; j++) {
float lng = 2 * J3ML::Math::Pi * (float) (j - 1) / longs;
for (int j = 0; j <= stacks; j++) {
float lng = two_pi * (float) (j - 1) / stacks;
float x = J3ML::Math::Cos(lng);
float y = J3ML::Math::Sin(lng);
float pos_x = r * x * zr;
float pos_y = r * y * zr;
float pos_z = r * z;
pos_x += position.x;
pos_y += position.y;
pos_z += position.z;
vertices[index++] = Vector3(pos_x, pos_y, pos_z);
}
}
for (int i = 0; i < sectors; i++) {
for (int j = 0; j < stacks; j++) {
int first_index = i * (stacks + 1) + j;
int second_index = first_index + stacks + 1;
indices.push_back(first_index);
indices.push_back(second_index);
indices.push_back(first_index + 1);
indices.push_back(second_index);
indices.push_back(second_index + 1);
indices.push_back(first_index + 1);
}
}
VRamList vertex_data(vertices.data(), vertices.size());
VRamList index_data(indices.data(), indices.size());
glColor4ubv(color.ptr());
glVertexPointer(3, GL_FLOAT, sizeof(Vector3), vertices.data());
glDrawArrays(GL_TRIANGLE_STRIP, 0, vertices.size()); // TODO: Make it render correctingly!
glColor4fv(baseColor);
glBindBuffer(GL_ARRAY_BUFFER, vertex_data.GetHandle());
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_data.GetHandle());
glVertexPointer(3, GL_FLOAT, sizeof(Vector3), nullptr);
for (size_t i = 0; i < sphere_count; i++) {
const Vector3 position = spheres[i].Position;
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);
glPopMatrix();
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glColor4fv(oldColor);
}
void J3D::WireframeAABB(const Color4 &color, const AABB &aabb, float thickness) {
WireframeAABB(color, aabb.Centroid(), aabb.Size(), thickness);
void J3D::FillSphere(const Color4& color, const Vector3& position, float radius, unsigned int sectors, unsigned int stacks) {
Sphere sphere = {position, radius};
BatchFillSphere(color, &sphere, 1, sectors, stacks);
}
// Why is this here?
float ComputeBinomial(int n, int k)
{
float value = 1.f;
for (int i = 1; i <= k; i++)
value = value * ((n + 1 - i) / i);
if (n == k)
value = 1;
return value;
void J3D::WireframeAABB(const Color4& color, const AABB& aabb, float thickness) {
BatchWireframeAABB(color, &aabb, 1, thickness);
}
void J3D::WireframeOBB(const Color4& color, const J3ML::Geometry::OBB& obb, float thickness) {
// TODO Make it work the same as AABB batching. I couldn't get rotation to do anything more than twitch in place :(.
void J3D::BatchWireframeOBB(const Color4& color, const OBB* boxes, const size_t& box_count, float thickness) {
std::array<Vector3, 8> corner_points;
obb.GetCornerPoints(corner_points.data());
std::array<GLuint, 24> indices =
{
0, 1, 1, 2, 2, 3, 3, 0,
4, 5, 5, 6, 6, 7, 7, 4,
0, 4, 1, 5, 2, 6, 3, 7
0, 1, 1, 2, 2, 3, 3, 0,
4, 5, 5, 6, 6, 7, 7, 4,
0, 4, 1, 5, 2, 6, 3, 7
};
glPushMatrix();
glTranslatef(obb.pos.x, obb.pos.y, obb.pos.z);
glLineWidth(thickness);
glColor4ubv(color.ptr());
glVertexPointer(3, GL_FLOAT, sizeof(Vector3), corner_points.data());
glDrawElements(GL_LINES, indices.size(), GL_UNSIGNED_INT, indices.data());
glColor4fv(baseColor);
glPopMatrix();
for (size_t i = 0; i < box_count; i++) {
boxes[i].GetCornerPoints(corner_points.data());
glPushMatrix();
glTranslatef(boxes[i].pos.x, boxes[i].pos.y, boxes[i].pos.z);
glDrawElements(GL_LINES, indices.size(), GL_UNSIGNED_INT, indices.data());
glPopMatrix();
}
glColor4fv(oldColor);
}
void J3D::WireframeOBB(const Color4& color, const J3ML::LinearAlgebra::Vector3& position, const J3ML::LinearAlgebra::Vector3& radii,
const J3ML::LinearAlgebra::EulerAngle& orientation, float thickness) {
Matrix3x3 rotation = orientation.ToQuaternion().ToMatrix3x3();
WireframeOBB(color, OBB(position, radii, rotation * Vector3::UnitX, rotation * Vector3::UnitY, rotation * Vector3::UnitZ), thickness);
void J3D::WireframeOBB(const Color4& color, const OBB& obb, float thickness) {
BatchWireframeOBB(color, &obb, 1, thickness);
}
/*
void J3D::WireframeOBB(const Color4& color, const Vector3& position, const Vector3& radii, const Matrix3x3& orientation, float thickness) {
WireframeOBB(color, OBB(position, radii, orientation. * Vector3::UnitX, rotation * Vector3::UnitY, rotation * Vector3::UnitZ), thickness);
}
*/
void J3D::DrawCubicBezierCurve(const Color4& color, const Vector3& controlA, const Vector3& pointA,
const Vector3& pointB, const Vector3& controlB, int subdivisions, float thickness) {
Vector3 last = controlA;

38
src/ShapeCache.cpp Normal file
View File

@@ -0,0 +1,38 @@
#include <JGL/JGL.h>
void JGL::ShapeCache::Init() {
if (!cube_vertex_data) {
std::array<Vector3, 24> vertices {
Vector3(-1, 1, -1), Vector3(1, 1, -1),
Vector3(1, 1, -1), Vector3(1, 1, 1),
Vector3(1, 1, 1), Vector3(-1, 1, 1),
Vector3(-1, 1, 1), Vector3(-1, 1, -1),
Vector3(-1, -1, -1), Vector3(1, -1, -1),
Vector3(1, -1, -1), Vector3(1, -1, 1),
Vector3(1, -1, 1), Vector3(-1, -1, 1),
Vector3(-1, -1, 1), Vector3(-1, -1, -1),
Vector3(-1, -1, -1), Vector3(-1, 1, -1),
Vector3(1, -1, -1), Vector3(1, 1, -1),
Vector3(1, -1, 1), Vector3(1, 1, 1),
Vector3(-1, -1, 1), Vector3(-1, 1, 1)
};
cube_vertex_data = new VRamList(vertices.data(), vertices.size());
}
if (!cube_index_data) {
std::array<GLuint, 36> indices {
0, 1, 3, 0, 3, 5,
8, 9, 11, 8, 11, 13,
5, 3, 11, 5, 11, 13,
0, 1, 9, 0, 9, 8,
0, 5, 13, 0, 13, 8,
1, 3, 11, 1, 11, 9
};
cube_index_data = new VRamList(indices.data(), indices.size());
}
}

View File

@@ -162,7 +162,7 @@ namespace JGL {
glDisable(GL_TEXTURE_2D);
}
void J3D::DrawString(const Color4& color, const std::string& text, const Vector3& pos, float scale, u32 size, const Font& font, const EulerAngle& angle, bool draw_back_face) {
void J3D::DrawString(const Color4& color, const std::string& text, const Vector3& pos, float scale, u32 size, const Font& font, const EulerAngleXYZ& angle, bool draw_back_face) {
// TODO: Determine the proper scale factor mathematically
// This number was arrived at holistically.
scale = scale * 0.002f;

View File

@@ -4,6 +4,7 @@
#include <iostream>
#include <glad/glad.h>
#include <JGL/logger/logger.h>
#include <JGL/JGL.h>
#if __linux__
#include <freetype2/ft2build.h>
@@ -58,25 +59,38 @@ namespace JGL {
return Detail::InitTextEngine();
}
Font::Font(const unsigned char* data, const size_t& size) {
if (Detail::ft == nullptr)
throw std::runtime_error("Error::FREETYPE: FT_Library was not initialized before attempting to load a font!");
if (FT_New_Memory_Face(Detail::ft, data, size, 0, &face))
throw std::runtime_error("Error::FREETYPE: Failed to load font!");
unsigned int new_index = 0;
for (const auto& f : Detail::fonts)
if (f.index >= new_index)
new_index = f.index + 1;
index = new_index;
Detail::fonts.push_back(*this);
std::cout << "Loaded font from memory at " << static_cast<const void*>(data) << " with index " << new_index << std::endl;
}
Font::Font(const std::filesystem::path& path) {
if (Detail::ft == nullptr)
throw std::runtime_error("Error::FREETYPE: FT_Library was not initialized before attempting to load a font!");
Font font;
if (FT_New_Face(Detail::ft, path.string().c_str(), 0, &face)) {
std::cout << "Error::FREETYPE: Failed to load font!" << std::endl;
if (FT_New_Face(Detail::ft, path.string().c_str(), 0, &face))
throw std::runtime_error("Error::FREETYPE: Failed to load font!");
//return -1;
}
unsigned int newIndex = 0;
for (const auto& f : Detail::fonts)
if (f.index >= newIndex)
newIndex = f.index + 1;
index = newIndex;
Detail::fonts.push_back(font);
std::cout << "Loaded font from " << path << " with index " << newIndex << std::endl;
//return newIndex;
unsigned int new_index = 0;
for (const auto& f : Detail::fonts)
if (f.index >= new_index)
new_index = f.index + 1;
index = new_index;
Detail::fonts.push_back(*this);
std::cout << "Loaded font from " << path << " with index " << new_index << std::endl;
}
Font::~Font() {
@@ -119,14 +133,14 @@ namespace JGL {
return extents;
}
jlog::Warning("Measuring a font size that is not cached, This is *super* slow.");
FT_Set_Pixel_Sizes(this->face, ptSize, ptSize);
jlog::Warning("Measuring a font size that is not cached, Defaulting to Jupiteroid.");
FT_Set_Pixel_Sizes(Fonts::Jupiteroid.face, ptSize, ptSize);
for (const char& c : text) {
FT_GlyphSlot slot = face->glyph;
auto glyph_index = FT_Get_Char_Index(this->face, c);
FT_GlyphSlot slot = Fonts::Jupiteroid.face->glyph;
auto glyph_index = FT_Get_Char_Index(Fonts::Jupiteroid.face, c);
auto error = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT);
auto error = FT_Load_Glyph(Fonts::Jupiteroid.face, glyph_index, FT_LOAD_DEFAULT);
if (error)
continue;
@@ -146,9 +160,6 @@ namespace JGL {
if (extents.y < ptSize)
extents.y = ptSize;
}
return extents;
}
}

View File

@@ -1,19 +1,64 @@
#include <JGL/types/Light.h>
#include <glad/glad.h>
JGL::Light::Light(const Vector3& position, const Color4& ambient, const Color4& diffuse, const Color4& specular) {
JGL::PointLight::PointLight(const Vector3& position, const Color4& ambient, const Color4& diffuse, const Color4& specular, float constant_attenuation, float linear_attenuation, float quadratic_attenuation) {
this->position = Vector4(position, 1.0f);
this->ambient = ambient;
this->diffuse = diffuse;
this->specular = specular;
this->constant_attenuation = constant_attenuation;
this->linear_attenuation = linear_attenuation;
this->quadratic_attenuation = quadratic_attenuation;
}
Vector3 JGL::Light::GetNormalizedSceenSpaceCoordinates() const {
GLint viewport[4];
glGetIntegerv(GL_VIEWPORT, viewport);
float JGL::PointLight::GetAttenuationAtPosition(const Vector3& pos) const {
Vector3 light_pos = {position.x, position.y, position.z};
Vector3 vector_to_position = pos - light_pos;
float distance = vector_to_position.Length();
float normalized_x = 2.0f * (position.x - viewport[0]) / viewport[2] - 1.0f;
float normalized_y = 2.0f * (position.y - viewport[1]) / viewport[3] - 1.0f;
return {normalized_x, normalized_y, position.z};
return 1.0f / (GetConstantAttenuation() + GetLinearAttenuation() * distance + GetQuadraticAttenuation() * distance * distance);
}
JGL::SpotLight::SpotLight(const Vector3& position, const Matrix3x3& ro_mat, float cone_size_degrees, float exponent, const Color4& ambient, const Color4& diffuse, const Color4& specular,
float constant_attenuation, float linear_attenuation, float quadratic_attenuation) {
this->position = Vector4(position, 1);
//TODO RotationMatrix to "Normalized direction vector."
orientation = ro_mat;
this->cut = cone_size_degrees;
this->exponent = exponent;
this->ambient = ambient;
this->diffuse = diffuse;
this->specular = specular;
this->constant_attenuation = constant_attenuation;
this->linear_attenuation = linear_attenuation;
this->quadratic_attenuation = quadratic_attenuation;
}
Vector3 JGL::LightBase::GetPosition() const {
return {position.x, position.y, position.z};
}
Color4 JGL::LightBase::GetAmbient() const {
return ambient;
}
Color4 JGL::LightBase::GetDiffuse() const {
return diffuse;
}
Color4 JGL::LightBase::GetSpecular() const {
return specular;
}
float JGL::LightBase::GetConstantAttenuation() const {
return constant_attenuation;
}
float JGL::LightBase::GetLinearAttenuation() const {
return linear_attenuation;
}
float JGL::LightBase::GetQuadraticAttenuation() const {
return quadratic_attenuation;
}

View File

@@ -331,12 +331,14 @@ void JGL::RenderTarget::Blit(const JGL::RenderTarget& source, JGL::RenderTarget*
glBindFramebuffer(GL_FRAMEBUFFER, current_fbo);
}
// To avoid repeatedly allocating and deallocating.
JGL::RenderTarget* pixel = nullptr;
void JGL::RenderTarget::Blit(const Color4& color, const Vector2& position, JGL::RenderTarget* destination) {
if (position.x > destination->size.x || position.y > destination->size.y)
Logger::Warning("Blitting outside of the renderable area of the destination.");
Texture texture(Vector2(1,1));
RenderTarget source(&texture,color);
if (pixel == nullptr)
pixel = new RenderTarget({1,1});
GLint current_draw_fbo = 0;
GLint current_read_fbo = 0;
@@ -348,13 +350,14 @@ void JGL::RenderTarget::Blit(const Color4& color, const Vector2& position, JGL::
glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &current_read_fbo);
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &current_draw_fbo);
SetActiveGLRenderTarget(source);
SetActiveGLRenderTarget(*pixel);
glClearColor(color.RedChannelNormalized(), color.GreenChannelNormalized(), color.BlueChannelNormalized(), color.AlphaChannelNormalized());
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glClear(GL_COLOR_BUFFER_BIT);
// Invert so it's relative to the top left corner.
int target_y = destination->size.y - position.y - 1;
glBindFramebuffer(GL_READ_FRAMEBUFFER, source.framebuffer_object);
glBindFramebuffer(GL_READ_FRAMEBUFFER, pixel->framebuffer_object);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, destination->framebuffer_object);
glBlitFramebuffer(0, 0, 1, 1, position.x, target_y, position.x + 1, target_y + 1, GL_COLOR_BUFFER_BIT, GL_NEAREST);