Compare commits
59 Commits
Release-12
...
Prerelease
Author | SHA1 | Date | |
---|---|---|---|
af42d8fd25 | |||
ff4d014739 | |||
9dda4bebc5 | |||
a5bfb4972a | |||
4348f2708a | |||
32dd2b54ca | |||
e8b601aa25 | |||
d5fd3e1a49 | |||
f593a0beac | |||
0b7af6fd31 | |||
eca4309e85 | |||
4be97f52d9 | |||
613a13618c | |||
4150c93c85 | |||
9dee59fd45 | |||
b6b2ca1bfe | |||
4eca3311c8 | |||
54711355c4 | |||
8bddf3e0a7 | |||
26d17dae38 | |||
4ff8d8ff07 | |||
162732e4b7 | |||
289157ab8a | |||
3a658b1096 | |||
480502f89e | |||
d28f680cd0 | |||
2d1e42c23b | |||
abd691b648 | |||
e261b610c2 | |||
8625c52ee9 | |||
74a4705e44 | |||
ae327b96a5 | |||
eb3e037c96 | |||
672a363c53 | |||
6a16c3f87e | |||
23d3d1f378 | |||
523806f9ef | |||
d118ae2f8e | |||
5979ae41fc | |||
652626b2e4 | |||
a8145cb926 | |||
0914b496e7 | |||
c62b58aa16 | |||
11ce9ac5fe | |||
7310825ddd | |||
513dc1e332 | |||
63bbb8794e | |||
178f328728 | |||
1c74ee9c71 | |||
608e9e29f5 | |||
c53fa52f6f | |||
7fa75cac54 | |||
|
d5f9eaa5a8 | ||
dead018993 | |||
|
e9a339182b | ||
|
076398b290 | ||
cf72d92c28 | |||
5d99f8ec1f | |||
a388ee8021 |
22
.gitea/workflows/buildtest.yml
Normal file
22
.gitea/workflows/buildtest.yml
Normal file
@@ -0,0 +1,22 @@
|
||||
name: Run ReCI Build Test
|
||||
run-name: Run ReCI Build Test For ${{ gitea.repository }}.
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
Explore-Gitea-Actions:
|
||||
runs-on: ubuntu-22.04
|
||||
env:
|
||||
RECI_GIT: https://git.redacted.cc/maxine/ReCI
|
||||
RECI: /RECI
|
||||
steps:
|
||||
- run: echo "The job was automatically triggered by a ${{ gitea.event_name }} event."
|
||||
- run: echo "This job is now running on a ${{ runner.os }} server hosted by Gitea!"
|
||||
- run: echo "The name of your branch is ${{ gitea.ref }} and your repository is ${{ gitea.repository }}."
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v3
|
||||
- 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 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 }}."
|
@@ -1,4 +1,4 @@
|
||||
cmake_minimum_required(VERSION 3.25)
|
||||
cmake_minimum_required(VERSION 3.18)
|
||||
project(JGL
|
||||
VERSION 1.0
|
||||
LANGUAGES CXX
|
||||
@@ -9,50 +9,97 @@ if (PROJECT_SOURCE_DIR STREQUAL PROJECT_BINARY_DIR)
|
||||
endif()
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
if (WIN32)
|
||||
set(CMAKE_CXX_FLAGS "-municode")
|
||||
endif(WIN32)
|
||||
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
||||
|
||||
|
||||
# Enable Package Managers
|
||||
include(cmake/CPM.cmake)
|
||||
|
||||
CPMAddPackage(
|
||||
NAME J3ML
|
||||
URL https://git.redacted.cc/josh/j3ml/archive/Release-6.zip
|
||||
URL https://git.redacted.cc/josh/j3ml/archive/Release-2.2.zip
|
||||
)
|
||||
|
||||
CPMAddPackage(
|
||||
NAME ReWindow
|
||||
URL https://git.redacted.cc/Redacted/ReWindow/archive/vA0.2.30.zip
|
||||
URL https://git.redacted.cc/Redacted/ReWindow/archive/Prerelease-3.zip
|
||||
)
|
||||
|
||||
CPMAddPackage(
|
||||
NAME GLAD
|
||||
URL https://git.redacted.cc/Redacted/glad/archive/v2.1.zip
|
||||
URL https://git.redacted.cc/Redacted/glad/archive/v2.1ext_mt.zip
|
||||
)
|
||||
|
||||
CPMAddPackage(
|
||||
NAME jlog
|
||||
URL https://git.redacted.cc/josh/jlog/Prerelease-12.zip
|
||||
)
|
||||
|
||||
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.zip
|
||||
)
|
||||
|
||||
if (WIN32)
|
||||
#CPMAddPackage(
|
||||
#NAME harfbuzz
|
||||
#URL https://github.com/harfbuzz/harfbuzz/archive/refs/tags/9.0.0.zip
|
||||
#)
|
||||
|
||||
CPMAddPackage(
|
||||
NAME freetype
|
||||
URL https://github.com/freetype/freetype/archive/refs/tags/VER-2-13-2.zip
|
||||
)
|
||||
endif()
|
||||
|
||||
file(COPY "assets" DESTINATION "${PROJECT_BINARY_DIR}")
|
||||
file(GLOB_RECURSE ASSETS "assets/*")
|
||||
file(GLOB_RECURSE HEADERS "include/*.h" "include/*.hpp")
|
||||
file(GLOB_RECURSE SOURCES "src/*.c" "src/*.cpp" )
|
||||
file(GLOB_RECURSE ASSETS "assets/*")
|
||||
|
||||
add_library(JGL SHARED ${SOURCES})
|
||||
set_target_properties(JGL PROPERTIES LINKER_LANGUAGE CXX)
|
||||
|
||||
find_package(OpenGL REQUIRED)
|
||||
find_package(Freetype REQUIRED)
|
||||
|
||||
if (UNIX AND NOT APPLE)
|
||||
find_package(Freetype REQUIRED)
|
||||
add_library(JGL SHARED ${SOURCES})
|
||||
endif()
|
||||
|
||||
if (WIN32)
|
||||
add_library(JGL STATIC ${SOURCES})
|
||||
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}
|
||||
${J3ML_SOURCE_DIR}/include
|
||||
${Event_SOURCE_DIR}/include
|
||||
${ReWindow_SOURCE_DIR}/include
|
||||
${glad_SOURCE_DIR}/include
|
||||
${jlog_SOURCE_DIR}/include
|
||||
)
|
||||
|
||||
add_executable(JGL_Demo main.cpp)
|
||||
set_target_properties(JGL_Demo PROPERTIES LINK_FLAGS "-Wl,-rpath,./lib")
|
||||
include_directories(${PROJECT_SOURCE_DIR}/include)
|
||||
include_directories(${OPENGL_INCLUDE_DIRS})
|
||||
include_directories(${J3ML_SOURCE_DIR}/include)
|
||||
include_directories(${ReWindow_SOURCE_DIR}/include)
|
||||
include_directories(${glad_SOURCE_DIR}/include)
|
||||
#set_target_properties(JGL_Demo PROPERTIES LINK_FLAGS "-Wl,-rpath,./lib")
|
||||
|
||||
target_include_directories(JGL PRIVATE ${FREETYPE_INCLUDE_DIRS})
|
||||
target_link_libraries(JGL PRIVATE ${FREETYPE_LIBRARIES})
|
||||
target_include_directories(JGL PRIVATE ${FREETYPE_INCLUDE_DIRS})
|
||||
target_link_libraries(JGL_Demo PUBLIC JGL ${OPENGL_LIBRARIES} J3ML ReWindowLibrary glad)
|
||||
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)
|
||||
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)
|
||||
endif()
|
||||
|
||||
target_link_libraries(JGL_Demo PUBLIC JGL ReTexture)
|
64
README.md
64
README.md
@@ -1,11 +1,59 @@
|
||||
# Josh Graphics Library
|
||||
|
||||
# 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.
|
||||
Yet Another C++ Rendering Toolkit
|
||||
|
||||
# Non-Goals
|
||||
* Full Rendering Engine
|
||||
* OpenGL/Vulkan Wrapper
|
||||
* Asset Loading & Management
|
||||

|
||||
|
||||
|
||||
## Features
|
||||
|
||||
* Modern C++ (20)
|
||||
* 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
|
||||
* DrawPixel
|
||||
* 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
|
||||
|
||||
Install instructions and code samples coming soon :tm: !
|
||||
|
||||
## Documentation
|
||||
|
||||
Documentation is (sic: will be) automatically generated from latest commit and is hosted at https://doc.redacted.cc/jgl .
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions to JGL are welcome! If you find a bug, have a feature request, or would like to contribute code, please submit an issue or pull request to our repository!
|
||||
|
||||
## License
|
||||
|
||||
JGL is licensed under the Public Domain. See the LICENSE file for details.
|
||||
|
||||
## Acknowledgements
|
||||
|
||||
JGL is developed and maintained by Josh O'Leary @ Co from Redacted Software and contributors. Special thanks to Redacted.
|
BIN
assets/fonts/Jupiteroid.ttf
Normal file
BIN
assets/fonts/Jupiteroid.ttf
Normal file
Binary file not shown.
BIN
assets/sprites/Re3D.png
Normal file
BIN
assets/sprites/Re3D.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.0 KiB |
@@ -1,31 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include <JGL/Color4.h>
|
||||
#include <J3ML/J3ML.h>
|
||||
|
||||
namespace JGL
|
||||
{
|
||||
using namespace J3ML;
|
||||
struct Color3 {
|
||||
/// Represents a 3-channel color value, with Red, Green and Blue components.
|
||||
class Color3 {
|
||||
public:
|
||||
u8 r;
|
||||
u8 g;
|
||||
u8 b;
|
||||
|
||||
public:
|
||||
/// Explicitly constructs a Color3 from the given Red, Green, and Blue values.
|
||||
Color3(u8 R, u8 G, u8 B);
|
||||
/// Returns a Color3 parsed from the given hexadecimal string.
|
||||
static Color3 FromHex(const std::string& hexCode);
|
||||
public:
|
||||
/// Returns a Color3 that is somewhere in-between this and the given Color3, determined by the alpha [0-1].
|
||||
Color3 Lerp(const Color3& rhs, float alpha) const;
|
||||
Color4 WithAlphaChannel(float alpha = 1) const;
|
||||
Color3(u8 R, u8 G, u8 B) : r(R), g(G), b(B) {}
|
||||
Color3(const Color4& c)
|
||||
{
|
||||
this->r = c.r;
|
||||
this->g = c.g;
|
||||
this->b = c.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;}
|
||||
|
||||
/// Returns the red channel [0-255].
|
||||
u8 RedChannel () const;
|
||||
/// Returns the green channel [0-255].
|
||||
u8 GreenChannel() const;
|
||||
/// Returns the blue channel [0-255].
|
||||
u8 BlueChannel () const;
|
||||
/// Returns the red channel normalized from [0-255] to [0-1].
|
||||
float RedChannelNormalized () const;
|
||||
/// Returns the green channel normalized from [0-255] to [0-1].
|
||||
float GreenChannelNormalized() const;
|
||||
/// Returns the blue channel normalized from [0-255] to [0-1].
|
||||
float BlueChannelNormalized() const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
@@ -1,11 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include <JGL/Color3.h>
|
||||
|
||||
namespace JGL
|
||||
{
|
||||
struct Color4{
|
||||
int r;
|
||||
int g;
|
||||
int b;
|
||||
int a;
|
||||
|
||||
/// Represents a 4-channel color value, with Red, Green, Blue, and Alpha components.
|
||||
class Color4 {
|
||||
public:
|
||||
u8 r;
|
||||
u8 g;
|
||||
u8 b;
|
||||
u8 a;
|
||||
public:
|
||||
explicit Color4(const Color3& color3, u8 alpha = 255);
|
||||
Color4(u8 red, u8 green, u8 blue, u8 alpha = 255);
|
||||
static Color4 FromColor3(const Color3& color3, u8 alpha = 255);
|
||||
static Color4 FromHex(const std::string& hexCode, u8 alpha = 255);
|
||||
|
||||
public:
|
||||
u8 RedChannel() const;
|
||||
u8 GreenChannel() const;
|
||||
u8 BlueChannel() const;
|
||||
u8 AlphaChannel() const;
|
||||
|
||||
float RedChannelNormalized() const;
|
||||
float GreenChannelNormalized() const;
|
||||
float BlueChannelNormalized() const;
|
||||
float AlphaChannelNormalized() const;
|
||||
};
|
||||
}
|
40
include/JGL/Font.h
Normal file
40
include/JGL/Font.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <filesystem>
|
||||
#include <J3ML/LinearAlgebra.h>
|
||||
#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;
|
||||
};
|
||||
}
|
67
include/JGL/FontCache.h
Normal file
67
include/JGL/FontCache.h
Normal 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();
|
||||
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();
|
||||
GLsizei getTextureWidth() const;
|
||||
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;
|
||||
}
|
@@ -1,36 +1,37 @@
|
||||
//
|
||||
// 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/enums.h>
|
||||
#include <JGL/FontCache.h>
|
||||
#include <JGL/Font.h>
|
||||
#include <J3ML/LinearAlgebra.h>
|
||||
#include <J3ML/LinearAlgebra/Vector2.h>
|
||||
#include <J3ML/LinearAlgebra/Vector3.h>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <JGL/Color3.h>
|
||||
#include <J3ML/Geometry/Sphere.h>
|
||||
#include <J3ML/Geometry/OBB.h>
|
||||
#include <J3ML/Geometry/Capsule.h>
|
||||
#include <J3ML/Geometry/Plane.h>
|
||||
#include <J3ML/Geometry/TriangleMesh.h>
|
||||
|
||||
// OpenGL Wrapper for rendering 2D graphics primitives in both a 2D and 3D context
|
||||
namespace JGL {
|
||||
|
||||
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;
|
||||
using namespace J3ML::LinearAlgebra;
|
||||
using namespace J3ML::Geometry;
|
||||
|
||||
/// TODO:
|
||||
struct HSV {
|
||||
float hue;
|
||||
float saturation;
|
||||
@@ -51,45 +52,133 @@ namespace JGL {
|
||||
Vector3 C;
|
||||
};
|
||||
|
||||
bool InitTextEngine();
|
||||
void Update(const Vector2& window_size);
|
||||
//bool InitTextEngine();
|
||||
Font LoadFont(const std::string& font_path); // TODO: Fully deprecate
|
||||
void PurgeFontCache();
|
||||
|
||||
void SetActiveFont(const Font& font); // TODO: Implement
|
||||
// TODO: implement correct coloring
|
||||
|
||||
/// Drawing functions for primitive 2D Shapes.
|
||||
/// Each function is overloaded with Color3 and Color4 for optional transparency.
|
||||
namespace J2D {
|
||||
void DrawPixel2D(const Color3 &color, const Vector2 &coordinates);
|
||||
void DrawPixel2D(const Color3 &color, float x, float y);
|
||||
void DrawLine2D(const Color3 &color, const Vector2 &A, const Vector2 &B, float thickness = 1);
|
||||
void DrawLine2D(const Color3 &color, float x, float y, float w, float h, float thickness = 1);
|
||||
void DrawCubicBezierCurve2D();
|
||||
void OutlineCircle2D(const Color3 &color, const Vector2 ¢er, float radius, int subdivisions,
|
||||
float thickness = 1);
|
||||
void FillCircle2D(const Color3 &color, const Vector2 ¢er, float radius, int subdivisions);
|
||||
void OutlineTriangle2D(const Color3 &color, const Triangle2D &tri);
|
||||
void FillTriangle2D(const Color3 &color, const Triangle2D &tri);
|
||||
void FillTexturedTriangle2D();
|
||||
void FillTexturedPolygon2D();
|
||||
void DrawSprite2D();
|
||||
void DrawPartialSprite2D();
|
||||
void DrawString2D(const Color3& color, std::string text, float x, float y, float scale, u32 size = 16);
|
||||
void FillRect2D(const Color3 &color, const Vector2 &pos, const Vector2 &size);
|
||||
void OutlineRect2D ( const Color3& color, const Vector2& pos, const Vector2& size, float thickness = 1);
|
||||
void FillRoundedRect2D (const Color3& color, const Vector2& pos, const Vector2& size, float radius);
|
||||
void OutlineRoundedRect2D(const Color3& color, const Vector2& pos, const Vector2& size, float radius, float thickness = 1);
|
||||
void OutlinePolygon2D (const Color3& color, std::vector<Vector2> points);
|
||||
void FillPolygon2D (const Color3& color, std::vector<Vector2> points, float thickness = 1);
|
||||
void GradientFillRect2D ();
|
||||
|
||||
/// 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.
|
||||
/// This keeps our code from, say, clobbering the OpenGL rendering context driving 3D content in between our calls.
|
||||
void Begin();
|
||||
/// Closes a 2-D rendering context with the underlying graphics system (In this case & by default OpenGL).
|
||||
/// @see Begin().
|
||||
void End();
|
||||
|
||||
/// Plots a single pixel on the screen.
|
||||
/// @param color A 3-or-4 channel color value. @see classes Color3, 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);
|
||||
|
||||
/// 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.
|
||||
void DrawSprite(GLuint texture, const Vector2& pos, const Vector2& size, u8 opacity = 255, Inversion::Inversion inversion = Inversion::None);
|
||||
void DrawSprite(GLuint texture, float x, float y, float w, float h, u8 opacity = 255, Inversion::Inversion inversion = Inversion::None);
|
||||
|
||||
///Draws a non axis-aligned fill rect to the screen.
|
||||
///The order of the vertices must be such that if you were to connect them you'd never go diagonally across the quad.
|
||||
void FillQuad(const Color4& color, const Vector2& v1, const Vector2& v2, const Vector2& v3, const Vector2& v4);
|
||||
void FillQuad(const Color3& color, const Vector2& v1, const Vector2& v2, const Vector2& v3, const Vector2& v4);
|
||||
|
||||
///Draws a non axis-aligned outline rect to the screen.
|
||||
///The order of the vertices must be such that if you were to connect them you'd never go diagonally across the quad.
|
||||
void OutlineQuad(const Color4& color, const Vector2& v1, const Vector2& v2, const Vector2& v3, const Vector2& v4, float thickness = 1);
|
||||
void OutlineQuad(const Color3& 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& gradient, const Vector2& pos, const Vector2& size);
|
||||
void FillGradientRect(const Color3& color1, const Color3& color2, const Gradient::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);
|
||||
|
||||
/// 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.
|
||||
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
|
||||
|
||||
/// Draws a filled triangle on the screen.
|
||||
void FillTriangle(const Color4& color, const Triangle2D& tri);
|
||||
void FillTriangle(const Color3& color, const Triangle2D& tri);
|
||||
|
||||
/// 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);
|
||||
void FillGradientTriangle(const Color3& a_color, const Color3& b_color, const Color3& c_color, const Triangle2D& tri);
|
||||
// TODO: Implement an overload that simply takes 3 Vector3's
|
||||
|
||||
|
||||
/// 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, const Font& font);
|
||||
void DrawString(const Color3& color, const std::string& text, float x, float y, float scale, u32 size, const Font& font);
|
||||
|
||||
// TODO: Implement the following:
|
||||
void FillTexturedTriangle();
|
||||
void FillTexturedPolygon();
|
||||
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);
|
||||
}
|
||||
|
||||
namespace J3D {
|
||||
void DrawLine3D(const Color3& color, const Vector3 &A, const Vector3 &B, float thickness = 1);
|
||||
void FillSphere3D(const Color3& color, const Sphere& sphere);
|
||||
void WireframeSphere3D(const Color3& color, const Sphere& sphere, float thickness = 1);
|
||||
void FillOBB3D(const Color3& color, const OBB& obb);
|
||||
void WireframeOBB3D(const Color3& color, const OBB& obb, float thickness = 1);
|
||||
void FillCapsule3D(const Color3& color, const Capsule& capsule);
|
||||
void WireframeCapsule3D(const Color3& color, const Capsule& cap, float thickness = 1);
|
||||
void FillTriangleMesh3D(const Color3& color, const TriangleMesh& mesh);
|
||||
void WireframeTriangleMesh3D(const Color3& color, const TriangleMesh& mesh, float thickness = 1);
|
||||
void DrawString3D(const Color3& color, const std::string& text, const Vector3& pos, float scale, u32 size = 12);
|
||||
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);
|
||||
void WireframeOBB(const Color3& color, const OBB& obb, float thickness = 1);
|
||||
void FillCapsule(const Color3& color, const Capsule& capsule);
|
||||
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, const Font& font);
|
||||
|
||||
void DrawMatrixGizmo (const Matrix3x3&, const Vector3&);
|
||||
void DrawMatrixGizmo (const Matrix4x4&);
|
||||
|
26
include/JGL/enums.h
Normal file
26
include/JGL/enums.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
namespace JGL::Inversion {
|
||||
enum Inversion {
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
namespace JGL::Gradient {
|
||||
enum Gradient {
|
||||
Vertical = 0,
|
||||
Horizontal = 1,
|
||||
DiagonalTopLeft = 2,
|
||||
DiagonalBottomLeft = 3
|
||||
};
|
||||
|
||||
inline Gradient operator|(Gradient a, Gradient b) {
|
||||
return static_cast<Gradient>(static_cast<int>(a) | static_cast<int>(b));
|
||||
}
|
||||
}
|
203
main.cpp
203
main.cpp
@@ -1,26 +1,59 @@
|
||||
#include <glad/glad.h>
|
||||
|
||||
#include <JGL/JGL.h>
|
||||
#include <rewindow/types/window.h>
|
||||
#include <JGL/Colors.h>
|
||||
#include <J3ML/LinearAlgebra/Vector2.h>
|
||||
#include <JGL/Font.h>
|
||||
#include <jlog/jlog.hpp>
|
||||
#include <ReTexture/Texture.h>
|
||||
|
||||
using J3ML::LinearAlgebra::Vector2;
|
||||
using namespace JGL;
|
||||
using namespace ReTexture;
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
#include <windows.h>
|
||||
#endif
|
||||
Texture* image;
|
||||
GLuint imageID;
|
||||
//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;
|
||||
}
|
||||
|
||||
struct Character
|
||||
{
|
||||
unsigned int TextureID; // ID handle of the glyph texture
|
||||
Vector2 Size; // Size of glyph
|
||||
Vector2 Bearing; // Offset from baseline to left/top of glyph
|
||||
unsigned int Advance; // Offset to advance to next glyph
|
||||
class Camera {
|
||||
public:
|
||||
Vector3 position = {0,0,0};
|
||||
Vector3 angle = {0,0,0};
|
||||
|
||||
std::vector<GLfloat> lookAt(const Vector3& eye, const Vector3& center, const Vector3& up) {
|
||||
Vector3 f = Vector3::Normalized((center - eye));
|
||||
Vector3 upN = Vector3::Normalized(up);
|
||||
Vector3 s = Vector3::Normalized(f.Cross(upN));
|
||||
Vector3 u = Vector3::Normalized(s.Cross(f));
|
||||
|
||||
std::vector<GLfloat> result = {
|
||||
s.x, u.x, -f.x, 0.0f,
|
||||
s.y, u.y, -f.y, 0.0f,
|
||||
s.z, u.z, -f.z, 0.0f,
|
||||
-s.Dot(eye), -u.Dot(eye), f.Dot(eye), 1.0f
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
void render() {
|
||||
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);
|
||||
glMultMatrixf(lookAt({position.x, position.y, position.z}, {position.x, position.y, 100.f + position.z}, {0,1,0}).data());
|
||||
}
|
||||
};
|
||||
|
||||
std::map<char, Character> Characters;
|
||||
GLuint VAO, VBO;
|
||||
|
||||
Camera* camera;
|
||||
using J3ML::LinearAlgebra::Matrix4x4;
|
||||
|
||||
struct point {
|
||||
@@ -30,91 +63,115 @@ struct point {
|
||||
GLfloat t;
|
||||
};
|
||||
|
||||
JGL::Font FreeSans;
|
||||
JGL::Font Jupiteroid;
|
||||
|
||||
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];
|
||||
|
||||
JGLDemoWindow() : ReWindow::RWindow() {}
|
||||
JGLDemoWindow(const std::string& title, int width, int height) :
|
||||
ReWindow::RWindow(title, width, height)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void initGL()
|
||||
{
|
||||
gladLoadGL();
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
glOrtho(0, getSize().x, getSize().y, 0, -1, 1);
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
|
||||
glEnable(GL_CULL_FACE);
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
|
||||
JGL::InitTextEngine();
|
||||
}
|
||||
|
||||
|
||||
void display()
|
||||
{
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
JGL::Update(getSize());
|
||||
FreeSans = JGL::Font("assets/fonts/FreeSans.ttf");
|
||||
Jupiteroid = JGL::Font("assets/fonts/Jupiteroid.ttf");
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
glOrtho(0, getSize().x, getSize().y, 0, -1, 1);
|
||||
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);
|
||||
|
||||
JGL::J2D::FillRect2D(JGL::Colors::Blue, {32, 32}, {100.5, 100.5});
|
||||
JGL::J2D::FillRect2D(JGL::Colors::Blue, {32, 32}, {100.5, 100.5});
|
||||
image = new Texture("assets/sprites/Re3D.png");
|
||||
glGenTextures(1, &imageID);
|
||||
glBindTexture(GL_TEXTURE_2D, imageID);
|
||||
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);
|
||||
|
||||
JGL::Triangle2D tri
|
||||
{
|
||||
{140, 200},
|
||||
{135, 100},
|
||||
{105, 100}
|
||||
};
|
||||
JGL::J2D::FillTriangle2D(JGL::Colors::Yellow, tri);
|
||||
if (image->getTextureFormat() == TextureFormat::RGBA)
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image->getWidth(), image->getHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, image->pixelData.data());
|
||||
if (image->getTextureFormat() == TextureFormat::RGB)
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, image->getWidth(), image->getHeight(), 0, GL_RGB, GL_UNSIGNED_BYTE, image->pixelData.data());
|
||||
|
||||
|
||||
JGL::J3D::DrawString3D(JGL::Colors::Red, "JGL Sample Text", {1, -120, 0.5f}, 2.f);
|
||||
JGL::J2D::DrawString2D(JGL::Colors::Green, "William J. Tomasine II ", 0.f, -120.f, 1.f);
|
||||
|
||||
JGL::J2D::DrawLine2D(JGL::Colors::Greens::DarkGreen, {10, 10}, {200, 300});
|
||||
JGL::J3D::DrawLine3D(JGL::Colors::Red, {0,0,0}, {1,0,0});
|
||||
JGL::J3D::DrawLine3D(JGL::Colors::Red, {0,0,0}, {1,0,0});
|
||||
|
||||
//glFlush();
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
void OnRefresh(float elapsed) override
|
||||
{
|
||||
Vector3 textAngle = {0,0,0};
|
||||
void display() {
|
||||
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::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::DrawSprite(imageID, {0, 52}, {(float) image->getWidth(), (float) image->getHeight()}, 128);
|
||||
J2D::FillRect(Color4::FromColor3(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::FillCircle(JGL::Colors::White, {52, 204}, 50, 24);
|
||||
J2D::OutlineCircle(JGL::Colors::White, {153, 204}, 50, 24);
|
||||
|
||||
//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);
|
||||
auto result = Jupiteroid.MeasureString("Jupiteroid Font", 16);
|
||||
|
||||
J2D::FillRect(JGL::Colors::Gray, {0, 0}, result);
|
||||
J2D::DrawString(JGL::Colors::Green, "Jupteroid Font", 0.f, 0, 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, 16, 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, 33, 1,16, Jupiteroid);
|
||||
J2D::End();
|
||||
}
|
||||
|
||||
void OnRefresh(float elapsed) override {
|
||||
display();
|
||||
this->glSwapBuffers();
|
||||
}
|
||||
bool OnResizeRequest(const ReWindow::WindowResizeRequestEvent& e) override
|
||||
{
|
||||
return true;
|
||||
int glError = glGetError();
|
||||
if (glError != GL_NO_ERROR)
|
||||
std::cout << glError << std::endl;
|
||||
glSwapBuffers();
|
||||
}
|
||||
|
||||
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){}
|
||||
};
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
int main(int argc, char** argv) {
|
||||
auto* window = new JGLDemoWindow("JGL Demo Window", 1280, 720);
|
||||
window->setRenderer(RenderingAPI::OPENGL);
|
||||
window->Open();
|
||||
window->initGL();
|
||||
window->setResizable(true);
|
||||
while (window->isAlive())
|
||||
{
|
||||
window->setResizable(false);
|
||||
|
||||
while (window->isAlive()) {
|
||||
window->pollEvents();
|
||||
window->refresh();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
1
reci/scripts/builddeps.reci
Normal file
1
reci/scripts/builddeps.reci
Normal file
@@ -0,0 +1 @@
|
||||
Main:new("Install build dependencies", "apt-get install -yq libgl1-mesa-dev libfreetype-dev")
|
7
reci/scripts/install_build_dependencies.sh
Normal file
7
reci/scripts/install_build_dependencies.sh
Normal file
@@ -0,0 +1,7 @@
|
||||
source $RECI/shlib/info.sh
|
||||
|
||||
INFO "Installing build dependencies for JGL"
|
||||
GROUP="Install build dependencies" RUN '
|
||||
apt-get install -yq libgl1-mesa-dev libfreetype-dev
|
||||
'
|
||||
|
107
src/FontCache.cpp
Normal file
107
src/FontCache.cpp
Normal file
@@ -0,0 +1,107 @@
|
||||
#include <JGL/FontCache.h>
|
||||
|
||||
using namespace JGL;
|
||||
|
||||
char CachedGlyph::getCharacter() {
|
||||
return character;
|
||||
}
|
||||
|
||||
const std::array<GLfloat, 12> CachedGlyph::getTexCoords() const {
|
||||
return texcoords;
|
||||
}
|
||||
|
||||
CachedGlyph::CachedGlyph(char c, std::array<GLfloat, 12> texcoords, float x2offset, float y2offset, float w, float h, float advanceX, float advanceY) {
|
||||
character = c;
|
||||
this->x2offset = x2offset;
|
||||
this->y2offset = y2offset;
|
||||
this->w = w;
|
||||
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.emplace(glyph->getCharacter(), glyph);
|
||||
}
|
||||
|
||||
unsigned int JGL::CachedFont::getFontSize() {
|
||||
return font_size;
|
||||
}
|
||||
|
||||
unsigned int JGL::CachedFont::getFontIndex() {
|
||||
return font_index;
|
||||
}
|
||||
|
||||
//TODO make this code go faster.
|
||||
CachedGlyph* JGL::CachedFont::getGlyph(char c) {
|
||||
auto it = glyphs.find(c);
|
||||
if (it != glyphs.end())
|
||||
return it->second;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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(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) {
|
||||
delete cachedFonts[i];
|
||||
cachedFonts.erase(cachedFonts.begin() + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FontCache::purgeCache() {
|
||||
//Remove every font from the cache.
|
||||
for (const auto& font : cachedFonts)
|
||||
eraseFont(font);
|
||||
}
|
||||
|
||||
std::vector<CachedFont*> FontCache::getFonts() {
|
||||
return cachedFonts;
|
||||
}
|
||||
|
||||
CachedFont* FontCache::getFont(unsigned int font_size, unsigned int font_index) {
|
||||
if (cachedFonts.empty())
|
||||
return nullptr;
|
||||
|
||||
for (auto* f : cachedFonts)
|
||||
if (f->getFontIndex() == font_index && f->getFontSize() == font_size)
|
||||
return f;
|
||||
return nullptr;
|
||||
}
|
531
src/JGL.cpp
Normal file
531
src/JGL.cpp
Normal file
@@ -0,0 +1,531 @@
|
||||
//
|
||||
// Created by dawsh on 1/17/24.
|
||||
//
|
||||
|
||||
#include <JGL/JGL.h>
|
||||
#include <glad/glad.h>
|
||||
#include <JGL/Color3.h>
|
||||
#include <jlog/jlog.hpp>
|
||||
|
||||
GLfloat oldColor[4] = {0, 0, 0, 1};
|
||||
GLfloat baseColor[4] = {1, 1, 1, 1};
|
||||
bool inJ2D = false;
|
||||
bool inJ3D = false;
|
||||
bool wasTexture2DEnabled = false;
|
||||
bool wasTextureCoordArrayEnabled = false;
|
||||
bool wasDepthTestEnabled = false;
|
||||
bool wasVertexArraysEnabled = false;
|
||||
bool wasCullFaceEnabled = false;
|
||||
bool wasBlendEnabled = false;
|
||||
bool wasColorArrayEnabled = false;
|
||||
GLint activeTextureUnit = 0;
|
||||
|
||||
namespace JGL {
|
||||
using namespace J3ML;
|
||||
Vector2 wS;
|
||||
|
||||
void Update(const Vector2& window_size) {
|
||||
wS = window_size;
|
||||
}
|
||||
|
||||
|
||||
void J2D::Begin() {
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPushMatrix();
|
||||
glLoadIdentity();
|
||||
glOrtho(0, wS.x, wS.y, 0, -1, 1);
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glPushMatrix();
|
||||
glLoadIdentity();
|
||||
|
||||
//Get what the draw color was before we did anything.
|
||||
glGetFloatv(GL_CURRENT_COLOR, oldColor);
|
||||
glColor4f(baseColor[0], baseColor[1], baseColor[2], baseColor[3]);
|
||||
|
||||
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);
|
||||
|
||||
wasVertexArraysEnabled = true;
|
||||
if (!glIsEnabled(GL_VERTEX_ARRAY))
|
||||
wasVertexArraysEnabled = false,
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
|
||||
wasCullFaceEnabled = true;
|
||||
if (!glIsEnabled(GL_CULL_FACE))
|
||||
wasCullFaceEnabled = false,
|
||||
glEnable(GL_CULL_FACE),
|
||||
glCullFace(GL_BACK);
|
||||
|
||||
wasBlendEnabled = true;
|
||||
if (!glIsEnabled(GL_BLEND))
|
||||
wasBlendEnabled = false,
|
||||
glEnable(GL_BLEND),
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
wasTexture2DEnabled = true;
|
||||
if (!glIsEnabled(GL_TEXTURE_2D))
|
||||
wasTexture2DEnabled = false,
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
|
||||
wasTextureCoordArrayEnabled = true;
|
||||
if (!glIsEnabled(GL_TEXTURE_COORD_ARRAY))
|
||||
wasTextureCoordArrayEnabled = false,
|
||||
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
|
||||
wasColorArrayEnabled = false;
|
||||
if (glIsEnabled(GL_COLOR_ARRAY))
|
||||
wasColorArrayEnabled = true,
|
||||
glDisableClientState(GL_COLOR_ARRAY);
|
||||
|
||||
if (!inJ3D)
|
||||
inJ2D = true;
|
||||
else { 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.)
|
||||
glPopMatrix();
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPopMatrix();
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
if (wasDepthTestEnabled)
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
|
||||
if (!wasVertexArraysEnabled)
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
|
||||
if (!wasCullFaceEnabled)
|
||||
glDisable(GL_CULL_FACE);
|
||||
|
||||
if (!wasBlendEnabled)
|
||||
glDisable(GL_BLEND);
|
||||
|
||||
if (!wasTexture2DEnabled)
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
|
||||
if (!wasTextureCoordArrayEnabled)
|
||||
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
|
||||
if (wasColorArrayEnabled)
|
||||
glEnableClientState(GL_COLOR_ARRAY);
|
||||
|
||||
//Select whatever texture 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]);
|
||||
|
||||
inJ2D = false;
|
||||
}
|
||||
|
||||
//TODO rotation, I'm unsure if @josh wants degrees or radians.
|
||||
void J2D::DrawSprite(GLuint texture, const Vector2& pos, const Vector2& size, u8 opacity, Inversion::Inversion inversion) {
|
||||
if (!inJ2D)
|
||||
ERROR("Attempt to Render J2D element before J2D begin.")
|
||||
|
||||
std::array<Vector2, 4> textureCoordinates = {Vector2(0, 0), Vector2(0, 1), Vector2(1, 1), Vector2(1, 0)};;
|
||||
const Vector2 vertices[] = {{pos.x, pos.y}, {pos.x, pos.y + size.y}, {pos.x + size.x, pos.y + size.y}, {pos.x + size.x, pos.y}};
|
||||
|
||||
if (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)};
|
||||
|
||||
glColor4f(baseColor[0],baseColor[1],baseColor[2],opacity / 255.f);
|
||||
glBindTexture(GL_TEXTURE_2D, texture);
|
||||
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices);
|
||||
glTexCoordPointer(2, GL_FLOAT, sizeof(Vector2), textureCoordinates.data());
|
||||
glDrawArrays(GL_QUADS, 0, 4);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glColor4f(baseColor[0], baseColor[1], baseColor[2], baseColor[3]);
|
||||
}
|
||||
|
||||
void J2D::FillQuad(const Color4& color, const Vector2& v1, const Vector2& v2, const Vector2& v3, const Vector2& v4) {
|
||||
if (!inJ2D)
|
||||
ERROR("Attempt to Render J2D element before J2D begin.")
|
||||
|
||||
Vector2 vertices[] = {v1, v2, v3, v4};
|
||||
glColor4f(color.r / 255.f, color.g / 255.f, color.b / 255.f, color.a / 255.f);
|
||||
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices);
|
||||
glDrawArrays(GL_QUADS, 0, 4);
|
||||
glColor4f(baseColor[0], baseColor[1], baseColor[2], baseColor[3]);
|
||||
}
|
||||
|
||||
void J2D::FillQuad(const Color3& color, const Vector2& v1, const Vector2& v2, const Vector2& v3, const Vector2& v4) {
|
||||
J2D::FillQuad(Color4(color), v1, v2, v3, v4);
|
||||
}
|
||||
|
||||
void J2D::OutlineQuad(const Color4& color, const Vector2& v1, const Vector2& v2, const Vector2& v3, const Vector2& v4, float thickness) {
|
||||
if (!inJ2D)
|
||||
ERROR("Attempt to Render J2D element before J2D begin.")
|
||||
|
||||
Vector2 vertices[] = {v1, v2, v3, v4};
|
||||
glLineWidth(thickness);
|
||||
glColor4f(color.r / 255.f, color.g / 255.f, color.b / 255.f, color.a / 255.f);
|
||||
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices);
|
||||
glDrawArrays(GL_LINE_LOOP, 0, 4);
|
||||
glColor4f(baseColor[0], baseColor[1], baseColor[2], baseColor[3]);
|
||||
}
|
||||
|
||||
void J2D::OutlineQuad(const Color3& color, const Vector2& v1, const Vector2& v2, const Vector2& v3, const Vector2& v4, float thickness) {
|
||||
J2D::OutlineQuad(Color4(color), v1, v2, v3, v4);
|
||||
}
|
||||
|
||||
void J2D::DrawSprite(GLuint texture, float x, float y, float w, float h, u8 opacity, Inversion::Inversion inversion) {
|
||||
J2D::DrawSprite(texture, {x, y}, {w, h}, opacity, inversion);
|
||||
}
|
||||
|
||||
void J2D::FillRect(const Color4& color, const Vector2& pos, const Vector2& size) {
|
||||
if (!inJ2D)
|
||||
ERROR("Attempt to Render 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);
|
||||
glDrawArrays(GL_QUADS, 0, 4);
|
||||
glColor4f(baseColor[0], baseColor[1], baseColor[2], baseColor[3]);
|
||||
}
|
||||
|
||||
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::FillGradientRect(const Color4& color1, const Color4& color2, const Gradient::Gradient& gradient, const Vector2& pos, const Vector2& size) {
|
||||
if (!inJ2D)
|
||||
ERROR("Attempt to Render J2D element before J2D begin.")
|
||||
|
||||
Vector2 vertices[] = {{pos.x, pos.y}, {pos.x, pos.y + size.y}, {pos.x + size.x, pos.y + size.y}, {pos.x + size.x, pos.y}};
|
||||
std::vector<GLfloat> colors = {};
|
||||
|
||||
if (gradient == 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};
|
||||
|
||||
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};
|
||||
|
||||
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};
|
||||
|
||||
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};
|
||||
|
||||
glEnableClientState(GL_COLOR_ARRAY);
|
||||
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices);
|
||||
glColorPointer(4, GL_FLOAT, 0, colors.data());
|
||||
glDrawArrays(GL_QUADS, 0, 4);
|
||||
glDisableClientState(GL_COLOR_ARRAY);
|
||||
glColor4f(baseColor[0], baseColor[1], baseColor[2], baseColor[3]);
|
||||
}
|
||||
|
||||
void J2D::FillGradientRect(const Color3& color1, const Color3& color2, const Gradient::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) {
|
||||
if (!inJ2D)
|
||||
ERROR("Attempt to Render 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});
|
||||
|
||||
J2D::FillCircle(color, {pos.x + radius, pos.y + radius}, radius, subdivisions);
|
||||
J2D::FillCircle(color, {pos.x + size.x - radius, pos.y + radius}, radius, subdivisions);
|
||||
J2D::FillCircle(color, {pos.x + radius, pos.y + size.y - radius}, radius, subdivisions);
|
||||
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.")
|
||||
|
||||
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);
|
||||
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices);
|
||||
glDrawArrays(GL_LINE_LOOP, 0, 4);
|
||||
glColor4f(baseColor[0], baseColor[1], baseColor[2], baseColor[3]);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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.");
|
||||
|
||||
Vector2 vertices[] = {A, B};
|
||||
|
||||
glLineWidth(thickness);
|
||||
glColor4f(color.r / 255.f, color.g / 255.f, color.b / 255.f, color.a / 255.f);
|
||||
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices);
|
||||
glDrawArrays(GL_LINES, 0, 2);
|
||||
glColor4f(baseColor[0], baseColor[1], baseColor[2], baseColor[3]);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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.");
|
||||
|
||||
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};
|
||||
|
||||
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);
|
||||
glDisableClientState(GL_COLOR_ARRAY);
|
||||
glColor4f(baseColor[0], baseColor[1], baseColor[2], baseColor[3]);
|
||||
}
|
||||
|
||||
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::DrawPixel(const Color4& color, const Vector2& coordinates) {
|
||||
if (!inJ2D)
|
||||
ERROR("Attempt to Render 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);
|
||||
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices);
|
||||
glDrawArrays(GL_LINES, 0, 1);
|
||||
glColor4f(baseColor[0], baseColor[1], baseColor[2], baseColor[3]);
|
||||
}
|
||||
|
||||
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::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.")
|
||||
|
||||
float step = (2.f * M_PI) / (float) subdivisions;
|
||||
std::vector<Vector2> vertices{};
|
||||
GLfloat angle, x, y;
|
||||
|
||||
for (angle = 0.0f; angle < (2.f * M_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);
|
||||
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices.data());
|
||||
glDrawArrays(GL_LINE_LOOP, 0, vertices.size());
|
||||
glColor4f(baseColor[0], baseColor[1], baseColor[2], baseColor[3]);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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.")
|
||||
|
||||
float step = (2.f * M_PI) / (float) subdivisions;;
|
||||
std::vector<Vector2> vertices{};
|
||||
GLfloat angle, x, y;
|
||||
|
||||
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});
|
||||
|
||||
glColor4f(color.r / 255.f, color.g / 255.f, color.b / 255.f, color.a / 255.f);
|
||||
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices.data());
|
||||
glDrawArrays(GL_TRIANGLE_FAN, 0, vertices.size());
|
||||
glColor4f(baseColor[0], baseColor[1], baseColor[2], baseColor[3]);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void J2D::OutlineTriangle(const Color4& color, const Triangle2D& tri, float thickness) {
|
||||
if (!inJ2D)
|
||||
ERROR("Attempt to Render 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);
|
||||
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices);
|
||||
glDrawArrays(GL_LINE_LOOP, 0, 3);
|
||||
glColor4f(baseColor[0], baseColor[1], baseColor[2], baseColor[3]);
|
||||
}
|
||||
|
||||
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) {
|
||||
if (!inJ2D)
|
||||
ERROR("Attempt to Render 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);
|
||||
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices);
|
||||
glDrawArrays(GL_TRIANGLES, 0, 3);
|
||||
glColor4f(baseColor[0], baseColor[1], baseColor[2], baseColor[3]);
|
||||
}
|
||||
|
||||
void J2D::FillGradientTriangle(const Color4& a_color, const Color4& b_color, const Color4& c_color, const Triangle2D& tri) {
|
||||
if (!inJ2D)
|
||||
ERROR("Attempt to Render 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.r / 255.f, a_color.g / 255.f, a_color.b / 255.f, a_color.a / 255.f,b_color.r / 255.f,
|
||||
b_color.g / 255.f, b_color.b / 255.f, b_color.a / 255.f,c_color.r / 255.f, c_color.g / 255.f, c_color.b / 255.f,
|
||||
c_color.a / 255.f };
|
||||
|
||||
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);
|
||||
glColor4f(baseColor[0], baseColor[1], baseColor[2], baseColor[3]);
|
||||
}
|
||||
|
||||
void J2D::FillGradientTriangle(const Color3& a_color, const Color3& b_color, const Color3& c_color, const Triangle2D& tri) {
|
||||
J2D::FillGradientTriangle(Color4(a_color), Color4(b_color), Color4(c_color), tri);
|
||||
}
|
||||
|
||||
|
||||
void J2D::FillTriangle(const Color3& color, const Triangle2D& tri) {
|
||||
J2D::FillTriangle({color.r, color.g, color.b, 255}, tri);
|
||||
}
|
||||
|
||||
void J3D::Begin() {
|
||||
|
||||
//Get what the draw color was before we did anything.
|
||||
glGetFloatv(GL_CURRENT_COLOR, oldColor);
|
||||
glColor4f(baseColor[0], baseColor[1], baseColor[2], baseColor[3]);
|
||||
|
||||
wasDepthTestEnabled = false;
|
||||
if (glIsEnabled(GL_DEPTH_TEST))
|
||||
wasDepthTestEnabled = true,
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
|
||||
wasVertexArraysEnabled = false;
|
||||
if (!glIsEnabled(GL_VERTEX_ARRAY))
|
||||
wasVertexArraysEnabled = false,
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
|
||||
wasTexture2DEnabled = true;
|
||||
if (!glIsEnabled(GL_TEXTURE_2D))
|
||||
wasTexture2DEnabled = false,
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
|
||||
// TODO: implement bool drawBackface as DrawString parameter.
|
||||
wasCullFaceEnabled = false;
|
||||
if (glIsEnabled(GL_CULL_FACE))
|
||||
wasCullFaceEnabled = true,
|
||||
glDisable(GL_CULL_FACE);
|
||||
|
||||
wasBlendEnabled = true;
|
||||
if (!glIsEnabled(GL_BLEND))
|
||||
wasBlendEnabled = false,
|
||||
glEnable(GL_BLEND),
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
|
||||
|
||||
if (!inJ2D)
|
||||
inJ3D = true;
|
||||
else { ERROR("Attempt to Begin J3D inside of J2D context.")}
|
||||
}
|
||||
|
||||
void J3D::End() {
|
||||
if (wasDepthTestEnabled)
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
|
||||
if (!wasVertexArraysEnabled)
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
|
||||
if (wasTexture2DEnabled)
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
|
||||
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]);
|
||||
|
||||
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.")
|
||||
|
||||
Vector3 vertices[] = {A, B};
|
||||
|
||||
glLineWidth(thickness);
|
||||
glColor4f(color.r / 255.f, color.g / 255.f, color.b / 255.f, color.a / 255.f);
|
||||
glVertexPointer(3, GL_FLOAT, sizeof(Vector3), vertices);
|
||||
glDrawArrays(GL_LINES, 0, 2);
|
||||
glColor4f(baseColor[0], baseColor[1], baseColor[2], baseColor[3]);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
25
src/JGL/Color3.cpp
Normal file
25
src/JGL/Color3.cpp
Normal file
@@ -0,0 +1,25 @@
|
||||
#include <JGL/Color3.h>
|
||||
|
||||
namespace JGL
|
||||
{
|
||||
|
||||
u8 Color3::RedChannel() const { return r; }
|
||||
|
||||
u8 Color3::GreenChannel() const { return g; }
|
||||
|
||||
u8 Color3::BlueChannel() const { return b; }
|
||||
|
||||
float Color3::RedChannelNormalized() const { return static_cast<float>(r) / 255.f;}
|
||||
|
||||
float Color3::BlueChannelNormalized() const { return static_cast<float>(b) / 255.f;}
|
||||
|
||||
float Color3::GreenChannelNormalized() const { return static_cast<float>(g) / 255.f;}
|
||||
|
||||
Color3::Color3(u8 R, u8 G, u8 B) : r(R), g(G), b(B) {}
|
||||
|
||||
Color3 Color3::FromHex(const std::string &hexCode) {
|
||||
u8 r, g, b;
|
||||
std::sscanf(hexCode.c_str(), "#%02x%02x%02x", &r, &g, &b);
|
||||
return {r, g, b};
|
||||
}
|
||||
}
|
32
src/JGL/Color4.cpp
Normal file
32
src/JGL/Color4.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
#include <JGL/Color4.h>
|
||||
|
||||
namespace JGL
|
||||
{
|
||||
|
||||
Color4::Color4(u8 red, u8 green, u8 blue, u8 alpha) : r(red), g(green), b(blue), a(alpha) {}
|
||||
|
||||
Color4::Color4(const Color3 &color3, u8 alpha) {r = color3.r; g = color3.g; b = color3.b; a = alpha;}
|
||||
|
||||
Color4 Color4::FromColor3(const Color3 &color3, u8 alpha) {return Color4(color3, alpha);}
|
||||
|
||||
u8 Color4::RedChannel() const { return r;}
|
||||
|
||||
u8 Color4::GreenChannel() const { return g;}
|
||||
|
||||
u8 Color4::BlueChannel() const {return b;}
|
||||
|
||||
u8 Color4::AlphaChannel() const {return a;}
|
||||
|
||||
float Color4::RedChannelNormalized() const {return static_cast<float>(r/255.f); }
|
||||
|
||||
float Color4::GreenChannelNormalized() const {return static_cast<float>(g/255.f); }
|
||||
|
||||
float Color4::BlueChannelNormalized() const {return static_cast<float>(b/255.f); }
|
||||
|
||||
float Color4::AlphaChannelNormalized() const {return static_cast<float>(a/255.f); }
|
||||
JGL::Color4 JGL::Color4::FromHex(const std::string &hexCode, u8 alpha) {
|
||||
u8 r, g, b;
|
||||
std::sscanf(hexCode.c_str(), "#%02x%02x%02x", &r, &g, &b);
|
||||
return {r, g, b, alpha};
|
||||
}
|
||||
}
|
165
src/JGL/Font.cpp
Normal file
165
src/JGL/Font.cpp
Normal file
@@ -0,0 +1,165 @@
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <glad/glad.h>
|
||||
|
||||
#if __linux__
|
||||
#include <freetype2/ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
#include FT_OUTLINE_H
|
||||
#include <JGL/FontCache.h>
|
||||
|
||||
#endif
|
||||
#if _WIN32
|
||||
#include <ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
#endif
|
||||
|
||||
#include <JGL/Font.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.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) {
|
||||
|
||||
// TODO: Check if a font of that size is in the cache and if so use the info from that because this is sloooooooooooooow.
|
||||
// That'll make it go vroom vroom.
|
||||
// TODO: Work in-progress implementation unfortunately.
|
||||
// This is likely returning slightly incorrect results for likely several reasons.
|
||||
// Half-ass solution for now ~ dawsh.
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
}
|
298
src/JGL/JGL.cpp
298
src/JGL/JGL.cpp
@@ -1,298 +0,0 @@
|
||||
//
|
||||
// Created by dawsh on 1/17/24.
|
||||
//
|
||||
|
||||
#include <glad/glad.h>
|
||||
#include <JGL/JGL.h>
|
||||
#include <freetype2/ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
#include <JGL/Color3.h>
|
||||
#include <iostream>
|
||||
|
||||
|
||||
GLuint program;
|
||||
|
||||
namespace JGL
|
||||
{
|
||||
FT_Face face;
|
||||
FT_Library ft;
|
||||
|
||||
using namespace J3ML;
|
||||
|
||||
bool InitTextEngine() {
|
||||
constexpr u32 default_font_size = 16;
|
||||
if (FT_Init_FreeType(&ft))
|
||||
{
|
||||
std::cout << "Error::FREETYPE: " << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (FT_New_Face(ft, "assets/fonts/FreeSans.ttf", 0, &face))
|
||||
{
|
||||
std::cout << "Error::FREETYPE: Failed to load font!" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace J2D
|
||||
{
|
||||
void FillRect2D(const Color3 &color, const Vector2 &pos, const Vector2 &size) {
|
||||
auto vp_pos = pos;
|
||||
auto vp_size = size;
|
||||
glBegin(GL_QUADS);
|
||||
glColor3f(color.r/255.f, color.g/255.f, color.b/255.f);
|
||||
glVertex2f(vp_pos.x, vp_pos.y);
|
||||
glVertex2f(vp_pos.x, vp_pos.y + vp_size.y);
|
||||
glVertex2f(vp_pos.x + vp_size.x, vp_pos.y + vp_size.y);
|
||||
glVertex2f(vp_pos.x + vp_size.x, vp_pos.y);
|
||||
glEnd();
|
||||
}
|
||||
|
||||
void OutlineRect2D(const Color3 &color, const Vector2 &pos, const Vector2 &size, float thickness) {
|
||||
auto vp_pos = pos;
|
||||
auto vp_size = size;
|
||||
glBegin(GL_LINE_LOOP);
|
||||
glLineWidth(thickness);
|
||||
glColor3f(color.r, color.g, color.b);
|
||||
glVertex2f(vp_pos.x, vp_pos.y);
|
||||
glVertex2f(vp_pos.x, vp_pos.y + vp_size.y);
|
||||
glVertex2f(vp_pos.x + vp_size.x, vp_pos.y + vp_size.y);
|
||||
glVertex2f(vp_pos.x + vp_size.x, vp_pos.y);
|
||||
glEnd();
|
||||
}
|
||||
|
||||
void DrawLine2D(const Color3 &color, const Vector2 &A, const Vector2 &B, float thickness) {
|
||||
auto vp_a = A;
|
||||
auto vp_b = B;
|
||||
|
||||
glBegin(GL_LINES);
|
||||
glLineWidth(thickness);
|
||||
glColor3f(color.r, color.g, color.b);
|
||||
glVertex2f(vp_a.x, vp_a.y);
|
||||
glVertex2f(vp_b.x, vp_b.y);
|
||||
glEnd();
|
||||
|
||||
}
|
||||
|
||||
void DrawLine2D(const Color3 &color, float x, float y, float w, float h, float thickness) {
|
||||
DrawLine2D(color, {x, y}, {w, h}, thickness);
|
||||
}
|
||||
|
||||
void DrawPixel2D(const Color3 &color, float x, float y) {
|
||||
DrawPixel2D(color, {x, y});
|
||||
}
|
||||
|
||||
void DrawPixel2D(const Color3 &color, const Vector2 &coordinates) {
|
||||
auto vp_pos = coordinates;
|
||||
glBegin(GL_POINT);
|
||||
glColor3f(color.r, color.g, color.b);
|
||||
glVertex2f(vp_pos.x, vp_pos.y);
|
||||
glEnd();
|
||||
}
|
||||
|
||||
void
|
||||
OutlineCircle2D(const Color3 &color, const Vector2 ¢er, float radius, int subdivisions, float thickness) {
|
||||
glBegin(GL_LINE_LOOP);
|
||||
GLfloat angle;
|
||||
glColor3f(color.r, color.g, color.b);
|
||||
float step = (2.f * M_PI) / (float)subdivisions;
|
||||
for (angle = 0.0f; angle < (2.f * M_PI); angle += step)
|
||||
{
|
||||
GLfloat x = radius * sin(angle);
|
||||
GLfloat y = radius * cos(angle);
|
||||
x += center.x;
|
||||
y += center.y;
|
||||
glVertex2f(x, y);
|
||||
}
|
||||
glEnd();
|
||||
}
|
||||
|
||||
void FillCircle2D(const Color3 &color, const Vector2 ¢er, float radius, int subdivisions) {
|
||||
glBegin(GL_POLYGON);
|
||||
GLfloat angle;
|
||||
glColor3f(color.r, color.g, color.b);
|
||||
float step = (2.f * M_PI) / (float)subdivisions;
|
||||
for (angle = 0.0f; angle < (2.f * M_PI); angle += step)
|
||||
{
|
||||
GLfloat x = radius * sin(angle);
|
||||
GLfloat y = radius * cos(angle);
|
||||
x += center.x;
|
||||
y += center.y;
|
||||
glVertex2f(x, y);
|
||||
}
|
||||
glEnd();
|
||||
}
|
||||
|
||||
void OutlineTriangle2D(const Color3& color, const Triangle2D& tri)
|
||||
{
|
||||
glBegin(GL_LINE_LOOP);
|
||||
glColor3f(color.r/255.f, color.g/255.f, color.b/255.f);
|
||||
glVertex2f(tri.A.x, tri.A.y);
|
||||
glVertex2f(tri.B.x, tri.B.y);
|
||||
glVertex2f(tri.C.x, tri.C.y);
|
||||
glEnd();
|
||||
}
|
||||
|
||||
void FillTriangle2D(const Color3& color, const Triangle2D& tri)
|
||||
{
|
||||
glBegin(GL_LINE_LOOP);
|
||||
glColor3f(color.r/255.f, color.g/255.f, color.b/255.f);
|
||||
glVertex2f(tri.A.x, tri.A.y);
|
||||
glVertex2f(tri.B.x, tri.B.y);
|
||||
glVertex2f(tri.C.x, tri.C.y);
|
||||
glEnd();
|
||||
}
|
||||
|
||||
void DrawString2D(const Color3& color, std::string text, float x, float y, float scale, u32 size) {
|
||||
glUseProgram(0); // Fixed-function pipeline.
|
||||
glColor3f(color.r/255.f, color.g/255.f, color.b/255.f);
|
||||
|
||||
FT_Set_Pixel_Sizes(face, 0, size);
|
||||
|
||||
GLuint textures[text.size()];
|
||||
for (int i = 0; i < text.length(); i++)
|
||||
{
|
||||
if (FT_Load_Char(face, text.c_str()[i], FT_LOAD_RENDER))
|
||||
continue;
|
||||
|
||||
FT_GlyphSlot g = face->glyph;
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glGenTextures(1, &textures[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 w = g->bitmap.width * scale;
|
||||
float h = g->bitmap.rows * scale;
|
||||
|
||||
glBegin(GL_TRIANGLES);
|
||||
|
||||
glTexCoord2f(0, 0);
|
||||
glVertex2f(x2, y2);
|
||||
|
||||
glTexCoord2f(0, 1);
|
||||
glVertex2f(x2, y2 + h);
|
||||
|
||||
glTexCoord2f(1, 1);
|
||||
glVertex2f(x2 + w, y2 + h);
|
||||
|
||||
glTexCoord2f(0, 0);
|
||||
glVertex2f(x2, y2);
|
||||
|
||||
glTexCoord2f(1, 1);
|
||||
glVertex2f(x2 + w, y2 + h);
|
||||
|
||||
glTexCoord2f(1, 0);
|
||||
glVertex2f(x2 + w, y2);
|
||||
|
||||
glEnd();
|
||||
|
||||
x += (g->advance.x >> 6) * scale;
|
||||
y += (g->advance.y >> 6) * scale;
|
||||
}
|
||||
for (auto& t : textures)
|
||||
glDeleteTextures(1, &t);
|
||||
glBindTexture(GL_TEXTURE_2D, 0); // Unbind texture
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
namespace J3D
|
||||
{
|
||||
void DrawLine3D(const Color3& color, const Vector3& A, const Vector3& B, float thickness)
|
||||
{
|
||||
glBegin(GL_LINES);
|
||||
glLineWidth(thickness);
|
||||
glColor3f(color.r/255.f, color.g/255.f, color.b/255.f);
|
||||
glVertex3f(A.x, A.y, A.z);
|
||||
glVertex3f(B.x, B.y, B.z);
|
||||
glEnd();
|
||||
}
|
||||
|
||||
void DrawString3D(const Color3& color, const std::string& text, const Vector3& pos, float scale, u32 size)
|
||||
{
|
||||
float x = pos.x;
|
||||
float y = pos.y;
|
||||
float z = pos.z;
|
||||
GLfloat currentColor[4];
|
||||
GLuint textures[text.size()];
|
||||
glGetFloatv(GL_CURRENT_COLOR, currentColor);
|
||||
glUseProgram(0); // Fixed-function pipeline.
|
||||
glColor4f(color.r, color.g, color.b, 1.0f);
|
||||
|
||||
FT_Set_Pixel_Sizes(face, 0, size);
|
||||
|
||||
//for (c = text.c_str(); *c; c++)
|
||||
for (int i = 0; i < text.length(); i++)
|
||||
{
|
||||
if (FT_Load_Char(face, text.c_str()[i], FT_LOAD_RENDER))
|
||||
continue;
|
||||
|
||||
FT_GlyphSlot g = face->glyph;
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
glGenTextures(1, &textures[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 (auto& t : textures)
|
||||
glDeleteTextures(1, &t);
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
glDisable(GL_BLEND);
|
||||
glBindTexture(GL_TEXTURE_2D, 0); // Unbind texture
|
||||
glColor4f(currentColor[0], currentColor[1], currentColor[2], currentColor[3]); //Set draw color back to whatever it was before.
|
||||
}
|
||||
|
||||
}
|
||||
}
|
248
src/JGL/TextRendering.cpp
Normal file
248
src/JGL/TextRendering.cpp
Normal file
@@ -0,0 +1,248 @@
|
||||
#include <JGL/JGL.h>
|
||||
|
||||
|
||||
#if __linux__
|
||||
#include <freetype2/ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
#include FT_OUTLINE_H
|
||||
#include "jlog/jlog.hpp"
|
||||
|
||||
#endif
|
||||
#if _WIN32
|
||||
#include <ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
#endif
|
||||
|
||||
#include <JGL/Font.h>
|
||||
#include <JGL/FontCache.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);
|
||||
|
||||
//Set up the regular font.
|
||||
//for (const auto &f : Font::GetLoadedFonts())
|
||||
// if (f.index == font.index)
|
||||
// font = f;
|
||||
|
||||
|
||||
|
||||
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) {
|
||||
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 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);
|
||||
}
|
||||
}
|
||||
|
||||
glColor4f(color.r / 255.f, color.g / 255.f, color.b / 255.f, color.a / 255.f);
|
||||
|
||||
//Texture parameters are restored when the texture 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, vertices.size() * 6);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glColor4f(1, 1, 1, 1);
|
||||
}
|
||||
|
||||
void J2D::DrawString(const Color3& color, const std::string& text, float x, float y, float scale, u32 size, const Font& font) {
|
||||
J2D::DrawString(Color4::FromColor3(color, 255), text, x, y, scale, size, font);
|
||||
}
|
||||
|
||||
void J3D::DrawString(const Color3& color, const std::string& text, const Vector3& pos, const Vector3& angle, float scale, u32 size, const Font& font) {
|
||||
//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 : Font::GetLoadedFonts())
|
||||
//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
|
||||
glColor4f(1, 1, 1, 1);
|
||||
glPopMatrix();
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user