Compare commits

...

60 Commits

Author SHA1 Message Date
e809b1b665 Rotate sprite & sub-sprite about origin
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 5m24s
2024-09-08 21:52:49 -04:00
d15b3f660d J3D draw string draw_back_face
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 7m30s
2024-09-05 11:50:12 -04:00
ff2a8ab787 Refactor & undo shader commits AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 6m18s
2024-09-05 11:20:57 -04:00
55160044b6 Fix cmake_minimum_required using rebitch
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 1m40s
2024-08-26 19:46:51 -04:00
04a4cbd54d Add Attributes to Shader.
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 1m40s
2024-08-24 23:27:10 -04:00
f96a3851a1 Update engine components
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 1m32s
2024-08-24 10:58:37 -04:00
4996288163 More work on shader support.
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 1m24s
2024-08-23 19:50:41 -04:00
f3c2fd5e93 Initial shader class & restructure
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 2m38s
2024-08-23 12:25:46 -04:00
6650af4fc4 Framerate test (Press 1)
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m42s
2024-08-22 19:27:31 -04:00
1d8823b046 Outline Polygon
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 5m8s
2024-08-22 11:55:56 -04:00
c0b65818c8 DrawPartialSprite
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 7m11s
2024-08-22 10:58:20 -04:00
32de87229e Update Texture.h
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 18m9s
2024-08-21 20:59:25 -04:00
3759affa5d Fix texture
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 6m10s
2024-08-21 20:36:31 -04:00
8ce9a08951 Resize fix
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 6m54s
2024-08-21 13:20:42 -04:00
ec964dbee1 Wondows :(
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 5m25s
2024-08-08 00:14:24 -04:00
91baa826fa Allow changing the fov and render distance
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 4m30s
2024-08-06 12:31:44 -04:00
c2446763ad Merge remote-tracking branch 'origin/master'
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m24s
2024-08-05 20:11:18 -04:00
6286b62998 Pull out debug iteration steps from DrawCubicBezierCurve 2024-08-05 20:11:12 -04:00
4de44ba1e2 Put this code back because it got removed again. Make sure you have the latest commit before doing anything :/
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m26s
2024-08-05 19:49:30 -04:00
166db43f2e Migrate to latest J3ML and ReWindow
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m36s
2024-08-05 18:31:13 -04:00
2a1085df2d Revert "Merge remote-tracking branch 'origin/master'"
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 1m20s
This reverts commit 240fa7ddbc, reversing
changes made to d89f79e70e.
2024-08-05 15:04:22 -04:00
240fa7ddbc Merge remote-tracking branch 'origin/master'
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 1m20s
# Conflicts:
#	main.cpp
2024-08-05 14:39:36 -04:00
d89f79e70e Cool BezierCurve demo (You can drag the endpoints around) 2024-08-05 14:38:42 -04:00
7bf30f6f39 Idk like make the viewport work maybe?
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 4m29s
2024-08-05 13:41:12 -04:00
2a70981bf0 Get software texture back.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 4m44s
2024-08-05 11:30:40 -04:00
0147245325 Refactored DrawSprite
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m16s
2024-08-05 00:52:08 -04:00
15dcb79479 Upd8
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m29s
2024-08-04 20:28:08 -04:00
50895153f5 Texture filtering
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m46s
2024-08-03 22:17:21 -04:00
9688854533 Texture class
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m25s
2024-08-02 20:03:32 -04:00
0005c036b4 Texture2D header
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 5m32s
2024-08-02 13:05:34 -04:00
af42d8fd25 Merge remote-tracking branch 'origin/master'
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m26s
2024-08-02 10:49:05 -04:00
ff4d014739 Implement Color3::FromHex() 2024-08-02 10:49:00 -04:00
9dda4bebc5 Make measure string go brrrrrrr
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 4m9s
2024-07-31 12:01:57 -04:00
a5bfb4972a Fill quad
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m32s
2024-07-30 23:37:47 -04:00
4348f2708a Gradient triangle
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m32s
2024-07-30 15:49:13 -04:00
32dd2b54ca incorporate builddeps.reci
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m40s
2024-07-29 19:30:23 -04:00
e8b601aa25 install build deps reci script
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 45s
2024-07-29 19:29:11 -04:00
d5fd3e1a49 reci lua edition
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 48s
2024-07-29 19:24:55 -04:00
f593a0beac Cleanup & Update ReTexture.
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 1m29s
2024-07-19 17:16:38 -04:00
0b7af6fd31 Cleanup
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 1m46s
2024-07-19 01:34:43 -04:00
eca4309e85 DrawSprite
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 1m21s
2024-07-18 22:24:19 -04:00
4be97f52d9 Minor documentation additions.
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 1m19s
2024-07-18 14:51:19 -04:00
613a13618c Merge branch 'master' of https://git.redacted.cc/Josh/JGL
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Has been cancelled
2024-07-18 14:49:19 -04:00
4150c93c85 Make it like, go faster. 2024-07-18 14:49:15 -04:00
9dee59fd45 Merge remote-tracking branch 'origin/master'
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 1m32s
2024-07-18 14:04:16 -04:00
b6b2ca1bfe Remove old LoadFont function. 2024-07-18 14:04:11 -04:00
4eca3311c8 Fix text being shifted up by it's own height.
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Has been cancelled
2024-07-18 14:01:58 -04:00
54711355c4 Merge branch 'master' of https://git.redacted.cc/Josh/JGL
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 1m29s
2024-07-18 14:01:14 -04:00
8bddf3e0a7 Update JGL.cpp
Fix glBlend always being enabled because I'm an idiot
2024-07-18 14:00:57 -04:00
26d17dae38 Move glPixelStore call inside of InitTextEngine so users don't need to call it. Also adjusted Color3 and Color4, more work coming soon.
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 2m18s
2024-07-18 13:45:11 -04:00
4ff8d8ff07 Update Font.cpp
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 1m33s
fix memleak
2024-07-17 16:52:31 -04:00
162732e4b7 Fix Dereference
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 1m24s
2024-07-17 16:11:36 -04:00
289157ab8a Fiddle with README some
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 2m37s
2024-07-17 15:54:39 -04:00
3a658b1096 Testing MeasureString with underlaid box. But it appears to be offset by half-height?
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 4m39s
2024-07-17 15:19:38 -04:00
480502f89e Implement Font::MeasureString (TODO: double check!!)
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 1m53s
2024-07-17 14:59:34 -04:00
d28f680cd0 Half-baked Font class implementation
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 1m29s
2024-07-16 14:54:47 -04:00
2d1e42c23b Half-ass Font class implementation
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 1m30s
2024-07-16 14:39:44 -04:00
abd691b648 uhhhhhhhhhh yeeeeeeea
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 1m28s
2024-07-16 14:19:39 -04:00
e261b610c2 Update TextRendering.cpp
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 5m24s
Reset the Texture after drawing text.
2024-07-15 11:50:27 -04:00
8625c52ee9 Implement optimizations for 2D text rendering (#23)
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 1m51s
The following patches are included:

## J2D: Rewrite text rendering

This patch rewrites text rendering for `J2D::DrawString` to now construct
a texture atlas for all ASCII-range glyphs in the FT font face, instead
of constructing a texture for every glyph.

This improves text rendering performance for several reasons:

1. Binding textures is relatively expensive as the GPU is required to do
   a context switch for internal data like texture parameters, and also
   cannot optimize for accesses to the same texture across draw calls.
   This patch removes the need to call `glBindTexture` more than once per
   call to `J2D::DrawString`.
2. As a consequence of the above, all glyphs for a given string can now
   be rendered in a single call to `glDrawArrays`. This is done by storing
   the cached texture coordinates on `CachedGlyph` and constructing a full
   array of vertices and texture coordinates for the entire string at
   once, resulting in only /one/ set of client-to-device attribute
   uploads and only one draw call, instead of being required to upload
   attribute data for each glyph separately.

## FontCache: Use map for efficient glyph lookup

This patch updates `CachedFont` to now use an `std::map` for cached glyphs,
instead of an `std::vector`. `std::map` allows O(log n) lookup, whereas
`std::vector` only allows O(n) lookup.

Note: `std::unordered_map` technically has better lookup complexity here,
with amortized O(1) lookup. However, hashmaps have a higher inherent
overhead than red-black trees so this would only be viable when going
above around 1000 entries, which should never happen here for ASCII
glyphs.

Co-authored-by: Ori Sky Farrell <ori.sky.farrell+git@gmail.com>
Reviewed-on: #23
Co-authored-by: ori_sky <redacted@ori.mx>
Co-committed-by: ori_sky <redacted@ori.mx>
2024-07-15 11:38:20 -04:00
25 changed files with 1487 additions and 837 deletions

View File

@@ -17,8 +17,6 @@ jobs:
- run: echo "The ${{ gitea.repository }} repository has been cloned to the runner."
- run: echo "The workflow is now ready to run your tests on the runner."
- run: echo "Install toolchain and run ReCI build test"
- run: apt-get update && apt-get install -y git && git clone $RECI_GIT $RECI
- run: bash $RECI/scripts/setup_build_tools.sh
- run: bash reci/scripts/install_build_dependencies.sh
- run: bash $RECI/scripts/run_buildtest.sh ${{ gitea.repository }}
- run: apt-get update && apt-get install -y lua5.3 git && git clone $RECI_GIT $RECI
- run: lua $RECI/reci.lua -f $RECI/scripts/buildtools.reci -f reci/scripts/builddeps.reci -f $RECI/scripts/buildtest.reci
- run: echo "This job's status is ${{ job.status }}."

View File

@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.18)
cmake_minimum_required(VERSION 3.18..3.27)
project(JGL
VERSION 1.0
LANGUAGES CXX
@@ -14,14 +14,20 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
# Enable Package Managers
include(cmake/CPM.cmake)
CPMAddPackage(
NAME mcolor
URL https://git.redacted.cc/maxine/mcolor/archive/Prerelease-4.zip
)
CPMAddPackage(
NAME J3ML
URL https://git.redacted.cc/josh/j3ml/archive/Release-2.2.zip
URL https://git.redacted.cc/josh/j3ml/archive/Release-3.1.zip
)
CPMAddPackage(
NAME ReWindow
URL https://git.redacted.cc/Redacted/ReWindow/archive/Prerelease-3.zip
URL https://git.redacted.cc/Redacted/ReWindow/archive/Prerelease-13.zip
)
CPMAddPackage(
@@ -38,6 +44,12 @@ CPMAddPackage(
NAME Event
URL https://git.redacted.cc/josh/Event/archive/Release-6.zip
)
CPMAddPackage(
NAME ReTexture
URL https://git.redacted.cc/Redacted/ReTexture/archive/Release-1.2.zip
)
if (WIN32)
#CPMAddPackage(
#NAME harfbuzz
@@ -68,9 +80,13 @@ endif()
set_target_properties(JGL PROPERTIES LINKER_LANGUAGE CXX)
#Don't expose this one because it's only to be used in the demo program.
include_directories(${ReTexture_SOURCE_DIR}/include)
target_include_directories(JGL PUBLIC
${PROJECT_SOURCE_DIR}/include
${OPENGL_INCLUDE_DIRS}
${mcolor_SOURCE_DIR}/include
${J3ML_SOURCE_DIR}/include
${Event_SOURCE_DIR}/include
${ReWindow_SOURCE_DIR}/include
@@ -84,13 +100,13 @@ add_executable(JGL_Demo main.cpp)
if (UNIX AND NOT APPLE)
target_include_directories(JGL PRIVATE ${FREETYPE_INCLUDE_DIRS})
target_link_libraries(JGL PRIVATE ${FREETYPE_LIBRARIES})
target_link_libraries(JGL PUBLIC ${OPENGL_LIBRARIES} J3ML ReWindowLibrary glad jlog Event)
target_link_libraries(JGL PUBLIC ${OPENGL_LIBRARIES} mcolor J3ML ReWindowLibrary glad jlog Event ReTexture)
endif()
if (WIN32)
target_include_directories(JGL PRIVATE ${freetype_SOURCE_DIR}/include)
target_link_libraries(JGL PRIVATE freetype)
target_link_libraries(JGL PUBLIC ${OPENGL_LIBRARIES} J3ML ReWindowLibrary glad jlog Event)
target_link_libraries(JGL PUBLIC ${OPENGL_LIBRARIES} mcolor J3ML ReWindowLibrary glad jlog Event ReTexture)
endif()
target_link_libraries(JGL_Demo PUBLIC JGL)
target_link_libraries(JGL_Demo PUBLIC JGL)

View File

@@ -4,23 +4,39 @@ Yet Another C++ Rendering Toolkit
![Static Badge](https://img.shields.io/badge/Lit-Based-%20)
## Goals
* Provide single-function-calls to render various graphics primitives in 2D and 3D.
* Integrated directly with our other toolkits (ReWindow, J3ML)
* Quick Rendering of Debug Text, Geometric Widgets, Textures, and so forth.
## Non-Goals
* Full Rendering Engine
* OpenGL/Vulkan Wrapper
* Asset Loading & Management
## Features
* Modern C++ (20)
* Little-to-no overhead
* No hand-holding
* No-frills, straight up just renders shapes and text.
* Provides single-function-calls to render various graphics primitives in 2D and 3D.
* Integrates directly with our other toolkits (ReWindow, J3ML)
* Quick Rendering of Debug Text, Geometric Widgets, Textures, and so forth.
* Little-to-no overhead.
* Integrates right into an existing OpenGL rendering system.
* High-performance text rendering.
## API Overview
### J2D
* DrawPoint
* DrawLine / DrawGradientLine
* DrawSprite
* OutlineRect / FillRect / FillGradientRect / FillRoundedRect
* OutlineCircle / FillCircle
* OutlineTriangle / FillTriangle
* DrawString
### J3D
* DrawLine
* DrawString
* DrawMatrixGizmo (WIP)
* DrawAxisAngleGizmo (WIP)
* DrawQuaternionGizmo (WIP)
### Types
* Font
* Sprite
* Color4/Color3
* Gradient
## Usage

View File

@@ -0,0 +1,5 @@
#version 120
void main() {
gl_FragColor = vec4(1, 1, 1, 1);
}

View File

@@ -0,0 +1,7 @@
#version 120
attribute vec2 position;
void main() {
gl_Position = vec4(position.x, position.y, 1.0, 1.0);
}

BIN
assets/sprites/Re3D.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

View File

@@ -1,24 +0,0 @@
#pragma once
#include <J3ML/J3ML.h>
namespace JGL
{
using namespace J3ML;
struct Color3 {
u8 r;
u8 g;
u8 b;
Color3 Lerp(const Color3& rhs, float alpha) const;
Color3(u8 R, u8 G, u8 B) : r(R), g(G), b(B) {}
u8 RedChannel () const { return r; }
u8 GreenChannel() const { return g; }
u8 BlueChannel () const { return b; }
float RedChannelNormalized () const { return static_cast<float>(r) / 255.f;}
float BlueChannelNormalized() const { return static_cast<float>(b) / 255.f;}
float GreenChannelNormalized() const { return static_cast<float>(g) / 255.f;}
};
}

View File

@@ -1,17 +0,0 @@
#pragma once
#include <JGL/Color3.h>
namespace JGL
{
class Color4 {
public:
explicit Color4(const Color3& color3, unsigned int alpha) {r = color3.r; g = color3.g; b = color3.b; a = alpha;}
Color4(int red, int green, int blue, int alpha = 127) : r(red), g(green), b(blue), a(alpha) {}
static Color4 FromColor3(const Color3& color3, unsigned int alpha = 127) {return Color4(color3, alpha);}
int r;
int g;
int b;
int a;
};
}

View File

@@ -1,178 +0,0 @@
#pragma once
#include <JGL/Color3.h>
namespace JGL
{
namespace Colors {
namespace Primary {
static const Color3 Red{255, 0, 0};
static const Color3 Green{0, 255, 0};
static const Color3 Blue{0, 0, 255};
static const Color3 White{255, 255, 255};
static const Color3 Black{0, 0, 0};
static const Color3 Gray{128, 128, 128};
static const Color3 DarkGray{192, 192, 192};
static const Color3 LightGray{64, 64, 64};
static const Color3 Yellow{255, 255, 0};
}
using namespace Primary;
namespace Reds {
static const Color3 Fuchsia {255, 0, 255};
static const Color3 LightSalmon{255, 160, 122};
static const Color3 Salmon{250, 128, 114};
static const Color3 DarkSalmon{233, 150, 122};
static const Color3 LightCoral{240, 128, 128};
static const Color3 IndianRed{205, 92, 92};
static const Color3 Crimson{220, 20, 60};
static const Color3 Firebrick{178, 34, 34};
static const Color3 DarkRed{139, 0, 0};
}
namespace Oranges {
static const Color3 Coral{255, 127, 80};
static const Color3 Tomato{255, 99, 71};
static const Color3 OrangeRed{255, 69, 0};
static const Color3 Gold{255, 215, 0};
static const Color3 Orange{255, 165, 0};
static const Color3 DarkOrange{255, 140, 0};
}
namespace Yellows {
static const Color3 LightYellow{255, 255, 224};
static const Color3 LemonChiffon{255, 250, 205};
static const Color3 LightGoldenrodYellow{250, 250, 210};
static const Color3 PapayaWhip{255, 239, 213};
static const Color3 Moccasin{255, 228, 181};
static const Color3 PeachPuff{255, 218, 185};
static const Color3 PaleGoldenrod{238, 232, 170};
static const Color3 Khaki{240, 230, 140};
static const Color3 DarkKhaki{189, 183, 107};
}
namespace Greens {
static const Color3 LawnGreen{124, 252, 0};
static const Color3 Chartreuse{127, 255, 0};
static const Color3 LimeGreen{50, 205, 50};
static const Color3 ForestGreen{34, 139, 34};
static const Color3 DarkGreen{0, 100, 0};
static const Color3 GreenYellow{173, 255, 47};
static const Color3 YellowGreen{154, 205, 50};
static const Color3 SpringGreen{0, 255, 127};
static const Color3 MediumSpringGreen{0, 250, 154};
static const Color3 LightGreen{144, 238, 144};
static const Color3 PaleGreen{152, 251, 152};
static const Color3 DarkSeaGreen{143, 188, 143};
static const Color3 MediumSeaGreen{60, 179, 113};
static const Color3 SeaGreen{46, 139, 87};
static const Color3 DarkOliveGreen{85, 107, 47};
static const Color3 OliveDrab{107, 142, 35};
static const Color3 Lime{0, 255, 0};
static const Color3 Olive{128, 128, 0};
}
namespace Cyans {
static const Color3 LightCyan{224, 255, 255};
static const Color3 Cyan{0, 255, 255};
static const Color3 Aqua{0, 255, 255};
static const Color3 Aquamarine{127, 255, 212};
static const Color3 MediumAquamarine{102, 205, 170};
static const Color3 PaleTurquoise{175, 238, 238};
static const Color3 Turquoise{64, 224, 208};
static const Color3 MediumTurquoise{72, 209, 204};
static const Color3 DarkTurquoise{0, 206, 209};
static const Color3 LightSeaGreen{32, 178, 170};
static const Color3 CadetBlue{95, 158, 160};
static const Color3 DarkCyan{0, 139, 139};
static const Color3 Teal{0, 128, 128};
}
namespace Blues {
static const Color3 PowderBlue{176, 224, 230};
static const Color3 LightBlue{173, 216, 230};
static const Color3 LightSkyBlue{135, 206, 250};
static const Color3 SkyBlue{135, 206, 235};
static const Color3 DeepSkyBlue{0, 191, 255};
static const Color3 LightSteelBlue{176, 196, 222};
static const Color3 DodgerBlue{30, 144, 255};
static const Color3 CornflowerBlue{100, 149, 237};
static const Color3 SteelBlue{70, 130, 180};
static const Color3 RoyalBlue{65, 105, 225};
static const Color3 MediumBlue{0, 0, 205};
static const Color3 DarkBlue{0, 0, 139};
static const Color3 Navy{0, 0, 128};
static const Color3 MidnightBlue{25, 25, 112};
static const Color3 MediumSlateBlue{123, 104, 238};
static const Color3 SlateBlue{106, 90, 205};
static const Color3 DarkSlateBlue{72, 61, 139};
}
namespace Purples {
static const Color3 Lavender{230, 230, 250};
static const Color3 Thistle{216, 191, 216};
static const Color3 Plum{221, 160, 221};
static const Color3 Violet{238, 160, 221};
static const Color3 Orchid{218, 112, 214};
static const Color3 Fuchsia{255, 0, 255};
static const Color3 Magenta{255, 0, 255};
static const Color3 MediumOrchid{186, 85, 211};
static const Color3 MediumPurple{147, 112, 219};
static const Color3 BlueViolet{138, 43, 226};
static const Color3 DarkViolet{148, 0, 211};
static const Color3 DarkOrchid{153, 50, 204};
static const Color3 DarkMagenta{139, 0, 128};
static const Color3 Purple{128, 0, 128};
static const Color3 Indigo{75, 0, 130};
}
namespace Pinks {
static const Color3 Pink{255, 129, 203};
static const Color3 LightPink{255, 182, 193};
static const Color3 HotPink{255, 105, 180};
static const Color3 DeepPink{255, 20, 147};
static const Color3 PaleVioletRed{219, 112, 147};
static const Color3 MediumVioletRed{199, 21, 133};
}
namespace Whites {
static const Color3 Snow{255, 250, 250};
static const Color3 Honeydew{240, 255, 240};
static const Color3 MintCream{245, 255, 250};
static const Color3 Azure{240, 255, 255};
static const Color3 AliceBlue{240, 248, 255};
static const Color3 GhostWhite{248, 248, 255};
static const Color3 WhiteSmoke{245, 245, 245};
static const Color3 SeaShell{255, 245, 238};
static const Color3 Beige{245, 245, 220};
static const Color3 OldLace{253, 245, 230};
static const Color3 FloralWhite{255, 250, 240};
static const Color3 Ivory{255, 255, 240};
static const Color3 AntiqueWhite{250, 240, 215};
static const Color3 Linen{250, 240, 230};
static const Color3 LavenderBlush{255, 240, 245};
static const Color3 MistyRose{255, 228, 255};
}
namespace Grays {
static const Color3 Gainsboro{220, 220, 220};
static const Color3 LightGray{211, 211, 211};
static const Color3 Silver{192, 192, 192};
static const Color3 DimGray{105, 105, 105};
static const Color3 LightSlateGray{119, 136, 153};
static const Color3 SlateGray{112, 128, 144};
static const Color3 DarkSlateGray{47, 79, 79};
}
namespace Browns {
static const Color3 CornSilk{255, 248, 220};
static const Color3 BlanchedAlmond{255, 235, 205};
static const Color3 Bisque{255, 228, 196};
static const Color3 NavajoWhite{255, 222, 173};
static const Color3 Wheat{254, 222, 179};
static const Color3 BurlyWood{222, 184, 135};
static const Color3 Tan{210, 180, 140};
static const Color3 RosyBrown{188, 143, 143};
static const Color3 SandyBrown{244, 164, 96};
static const Color3 GoldenRod{218, 165, 32};
static const Color3 Peru{205, 133, 63};
static const Color3 Chocolate{210, 105, 30};
static const Color3 SaddleBrown{139, 69, 19};
static const Color3 Sienna{160, 82, 45};
static const Color3 Brown{164, 42, 42};
static const Color3 Maroon{128, 0, 0};
}
}
}

View File

@@ -1,53 +0,0 @@
#pragma once
#include <vector>
#include <glad/glad.h>
namespace JGL {
class CachedGlyph;
class CachedFont;
class FontCache;
}
class JGL::CachedGlyph {
private:
GLuint texture = 0;
char character;
public:
int x2offset = 0, y2offset = 0, w = 0, h = 0;
float advanceX = 0, advanceY = 0;
//CachedGlyph(GLuint texture_id, char c);
CachedGlyph(GLuint texture_id, char c, float x2o, float y2o, float w, float h, float advX, float advY);
char getCharacter();
const GLuint* getTexture();
};
class JGL::CachedFont {
private:
std::vector<CachedGlyph*> glyphs{};
unsigned int font_size = 0;
unsigned int font_index = 0;
public:
void appendGlyph(CachedGlyph* glyph);
void eraseGlyph(CachedGlyph* glyph);
void eraseGlyph(char c);
void eraseGlyph(GLuint texture_id);
unsigned int getFontSize();
unsigned int getFontIndex();
CachedGlyph* getGlyph(char c);
std::vector<CachedGlyph*> getGlyphs();
CachedFont(unsigned int font_size, unsigned int font_index);
};
class JGL::FontCache {
private:
std::vector<CachedFont*> cachedFonts = {};
public:
std::vector<CachedFont*> getFonts();
CachedFont* getFont(unsigned int font_size, unsigned int font_index);
void appendFont(CachedFont* font);
void newFont(unsigned int font_size, unsigned int font_index);
void eraseFont(CachedFont* font);
void purgeCache();
};

View File

@@ -1,12 +0,0 @@
#pragma once
#include <J3ML/J3ML.h>
namespace JGL {
enum class Gradient : u8 {
Vertical = 0,
Horizontal = 1,
DiagonalTopLeft = 2,
DiagonalBottomLeft = 3
};
}

View File

@@ -1,42 +1,48 @@
//
// Created by dawsh on 1/17/24.
//
/// Josh's Graphics Library
/// A C++20 Library for rendering 2D and 3D primitives in an OpenGL context.
/// Developed and Maintained by Josh O'Leary @ Redacted Software.
/// Special Thanks to William Tomasine II and Maxine Hayes.
/// (c) 2024 Redacted Software
/// This work is dedicated to the public domain.
/// @file JGL.h
/// @desc All JGL usable functions are defined here. This is the public API.
/// @edit 2024-07-16
#pragma once
#include <string>
#include <iostream>
#include <JGL/Color4.h>
#include <JGL/Gradient.h>
#include <JGL/FontCache.h>
#include <J3ML/LinearAlgebra.h>
#include <J3ML/LinearAlgebra/Vector2.h>
#include <J3ML/LinearAlgebra/Vector3.h>
#include <J3ML/Geometry/Sphere.h>
#include <J3ML/Geometry/Capsule.h>
#include <J3ML/Geometry/TriangleMesh.h>
#include <Color3.hpp>
#include <Color4.hpp>
#include <Colors.hpp>
#include <JGL/types/Texture.h>
#include <JGL/types/enums.h>
#include <JGL/types/FontCache.h>
#include <JGL/types/Font.h>
#include <J3ML/LinearAlgebra.hpp>
#include <J3ML/LinearAlgebra/Vector2.hpp>
#include <J3ML/LinearAlgebra/Vector3.hpp>
#include <J3ML/Geometry/Sphere.hpp>
#include <J3ML/Geometry/Capsule.hpp>
#include <J3ML/Geometry/TriangleMesh.hpp>
#include <JGL/Logger.h>
// OpenGL Wrapper for rendering 2D graphics primitives in both a 2D and 3D context
/// OpenGL Wrapper for rendering 2D graphics primitives in both a 2D and 3D context
namespace JGL {
using namespace J3ML::LinearAlgebra;
using namespace J3ML::Geometry;
using J3ML::LinearAlgebra::Vector2;
using J3ML::LinearAlgebra::Vector3;
using J3ML::LinearAlgebra::Matrix3x3;
using J3ML::LinearAlgebra::Matrix4x4;
using J3ML::LinearAlgebra::AxisAngle;
using J3ML::LinearAlgebra::Quaternion;
using J3ML::Geometry::Sphere;
using J3ML::Geometry::OBB;
using J3ML::Geometry::Capsule;
using J3ML::Geometry::TriangleMesh;
using J3ML::Geometry::Plane;
/// TODO: Implement HSV and other color representation conversions
struct HSV {
float hue;
float saturation;
float value;
};
/// TODO: Migrate to using J3ML's definition once finished (hint hint)
struct Triangle2D
{
Vector2 A;
@@ -44,25 +50,18 @@ namespace JGL {
Vector2 C;
};
struct Triangle3D
{
Vector3 A;
Vector3 B;
Vector3 C;
};
bool Update(const Vector2& window_size);
bool InitTextEngine();
int LoadFont(const std::string& font_path);
void Update(const Vector2& window_size);
void PurgeFontCache();
void UnloadFont(int font_index);
// TODO: implement correct coloring
// TODO: Implement
void SetActiveFont(const Font& font);
// TODO: Overrides specifically for Color3 are not **strictly** necessary, Color3 and Color4 should implicitly convert back and forth.
/// Drawing functions for primitive 2D Shapes.
/// Each function is overloaded with Color3 and Color4 for optional transparency.
namespace J2D {
/// Open a 2-D rendering context with the underlying graphics system (In this case & by default OpenGL).
/// @note This call may not strictly be necessary on some setups, but is provided to keep the API constant.
/// It is recommended to always open a JGL 2D context to render your content, then close when completed.
@@ -73,84 +72,132 @@ namespace JGL {
void End();
/// Plots a single pixel on the screen.
/// @param color A 3-or-4 channel color value. @see classes Color3, Color4
/// @param color A 3-or-4 channel color value. @see class Color3, class Color4
/// @param coordinates The pixel-point on-screen at which to plot the pixel.
void DrawPixel(const Color3& color, const Vector2& coordinates);
void DrawPixel(const Color3& color, float x, float y);
void DrawPixel(const Color4& color, const Vector2& coordinates);
void DrawPixel(const Color4& color, float x, float y);
void DrawPoint(const Color4& color, const Vector2& coordinates, float radius = 1.f);
void DrawPoint(const Color4& color, float x, float y, float radius = 1.f);
/// Plots a line (segment) on the screen.
/// @param color A 3-or-4 channel color value. @see classes Color3, Color4.
/// @param A The starting point of the line segment.
/// @param B The end point of the line segment.
/// @param thickness The width at which to render the line.
void DrawLine(const Color3& color, const Vector2& A, const Vector2& B, float thickness = 1);
void DrawLine(const Color3 &color, float x, float y, float w, float h, float thickness = 1);
void DrawLine(const Color4& color, const Vector2& A, const Vector2& B, float thickness = 1);
void DrawLine(const Color4& color, float x1, float y1, float x2, float y2, float thickness = 1);
///Draws a line with a gradient that transitions across it.
void DrawGradientLine(const Color4& color1, const Color4& color2, const Vector2& A, const Vector2& B, float thickness = 1);
void DrawGradientLine(const Color3& color1, const Color3& color2, const Vector2& A, const Vector2& B, float thickness = 1);
void DrawGradientLine(const Color4& color1, const Color4& color2, float x, float y, float w, float h, float thickness = 1);
void DrawGradientLine(const Color3& color1, const Color3& color2, float x, float y, float w, float h, float thickness = 1);
/// Draws an outline of a rectangle on the screen.
void OutlineRect(const Color4& color, const Vector2& pos, const Vector2& size, float thickness = 1);
void OutlineRect(const Color3& color, const Vector2& pos, const Vector2& size, float thickness = 1);
/// 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
/// @param position
/// @param origin The center point around which the image should have all transformations applied to it.
/// @param scale The scale transformation for the image. X and Y axis are independently-scalable.
/// @param rad_rotation A float representing the rotation of the sprite where 0 is no rotation and 1 is the maximum rotation (would look the same as 0).
/// @param color A 32-bit RGBA value represented as four unsigned 8-bit integers.
/// @param inversion inverts the texture only.
/// @see class Texture
void DrawSprite(const Texture& texture, const Vector2& position, float rad_rotation = 0, const Vector2& origin = Vector2(0,0),
const Vector2& scale = Vector2(1,1), const Color4& color = Colors::White, Inversion inversion = Inversion::None);
void DrawSprite(const Texture& texture,
float positionX, float positionY,
float rad_rotation = 0,
float originX = 0, float originY = 0,
float scaleX = 1, float scaleY = 1,
const Color4& color = Colors::White,
Inversion inversion = Inversion::None);
/// Draws a piece of a sprite to the screen, similar to DrawSprite.
/// @param texture
/// @param position
/// @param sub_texture_position The top left corner of the sub-texture to be drawn.
/// @param sub_texture_size The size of the sub-texture in px.
/// @param origin
/// @param scale
/// @param color
/// @param inversion
void DrawPartialSprite(const Texture& texture, const Vector2& position, const Vector2& sub_texture_position, const Vector2& sub_texture_size, float rad_rotation = 0,
const Vector2& origin = Vector2(0,0), const Vector2& scale = Vector2(1, 1), const Color4& color = Colors::White, Inversion inversion = Inversion::None);
void DrawPartialSprite(const Texture& texture, float positionX, float positionY, float sub_texture_positionX, float sub_texture_positionY, unsigned int sub_texture_sizeX, unsigned int sub_texture_sizeY,
float rad_rotation = 0, float originX = 0, float originY = 0, float scaleX = 1, float scaleY = 1, const Color4& color = Colors::White, Inversion inversion = Inversion::None);
/// Draws a non axis-aligned fill rect to the screen.
/// @param color
/// @param v1 top-left vertex.
/// @param v2 bottom-left vertex.
/// @param v3 bottom-right vertex.
/// @param v4 top-right vertex.
void FillQuad(const Color4& color, const Vector2& v1, const Vector2& v2, const Vector2& v3, const Vector2& v4);
/// Draws a non axis-aligned outline rect to the screen.
/// @param color
/// @param v1 top-left vertex.
/// @param v2 bottom-left vertex.
/// @param v3 bottom-right vertex.
/// @param v4 top-right vertex.
/// @param thickness the thickness of the GL_LINES to be connected together.
void OutlineQuad(const Color4& color, const Vector2& v1, const Vector2& v2, const Vector2& v3, const Vector2& v4, float thickness = 1);
/// Draws a filled rectangle on the screen.
void FillRect(const Color4& color, const Vector2& pos, const Vector2& size);
void FillRect(const Color3& color, const Vector2& pos, const Vector2& size);
/// Draws a filled rectangle where the color transitions across it.
void FillGradientRect(const Color4& color1, const Color4& color2, const Gradient& gradient, const Vector2& pos, const Vector2& size);
void FillGradientRect(const Color3& color1, const Color3& color2, const Gradient& gradient, const Vector2& pos, const Vector2& size);
/// Draws a filled rectangle with rounded corners on the screen.
void FillRoundedRect(const Color4 &color, const Vector2 &pos, const Vector2 &size, float radius = 5, unsigned int subdivisions = 8);
void FillRoundedRect(const Color3& color, const Vector2& pos, const Vector2& size, float radius = 5, unsigned int subdivisions = 8);
void FillRoundedRect(const Color4& color, const Vector2 &pos, const Vector2 &size, float radius = 5, unsigned int subdivisions = 8);
/// Draws an outline of a circle on the screen.
void OutlineCircle(const Color4& color, const Vector2& center, float radius, unsigned int subdivisions = 16, float thickness = 1);
void OutlineCircle(const Color3& color, const Vector2& center, float radius, unsigned int subdivisions = 16, float thickness = 1);
/// Draws a filled circle on the screen.
void FillCircle(const Color4& color, const Vector2& center, float radius, unsigned int subdivisions = 8);
void FillCircle(const Color3& color, const Vector2& center, float radius, unsigned int subdivisions = 8);
/// Draws an outline of a triangle on the screen.
/// @param color
/// @param tri
/// @param thickness
void OutlineTriangle(const Color4& color, const Triangle2D& tri, float thickness = 1);
void OutlineTriangle(const Color3& color, const Triangle2D& tri, float thickness = 1);
// TODO: Implement an overload that simply takes 3 Vector3's
void OutlineTriangle(const Color4& color, const Vector2& triA, const Vector2& triB, const Vector2& triC, float thickness = 1);
/// Draws a filled triangle on the screen.
void FillTriangle(const Color4& color, const Triangle2D &tri);
void FillTriangle(const Color3& color, const Triangle2D& tri);
// TODO: Implement an overload that simply takes 3 Vector3's
void FillTriangle(const Color4& color, const Triangle2D& tri);
void FIllTriangle(const Color4& color, const Vector2& triA, const Vector2& triB, const Vector2& triC);
/// Draws a triangle where each corner is defined by a given color, Smoothly transitioning between them.
void FillGradientTriangle(const Color4& a_color, const Color4& b_color, const Color4& c_color, const Triangle2D& tri);
/// Draws a text string on the screen with a given point-size and font.
void DrawString(const Color4& color, const std::string& text, float x, float y, float scale, u32 size, unsigned int font_index);
void DrawString(const Color3& color, const std::string& text, float x, float y, float scale, u32 size, unsigned int font_index);
void DrawString(const Color4& color, const std::string& text, float x, float y, float scale, u32 size, const Font& font);
// TODO: Implement the following:
void FillTexturedTriangle();
void DrawCubicBezierCurve(const Color4& color, const Vector2& controlA, const Vector2& pointA, const Vector2& pointB, const Vector2& controlB,
int subdivisions = 10, float thickness = 1);
/// Draws a series of points where the last point always connects to the first point.
void OutlinePolygon(const Color4& color, const std::vector<Vector2>& points, float thickness = 1);
/// TODO Implement the following. These ones are going to be extremely annoying.
void FillPolygon(const Color4& color, const std::vector<Vector2>& points);
void FillTexturedPolygon();
void DrawSprite();
void DrawPartialSprite();
void DrawCubicBezierCurve();
void OutlinePolygon (const Color4& color, std::vector<Vector2> points);
void FillPolygon (const Color4& color, std::vector<Vector2> points, float thickness = 1);
void OutlineRoundedRect(const Color4& color, const Vector2& pos, const Vector2& size, float radius = 5, float thickness = 1);
void FillTexturedTriangle();
}
/// Drawing functions for primitive 3D Shapes.
namespace J3D {
void Init(const Vector2& window_size, float fov, float far_plane);
void ChangeFOV(float fov);
void ChangeFarPlane(float far_plane);
void Begin();
void End();
void SetMatrix(const std::vector<GLfloat>& matrix, const Vector2& window_size);
void DrawLine(const Color4& color, const Vector3& A, const Vector3& B, float thickness = 1);
void DrawLine(const Color3& color, const Vector3& A, const Vector3& B, float thickness = 1);
void FillSphere(const Color3& color, const Sphere& sphere);
void WireframeSphere(const Color3& color, const Sphere& sphere, float thickness = 1);
void FillOBB(const Color3& color, const OBB& obb);
@@ -159,8 +206,8 @@ namespace JGL {
void WireframeCapsule(const Color3& color, const Capsule& cap, float thickness = 1);
void FillTriangleMesh(const Color3& color, const TriangleMesh& mesh);
void WireframeTriangleMesh(const Color3& color, const TriangleMesh& mesh, float thickness = 1);
void DrawString(const Color3& color, const std::string& text, const Vector3& pos, const Vector3& angle, float scale, u32 size, unsigned int font_index);
void DrawString(const Color4& color, const std::string& text, const Vector3& pos, float scale, u32 size, const Font& font, const EulerAngle& angle = {0, 0, 0}, bool draw_back_face = false);
void DrawSprite();
void DrawMatrixGizmo (const Matrix3x3&, const Vector3&);
void DrawMatrixGizmo (const Matrix4x4&);
void DrawAxisAngleGizmo (const AxisAngle&, const Vector3&);

11
include/JGL/Logger.h Normal file
View File

@@ -0,0 +1,11 @@
#pragma once
#include <jlog/Logger.hpp>
namespace JGL::Logger {
using namespace jlog;
extern GenericLogger Fatal;
extern GenericLogger Debug;
extern GenericLogger Error;
}

40
include/JGL/types/Font.h Normal file
View File

@@ -0,0 +1,40 @@
#pragma once
#include <vector>
#include <filesystem>
#include <J3ML/LinearAlgebra.hpp>
#include <filesystem>
#include <iostream>
/// Defines external C references to FreeType Data Structures
extern "C" typedef struct FT_FaceRec_* FT_Face;
extern "C" typedef struct FT_LibraryRec_* FT_Library;
namespace JGL
{
/// Initializes FreeType engine. Remember to call this during program initialization.
bool InitTextEngine();
/// A Font class implementation.
/// Wraps the font's FreeType asset handle and provides helper functions.
class Font {
public:
/// Default constructor does not initialize any members
Font() = default;
Font(const std::filesystem::path& path);
/// Destructor handles freeing of the underlying asset handle.
~Font();
static Font LoadTTF(const std::filesystem::path& filepath);
static std::vector<Font> GetLoadedFonts();
/// Returns the bounding-box the given string would occupy at the given point-size, assuming normal (1) scaling.
/// @param text The string to measure.
/// @param ptSize The font size at which to measure.
/// @return The size-in-pixels that would contain the entire text.
Vector2 MeasureString(const std::string& text, unsigned int ptSize);
public:
int index = 0;
FT_Face face;
};
}

View File

@@ -0,0 +1,67 @@
#pragma once
#include <array>
#include <map>
#include <vector>
#include <glad/glad.h>
/// TODO: FontCache mechanism works amazing, but makes no fucking sense
/// Let's document and reorganize it to be a little nicer on the mental :)
namespace JGL {
class CachedGlyph;
class CachedFont;
class FontCache;
}
/// Represents a single font-character "glyph", that has been cached in-memory for fast retrieval.
class JGL::CachedGlyph {
private:
char character;
std::array<GLfloat, 12> texcoords;
public:
int x2offset = 0, y2offset = 0, w = 0, h = 0;
float advanceX = 0, advanceY = 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();
[[nodiscard]] const std::array<GLfloat, 12> getTexCoords() const;
};
/// Represents a Font object as it exists in the font-cache.
class JGL::CachedFont {
private:
std::map<char, CachedGlyph*> glyphs;
GLuint texture = 0;
GLsizei texture_width = 0, texture_height = 0;
unsigned int font_size = 0;
unsigned int font_index = 0;
public:
void appendGlyph(CachedGlyph* glyph);
unsigned int getFontSize();
unsigned int getFontIndex();
CachedGlyph* getGlyph(char c);
std::map<char, CachedGlyph*> getGlyphs();
const GLuint* getTexture();
[[nodiscard]] GLsizei getTextureWidth() const;
[[nodiscard]] GLsizei getTextureHeight() const;
CachedFont(GLuint texture_id, GLsizei texture_width, GLsizei texture_height, unsigned int font_size, unsigned int font_index);
};
class JGL::FontCache {
private:
std::vector<CachedFont*> cachedFonts{};
public:
std::vector<CachedFont*> getFonts();
CachedFont* getFont(unsigned int font_size, unsigned int font_index);
void appendFont(CachedFont* font);
void newFont(GLuint texture_id, GLsizei texture_width, GLsizei texture_height, unsigned int font_size, unsigned int font_index);
void eraseFont(CachedFont* font);
void purgeCache();
};
namespace JGL {
inline FontCache fontCache;
}

View File

@@ -0,0 +1,52 @@
#pragma once
#include <ReTexture/Texture.h>
#include <J3ML/LinearAlgebra.hpp>
#include <Color3.hpp>
#include <Color4.hpp>
#include <glad/glad.h>
namespace JGL {
using namespace ReTexture;
enum class TextureFilteringMode : 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.
MIPMAP_BILINEAR = 3, //Bilinear with mipmaps, Fast and pretty. Uses more vram.
MIPMAP_TRILINEAR = 4 //The prettiest. Still decent speed. Uses more vram.
};
enum class TextureWrappingMode : u8 {
REPEAT = 0,
MIRRORED_REPEAT = 1,
CLAMP_TO_EDGE = 2,
CLAMP_TO_BORDER = 3 //Effectively the same as clamp_to_edge
};
/// Represents texture data loaded on the GPU. Contains a handle that can be passed to OpenGL draw calls.
class Texture {
protected:
GLuint texture_handle = 0;
Vector2 texture_size = {0, 0};
ReTexture::TextureFlag texture_flags;
ReTexture::TextureFormat texture_format;
TextureFilteringMode texture_filtering_mode;
TextureWrappingMode texture_wrapping_mode;
virtual void load(SoftwareTexture* software_texture, const Vector2& size, const TextureFormat& format, TextureFilteringMode filtering_mode, TextureWrappingMode wrapping_mode);
public:
explicit Texture(const std::string& file, TextureFilteringMode filtering_mode = TextureFilteringMode::BILINEAR, TextureWrappingMode wrapping_mode = TextureWrappingMode::CLAMP_TO_EDGE);
Texture(const std::string& file, const TextureFlag& flags, TextureFilteringMode filtering_mode = TextureFilteringMode::BILINEAR, TextureWrappingMode wrapping_mode = TextureWrappingMode::CLAMP_TO_EDGE);
Texture() = default;
public:
GLuint GetGLTextureHandle() const;
Vector2 GetDimensions() const;
TextureFilteringMode GetFilteringMode() const;
TextureFlag GetFlags() const;
TextureFormat GetFormat() const;
std::vector<Color4> GetPixelData() const;
void SetTextureHandle(GLuint handle);
void Erase();
};
}

33
include/JGL/types/enums.h Normal file
View File

@@ -0,0 +1,33 @@
#pragma once
namespace JGL {
enum class Inversion : u8 {
None = 0,
Vertical = 1,
Horizontal = 2,
};
inline Inversion operator|(Inversion a, Inversion b) {
return static_cast<Inversion>(static_cast<int>(a) | static_cast<int>(b));
}
inline bool operator&(Inversion a, Inversion b) {
return (u8)a & (u8)b;
}
}
namespace JGL {
enum class Gradient : u8 {
Vertical = 0,
Horizontal = 1,
DiagonalTopLeft = 2,
DiagonalBottomLeft = 4
};
inline Gradient operator|(Gradient a, Gradient b) {
return static_cast<Gradient>(static_cast<int>(a) | static_cast<int>(b));
}
inline bool operator&(Gradient a, Gradient b) {
return (u8)a & (u8)b;
}
}

220
main.cpp
View File

@@ -1,23 +1,77 @@
#include <glad/glad.h>
#include <JGL/JGL.h>
#include <rewindow/types/window.h>
#include <JGL/Colors.h>
#include <J3ML/LinearAlgebra/Vector2.h>
#include <Colors.hpp>
#include <chrono>
#include <J3ML/LinearAlgebra/Vector2.hpp>
#include <JGL/types/Font.h>
#include <JGL/Logger.h>
#include <ReTexture/Texture.h>
using J3ML::LinearAlgebra::Vector2;
using namespace JGL;
//The Re3D style base projection.
std::vector<GLfloat> perspective(float fov, float aspect, float nearPlane, float farPlane) {
std::vector<float> result(16);
float f = 1.0f / tan(fov * 0.5f * M_PI / 180.0f);
result[0] = f / aspect;
result[5] = f;
result[10] = (farPlane + nearPlane) / (nearPlane - farPlane);
result[11] = -1.0f;
result[14] = (2.0f * farPlane * nearPlane) / (nearPlane - farPlane);
return result;
}
JGL::Font FreeSans;
JGL::Font Jupiteroid;
class Gizmo
{
public:
Gizmo() {}
Gizmo(const Vector2& pos) : position(pos) {}
bool dragging = false;
bool hovered = false;
Vector2 position;
float range = 6.f;
void Grab() {
if (hovered)
{
dragging = true;
}
}
void Release() {
dragging = false;
}
void Update(const Vector2& mouse)
{
if (dragging)
{
position = position.Lerp(mouse, 0.25f);
}
if (mouse.Distance(position) < range)
{
hovered = true;
} else
{
hovered = false;
}
}
void Draw()
{
if (dragging)
{
J2D::DrawPoint(Colors::White, position, 4.f);
} else if (hovered)
{
J2D::DrawPoint(Colors::Reds::Crimson, position, 6.f);
} else
{
J2D::DrawPoint(Colors::Reds::Salmon, position, 3.f);
}
J2D::DrawString(Colors::White, std::format("{:.1f},{:.1f}", position.x, position.y), position.x, position.y, 1.f, 10, FreeSans);
}
protected:
private:
};
Texture* image;
class Camera {
public:
@@ -57,74 +111,134 @@ struct point {
GLfloat t;
};
int FreeSans;
int Jupiteroid;
Gizmo a({250, 150});
Gizmo b({200, 250});
Gizmo c({350, 300});
Gizmo d({450, 250});
class JGLDemoWindow : public ReWindow::RWindow
{
public:
void initGL() {
camera = new Camera;
auto window_size = getSize();
auto aspect = (float) window_size[0] / (float) window_size[1];
gladLoadGL();
JGL::InitTextEngine();
JGL::Update(getSize());
FreeSans = JGL::LoadFont("assets/fonts/FreeSans.ttf");
Jupiteroid = JGL::LoadFont("assets/fonts/Jupiteroid.ttf");
J3D::Init(getSize(), 90, 100);
FreeSans = JGL::Font("assets/fonts/FreeSans.ttf");
Jupiteroid = JGL::Font("assets/fonts/Jupiteroid.ttf");
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glMultMatrixf(perspective(75, aspect, 0.001, 100).data());
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glClearColor(0.f, 0.f, 0.f, 0.f);
glViewport(0,0,window_size.x,window_size.y);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);
glDepthMask(GL_TRUE);
image = new Texture("assets/sprites/Re3D.png", TextureFilteringMode::BILINEAR);
}
Vector3 textAngle = {0,0,0};
float fov = 90;
float sprite_radians = 0;
bool fov_increasing = true;
unsigned long long frames = 0;
bool framerate_measurement = false;
std::chrono::system_clock::time_point start;
float elapsed = 0.0f;
void display() {
if (framerate_measurement)
start = std::chrono::high_resolution_clock::now();
JGL::Update(getSize());
if (fov_increasing)
fov += 0.25;
else
fov -= 0.50;
if (fov >= 120)
fov_increasing = false;
else if (fov <= 75)
fov_increasing = true;
J3D::ChangeFOV(fov);
sprite_radians += 0.05;
textAngle.y += 2.0f;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
camera->render();
///All 3D elements of the scene and JGL elements *must* be rendered before the 2d stuff.
J3D::Begin();
J3D::DrawLine(JGL::Colors::Red, {-0.33,-0.125,1}, {-1,-0.125,1});
J3D::DrawLine(JGL::Colors::Red, {-0.33,-0.125,1}, {-0.33,0.25,1});
J3D::DrawString(JGL::Colors::Red, "JGL Sample Text", {-0.33, -0.1, 1.0f},textAngle, 1.f, 32, FreeSans);
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::End();
J2D::Begin();
J2D::FillQuad(Color4(Colors::Red), {500, 52}, {500, 152}, {600, 152}, {600, 52});
J2D::FillRect(Colors::Blue, {0,52}, {100,100});
J2D::FillRect(Color4::FromColor3(Colors::Pinks::HotPink), {68, 120}, {32, 32});
J2D::DrawSprite(*image, {252, 252}, sprite_radians, {0.5, 0.5});
J2D::DrawPartialSprite(*image, {200, 252}, image->GetDimensions() * 0.25, image->GetDimensions() * 0.75, sprite_radians, {0.5, 0.5});
J2D::FillRect(Colors::Pinks::HotPink, {68, 120}, {32, 32});
J2D::FillGradientRect(Colors::Red, Colors::Blue, Gradient::DiagonalBottomLeft, {100,52}, {100,100});
J2D::FillRoundedRect(JGL::Colors::Red, {200, 52}, {100, 100}, 8, 8);
J2D::FillRoundedRect(JGL::Colors::Purples::BlueViolet, {300, 52}, {100, 100}, 8, 4);
J2D::FillRoundedRect(Colors::Red, {200, 52}, {100, 100}, 8, 8);
J2D::FillRoundedRect(Colors::Purples::BlueViolet, {300, 52}, {100, 100}, 8, 4);
J2D::FillCircle(JGL::Colors::White, {52, 204}, 50, 24);
J2D::OutlineCircle(JGL::Colors::White, {153, 204}, 50, 24);
J2D::FillCircle(Colors::White, {52, 204}, 50, 24);
J2D::OutlineCircle(Colors::White, {153, 204}, 50, 24);
J2D::FillTriangle(Colors::Red, {{0, 275}, {0, 375}, {100, 375}});
//J2D::FillTriangle(Colors::Red, {{0, 275}, {0, 375}, {100, 375}});
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(JGL::Colors::Red, JGL::Colors::Blue, {105, 375}, {200, 275}, 2);
J2D::DrawGradientLine(Colors::Red, Colors::Blue, {105, 375}, {200, 275}, 2);
auto result = Jupiteroid.MeasureString("Jupiteroid Font", 16);
J2D::DrawString(JGL::Colors::Green, "Jupteroid Font", 0.f, 16, 1.f, 16, Jupiteroid);
J2D::DrawString(JGL::Colors::White, "Position: " + std::to_string(camera->position.x) + " " + std::to_string(camera->position.y) + " " + std::to_string(camera->position.z), 0, 33, 1,16, Jupiteroid);
J2D::DrawString(JGL::Colors::White, "ViewAngle: " + std::to_string(camera->angle.x) + " " + std::to_string(camera->angle.y) + " " + std::to_string(camera->angle.z), 0, 50, 1,16, Jupiteroid);
J2D::FillRect(Colors::Gray, {0, 0}, result);
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::OutlinePolygon(Colors::White, {{200, 400}, {220, 420}, {220, 430}, {230, 410}, {200, 400}});
//J2D::FillPolygon(Colors::White, {{200, 400}, {220, 420}, {220, 430}, {230, 410}, {200, 400}});
J2D::DrawCubicBezierCurve(Colors::Blues::CornflowerBlue,
a.position,
b.position,
c.position,
d.position
, 20, 1.5f);
a.Draw();
b.Draw();
c.Draw();
d.Draw();
J2D::End();
if (framerate_measurement) {
frames++;
std::chrono::system_clock::time_point stop = std::chrono::high_resolution_clock::now();
std::chrono::duration<float> frame_time = stop - start;
elapsed += frame_time.count();
if (elapsed >= 1)
std::cout << "Framerate: " << frames << std::endl,
frames = 0,
elapsed = 0,
framerate_measurement = false,
setVsyncEnabled(true);
} else if (isKeyDown(Keys::One))
framerate_measurement = true,
frames = 0,
setVsyncEnabled(false);
}
void OnRefresh(float elapsed) override {
auto mouse = GetMouseCoordinates();
a.Update(mouse);
b.Update(mouse);
c.Update(mouse);
d.Update(mouse);
display();
int glError = glGetError();
if (glError != GL_NO_ERROR)
@@ -132,6 +246,25 @@ public:
glSwapBuffers();
}
void OnMouseButtonDown(const ReWindow::WindowEvents::MouseButtonDownEvent & ev) override
{
RWindow::OnMouseButtonDown(ev);
a.Grab();
b.Grab();
c.Grab();
d.Grab();
}
void OnMouseButtonUp(const ReWindow::WindowEvents::MouseButtonUpEvent & ev) override
{
RWindow::OnMouseButtonUp(ev);
a.Release();
b.Release();
c.Release();
d.Release();
}
bool OnResizeRequest(const ReWindow::WindowResizeRequestEvent& e) override {return true;}
JGLDemoWindow() : ReWindow::RWindow() {}
JGLDemoWindow(const std::string& title, int width, int height) : ReWindow::RWindow(title, width, height){}
@@ -142,7 +275,8 @@ int main(int argc, char** argv) {
window->setRenderer(RenderingAPI::OPENGL);
window->Open();
window->initGL();
window->setResizable(false);
window->setResizable(true);
window->setVsyncEnabled(true);
while (window->isAlive()) {
window->pollEvents();

View File

@@ -0,0 +1 @@
Main:new("Install build dependencies", "apt-get install -yq libgl1-mesa-dev libfreetype-dev")

View File

@@ -4,10 +4,12 @@
#include <JGL/JGL.h>
#include <glad/glad.h>
#include <JGL/Color3.h>
#include <jlog/jlog.hpp>
#include <Color3.hpp>
#include <jlog/Logger.hpp>
#include <J3ML/Algorithm/Bezier.hpp>
GLfloat oldColor[4] = {0, 0, 0, 255};
GLfloat oldColor[4] = {0, 0, 0, 1};
GLfloat baseColor[4] = {1, 1, 1, 1};
bool inJ2D = false;
bool inJ3D = false;
bool wasTexture2DEnabled = false;
@@ -23,70 +25,82 @@ namespace JGL {
using namespace J3ML;
Vector2 wS;
bool Update(const Vector2& window_size) {
void Update(const Vector2& window_size) {
wS = window_size;
return JGL::InitTextEngine();
glViewport(0, 0, (int) wS.x, (int) wS.y);
}
void J2D::Begin() {
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glViewport(0, 0, (int) wS.x, (int) wS.y);
glOrtho(0, wS.x, wS.y, 0, -1, 1);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
glGetIntegerv(GL_ACTIVE_TEXTURE, &activeTextureUnit);
//Get what the draw color was before we did anything.
glGetFloatv(GL_CURRENT_COLOR, oldColor);
glColor4fv(baseColor);
glGetIntegerv(GL_ACTIVE_TEXTURE,& activeTextureUnit);
activeTextureUnit = activeTextureUnit - GL_TEXTURE0;
if (activeTextureUnit != 0)
glActiveTexture(GL_TEXTURE0);
wasDepthTestEnabled = false;
if (glIsEnabled(GL_DEPTH_TEST))
wasDepthTestEnabled = true,
glDisable(GL_DEPTH_TEST);
else
wasDepthTestEnabled = false;
wasVertexArraysEnabled = true;
if (!glIsEnabled(GL_VERTEX_ARRAY))
wasVertexArraysEnabled = false,
glEnableClientState(GL_VERTEX_ARRAY);
else
wasVertexArraysEnabled = true;
wasCullFaceEnabled = true;
if (!glIsEnabled(GL_CULL_FACE))
wasCullFaceEnabled = false,
glEnable(GL_CULL_FACE),
glCullFace(GL_BACK);
else
wasCullFaceEnabled = true;
wasBlendEnabled = true;
if (!glIsEnabled(GL_BLEND))
wasBlendEnabled = false,
glEnable(GL_BLEND),
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
else
wasBlendEnabled = true;
wasTexture2DEnabled = true;
if (!glIsEnabled(GL_TEXTURE_2D))
wasTexture2DEnabled = false,
glEnable(GL_TEXTURE_2D);
else
wasTexture2DEnabled = true;
wasTextureCoordArrayEnabled = true;
if (!glIsEnabled(GL_TEXTURE_COORD_ARRAY))
wasTextureCoordArrayEnabled = false,
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
else
wasTextureCoordArrayEnabled = true;
wasColorArrayEnabled = false;
if (glIsEnabled(GL_COLOR_ARRAY))
wasColorArrayEnabled = true,
glDisableClientState(GL_COLOR_ARRAY);
else
wasColorArrayEnabled = false;
if (!inJ3D)
inJ2D = true;
else { ERROR("Attempt to Begin J2D inside of J3D context.") }
else { jlog::Error("Attempt to Begin J2D inside of J3D context."); }
}
void J2D::End() {
//Change back to the previous projection (The 3D one in Re3D's case.)
glDisable(GL_TEXTURE_2D);
glPopMatrix();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
@@ -112,70 +126,208 @@ namespace JGL {
if (wasColorArrayEnabled)
glEnableClientState(GL_COLOR_ARRAY);
/*
//Select whatever texture unit was selected before.
//Select whatever texture_handle unit was selected before.
glActiveTexture(GL_TEXTURE0 + activeTextureUnit);
*/
//Put the draw color back how it was before.
glColor4f(oldColor[0], oldColor[1], oldColor[2], oldColor[3]);
glColor4fv(oldColor);
inJ2D = false;
}
void J2D::FillRect(const Color4& color, const Vector2& pos, const Vector2& size) {
void J2D::DrawSprite(const Texture& texture, const Vector2& pos, float rad_rotation, const Vector2& origin, const Vector2& scale, const Color4& color, Inversion inversion) {
if (!inJ2D)
ERROR("Attempt to Render J2D element before J2D begin.")
jlog::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}};
glColor4f(color.r / 255.f, color.g / 255.f, color.b / 255.f, color.a / 255.f);
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices);
const Vector2 size = texture.GetDimensions();
std::array<Vector2, 4> 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 - offset * scale;
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& Inversion::Vertical)
textureCoordinates = {Vector2(0, 1), Vector2(0, 0), Vector2(1, 0), Vector2(1, 1)};
if (inversion& Inversion::Horizontal)
textureCoordinates = {Vector2(1, 0), Vector2(1, 1), Vector2(0, 1), Vector2(0, 0)};
if ((inversion& Inversion::Horizontal) && (inversion& Inversion::Vertical))
textureCoordinates = {Vector2(1, 1), Vector2(1, 0), Vector2(0, 0), Vector2(0, 1)};
glColor4ubv(color.ptr());
glBindTexture(GL_TEXTURE_2D, texture.GetGLTextureHandle());
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices.data());
glTexCoordPointer(2, GL_FLOAT, sizeof(Vector2), textureCoordinates.data());
glDrawArrays(GL_QUADS, 0, 4);
glBindTexture(GL_TEXTURE_2D, 0);
glColor4fv(baseColor);
}
void J2D::FillRect(const Color3& color, const Vector2& pos, const Vector2& size) {
J2D::FillRect({color.r, color.g, color.b, 255}, pos, size);
void J2D::DrawSprite(const Texture& texture, float positionX, float positionY, float rad_rotation, float originX, float originY, float scaleX, float scaleY, const Color4& color, Inversion inversion)
{
DrawSprite(texture,
{positionX, positionY},
rad_rotation,
{originX, originY},
{scaleX, scaleY},
color, inversion);
}
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, Inversion inversion) {
if (!inJ2D)
jlog::Error("Drawing J2D element before J2D begin.");
const Vector2 textureSize = texture.GetDimensions();
// Calculate texture coordinates (relative to the whole texture)
std::array<GLfloat, 8> textureCoordinates = {
sub_texture_position.x / textureSize.x,
sub_texture_position.y / textureSize.y,
sub_texture_position.x / textureSize.x,
(sub_texture_position.y + sub_texture_size.y) / textureSize.y,
(sub_texture_position.x + sub_texture_size.x) / textureSize.x,
(sub_texture_position.y + sub_texture_size.y) / textureSize.y,
(sub_texture_position.x + sub_texture_size.x) / textureSize.x,
sub_texture_position.y / textureSize.y
};
if (inversion & Inversion::Vertical)
std::swap(textureCoordinates[1], textureCoordinates[3]),
std::swap(textureCoordinates[5], textureCoordinates[7]);
if (inversion & Inversion::Horizontal)
std::swap(textureCoordinates[0], textureCoordinates[6]),
std::swap(textureCoordinates[2], textureCoordinates[4]);
const Vector2 offset = origin * sub_texture_size;
Vector2 pos2 = position - offset * scale;
Vector2 scaled_size = scale * sub_texture_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
{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};
glColor4ubv(color.ptr());
glBindTexture(GL_TEXTURE_2D, texture.GetGLTextureHandle());
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices.data());
glTexCoordPointer(2, GL_FLOAT, 0, textureCoordinates.data());
glDrawArrays(GL_QUADS, 0, 4);
glBindTexture(GL_TEXTURE_2D, 0);
glColor4fv(baseColor);
}
void J2D::DrawPartialSprite(const JGL::Texture& texture, float positionX, float positionY, float sub_texture_positionX, float sub_texture_positionY, unsigned int sub_texture_sizeX,
unsigned int sub_texture_sizeY, float originX,float originY, float rad_rotation, float scaleX, float scaleY, const Color4& color,JGL::Inversion inversion) {
J2D::DrawPartialSprite(texture, {positionX, positionY}, {sub_texture_positionX, sub_texture_positionY},
{(float) sub_texture_sizeX, (float) sub_texture_sizeY}, rad_rotation, {originX, originY}, {scaleX, scaleY}, color, inversion);
}
void J2D::FillQuad(const Color4& color, const Vector2& v1, const Vector2& v2, const Vector2& v3, const Vector2& v4) {
if (!inJ2D)
jlog::Error("Drawing J2D element before J2D begin.");
Vector2 vertices[4] = {v1, v2, v3, v4};
glColor4ubv(color.ptr());
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices);
glDrawArrays(GL_QUADS, 0, 4);
glColor4fv(baseColor);
}
void J2D::OutlineQuad(const Color4& color, const Vector2& v1, const Vector2& v2, const Vector2& v3, const Vector2& v4, float thickness) {
if (!inJ2D)
jlog::Error("Drawing J2D element before J2D begin.");
Vector2 vertices[] = {v1, v2, v3, v4};
glLineWidth(thickness);
glColor4ubv(color.ptr());
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices);
glDrawArrays(GL_LINE_LOOP, 0, 4);
glColor4fv(baseColor);
}
void J2D::FillRect(const Color4& color, const Vector2& pos, const Vector2& size) {
if (!inJ2D)
jlog::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}};
glColor4ubv(color.ptr());
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices);
glDrawArrays(GL_QUADS, 0, 4);
glColor4fv(baseColor);
}
void J2D::FillGradientRect(const Color4& color1, const Color4& color2, const Gradient& gradient, const Vector2& pos, const Vector2& size) {
if (!inJ2D)
ERROR("Attempt to Render J2D element before J2D begin.")
jlog::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 = {};
std::vector<GLfloat> colors{};
if (gradient == Gradient::Horizontal)
colors = {color1.r / 255.f, color1.g / 255.f, color1.b / 255.f, color1.a / 255.f, color1.r / 255.f, color1.g / 255.f, color1.b / 255.f, color1.a / 255.f,
color2.r / 255.f, color2.g / 255.f, color2.b / 255.f, color2.a / 255.f, color2.r / 255.f, color2.g / 255.f, color2.b / 255.f, color2.a / 255.f};
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 == Gradient::Vertical)
colors = {color1.r / 255.f, color1.g / 255.f, color1.b / 255.f, color1.a / 255.f, color2.r / 255.f, color2.g / 255.f, color2.b / 255.f, color2.a / 255.f,
color2.r / 255.f, color2.g / 255.f, color2.b / 255.f, color2.a / 255.f, color1.r / 255.f, color1.g / 255.f, color1.b / 255.f, color1.a / 255.f};
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 == Gradient::DiagonalBottomLeft)
colors = {(color1.r + color2.r) / 2.f / 255.f, (color1.g + color2.g) / 2.f / 255.f, (color1.b + color2.b) / 2.f / 255.f, (color1.a + color2.a) / 2.f / 255.f,
color1.r / 255.f, color1.g / 255.f, color1.b / 255.f, color1.a / 255.f,(color1.r + color2.r) / 2.f / 255.f, (color1.g + color2.g) / 2.f / 255.f,
(color1.b + color2.b) / 2.f / 255.f, (color1.a + color2.a) / 2.f / 255.f, color2.r / 255.f, color2.g / 255.f, color2.b / 255.f, color2.a / 255.f};
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 == Gradient::DiagonalTopLeft)
colors = {color1.r / 255.f, color1.g / 255.f, color1.b / 255.f, color1.a / 255.f,(color1.r + color2.r) / 2.f / 255.f, (color1.g + color2.g) / 2.f / 255.f,
(color1.b + color2.b) / 2.f / 255.f, (color1.a + color2.a) / 2.f / 255.f,color2.r / 255.f, color2.g / 255.f, color2.b / 255.f, color2.a / 255.f,
(color1.r + color2.r) / 2.f / 255.f, (color1.g + color2.g) / 2.f / 255.f, (color1.b + color2.b) / 2.f / 255.f,(color1.a + color2.a) / 2.f / 255.f};
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};
glEnableClientState(GL_COLOR_ARRAY);
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices);
glColorPointer(4, GL_FLOAT, 0, colors.data());
glDrawArrays(GL_QUADS, 0, 4);
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices);
glColorPointer(4, GL_FLOAT, 0, colors.data());
glDrawArrays(GL_QUADS, 0, 4);
glDisableClientState(GL_COLOR_ARRAY);
glColor4fv(baseColor);
}
void J2D::FillGradientRect(const Color3& color1, const Color3& color2, const Gradient& gradient, const Vector2& pos, const Vector2& size) {
J2D::FillGradientRect({color1.r, color1.g, color1.b, 255}, {color2.r, color2.g, color2.b, 255}, gradient, pos, size);
}
void J2D::FillRoundedRect(const Color4 &color, const Vector2 &pos, const Vector2 &size, float radius, unsigned int subdivisions) {
void J2D::FillRoundedRect(const Color4& color, const Vector2& pos, const Vector2& size, float radius, unsigned int subdivisions) {
if (!inJ2D)
ERROR("Attempt to Render J2D element before J2D begin.")
jlog::Error("Drawing J2D element before J2D begin.");
J2D::FillRect(color, {pos.x + radius, pos.y}, {size.x - 2 * radius, size.y});
J2D::FillRect(color, {pos.x, pos.y + radius}, {size.x, size.y - 2 * radius});
@@ -186,181 +338,231 @@ namespace JGL {
J2D::FillCircle(color, {pos.x + size.x - radius, pos.y + size.y - radius}, radius, subdivisions);
}
void J2D::FillRoundedRect(const JGL::Color3& color, const J3ML::LinearAlgebra::Vector2& pos, const J3ML::LinearAlgebra::Vector2& size, float radius, unsigned int subdivisions) {
J2D::FillRoundedRect({color.r, color.g, color.b, 255}, pos, size, radius, subdivisions);
}
void J2D::OutlineRect(const Color4& color, const Vector2& pos, const Vector2& size, float thickness) {
if (!inJ2D)
ERROR("Attempt to Render J2D element before J2D begin.")
jlog::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}};
glLineWidth(thickness);
glColor4f(color.r / 255.f, color.g / 255.f, color.b / 255.f, color.a / 255.f);
glColor4ubv(color.ptr());
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices);
glDrawArrays(GL_LINE_LOOP, 0, 4);
}
void J2D::OutlineRect(const Color3& color, const Vector2& pos, const Vector2& size, float thickness) {
J2D::OutlineRect({color.r, color.g, color.b, 255}, pos, size, thickness);
glColor4fv(baseColor);
}
void J2D::DrawLine(const Color4& color, const Vector2& A, const Vector2& B, float thickness) {
if (!inJ2D)
ERROR("Attempt to Render J2D element before J2D begin.");
jlog::Error("Drawing J2D element before J2D begin.");
Vector2 vertices[] = {A, B};
glLineWidth(thickness);
glColor4f(color.r / 255.f, color.g / 255.f, color.b / 255.f, color.a / 255.f);
glColor4ubv(color.ptr());
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices);
glDrawArrays(GL_LINES, 0, 2);
}
void J2D::DrawLine(const Color3& color, const Vector2& A, const Vector2& B, float thickness) {
J2D::DrawLine({color.r, color.g, color.b, 255}, A, B, thickness);
glColor4fv(baseColor);
}
void J2D::DrawLine(const Color4& color, float x, float y, float w, float h, float thickness) {
J2D::DrawLine(color, {x, y}, {w, h}, thickness);
}
void J2D::DrawLine(const Color3& color, float x, float y, float w, float h, float thickness) {
J2D::DrawLine({color.r, color.g, color.b, 255}, x, y, w, h, thickness);
}
void J2D::DrawGradientLine(const Color4& color1, const Color4& color2, const Vector2& A, const Vector2& B, float thickness) {
if (!inJ2D)
ERROR("Attempt to Render J2D element before J2D begin.");
jlog::Error("Drawing J2D element before J2D begin.");
Vector2 vertices[] = {A, B};
GLfloat colors[8] = {color1.r / 255.f, color1.g / 255.f, color1.b / 255.f, color1.a / 255.f,
color2.r / 255.f, color2.g / 255.f, color2.b / 255.f, color2.a / 255.f};
GLfloat colors[8] = {color1.RedChannelNormalized(), color1.GreenChannelNormalized(), color1.BlueChannelNormalized(), color1.AlphaChannelNormalized(),
color2.RedChannelNormalized(), color2.GreenChannelNormalized(), color2.BlueChannelNormalized(), color2.AlphaChannelNormalized() };
glEnableClientState(GL_COLOR_ARRAY);
glLineWidth(thickness);
glColorPointer(4,GL_FLOAT,sizeof(GL_FLOAT) * 4, colors);
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices);
glDrawArrays(GL_LINES, 0, 2);
glLineWidth(thickness);
glColorPointer(4,GL_FLOAT,sizeof(Color4), colors);
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices);
glDrawArrays(GL_LINES, 0, 2);
glDisableClientState(GL_COLOR_ARRAY);
glColor4fv(baseColor);
}
void J2D::DrawGradientLine(const Color3& color1, const Color3& color2, const Vector2& A, const Vector2& B, float thickness) {
J2D::DrawGradientLine({color1.r, color1.g, color1.b, 255}, {color2.r, color2.g, color2.b, 255}, A, B, thickness);
}
void DrawGradientLine(const Color4& color1, const Color4& color2, float x, float y, float w, float h, float thickness) {
J2D::DrawGradientLine(color1, color2, {x, y}, {w, h}, thickness);
}
void DrawGradientLine(const Color3& color1, const Color3& color2, float x, float y, float w, float h, float thickness) {
J2D::DrawGradientLine({color1.r, color1.g, color1.b, 255}, {color2.r, color2.g, color2.b, 255}, {x, y}, {w, h}, thickness);
void J2D::OutlinePolygon(const Color4 &color, const std::vector<Vector2>& points, float thickness) {
if (!inJ2D)
jlog::Error("Drawing J2D element before J2D begin.");
if (points.front() != points.back())
throw std::runtime_error("J2D::OutlinePolygon: The first point and the last point must connect.");
glLineWidth(thickness);
glColor4ubv(color.ptr());
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), points.data());
glDrawArrays(GL_LINE_LOOP, 0, (int) points.size());
glColor4fv(baseColor);
}
void J2D::DrawPixel(const Color4& color, const Vector2& coordinates) {
void J2D::DrawPoint(const Color4& color, const Vector2& coordinates, float radius) {
if (!inJ2D)
ERROR("Attempt to Render J2D element before J2D begin.");
jlog::Error("Drawing J2D element before J2D begin.");
Vector2 vertices[] = {coordinates};
glColor4f(color.r / 255.f, color.g / 255.f, color.b / 255.f, color.a / 255.f);
glPointSize(radius);
glColor4ubv(color.ptr());
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices);
glDrawArrays(GL_LINES, 0, 1);
glDrawArrays(GL_POINTS, 0, 1);
glColor4fv(baseColor);
}
void J2D::DrawPixel(const Color3& color, const Vector2& coordinates) {
J2D::DrawPixel({color.r, color.g, color.b, 255}, coordinates);
}
void J2D::DrawPixel(const Color4& color, float x, float y) {
DrawPixel(color, {x, y});
}
void J2D::DrawPixel(const Color3& color, float x, float y) {
DrawPixel({color.r, color.g, color.b, 255}, {x, y});
void J2D::DrawPoint(const Color4& color, float x, float y, float radius) {
DrawPoint(color, {x, y}, radius);
}
void J2D::OutlineCircle(const Color4& color, const Vector2& center, float radius, unsigned int subdivisions, float thickness) {
if (!inJ2D)
ERROR("Attempt to Render J2D element before J2D begin.")
jlog::Error("Drawing J2D element before J2D begin.");
float step = (2.f * M_PI) / (float) subdivisions;
float step = (2.f * Math::Pi) / (float) subdivisions;
std::vector<Vector2> vertices{};
GLfloat angle, x, y;
for (angle = 0.0f; angle < (2.f * M_PI); angle += step) {
for (angle = 0.0f; angle < (2.f * Math::Pi); angle += step) {
x = radius * std::sin(angle) + center.x;
y = radius * std::cos(angle) + center.y;
vertices.emplace_back(x,y);
}
glLineWidth(thickness);
glColor4f(color.r / 255.f, color.g / 255.f, color.b / 255.f, color.a / 255.f);
glColor4ubv(color.ptr());
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices.data());
glDrawArrays(GL_LINE_LOOP, 0, vertices.size());
}
void J2D::OutlineCircle(const Color3& color, const Vector2& center, float radius, unsigned int subdivisions, float thickness) {
J2D::OutlineCircle({color.r, color.g, color.b, 255}, center, radius, subdivisions, thickness);
glDrawArrays(GL_LINE_LOOP, 0, (int) vertices.size());
glColor4fv(baseColor);
}
void J2D::FillCircle(const Color4& color, const Vector2& center, float radius, unsigned int subdivisions) {
if (!inJ2D)
ERROR("Attempt to Render J2D element before J2D begin.")
jlog::Error("Drawing J2D element before J2D begin.");
float step = (2.f * M_PI) / (float) subdivisions;;
std::vector<Vector2> vertices{};
GLfloat angle, x, y;
float step = (2.f * Math::Pi) / (float) subdivisions;
std::vector<Vector2> vertices{};
for (angle = 0.0f; angle < (2.f * M_PI); angle += step)
x = radius * sin(angle) + center.x,
y = radius * cos(angle) + center.y,
vertices.push_back({x, y});
for (angle = 0.0f; angle < (2.f * Math::Pi); angle += step)
x = radius * std::sin(angle) + center.x,
y = radius * std::cos(angle) + center.y,
vertices.emplace_back(x, y);
glColor4f(color.r / 255.f, color.g / 255.f, color.b / 255.f, color.a / 255.f);
glColor4ubv(color.ptr());
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices.data());
glDrawArrays(GL_TRIANGLE_FAN, 0, vertices.size());
}
void J2D::FillCircle(const Color3& color, const Vector2& center, float radius, unsigned int subdivisions) {
J2D::FillCircle({color.r, color.g, color.b, 255}, center, radius, subdivisions);
glDrawArrays(GL_TRIANGLE_FAN, 0, (int) vertices.size());
glColor4fv(baseColor);
}
void J2D::OutlineTriangle(const Color4& color, const Triangle2D& tri, float thickness) {
if (!inJ2D)
ERROR("Attempt to Render J2D element before J2D begin.")
jlog::Error("Drawing J2D element before J2D begin.");
Vector2 vertices[] = {{tri.A.x, tri.A.y}, {tri.B.x, tri.B.y}, {tri.C.x, tri.C.y}};
glLineWidth(thickness);
glColor4f(color.r / 255.f, color.g / 255.f, color.b / 255.f, color.a / 255.f);
glColor4ubv(color.ptr());
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices);
glDrawArrays(GL_LINE_LOOP, 0, 3);
glColor4fv(baseColor);
}
void J2D::OutlineTriangle(const Color3& color, const Triangle2D& tri, float thickness) {
J2D::OutlineTriangle({color.r, color.g, color.b, 255}, tri, thickness);
}
void J2D::FillTriangle(const Color4& color, const Triangle2D &tri) {
void J2D::FillTriangle(const Color4& color, const Triangle2D& tri) {
if (!inJ2D)
ERROR("Attempt to Render J2D element before J2D begin.")
jlog::Error("Drawing J2D element before J2D begin.");
Vector2 vertices[] = {{tri.A.x, tri.A.y}, {tri.B.x, tri.B.y}, {tri.C.x, tri.C.y}};
glColor4f(color.r / 255.f, color.g / 255.f, color.b / 255.f, color.a / 255.f);
glColor4ubv(color.ptr());
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices);
glDrawArrays(GL_TRIANGLES, 0, 3);
glColor4fv(baseColor);
}
void J2D::FillTriangle(const Color3& color, const Triangle2D &tri) {
J2D::FillTriangle({color.r, color.g, color.b, 255}, tri);
void J2D::FillGradientTriangle(const Color4& a_color, const Color4& b_color, const Color4& c_color, const Triangle2D& tri) {
if (!inJ2D)
jlog::Error("Drawing J2D element before J2D begin.");
Vector2 vertices[] = {{tri.A.x, tri.A.y}, {tri.B.x, tri.B.y}, {tri.C.x, tri.C.y}};
GLfloat colors[] = {a_color.RedChannelNormalized(), a_color.GreenChannelNormalized(), a_color.BlueChannelNormalized(), a_color.AlphaChannelNormalized(),
b_color.RedChannelNormalized(),b_color.GreenChannelNormalized(), b_color.BlueChannelNormalized(), b_color.AlphaChannelNormalized(),
c_color.RedChannelNormalized(), c_color.GreenChannelNormalized(), c_color.BlueChannelNormalized(),c_color.AlphaChannelNormalized()};
glEnableClientState(GL_COLOR_ARRAY);
glColorPointer(4, GL_FLOAT, sizeof(Color4), colors);
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices);
glDrawArrays(GL_TRIANGLES, 0, 3);
glDisableClientState(GL_COLOR_ARRAY);
glColor4fv(baseColor);
}
void J2D::DrawCubicBezierCurve(const Color4 &color, const Vector2& controlA, const Vector2& pointA, const Vector2& pointB, const Vector2& controlB,
int subdivisions, float thickness) {
Vector2 last = controlA;
const Vector2& first = controlB;
for (int i = 0; i < subdivisions; ++i)
{
float alpha = (float) i / (float) subdivisions;
Vector2 step = J3ML::Algorithm::Bezier(alpha, controlA, pointA, pointB, controlB);
DrawLine(color, last, step, thickness);
last = step;
}
// Have to manually draw the last segment of the curve.
DrawLine(color, last, first, thickness);
}
//The 3D projection.
std::vector<GLfloat> perspective(float fov, float aspect, float nearPlane, float farPlane) {
std::vector<float> result(16);
float f = 1.0f / std::tan(fov * 0.5f * Math::Pi / 180.0f);
result[0] = f / aspect;
result[5] = f;
result[10] = (farPlane + nearPlane) / (nearPlane - farPlane);
result[11] = -1.0f;
result[14] = (2.0f * farPlane * nearPlane) / (nearPlane - farPlane);
return result;
}
bool j3d_initialized = false;
float j3d_far_plane = 0;
float j3d_fov = 0;
void J3D::Init(const J3ML::LinearAlgebra::Vector2& window_size, float fov, float far_plane) {
wS = window_size;
j3d_far_plane = far_plane;
j3d_fov = fov;
j3d_initialized = true;
}
void J3D::ChangeFOV(float fov) {
j3d_fov = fov;
}
void J3D::ChangeFarPlane(float far_plane) {
j3d_far_plane = far_plane;
}
void J3D::Begin() {
if (!j3d_initialized)
throw std::runtime_error("You have to run J3D::Init before rendering 3D elements.");
auto aspect = (float) wS.x / (float) wS.y;
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glMultMatrixf(perspective(j3d_fov, aspect, 0.001, j3d_far_plane).data());
glMatrixMode(GL_MODELVIEW);
//Get what the draw color was before we did anything.
glGetFloatv(GL_CURRENT_COLOR, oldColor);
glColor4fv(baseColor);
wasDepthTestEnabled = false;
if (glIsEnabled(GL_DEPTH_TEST))
@@ -377,7 +579,6 @@ namespace JGL {
wasTexture2DEnabled = false,
glEnable(GL_TEXTURE_2D);
//TODO We won't always want this but for now we do.
wasCullFaceEnabled = false;
if (glIsEnabled(GL_CULL_FACE))
wasCullFaceEnabled = true,
@@ -393,12 +594,11 @@ namespace JGL {
if (!inJ2D)
inJ3D = true;
else { ERROR("Attempt to Begin J3D inside of J2D context.") }
else
jlog::Error("Can't begin J3D context inside J2D context.");
}
void J3D::End() {
glDisable(GL_TEXTURE_2D);
if (wasDepthTestEnabled)
glEnable(GL_DEPTH_TEST);
@@ -408,31 +608,27 @@ namespace JGL {
if (wasTexture2DEnabled)
glDisable(GL_TEXTURE_2D);
if (wasBlendEnabled)
if (!wasBlendEnabled)
glDisable(GL_BLEND);
if (wasCullFaceEnabled)
glEnable(GL_CULL_FACE);
//Put the draw color back how it was before.
glColor4f(oldColor[0], oldColor[1], oldColor[2], oldColor[3]);
glColor4fv(oldColor);
inJ3D = false;
}
void J3D::DrawLine(const Color4& color, const Vector3& A, const Vector3& B, float thickness) {
if (!inJ3D)
ERROR("Attempt to Render J3D element before J3D begin.")
jlog::Error("Attempt to Render J3D element before J3D begin.");
Vector3 vertices[] = {A, B};
glLineWidth(thickness);
glColor4f(color.r / 255.f, color.g / 255.f, color.b / 255.f, color.a / 255.f);
glColor4ubv(color.ptr());
glVertexPointer(3, GL_FLOAT, sizeof(Vector3), vertices);
glDrawArrays(GL_LINES, 0, 2);
}
void J3D::DrawLine(const Color3& color, const Vector3& A, const Vector3& B, float thickness) {
J3D::DrawLine({color.r, color.g, color.b, 255}, A, B, thickness);
glColor4fv(baseColor);
}
}

View File

@@ -1,232 +0,0 @@
#include <JGL/JGL.h>
#if __linux__
#include <freetype2/ft2build.h>
#include FT_FREETYPE_H
#endif
#if _WIN32
#include <ft2build.h>
#include FT_FREETYPE_H
#endif
namespace JGL {
FT_Library ft;
struct Font {
int index = 0;
FT_Face face;
};
std::vector<Font> faces;
int LoadFont(const std::string &font_path) {
if (ft == nullptr)
return -1;
Font font;
if (FT_New_Face(ft, font_path.c_str(), 0, &font.face)) {
std::cout << "Error::FREETYPE: Failed to load font!" << std::endl;
return -1;
}
unsigned int newIndex = 0;
for (const auto& f : faces)
if (f.index >= newIndex)
newIndex = f.index + 1;
font.index = newIndex;
faces.push_back(font);
std::cout << "Loaded font from " << font_path << " with index " << newIndex << std::endl;
return newIndex;
}
bool InitTextEngine() {
if (FT_Init_FreeType(&ft))
return true;
return false;
}
void UnloadFont(int font_index) {
for (int i = 0; i < faces.size(); i++)
if (faces[i].index == font_index)
FT_Done_Face(faces[i].face),
faces.erase(faces.begin() + i);
}
FontCache fontCache;
void PurgeFontCache() {
fontCache.purgeCache();
}
//TODO multi-texturing for 8x speedup. I tried. We'll come back to it later.
void J2D::DrawString(const Color4& color, const std::string& text, float x, float y, float scale, u32 size, unsigned int font_index) {
glUseProgram(0); // Fixed-function pipeline.
Font font{};
CachedFont* cachedFont = fontCache.getFont(size, font_index);
//If the font doesn't exist in the cache yet.
if (!cachedFont) {
fontCache.newFont(size, font_index);
cachedFont = fontCache.getFont(size, font_index);
}
//Set up the regular font.
for (const auto& f : faces)
if (f.index == font_index)
font = f;
if (font.face == nullptr)
return;
FT_Set_Pixel_Sizes(font.face, 0, size);
glColor4f(color.r / 255.f, color.g / 255.f, color.b / 255.f, color.a / 255.f);
std::vector<GLuint> textures(text.length());
//For each character
for (int i = 0; i < text.length(); i++) {
float x2, y2, w, h;
//If the font is in the cache already.
if (cachedFont->getGlyph(text.c_str()[i])) {
CachedGlyph* glyph = cachedFont->getGlyph(text.c_str()[i]);
glBindTexture(GL_TEXTURE_2D, *glyph->getTexture());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
x2 = x + glyph->x2offset * scale;
y2 = y - glyph->y2offset * scale; // Adjust y-coordinate
w = glyph->w * scale;
h = glyph->h * scale;
x += glyph->advanceX * scale;
y += glyph->advanceY * scale;
} else {
if (FT_Load_Char(font.face, text.c_str()[i], FT_LOAD_RENDER))
continue;
FT_GlyphSlot g = font.face->glyph;
glGenTextures(1, &textures.at(i));
glBindTexture(GL_TEXTURE_2D, textures[i]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, g->bitmap.width, g->bitmap.rows, 0, GL_ALPHA, GL_UNSIGNED_BYTE, g->bitmap.buffer);
x2 = x + g->bitmap_left * scale;
y2 = -y - g->bitmap_top * scale; // Adjust y-coordinate
w = g->bitmap.width * scale;
h = g->bitmap.rows * scale;
x += (g->advance.x >> 6) * scale;
y += (g->advance.y >> 6) * scale;
cachedFont->appendGlyph(new CachedGlyph(textures.at(i), text.c_str()[i], g->bitmap_left, g->bitmap_top, g->bitmap.width, g->bitmap.rows, (g->advance.x >> 6), (g->advance.y >> 6)));
}
GLfloat vertices[12] = {x2, y2, x2, y2 + h, x2 + w, y2 + h,x2, y2, x2 + w, y2 + h, x2 + w, y2};
GLfloat textureCoordinates[12] = {0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0};
glTexCoordPointer(2, GL_FLOAT, sizeof(GL_FLOAT) * 2, &textureCoordinates);
glVertexPointer(2, GL_FLOAT, sizeof(GL_FLOAT) * 2, &vertices);
glDrawArrays(GL_TRIANGLES, 0, 6);
}
glBindTexture(GL_TEXTURE_2D, 0); // Unbind texture
}
void J2D::DrawString(const Color3& color, const std::string& text, float x, float y, float scale, u32 size, unsigned int font_index) {
J2D::DrawString(Color4::FromColor3(color, 255), text, x, y, scale, size, font_index);
}
void J3D::DrawString(const Color3& color, const std::string& text, const Vector3& pos, const Vector3& angle, float scale, u32 size, unsigned int font_index) {
//TODO figure out what the scale should actually be mathematically.
scale = scale * 0.002f;
scale = -scale;
float x = pos.x;
float y = pos.y;
float z = pos.z;
std::vector<GLuint> textures(text.length());;
glUseProgram(0); // Fixed-function pipeline.
glColor4f(color.r, color.g, color.b, 1.0f);
Font font;
for (auto& f : faces)
if (f.index == font_index)
font = f;
if (font.face == NULL) {
std::cout << "null font" << std::endl;
return;
}
FT_Set_Pixel_Sizes(font.face, 0, size);
glPushMatrix();
glTranslatef(x, y, z);
glRotatef(angle.x, 1.0f, 0.0f, 0.0f);
glRotatef(angle.y, 0.0f, 1.0f, 0.0f);
glRotatef(angle.z, 0.0f, 0.0f, 1.0f);
x = 0;
y = 0;
z = 0;
for (int i = 0; i < text.length(); i++)
{
if (FT_Load_Char(font.face, text.c_str()[i], FT_LOAD_RENDER))
continue;
FT_GlyphSlot g = font.face->glyph;
glGenTextures(1, &textures.at(i));
glBindTexture(GL_TEXTURE_2D, textures[i]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, g->bitmap.width, g->bitmap.rows, 0, GL_ALPHA, GL_UNSIGNED_BYTE, g->bitmap.buffer);
float x2 = x + g->bitmap_left * scale;
float y2 = y - g->bitmap_top * scale; // Adjust y-coordinate
float z2 = z;
float w = g->bitmap.width * scale;
float h = g->bitmap.rows * scale;
glBegin(GL_TRIANGLES);
glTexCoord2f(0, 0);
glVertex3f(x2, y2, z2);
glTexCoord2f(0, 1);
glVertex3f(x2, y2 + h, z2);
glTexCoord2f(1, 1);
glVertex3f(x2 + w, y2 + h, z2);
glTexCoord2f(0, 0);
glVertex3f(x2, y2, z2);
glTexCoord2f(1, 1);
glVertex3f(x2 + w, y2 + h, z2);
glTexCoord2f(1, 0);
glVertex3f(x2 + w, y2, z2);
glEnd();
x += (g->advance.x >> 6) * scale;
y += (g->advance.y >> 6) * scale;
}
for (unsigned int& texture : textures)
glDeleteTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, 0); // Unbind texture
glPopMatrix();
}
}

239
src/TextRendering.cpp Normal file
View File

@@ -0,0 +1,239 @@
#include <JGL/JGL.h>
#if __linux__
#include <freetype2/ft2build.h>
#include FT_FREETYPE_H
#include FT_OUTLINE_H
#endif
#if _WIN32
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_OUTLINE_H
#endif
#include <JGL/types/Font.h>
#include <JGL/types/FontCache.h>
#include <JGL/Logger.h>
namespace JGL {
void PurgeFontCache() {
fontCache.purgeCache();
}
void J2D::DrawString(const Color4& color, const std::string& text, float x, float y, float scale, u32 size, const Font& font) {
glUseProgram(0); // Fixed-function pipeline.
// Offset by height to render at "correct" location.
y += size;
CachedFont* cachedFont = fontCache.getFont(size, font.index);
if (font.face == nullptr)
return;
FT_Set_Pixel_Sizes(font.face, 0, size);
//If the font doesn't exist in the cache yet.
if (!cachedFont) {
jlog::Debug("Caching font data...");
GLuint texture_id;
glGenTextures(1, &texture_id);
glBindTexture(GL_TEXTURE_2D, texture_id);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
GLsizei width = 0;
GLsizei max_height = 0;
FT_ULong charcode;
FT_UInt gindex;
//We have to loop over the available glyphs twice as we need the
//final width and height of the texture_handle before we can construct it
//and subsequently upload the glyph data.
charcode = FT_Get_First_Char(font.face, &gindex);
//Strings are char-based so we only handle charcodes within the extended ASCII range.
while (gindex != 0 && charcode < 255) {
if (FT_Load_Char(font.face, charcode, FT_LOAD_RENDER))
std::cout << "Error::FREETYPE: Failed to load charcode: " << charcode << std::endl;
FT_GlyphSlot g = font.face->glyph;
width += g->bitmap.width;
max_height = std::max(max_height, (GLsizei) g->bitmap.rows);
charcode = FT_Get_Next_Char(font.face, charcode, &gindex);
}
fontCache.newFont(texture_id, width, max_height, size, font.index);
cachedFont = fontCache.getFont(size, font.index);
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, max_height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, nullptr);
GLsizei xoffset = 0;
charcode = FT_Get_First_Char(font.face, &gindex);
while (gindex != 0 && charcode < 255) {
if (FT_Load_Char(font.face, charcode, FT_LOAD_RENDER))
std::cout << "Error::FREETYPE: Failed to load charcode: " << charcode << std::endl;
FT_GlyphSlot g = font.face->glyph;
glTexSubImage2D(GL_TEXTURE_2D, 0, xoffset, 0, g->bitmap.width, g->bitmap.rows, GL_ALPHA, GL_UNSIGNED_BYTE, g->bitmap.buffer);
GLfloat u0 = (GLfloat)xoffset / cachedFont->getTextureWidth();
GLfloat u1 = u0 + (GLfloat)g->bitmap.width / cachedFont->getTextureWidth();
GLfloat v0 = 0.0f;
GLfloat v1 = (GLfloat)g->bitmap.rows / cachedFont->getTextureHeight();
std::array<GLfloat, 12> texcoords = {
u0, v0,
u0, v1,
u1, v1,
u0, v0,
u1, v1,
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)));
xoffset += g->bitmap.width;
charcode = FT_Get_Next_Char(font.face, charcode, &gindex);
}
}
glColor4ubv(color.ptr());
//Texture parameters are restored when the texture_handle is bound
glBindTexture(GL_TEXTURE_2D, *cachedFont->getTexture());
std::vector<std::array<GLfloat, 12>> vertices(text.size());
std::vector<std::array<GLfloat, 12>> texcoords(text.size());
for (int i = 0; i < text.length(); i++) {
float x2, y2, w, h;
CachedGlyph* glyph = cachedFont->getGlyph(text.c_str()[i]);
if (glyph == nullptr)
continue;
x2 = x + glyph->x2offset * scale;
y2 = y - glyph->y2offset * scale; // Adjust y-coordinate
w = glyph->w * scale;
h = glyph->h * scale;
x += glyph->advanceX * scale;
y += glyph->advanceY * scale;
std::array<GLfloat, 12> glyph_vertices = {
x2, y2,
x2, y2 + h,
x2 + w, y2 + h,
x2, y2,
x2 + w, y2 + h,
x2 + w, y2
};
auto glyph_texcoords = glyph->getTexCoords();
vertices[i] = glyph_vertices;
texcoords[i] = glyph_texcoords;
}
glVertexPointer(2, GL_FLOAT, sizeof(GLfloat) * 2, vertices.data());
glTexCoordPointer(2, GL_FLOAT, sizeof(GLfloat) * 2, texcoords.data());
glDrawArrays(GL_TRIANGLES, 0, (int) vertices.size() * 6);
glBindTexture(GL_TEXTURE_2D, 0);
glColor4f(1, 1, 1, 1);
}
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) {
//TODO figure out what the scale should actually be mathematically.
scale = scale * 0.002f;
scale = -scale;
float x = pos.x;
float y = pos.y;
float z = pos.z;
std::vector<GLuint> textures(text.length());
glUseProgram(0); // Fixed-function pipeline.
glColor4ubv(color.ptr());
if (font.face == nullptr)
throw std::runtime_error("FreeType font face is null.");
FT_Set_Pixel_Sizes(font.face, 0, size);
glPushMatrix();
glTranslatef(x, y, z);
glRotatef(angle.pitch, 1.0f, 0.0f, 0.0f);
glRotatef(angle.yaw, 0.0f, 1.0f, 0.0f);
glRotatef(angle.roll, 0.0f, 0.0f, 1.0f);
x = y = z = 0;
for (int i = 0; i < text.length(); i++)
{
if (FT_Load_Char(font.face, text.c_str()[i], FT_LOAD_RENDER))
continue;
FT_GlyphSlot g = font.face->glyph;
glGenTextures(1, &textures.at(i));
glBindTexture(GL_TEXTURE_2D, textures[i]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, g->bitmap.width, g->bitmap.rows, 0, GL_ALPHA, GL_UNSIGNED_BYTE, g->bitmap.buffer);
float x2 = x + g->bitmap_left * scale;
float y2 = y - g->bitmap_top * scale; // Adjust y-coordinate
float z2 = z;
float w = g->bitmap.width * scale;
float h = g->bitmap.rows * scale;
if (!draw_back_face)
glEnable(GL_CULL_FACE),
glCullFace(GL_BACK);
glBegin(GL_TRIANGLES);
glTexCoord2f(0, 0);
glVertex3f(x2, y2, z2);
glTexCoord2f(0, 1);
glVertex3f(x2, y2 + h, z2);
glTexCoord2f(1, 1);
glVertex3f(x2 + w, y2 + h, z2);
glTexCoord2f(0, 0);
glVertex3f(x2, y2, z2);
glTexCoord2f(1, 1);
glVertex3f(x2 + w, y2 + h, z2);
glTexCoord2f(1, 0);
glVertex3f(x2 + w, y2, z2);
glEnd();
if (!draw_back_face)
glDisable(GL_CULL_FACE);
x += (g->advance.x >> 6) * scale;
y += (g->advance.y >> 6) * scale;
}
for (unsigned int& texture : textures)
glDeleteTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, 0); // Unbind texture_handle
glColor4f(1, 1, 1, 1);
glPopMatrix();
}
}

156
src/types/Font.cpp Normal file
View File

@@ -0,0 +1,156 @@
#include <vector>
#include <string>
#include <iostream>
#include <glad/glad.h>
#if __linux__
#include <freetype2/ft2build.h>
#include FT_FREETYPE_H
#include FT_OUTLINE_H
#endif
#if _WIN32
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_OUTLINE_H
#endif
#include <JGL/types/Font.h>
#include <JGL/types/FontCache.h>
namespace JGL::Detail
{
FT_Library ft;
std::vector<Font> fonts;
bool InitTextEngine() {
glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // NOTE: This MUST be called for text rendering to work properly!!!
// Keep note of this, might cause problems later?
if (ft != nullptr)
throw std::runtime_error("Error::FREETYPE: FT_Library was initialized but is already initialized.");
if (FT_Init_FreeType(&ft))
return true;
return false;
}
void UnloadFont(int font_index) {
auto iter = fonts.begin();
while (iter != fonts.end())
{
if (iter->index == font_index){
FT_Done_Face(iter->face);
iter = fonts.erase(iter);
} else ++iter;
}
}
}
namespace JGL
{
bool InitTextEngine()
{
return Detail::InitTextEngine();
}
Font::Font(const std::filesystem::path& path)
{
if (Detail::ft == nullptr)
throw new 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;
throw new 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;
}
Font::~Font()
{
//Detail::UnloadFont(this->index);
}
std::vector<Font> Font::GetLoadedFonts() {
return Detail::fonts;
}
Font Font::LoadTTF(const std::filesystem::path& path)
{
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;
for(const auto& f : fontCache.getFonts()) {
if (f->getFontSize() == ptSize) {
font_of_size_in_cache = true;
break;
}
}
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;
}
FT_Set_Pixel_Sizes(this->face, ptSize, ptSize);
for (const char& c : text) {
FT_GlyphSlot slot = face->glyph;
auto glyph_index = FT_Get_Char_Index(this->face, c);
auto error = FT_Load_Glyph(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;
}
return extents;
}
}

View File

@@ -1,5 +1,4 @@
#include <JGL/FontCache.h>
#include <iostream>
#include <JGL/types/FontCache.h>
using namespace JGL;
@@ -7,12 +6,11 @@ char CachedGlyph::getCharacter() {
return character;
}
const GLuint* CachedGlyph::getTexture() {
return &texture;
const std::array<GLfloat, 12> CachedGlyph::getTexCoords() const {
return texcoords;
}
CachedGlyph::CachedGlyph(GLuint texture_id, char c, float x2offset, float y2offset, float w, float h, float advanceX, float advanceY) {
texture = texture_id;
CachedGlyph::CachedGlyph(char c, std::array<GLfloat, 12> texcoords, float x2offset, float y2offset, float w, float h, float advanceX, float advanceY) {
character = c;
this->x2offset = x2offset;
this->y2offset = y2offset;
@@ -20,13 +18,14 @@ CachedGlyph::CachedGlyph(GLuint texture_id, char c, float x2offset, float y2offs
this->h = h;
this->advanceX = advanceX;
this->advanceY = advanceY;
this->texcoords = texcoords;
}
//TODO
//Because most things shown would be english characters. We can cut down on the iteration time significantly
//by putting each english character at the beginning of the list in order of how often they usually occur in text.
void JGL::CachedFont::appendGlyph(JGL::CachedGlyph* glyph) {
glyphs.push_back(glyph);
glyphs.emplace(glyph->getCharacter(), glyph);
}
unsigned int JGL::CachedFont::getFontSize() {
@@ -37,64 +36,50 @@ unsigned int JGL::CachedFont::getFontIndex() {
return font_index;
}
//TODO make this code go faster.
CachedGlyph* JGL::CachedFont::getGlyph(char c) {
for (const auto& g : glyphs)
if (c == g->getCharacter())
return g;
auto it = glyphs.find(c);
if (it != glyphs.end())
return it->second;
return nullptr;
}
CachedFont::CachedFont(unsigned int font_size, unsigned int font_index) {
CachedFont::CachedFont(GLuint texture_id, GLsizei texture_width, GLsizei texture_height, unsigned int font_size, unsigned int font_index) {
this->texture = texture_id;
this->texture_width = texture_width;
this->texture_height = texture_height;
this->font_size = font_size;
this->font_index = font_index;
}
void CachedFont::eraseGlyph(CachedGlyph* glyph) {
if (glyph == nullptr)
return;
for (int i = 0; i < glyphs.size(); i++)
if (glyphs[i] == glyph)
glDeleteTextures(1, glyphs[i]->getTexture()),
delete glyphs[i],
glyphs.erase(glyphs.begin() + i);
}
void CachedFont::eraseGlyph(char c) {
for (int i = 0; i < glyphs.size(); i++)
if (glyphs[i]->getCharacter() == c)
glDeleteTextures(1, glyphs[i]->getTexture()),
delete glyphs[i],
glyphs.erase(glyphs.begin() + i);
}
void CachedFont::eraseGlyph(GLuint texture_id) {
for (int i = 0; i < glyphs.size(); i++)
if (glyphs[i]->getTexture() == &texture_id)
glDeleteTextures(1, glyphs[i]->getTexture()),
delete glyphs[i],
glyphs.erase(glyphs.begin() + i);
}
std::vector<CachedGlyph*> CachedFont::getGlyphs() {
std::map<char, CachedGlyph*> CachedFont::getGlyphs() {
return glyphs;
}
const GLuint* CachedFont::getTexture() {
return &texture;
}
GLsizei CachedFont::getTextureWidth() const {
return texture_width;
}
GLsizei CachedFont::getTextureHeight() const {
return texture_height;
}
void FontCache::appendFont(CachedFont* font) {
cachedFonts.push_back(font);
}
void FontCache::newFont(unsigned int font_size, unsigned int font_index) {
auto* font = new CachedFont(font_size, font_index);
void FontCache::newFont(GLuint texture_id, GLsizei texture_width, GLsizei texture_height, unsigned int font_size, unsigned int font_index) {
auto* font = new CachedFont(texture_id, texture_width, texture_height, font_size, font_index);
cachedFonts.push_back(font);
}
void FontCache::eraseFont(CachedFont* font) {
for (int i = 0; i < cachedFonts.size(); i++) {
if (cachedFonts[i] == font) {
for (auto& g: cachedFonts[i]->getGlyphs())
cachedFonts[i]->eraseGlyph(g);
delete cachedFonts[i];
cachedFonts.erase(cachedFonts.begin() + i);
}

163
src/types/Texture.cpp Normal file
View File

@@ -0,0 +1,163 @@
#include <JGL/types/Texture.h>
#include <iostream>
using namespace ReTexture;
namespace JGL
{
Texture::Texture(const std::string &file, const ReTexture::TextureFlag &flags, TextureFilteringMode filtering_mode, TextureWrappingMode wrapping_mode)
{
auto *t = new ReTexture::SoftwareTexture(file, flags);
load(t, {(float) t->getWidth(), (float) t->getHeight()}, t->getTextureFormat(), filtering_mode,
wrapping_mode);
texture_flags = flags;
delete t;
}
Texture::Texture(const std::string &file, TextureFilteringMode filtering_mode, TextureWrappingMode wrapping_mode) {
auto *t = new SoftwareTexture(file);
load(t, {(float) t->getWidth(), (float) t->getHeight()}, t->getTextureFormat(), filtering_mode,
wrapping_mode);
texture_flags = TextureFlag::NONE;
delete t;
}
void Texture::load(SoftwareTexture *software_texture, const Vector2 &size, const TextureFormat &format,
TextureFilteringMode filtering_mode, TextureWrappingMode wrapping_mode) {
glGenTextures(1, &texture_handle);
glBindTexture(GL_TEXTURE_2D, texture_handle);
if (format == TextureFormat::RGBA)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (int) size.x, (int) size.y, 0, GL_RGBA, GL_UNSIGNED_BYTE,
software_texture->pixelData.data());
else if (format == TextureFormat::RGB)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, (int) size.x, (int) size.y, 0, GL_RGB, GL_UNSIGNED_BYTE,
software_texture->pixelData.data());
if (wrapping_mode == TextureWrappingMode::CLAMP_TO_EDGE)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE),
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
else if (wrapping_mode == TextureWrappingMode::REPEAT)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT),
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
else if (wrapping_mode == TextureWrappingMode::MIRRORED_REPEAT)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT),
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
else if (wrapping_mode == TextureWrappingMode::CLAMP_TO_BORDER)
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 == TextureFilteringMode::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 == TextureFilteringMode::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 == TextureFilteringMode::MIPMAP_NEAREST ||
filtering_mode == TextureFilteringMode::MIPMAP_BILINEAR ||
filtering_mode == TextureFilteringMode::MIPMAP_TRILINEAR) {
//3 mipmap levels.
auto *m1 = new SoftwareTexture(software_texture->downscale(2));
auto *m2 = new SoftwareTexture(software_texture->downscale(4));
auto *m3 = new SoftwareTexture(software_texture->downscale(8));
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 3);
if (format == TextureFormat::RGBA) {
glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, m1->getWidth(), m1->getHeight(), 0, GL_RGBA,
GL_UNSIGNED_BYTE, m1->pixelData.data());
glTexImage2D(GL_TEXTURE_2D, 2, GL_RGBA, m2->getWidth(), m2->getHeight(), 0, GL_RGBA,
GL_UNSIGNED_BYTE, m2->pixelData.data());
glTexImage2D(GL_TEXTURE_2D, 3, GL_RGBA, m3->getWidth(), m3->getHeight(), 0, GL_RGBA,
GL_UNSIGNED_BYTE, m3->pixelData.data());
} else if (format == TextureFormat::RGB) {
glTexImage2D(GL_TEXTURE_2D, 1, GL_RGB, m1->getWidth(), m1->getHeight(), 0, GL_RGB, GL_UNSIGNED_BYTE,
m1->pixelData.data());
glTexImage2D(GL_TEXTURE_2D, 2, GL_RGB, m2->getWidth(), m2->getHeight(), 0, GL_RGB, GL_UNSIGNED_BYTE,
m2->pixelData.data());
glTexImage2D(GL_TEXTURE_2D, 3, GL_RGB, m3->getWidth(), m3->getHeight(), 0, GL_RGB, GL_UNSIGNED_BYTE,
m3->pixelData.data());
}
if (filtering_mode == TextureFilteringMode::MIPMAP_NEAREST)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST),
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
else if (filtering_mode == TextureFilteringMode::MIPMAP_BILINEAR)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST),
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
else if (filtering_mode == TextureFilteringMode::MIPMAP_TRILINEAR)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR),
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
delete m1;
delete m2;
delete m3;
}
glBindTexture(GL_TEXTURE_2D, 0);
texture_size = size;
texture_format = format;
texture_filtering_mode = filtering_mode;
}
std::vector<Color4> JGL::Texture::GetPixelData() const {
std::vector<Color4> result((size_t) (texture_size.x * texture_size.y));
glBindTexture(GL_TEXTURE_2D, texture_handle);
if (texture_format == TextureFormat::RGBA) {
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, result.data());
return result;
}
//if RGB
std::vector<Color3> color3((size_t) (texture_size.x * texture_size.y));
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGB, GL_UNSIGNED_BYTE, color3.data());
for (const auto &c: color3)
result.emplace_back(c);
return result;
}
void Texture::Erase() {
if (texture_handle != 0)
glDeleteTextures(1, &texture_handle);
}
GLuint Texture::GetGLTextureHandle() const {
return texture_handle;
}
Vector2 Texture::GetDimensions() const {
return texture_size;
}
TextureFlag Texture::GetFlags() const {
return texture_flags;
}
TextureFormat Texture::GetFormat() const {
return texture_format;
}
TextureFilteringMode Texture::GetFilteringMode() const {
return texture_filtering_mode;
}
void Texture::SetTextureHandle(GLuint handle) {
texture_handle = handle;
}
}