Compare commits

...

46 Commits

Author SHA1 Message Date
26600915db Update Texture.cpp
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Has been cancelled
2025-02-02 21:21:33 -05:00
7bc87d00ef Cleanup & remove ReImage
Some checks are pending
Run ReCI Build Test / Explore-Gitea-Actions (push) Waiting to run
2025-02-02 06:12:56 -05:00
5e65f17a90 Update RenderTarget.cpp
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 2m14s
fix case where it was possible to create a 0x0 RenderTarget.
2025-01-31 00:08:55 -05:00
426498d53c Performance optimization
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m49s
2025-01-30 23:16:30 -05:00
28bdc7f667 Fix GL error 1280 on windoze.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 2m11s
2025-01-30 14:18:42 -05:00
Redacted
e8bfa7b6f0 Update .gitea/workflows/buildtest.yml
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 2m13s
2025-01-29 19:17:12 -05:00
d60620ef7c Update J2D.cpp
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 2m11s
Fix a case that would sometimes cause Render Targets to appear strange due to being drawn at a sub-pixel position.
2025-01-28 19:06:17 -05:00
8cb470ad1c Update ReWindow & don't link to OpenGL.
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 4m57s
2025-01-27 11:57:55 -05:00
575a4a0f9b 🤷
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 2m20s
Removed the specific need for a RenderTarget's texture to have to be square because apparently the problem magically went away.
2025-01-20 20:42:19 -05:00
e9bdaf54b6 Update CMakeLists.txt
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 2m11s
Latest J3ML
2025-01-20 16:34:09 -05:00
7b2f7de032 Small restructure.
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Has been cancelled
2025-01-20 16:32:09 -05:00
e8245c4442 Latest ReWindow
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m51s
2025-01-15 23:47:58 -05:00
bd918aa351 Creating a texture atlas from multiple small textures.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m57s
2025-01-10 20:51:28 -05:00
a0cc8524d9 J2D DrawLines
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m59s
2025-01-05 12:18:18 -05:00
1964aeae86 Bugfix
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 2m44s
Fixed a bug that would sometimes cause rendering text after rendering an alpha-masked sprite to be invisible.
2025-01-03 22:22:37 -05:00
b84e2ee2c5 Just pushing what I have.
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 14s
2025-01-03 22:16:27 -05:00
1597662e2e Just pushing what I have.
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 47s
2024-12-22 00:38:16 -05:00
0823730e82 Better Wavefront OBJ loader.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 5m35s
2024-12-20 10:12:11 -05:00
6cbd369d51 Fixed one side of the cube looking weird
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 6m9s
How I understand it, When lighting a cube specifically. It will always look weird because it's just not enough information 🤷
2024-12-18 11:11:26 -05:00
926ae06834 Merge branch 'master' of https://git.redacted.cc/Josh/JGL
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m59s
2024-12-17 21:40:06 -05:00
d5fd68eba8 Light test 1 2024-12-17 21:39:58 -05:00
08b2dfbecc Update internals.h
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 2m20s
msvc
2024-12-17 13:58:07 -05:00
e33a51b7d4 Materials.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 6m18s
2024-12-16 22:25:36 -05:00
7f5ee5cf0c Pushing what I have.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 5m57s
2024-12-16 16:33:16 -05:00
401e2c0883 Minor edits.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m37s
2024-12-09 18:46:24 -05:00
e5bdc441d1 Merge branch 'master' of https://git.redacted.cc/Josh/JGL
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 6m45s
2024-12-09 14:21:44 -05:00
392375e56c small refactor 2024-12-09 14:21:34 -05:00
b007c78cfe Roll back to code from prelease-41. Fix segfault later
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 6m0s
2024-12-09 13:15:46 -05:00
512dc3cb1a Separate J2D and J3D.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 2m10s
2024-12-08 10:30:03 -05:00
a22a83d2f8 Fix out-of-date ReWindow API usage.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 2m0s
2024-12-06 12:05:29 -05:00
76cd48c9b7 Update Font.cpp
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 6m31s
Fixed several cases where the font index would be incorrect.
2024-12-06 11:34:38 -05:00
38bb1b22ce Include Jupiteroid as a default font
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m41s
2024-12-04 16:22:14 -05:00
122644a013 Update repositories
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 6m3s
2024-12-04 14:25:48 -05:00
Redacted
641f2de8d0 Update CMakeLists.txt
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 5m22s
2024-12-04 11:16:20 -05:00
6618aa5e6b just pushing what I have
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 2m20s
2024-12-01 11:00:48 -05:00
3970aa2718 Update GLAD
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 2m22s
2024-11-25 16:53:20 -05:00
fb5ca55fda Alpha masked sprite.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 7m44s
2024-11-22 07:46:48 -05:00
bcca6285af J2D DottedLine & DashedLine
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 6m41s
2024-11-20 18:04:21 -05:00
f8395726cd Improve performance of single-pixel blit.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 6m5s
2024-11-20 09:23:09 -05:00
ca7abb3044 DrawPartialRenderTarget
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m57s
2024-11-19 19:55:08 -05:00
14c45ab0f1 Merge branch 'master' of https://git.redacted.cc/Josh/JGL
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 5m34s
2024-11-14 18:19:42 -05:00
1ca5e5a694 Fix for Windows. 2024-11-14 18:22:28 -05:00
25fc3f8698 Fix for Windows.
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Has been cancelled
2024-11-14 18:20:57 -05:00
a836fc7b32 More batching & performance optimization.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 5m56s
2024-11-14 16:14:18 -05:00
e6dcc9d61e Reformatting
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 6m56s
2024-11-14 10:57:00 -05:00
6dff2f97c1 BatchWireframeAABB
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 5m54s
2024-11-13 21:07:21 -05:00
36 changed files with 20658 additions and 2487 deletions

View File

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

View File

@@ -17,22 +17,22 @@ include(cmake/CPM.cmake)
CPMAddPackage(
NAME mcolor
URL https://git.redacted.cc/maxine/mcolor/archive/Prerelease-4.zip
URL https://git.redacted.cc/maxine/mcolor/archive/Prerelease-5.zip
)
CPMAddPackage(
NAME J3ML
URL https://git.redacted.cc/josh/j3ml/archive/Release-3.4.zip
URL https://git.redacted.cc/josh/j3ml/archive/3.4.5.zip
)
CPMAddPackage(
NAME ReWindow
URL https://git.redacted.cc/Redacted/ReWindow/archive/Prerelease-21.zip
URL https://git.redacted.cc/Redacted/ReWindow/archive/Prerelease-32.zip
)
CPMAddPackage(
NAME GLAD
URL https://git.redacted.cc/Redacted/glad/archive/v2.1ext_fboV2.zip
URL https://git.redacted.cc/Redacted/glad/archive/v2.1ext_fbo_depthtexture_shadow.zip
)
CPMAddPackage(
@@ -40,17 +40,7 @@ CPMAddPackage(
URL https://git.redacted.cc/josh/jlog/Prerelease-16.zip
)
CPMAddPackage(
NAME ReImage
URL https://git.redacted.cc/Redacted/ReImage/archive/Release-2.0.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
@@ -61,10 +51,8 @@ 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" )
find_package(OpenGL REQUIRED)
file(GLOB_RECURSE HEADERS "include/*.h" "include/*.hpp" )
file(GLOB_RECURSE SOURCES "src/*.c" "src/*.cpp" "src/*.h")
if (UNIX AND NOT APPLE)
find_package(Freetype REQUIRED)
@@ -85,8 +73,6 @@ include_directories(
target_include_directories(JGL PUBLIC
${PROJECT_SOURCE_DIR}/include
${OPENGL_INCLUDE_DIRS}
${ReImage_SOURCE_DIR}/include
${mcolor_SOURCE_DIR}/include
${J3ML_SOURCE_DIR}/include
${jlog_SOURCE_DIR}/include
@@ -99,13 +85,13 @@ add_executable(JGL_Demo main.cpp)
if (UNIX AND NOT APPLE)
target_include_directories(JGL PRIVATE ${FREETYPE_INCLUDE_DIRS} )
target_link_libraries(JGL PRIVATE ${FREETYPE_LIBRARIES})
target_link_libraries(JGL PUBLIC ${OPENGL_LIBRARIES} mcolor J3ML jlog ReImage glad)
target_link_libraries(JGL PUBLIC mcolor J3ML jlog glad)
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} mcolor J3ML glad jlog ReImage glad)
target_link_libraries(JGL PUBLIC ${OPENGL_LIBRARIES} mcolor J3ML glad jlog glad)
endif()
target_link_libraries(JGL_Demo PUBLIC JGL ReWindowLibrary Event glad)
target_link_libraries(JGL_Demo PUBLIC JGL ReWindow Event glad)

View File

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

Binary file not shown.

164
assets/models/cone.obj Normal file
View File

@@ -0,0 +1,164 @@
# Blender v3.6.4 OBJ File: ''
# www.blender.org
o Cone
v 0.000000 -1.000000 -1.000000
v 0.195090 -1.000000 -0.980785
v 0.382683 -1.000000 -0.923880
v 0.555570 -1.000000 -0.831470
v 0.707107 -1.000000 -0.707107
v 0.831470 -1.000000 -0.555570
v 0.923880 -1.000000 -0.382683
v 0.980785 -1.000000 -0.195090
v 1.000000 -1.000000 0.000000
v 0.980785 -1.000000 0.195090
v 0.923880 -1.000000 0.382683
v 0.831470 -1.000000 0.555570
v 0.707107 -1.000000 0.707107
v 0.555570 -1.000000 0.831470
v 0.382683 -1.000000 0.923880
v 0.195090 -1.000000 0.980785
v 0.000000 -1.000000 1.000000
v -0.195090 -1.000000 0.980785
v -0.382683 -1.000000 0.923880
v -0.555570 -1.000000 0.831470
v -0.707107 -1.000000 0.707107
v -0.831470 -1.000000 0.555570
v -0.923880 -1.000000 0.382683
v -0.980785 -1.000000 0.195090
v -1.000000 -1.000000 0.000000
v -0.980785 -1.000000 -0.195090
v -0.923880 -1.000000 -0.382683
v -0.831470 -1.000000 -0.555570
v -0.707107 -1.000000 -0.707107
v -0.555570 -1.000000 -0.831470
v -0.382683 -1.000000 -0.923880
v -0.195090 -1.000000 -0.980785
v 0.000000 1.000000 0.000000
vt 0.250000 0.490000
vt 0.250000 0.250000
vt 0.296822 0.485388
vt 0.341844 0.471731
vt 0.383337 0.449553
vt 0.419706 0.419706
vt 0.449553 0.383337
vt 0.471731 0.341844
vt 0.485388 0.296822
vt 0.490000 0.250000
vt 0.485388 0.203178
vt 0.471731 0.158156
vt 0.449553 0.116663
vt 0.419706 0.080294
vt 0.383337 0.050447
vt 0.341844 0.028269
vt 0.296822 0.014612
vt 0.250000 0.010000
vt 0.203178 0.014612
vt 0.158156 0.028269
vt 0.116663 0.050447
vt 0.080294 0.080294
vt 0.050447 0.116663
vt 0.028269 0.158156
vt 0.014612 0.203178
vt 0.010000 0.250000
vt 0.014612 0.296822
vt 0.028269 0.341844
vt 0.050447 0.383337
vt 0.080294 0.419706
vt 0.116663 0.449553
vt 0.158156 0.471731
vt 0.796822 0.014612
vt 0.514612 0.203178
vt 0.703178 0.485388
vt 0.203178 0.485388
vt 0.750000 0.490000
vt 0.796822 0.485388
vt 0.841844 0.471731
vt 0.883337 0.449553
vt 0.919706 0.419706
vt 0.949553 0.383337
vt 0.971731 0.341844
vt 0.985388 0.296822
vt 0.990000 0.250000
vt 0.985388 0.203178
vt 0.971731 0.158156
vt 0.949553 0.116663
vt 0.919706 0.080294
vt 0.883337 0.050447
vt 0.841844 0.028269
vt 0.750000 0.010000
vt 0.703178 0.014612
vt 0.658156 0.028269
vt 0.616663 0.050447
vt 0.580294 0.080294
vt 0.550447 0.116663
vt 0.528269 0.158156
vt 0.510000 0.250000
vt 0.514612 0.296822
vt 0.528269 0.341844
vt 0.550447 0.383337
vt 0.580294 0.419706
vt 0.616663 0.449553
vt 0.658156 0.471731
s off
f 1/1 33/2 2/3
f 2/3 33/2 3/4
f 3/4 33/2 4/5
f 4/5 33/2 5/6
f 5/6 33/2 6/7
f 6/7 33/2 7/8
f 7/8 33/2 8/9
f 8/9 33/2 9/10
f 9/10 33/2 10/11
f 10/11 33/2 11/12
f 11/12 33/2 12/13
f 12/13 33/2 13/14
f 13/14 33/2 14/15
f 14/15 33/2 15/16
f 15/16 33/2 16/17
f 16/17 33/2 17/18
f 17/18 33/2 18/19
f 18/19 33/2 19/20
f 19/20 33/2 20/21
f 20/21 33/2 21/22
f 21/22 33/2 22/23
f 22/23 33/2 23/24
f 23/24 33/2 24/25
f 24/25 33/2 25/26
f 25/26 33/2 26/27
f 26/27 33/2 27/28
f 27/28 33/2 28/29
f 28/29 33/2 29/30
f 29/30 33/2 30/31
f 30/31 33/2 31/32
f 16/33 24/34 32/35
f 31/32 33/2 32/36
f 32/36 33/2 1/1
f 32/35 1/37 2/38
f 2/38 3/39 4/40
f 4/40 5/41 6/42
f 6/42 7/43 8/44
f 8/44 9/45 10/46
f 10/46 11/47 12/48
f 12/48 13/49 14/50
f 14/50 15/51 16/33
f 16/33 17/52 18/53
f 18/53 19/54 20/55
f 20/55 21/56 22/57
f 22/57 23/58 24/34
f 24/34 25/59 26/60
f 26/60 27/61 28/62
f 28/62 29/63 30/64
f 30/64 31/65 32/35
f 32/35 2/38 8/44
f 2/38 4/40 8/44
f 4/40 6/42 8/44
f 8/44 10/46 16/33
f 10/46 12/48 16/33
f 12/48 14/50 16/33
f 16/33 18/53 24/34
f 18/53 20/55 24/34
f 20/55 22/57 24/34
f 24/34 26/60 32/35
f 26/60 28/62 32/35
f 28/62 30/64 32/35
f 32/35 8/44 16/33

46
assets/models/cube.amo Normal file
View File

@@ -0,0 +1,46 @@
ao Cube 1
v 8
1.000000 1.000000 1.000000
1.000000 1.000000 -1.000000
1.000000 -1.000000 1.000000
1.000000 -1.000000 -1.000000
-1.000000 1.000000 1.000000
-1.000000 1.000000 -1.000000
-1.000000 -1.000000 1.000000
-1.000000 -1.000000 -1.000000
vt 14
0.625000 0.500000
0.875000 0.500000
0.875000 0.750000
0.625000 0.750000
0.375000 0.750000
0.625000 1.000000
0.375000 1.000000
0.375000 0.000000
0.625000 0.000000
0.625000 0.250000
0.375000 0.250000
0.125000 0.500000
0.375000 0.500000
0.125000 0.750000
vn 6
0.000000 0.000000 1.000000
0.000000 -1.000000 0.000000
-1.000000 0.000000 0.000000
0.000000 0.000000 -1.000000
1.000000 0.000000 0.000000
0.000000 1.000000 0.000000
f 12
2 0 0 0 1 0 4 2 0
7 3 1 3 4 1 2 3 1
5 5 2 7 6 2 6 7 2
7 8 3 5 9 3 1 10 3
3 11 4 1 12 4 0 4 4
1 13 5 5 12 5 4 0 5
2 0 0 4 1 0 6 2 0
7 3 1 2 4 1 6 3 1
5 5 2 6 6 2 4 7 2
7 8 3 1 9 3 3 10 3
3 11 4 0 12 4 2 4 4
1 13 5 4 12 5 0 0 5
end

38
assets/models/cube.obj Normal file
View File

@@ -0,0 +1,38 @@
# Blender v3.6.4 OBJ File: ''
# www.blender.org
o Cube
v 1.000000 1.000000 -1.000000
v 1.000000 -1.000000 -1.000000
v 1.000000 1.000000 1.000000
v 1.000000 -1.000000 1.000000
v -1.000000 1.000000 -1.000000
v -1.000000 -1.000000 -1.000000
v -1.000000 1.000000 1.000000
v -1.000000 -1.000000 1.000000
vt 0.875000 0.500000
vt 0.625000 0.750000
vt 0.625000 0.500000
vt 0.375000 1.000000
vt 0.375000 0.750000
vt 0.625000 0.000000
vt 0.375000 0.250000
vt 0.375000 0.000000
vt 0.375000 0.500000
vt 0.125000 0.750000
vt 0.125000 0.500000
vt 0.625000 0.250000
vt 0.875000 0.750000
vt 0.625000 1.000000
s off
f 5/1 3/2 1/3
f 3/2 8/4 4/5
f 7/6 6/7 8/8
f 2/9 8/10 6/11
f 1/3 4/5 2/9
f 5/12 2/9 6/7
f 5/1 7/13 3/2
f 3/2 7/14 8/4
f 7/6 5/12 6/7
f 2/9 4/5 8/10
f 1/3 3/2 4/5
f 5/12 1/3 2/9

Binary file not shown.

After

Width:  |  Height:  |  Size: 942 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@@ -1,4 +1,19 @@
/// A simple wrapper for OpenGL materials. Lets you set things such as the "shininess" of your elements.
class Material {
#include <Color4.hpp>
#include <glad/glad.h>
namespace JGL {
class Material;
}
class JGL::Material {
public:
Color4 ambient_ref;
Color4 diffuse_ref;
Color4 specular_ref;
Color4 emission;
float shininess;
public:
Material(const Color4& ambient_reflection, const Color4& diffuse_reflection, const Color4& specular_reflection, const Color4& light_emission, float& shine);
/// @param material Material to use.
static void SetActiveMaterial(const Material& material);
};

View File

@@ -1,20 +1,21 @@
#pragma once
#include <vector>
#include <glad/glad.h>
#include <Color4.hpp>
#include <Colors.hpp>
#include <JGL/types/Enums.h>
#include <J3ML/LinearAlgebra/Vector2.hpp>
#include <J3ML/LinearAlgebra/Vector2i.hpp>
namespace JGL {
class RenderTarget;
class Texture; // Forward declare.
class Texture;
}
using J3ML::LinearAlgebra::Vector2i;
class JGL::RenderTarget {
private:
Color4 clear_color{0,0,0,0};
/// "Size" in this sense is the "Renderable Area" because OpenGL textures behave strangely if they're not square.
Vector2 size{0, 0};
Vector2i size{0, 0};
bool using_depth = false;
bool texture_created_by_us = false;
GLuint framebuffer_object = 0;
@@ -26,47 +27,97 @@ private:
GLuint msaa_render_buffer = 0;
void Erase();
public:
/// @returns The Render Target currently in use by OpenGL.
/// @note Zero is the screen.
static GLuint GetActiveGLFramebufferHandle();
/// Changes the Render Target that OpenGL will draw on.
/// @param render_target The new Render Target for OpenGL to draw on.
static void SetActiveGLRenderTarget(const RenderTarget& render_target);
/** Change the size of the renderable area of the Render Target. **/
/// @param new_size new size in px.
void Resize(const Vector2& new_size);
void SetMSAAEnabled(MSAA_SAMPLE_RATE sample_rate);
/// If you're using raw OpenGL commands to draw to this outside of J2D or J3D don't forget to do this.
/// Blits the MSAA FBO onto the regular FBO if MSAA is enabled and or If you're rendering to a texture which uses mipmaps,
/// It regenerates them so what you drew doesn't disappear at a distance. Otherwise it does nothing.
void Blit() const;
/// Changes the size of the renderable area of this Render Target.
/// @param new_size new width & height in pixels.
/// @note The data stored in this Render Target will be lost.
void Resize(const Vector2i& new_size);
/// Blit a render target onto another. Will break if they're not the same size.
static void Blit(const RenderTarget& source, RenderTarget* destination);
/// Sets the MSAA mode for this Render Target.
/// @returns false if the mode isn't available, true for success.
[[nodiscard]] bool SetMSAAEnabled(MSAA_SAMPLE_RATE sample_rate);
/// Blit a single pixel onto a Render Target.
/// If you're using MSAA and not using J2D || J3D Begin & End you must do this.
void MSAABlit() const;
/// Copy one Render Target onto another. Will break if they're not the same size.
// TODO support different sizes. If the destination is too small fix it for them but log a warning.
static void Blit(const RenderTarget& source, RenderTarget* destination, const Vector2i& position = {0, 0});
/// Plots a single pixel onto a Render Target.
/// @param color The color to render.
/// @param position The position in the destination to draw the pixel.
/// @param destination The destination RenderTarget.
static void Blit(const Color4& color, const Vector2& position, RenderTarget* destination);
[[nodiscard]] bool TextureCreatedByRenderTarget() const;
static void Blit(const Color4& color, const Vector2i& position, RenderTarget* destination);
/// Blit an input texture onto this render target at the given position.
/// @param source Source texture.
/// @param destination Render Target to draw on.
/// @param position Where in the destination to draw.
static void Blit(const Texture* source, RenderTarget* destination, const Vector2i& position = {0, 0});
/// @returns Whether or not this Render Target created it's Texture.
[[nodiscard]] bool OwnsTexture() const;
public:
[[nodiscard]] Vector2 GetDimensions() const;
/// @returns the size of the renderable area.
[[nodiscard]] Vector2i GetDimensions() const;
/// @returns The currently selected MSAA Sample Rate.
[[nodiscard]] MSAA_SAMPLE_RATE GetMSAASampleRate() const;
/// Returns whether or not MSAA is enabled, If it is and you're not using J2D || J3D Begin / End,
/// You need to run "Blit()" after rendering to your FBO before you show it.
/// @note Also, If the texture wasn't made by the RenderTarget you don't want this. It would destroy the texture.
/// @returns Whether or not this Render Target is using MSAA.
[[nodiscard]] bool MSAAEnabled() const;
[[nodiscard]] const Texture* GetJGLTexture() const;
[[nodiscard]] GLuint GetGLTextureHandle() const;
/// @returns The JGL texture this Render Target draws on.
[[nodiscard]] const Texture* GetTexture() const;
/// @returns The OpenGL handle for the texture this Render Target draws on.
[[nodiscard]] GLuint GetTextureHandle() const;
/// @returns The OpenGL handle for this Render Target.
[[nodiscard]] GLuint GetGLFramebufferObjectHandle() const;
/// @returns The handle to the OpenGL buffer containing depth information
/// @note Only valid if this Render Target is being used for 3D.
[[nodiscard]] GLuint GetGLDepthBufferHandle() const;
/// @returns The color that should be used to clear this Render Target.
[[nodiscard]] Color4 GetClearColor() const;
/// Get the data back from the FBO. This is *not* async friendly.
[[nodiscard]] std::vector<GLfloat> GetData() const;
/// @returns The color information for this Render Target.
/// @note Both the CPU & GPU cannot do anything while this takes place. It's very slow.
[[nodiscard]] std::vector<GLfloat> GetPixels() const;
public:
/// Copy constructor. Will always set "texture_created_by_us" to true and use our own texture to avoid memleaks.
/// Create a Render Target from a Render Target that already exists.
/** @note Render Targets that are copies of another will copy the Texture.
* This is so that deleting the copy doesn't delete the Texture of the original.
*/
RenderTarget(const RenderTarget& rhs);
/// Create a render target for a texture that already exists. For adding to an existing texture.
explicit RenderTarget(const Texture* texture, const Color4& clear_color = Colors::Black);
/// Create a Render Target with a brand new texture. Want to render JGL elements onto a texture and display it as a sprite?
explicit RenderTarget(const Vector2& size, const Color4& clear_color = Colors::Black, bool use_depth = false, MSAA_SAMPLE_RATE sample_rate = MSAA_SAMPLE_RATE::MSAA_NONE);
/// Create a Render Target for a texture that already exists.
/// @param texture The Texture that using this Render Target would draw on.
/// @param clear_color The color to be used if you want to clear the Render Target.
/// @note For Render Targets created this way, The destructor will not delete the texture.
explicit RenderTarget(const Texture* texture, const Color4& clear_color = Colors::Transparent);
/// Create a Render Target with a new texture.
/// @param size The width & height the Render Target should have.
/// @param clear_color The color to be used if you want to clear the Render Target.
/// @param use_depth Whether or not this Render Target will have depth information.
/// @param sample_rate The MSAA sample rate this Render Target will use.
explicit RenderTarget(const Vector2i& size, const Color4& clear_color = Colors::Transparent, bool use_depth = false, MSAA_SAMPLE_RATE sample_rate = MSAA_SAMPLE_RATE::MSAA_NONE);
/// Deletes this Render Target.
/** @note If this Render Target was made with a Texture that already existed
* the Texture will not be deleted. */
~RenderTarget();
};

View File

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

View File

@@ -1,57 +1,75 @@
#pragma once
#include <ReImage/Image.h>
#include <J3ML/LinearAlgebra.hpp>
#include<vector>
#include <filesystem>
#include <J3ML/LinearAlgebra/Vector2i.hpp>
#include <Color3.hpp>
#include <Color4.hpp>
#include <glad/glad.h>
namespace JGL {
using namespace ReImage;
enum class TextureFilteringMode : u8 {
NEAREST = 0, //Fastest for 2D, Sometimes causes graphical issues.
BILINEAR = 1, //Fast and pretty, The best for 2D.
using J3ML::LinearAlgebra::Vector2i;
MIPMAP_NEAREST = 2, //Nearest with mipmaps. The fastest for 3D, Sometimes causes graphical issues. Uses more vram.
MIPMAP_BILINEAR = 3, //Bilinear with mipmaps, Fast and pretty. Uses more vram.
MIPMAP_TRILINEAR = 4 //The prettiest. Still decent speed. Uses more vram.
enum class FilteringMode : u8 {
NEAREST = 0, // Fastest for 2D, Sometimes causes graphical issues.
BILINEAR = 1, // Fast and pretty, The best for 2D.
MIPMAP_NEAREST = 2, // Nearest with mipmaps. The fastest for 3D, Sometimes causes graphical issues. Uses more vram.
MIPMAP_BILINEAR = 3, // Bilinear with mipmaps, Fast and pretty. Uses more vram.
MIPMAP_TRILINEAR = 4 // The prettiest. Still decent speed. Uses more vram.
};
enum class TextureWrappingMode : u8 {
enum class WrappingMode : u8 {
REPEAT = 0,
MIRRORED_REPEAT = 1,
CLAMP_TO_EDGE = 2,
CLAMP_TO_BORDER = 3 //Effectively the same as clamp_to_edge
CLAMP_TO_BORDER = 3 // Effectively the same as clamp_to_edge
};
enum class ColorFormat : bool { RGB = false, RGBA = true };
class Texture;
}
/// Represents texture data loaded on the GPU. Contains a handle that can be passed to OpenGL draw calls.
class Texture {
private:
void Erase();
protected:
GLuint texture_handle = 0;
Vector2 texture_size = {0, 0};
ReImage::TextureFlag texture_flags;
ReImage::TextureFormat texture_format;
TextureFilteringMode texture_filtering_mode;
TextureWrappingMode texture_wrapping_mode;
void load(Image* software_texture, const Vector2& size, const TextureFormat& format, TextureFilteringMode filtering_mode, TextureWrappingMode wrapping_mode);
public:
/// Load a texture from a file,
explicit Texture(const std::string& file, TextureFilteringMode filtering_mode = TextureFilteringMode::BILINEAR, TextureWrappingMode wrapping_mode = TextureWrappingMode::CLAMP_TO_EDGE, const TextureFlag& flags = TextureFlag::INVERT_Y);
Texture(Image* software_texture, const Vector2& size, const TextureFormat& format, TextureFilteringMode filtering_mode, TextureWrappingMode wrapping_mode);
/* Initialize a texture filled with trash data
this is primarily for the RenderTarget */
explicit Texture(const Vector2& size);
Texture(const Texture& rhs);
~Texture();
public:
[[nodiscard]] GLuint GetGLTextureHandle() const;
[[nodiscard]] Vector2 GetDimensions() const;
[[nodiscard]] TextureFilteringMode GetFilteringMode() const;
[[nodiscard]] TextureWrappingMode GetWrappingMode() const;
[[nodiscard]] TextureFlag GetFlags() const;
[[nodiscard]] TextureFormat GetFormat() const;
[[nodiscard]] std::vector<Color4> GetPixelData() const;
};
/// TODO handle the case of a texture being loaded that exceeds the max texture size.
/// Represents texture data loaded on the GPU. Contains a handle that can be passed to OpenGL draw calls.
class JGL::Texture {
protected:
unsigned int texture_handle = 0;
bool invert_y = false;
Vector2i size = {0, 0};
ColorFormat format = ColorFormat::RGBA;
FilteringMode filtering_mode;
WrappingMode wrapping_mode;
void load(const unsigned char* pixels);
std::vector<unsigned char> png(const std::filesystem::path& file);
std::vector<unsigned char> bmp(const std::filesystem::path& file);
public:
/// @returns A handle used to identify this texture.
[[nodiscard]] unsigned int GetHandle() const;
/// @returns The size of this texture.
[[nodiscard]] Vector2i GetDimensions() const;
/// @returns The filtering mode this texture is using.
[[nodiscard]] FilteringMode GetFilteringMode() const;
/// @returns The way this texture behaves when used on geometry of different sizes.
[[nodiscard]] WrappingMode GetWrappingMode() const;
/// @returns The orientation of this texture in v-ram.
/// @note true is right-side-up because OpenGL defaults to upside-down.
[[nodiscard]] bool Inverted() const;
[[nodiscard]] ColorFormat GetFormat() const;
/// @returns The raw pixels this texture is made up of.
/// @note This will read-back from the GPU. Slow.
[[nodiscard]] std::vector<Color4> GetPixelData() const;
public:
/// Load a texture from a file,
explicit Texture(const std::filesystem::path& file, FilteringMode filtering_mode = FilteringMode::BILINEAR, WrappingMode wrapping_mode = WrappingMode::CLAMP_TO_EDGE, bool invert_y = true);
/// Load a texture from raw pixels.
Texture(const Color4* pixels, const Vector2i& size, const ColorFormat& format, FilteringMode filtering_mode, WrappingMode wrapping_mode);
/// Load a texture from raw pixels.
Texture(const Color3* pixels, const Vector2i& size, const ColorFormat& format, FilteringMode filtering_mode, WrappingMode wrapping_mode);
/// Construct a Texture Atlas from many different textures.
/// @note THIS IS UNFINISHED.
Texture(const Texture* textures, const size_t& texture_count);
/// Initialize a texture filled with trash data.
/// @see RenderTarget
explicit Texture(const Vector2i& size);
}
Texture(const Texture& rhs);
~Texture();
};

View File

@@ -20,21 +20,21 @@ private:
bool spin_lock = false;
void load(const GLfloat* data, const long& size);
void load(const GLuint* data, const long& size);
void SetData(void* data, const long& length);
void UpdateData(void* data, const long& offset, const long& length);
void SetData(void* data, const long& count);
void UpdateData(void* data, const long& offset, const long& count);
void Erase();
public:
VRamList(const GLuint* data, const long& length);
VRamList(const GLfloat* data, const long& length);
VRamList(const Vector2* data, const long& length);
VRamList(const Vector3* data, const long& length);
VRamList(const Vector4* data, const long& length);
VRamList(const GLuint* data, const long& count);
VRamList(const GLfloat* data, const long& count);
VRamList(const Vector2* data, const long& count);
VRamList(const Vector3* data, const long& count);
VRamList(const Vector4* data, const long& count);
~VRamList();
/** Copying around the VBO data to a new VBO like this is slow.
* Pass to function by const reference or pointer always. */
VRamList(const VRamList& rhs);
VRamList() = default;
VRamList() : list_handle(0), num_elements(0), element_array_buffer(false), spin_lock(false) {}
public:
[[nodiscard]] GLuint GetHandle() const;
/// Returns the number of elements in the list.
@@ -46,25 +46,23 @@ public:
[[nodiscard]] std::vector<GLfloat> GetDataF() const;
[[nodiscard]] std::vector<GLuint> GetDataUI() const;
[[nodiscard]] bool IsFloatArray() const;
/** Replace the data of an existing VBO in it's entirety. Must be same type.
* "length" refers to the number of elements in data. Not the number of bytes. */
void SetData(const GLfloat* data, const long& length);
void SetData(const Vector2* data, const long& length);
void SetData(const Vector3* data, const long& length);
void SetData(const Vector4* data, const long& length);
/** Replace the data of an existing VBO in it's entirety. Must be same type. */
void SetData(const GLfloat* data, const long& count);
void SetData(const Vector2* data, const long& count);
void SetData(const Vector3* data, const long& count);
void SetData(const Vector4* data, const long& count);
void SetData(const GLuint* data, const long& length);
void SetData(const Vector2i* data, const long& length);
void SetData(const GLuint* data, const long& count);
void SetData(const Vector2i* data, const long& count);
/** Update only a portion of the data in a VBO. Must be same type.
* "length" refers to the number of elements in data. Not the number of bytes.
* "offset" refers the number of Typename T into the buffer the data you want to change is.
* For ex, offset 0 and length of 1 overwrites the first value. Offset 1 the second etc */
void UpdateData(const GLfloat* data, const long& offset, const long& length);
void UpdateData(const Vector2* data, const long& offset, const long& length);
void UpdateData(const Vector3* data, const long& offset, const long& length);
void UpdateData(const Vector4* data, const long& offset, const long& length);
void UpdateData(const GLfloat* data, const long& offset, const long& count);
void UpdateData(const Vector2* data, const long& offset, const long& count);
void UpdateData(const Vector3* data, const long& offset, const long& count);
void UpdateData(const Vector4* data, const long& offset, const long& count);
void UpdateData(const GLuint* data, const long& offset, const long& length);
void UpdateData(const Vector2i* data, const long& offset, const long& length);
void UpdateData(const GLuint* data, const long& offset, const long& count);
void UpdateData(const Vector2i* data, const long& offset, const long& count);
};

View File

@@ -29,10 +29,10 @@ protected:
protected:
std::vector<Animation> animations{};
protected:
VRamList vertices;
VRamList indices;
VRamList normals;
VRamList texture_coordinates;
VRamList* vertices;
VRamList* indices;
VRamList* normals;
VRamList* texture_coordinates;
protected:
/** For models which are not animated, This is intended for a low quality version in
* system memory for calculations to be done on the CPU. For models that are, the default pose of the model is here.
@@ -43,10 +43,10 @@ protected:
std::vector<Normal> local_normals{};
public:
/** Don't use these for anything other than drawing because the GPU is gonna spin during read-back */
[[nodiscard]] VRamList GetVertices() const;
[[nodiscard]] VRamList GetIndices() const;
[[nodiscard]] VRamList GetNormals() const;
[[nodiscard]] VRamList GetTextureCoordinates() const;
[[nodiscard]] const VRamList* GetVertices() const;
[[nodiscard]] const VRamList* GetIndices() const;
[[nodiscard]] const VRamList* GetNormals() const;
[[nodiscard]] const VRamList* GetTextureCoordinates() const;
/** These are for cpu side calculations */
[[nodiscard]] std::vector<Vertex> GetLocalVertices() const;
@@ -83,6 +83,7 @@ public:
[[nodiscard]] AABB GetMEAABB(const Matrix3x3& rotation_matrix, const Vector3& scale = Vector3::One, const Vector3& translate_part = Vector3::Zero) const;
[[nodiscard]] AABB GetMEAABB(const Matrix4x4& instance_matrix, bool translate = false) const;
public:
VertexArray() = default;
/// Vertices are required, Everything else is optional.
VertexArray(const Vertex* vertex_positions, const long& vp_length, const unsigned int* vertex_indices = nullptr, const long& vi_length = 0,
const Normal* vertex_normals = nullptr, const long& vn_length = 0, const TextureCoordinate* texture_coordinates = nullptr, const long& vt_length = 0);
@@ -90,9 +91,13 @@ public:
/// Vertices are required, Everything else is optional.
explicit VertexArray(const std::vector<Vertex>& vertex_positions, const std::vector<unsigned int>& vertex_indices = {},
const std::vector<Normal>& vertex_normals = {}, const std::vector<TextureCoordinate>& texture_coordinates = {});
static VertexArray LoadWavefrontOBJ(const std::string& file_text);
static VertexArray LoadAMO(const std::string& file_text);
};
using namespace JGL;
static VertexArray Animate(int animation_id, float animation_time);
static VertexArray Animate(const AnimationState& anim_state);

180
main.cpp
View File

@@ -1,17 +1,21 @@
#include <JGL/JGL.h>
#include <rewindow/types/window.h>
#include <ReWindow/types/Window.h>
#include <Colors.hpp>
#include <chrono>
#include <J3ML/LinearAlgebra/Vector2.hpp>
#include <JGL/logger/logger.h>
#include <J3ML/Geometry/AABB.hpp>
#include <ReWindow/Logger.h>
#include <JGL/types/VertexArray.h>
using J3ML::LinearAlgebra::Vector2;
using namespace JGL::Fonts;
using namespace JGL;
using JGL::Font;
JGL::Font FreeSans;
JGL::Font Jupiteroid;
float fps = 0.0f;
/// A draggable 2D point that highlights when moused over and when clicked.
class Gizmo
{
public:
@@ -21,6 +25,15 @@ public:
bool hovered = false;
Vector2 position;
float range = 6.f;
float base_radius = 3.f;
float hover_radius = 6.f;
float drag_radius = 4.f;
Color4 base_color = Colors::Reds::Salmon;
Color4 hover_color = Colors::Reds::Firebrick;
Color4 drag_color = Colors::White;
float lerp_rate = 0.25f;
float text_scale = 1.f;
int text_size = 12;
void Grab() {
if (hovered)
@@ -32,27 +45,25 @@ public:
void Update(const Vector2& mouse) {
if (dragging)
position = position.Lerp(mouse, 0.25f);
position = position.Lerp(mouse, lerp_rate);
hovered = mouse.Distance(position) < range;
}
void Draw() {
if (dragging)
J2D::DrawPoint(Colors::White, position, 4.f);
J2D::DrawPoint(drag_color, position, drag_radius);
else if (hovered)
J2D::DrawPoint(Colors::Reds::Crimson, position, 6.f);
J2D::DrawPoint(hover_color, position, hover_radius);
else
J2D::DrawPoint(Colors::Reds::Salmon, position, 3.f);
J2D::DrawString(Colors::White, std::format("{:.1f},{:.1f}", position.x, position.y), position.x, position.y, 1.f, 10, FreeSans);
J2D::DrawPoint(base_color, position, base_radius);
J2D::DrawString(Colors::White, std::format("{:.1f},{:.1f}", position.x, position.y), position.x, position.y, text_scale, text_size);
}
};
/// A 3D Camera Controller.
class Camera {
public:
Vector3 position = {0,0,0};
@@ -96,48 +107,46 @@ Gizmo b({200, 250});
Gizmo c({350, 300});
Gizmo d({450, 250});
JGL::Font FreeSans;
Texture* image;
Texture* image2;
Texture* image_mask;
RenderTarget* j2d_render_target;
RenderTarget* image2_render_target;
class JGLDemoWindow : public ReWindow::RWindow
class JGLDemoWindow : public ReWindow::OpenGLWindow
{
public:
void initGL() {
camera = new Camera;
if (!JGL::Init(getSize(), 75, 100))
if (!JGL::Init({(float) GetSize().x, (float) GetSize().y}, 75, 100))
Logger::Fatal("Initialization failed.");
// Load a custom font.
FreeSans = JGL::Font("assets/fonts/FreeSans.ttf");
Jupiteroid = JGL::Font("assets/fonts/Jupiteroid.ttf");
glClearColor(0.f, 0.f, 0.f, 0.f);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);
glDepthMask(GL_TRUE);
image = new Texture("assets/sprites/Re3D.png", TextureFilteringMode::BILINEAR);
j2d_render_target = new RenderTarget({540, 540}, {0,0,0,0}, false, MSAA_SAMPLE_RATE::MSAA_NONE);
image2 = image;
image2_render_target = new RenderTarget(image2);
image = new Texture("assets/sprites/Re3D.png", FilteringMode::BILINEAR);
image_mask = new Texture("assets/sprites/alpha_mask_2.png");
j2d_render_target = new RenderTarget({540, 500}, {0,0,0,0}, false, MSAA_SAMPLE_RATE::MSAA_NONE);
J2D::Begin(image2_render_target);
J2D::DrawString(Colors::Red, "TEST", 0, 16, 1, 16, FreeSans);
J2D::FillRect(Colors::Blue, {0,0}, {4,4});
J2D::End();
//Texture::MultiplyByAlphaMask(*image, *image_mask);
}
Vector3 textAngle = {0,0,0};
EulerAngleXYZ textAngle = {0,0,0};
float fov = 90;
u8 pulse = 0;
float sprite_radians = 0;
bool fov_increasing = true;
int blit_pos = 0;
void display() {
pulse++;
float dt = GetDeltaTime();
float dt = 1.f / fps;
JGL::Update(getSize());
JGL::Update({(float) GetSize().x, (float) GetSize().y});
if (fov_increasing)
fov += 0.025;
@@ -151,7 +160,7 @@ public:
//J3D::ChangeFOV(fov);
sprite_radians += 0.005;
textAngle.y += (5.f * delta_time);
textAngle.yaw += 1;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
@@ -159,28 +168,30 @@ public:
camera->render();
// All 3D elements of the scene and JGL elements *must* be rendered before the 2D stuff
/* if rendering to screen space directly. */
auto test_light = PointLight({2,1,2}, {pulse,pulse,pulse, 255}, {pulse, pulse, pulse, 255}, {0,0,0}, 1, 0.1, 0.01);
// If a 3D object has transparency. The things you'd like to see through it must be drawn before.
J3D::Begin();
J3D::DrawLine(Colors::Red, {-0.33,-0.125,1}, {-1,-0.125,1});
J3D::DrawLine(Colors::Red, {-0.33,-0.125,1}, {-0.33,0.25,1});
J3D::DrawString(Colors::Red, "JGL Sample Text", {-0.33, -0.1, 1.0f}, 1.f, 32, FreeSans, textAngle * 100, true);
J3D::DrawString(Colors::Red, "JGL Sample Text", {-0.33, -0.1, 1.0f}, 1.f, 32, FreeSans, textAngle, true);
//J3D::WireframeSphere(Colors::Green, {0,0,0.5f}, 0.25f, 1, 128, 128);
Sphere sphere[5] = {{{0,0, 0.5f}, 0.2125}, {{0,0, 0.5f}, 0.025},{{0,0, 0.5f}, 0.05},{{0,0, 0.5f}, 0.075},{{0,0, 0.5f}, 0.1}};
J3D::BatchWireframeRevoSphere(Colors::Green, sphere, 1, 1, 16, 16, true);
//J3D::FillAABB(Colors::Whites::AliceBlue, {0,0,0.5f}, {0.1f, 0.05f, 0.1f});
//J3D::WireframeAABB(Colors::Gray, {0,0,0.5f}, {0.11f, 0.06f, 0.11f});
Sphere sphere = {{0,0, 0.5f}, 0.2125};
J3D::BatchWireframeRevoSphere(Colors::Green, &sphere, 1, 1, 8, 8, true);
J3D::RequiredLight(&test_light);
J3D::FillAABB(Colors::Whites::AliceBlue, {0,0,0.5f}, {0.05f, 0.05f, 0.05f});
J3D::WireframeAABB(Colors::Gray, {0,0,0.5f}, {0.11f, 0.06f, 0.11f});
J3D::WireframeOBB(Colors::Red, {0, 0, 1.5f}, {0.40f, 0.10f, 0.10f}, {0,textAngle.y, 0});
AABB boxes[1] = {{Vector3(-0.2125, -0.2125,0.28750), Vector3(0.2125,0.2125,0.7125)}};
J3D::BatchWireframeAABB(Colors::Yellow, boxes, 1, 1);
//J3D::DrawCubicBezierCurve(Colors::Blue, {0,0,0.3}, {0,0,0.5}, {0.2,0,0.3}, {0.2, 0.3, 0.1}, 30);
//J3D::WireframeIcosahedron(Colors::Green, {0,0,0.5f}, 0.125f, 1.f);
J3D::End();
J2D::Begin(j2d_render_target, true);
J2D::FillRect(Colors::Blue, {0,52}, {100,100});
J2D::DrawSprite(image2, {300, 400}, sprite_radians * 0.10f, {0.5,0.5}, {1, 1}, Colors::White);
J2D::DrawSprite(image, {300, 400}, sprite_radians * 0.10f, {0.5,0.5}, {1, 1}, Colors::White);
J2D::DrawMirrorSprite(image, {400, 300}, Direction::Horizontal | Direction::Vertical, sprite_radians, {0.5,0.5}, {1, 1}, Colors::White);
J2D::DrawPartialSprite(image, {225, 300}, image->GetDimensions() * 0.25, image->GetDimensions() * 0.75, sprite_radians, {0.5, 0.5}, {1,1}, Colors::White);
J2D::DrawPartialSprite(image, Vector2(225, 300), Vector2(image->GetDimensions()) * 0.25, Vector2(image->GetDimensions()) * 0.75, sprite_radians, {0.5, 0.5}, {1,1}, Colors::White);
J2D::FillRect(Colors::Pinks::HotPink, {68, 120}, {32, 32});
J2D::FillGradientRect(Colors::Red, Colors::Blue, Direction::Diagonal_SWNE, {100,52}, {100,100});
J2D::FillRoundedRect(Colors::Red, {200, 52}, {100, 100}, 8, 8);
@@ -221,40 +232,57 @@ public:
//Draw the Render Target that we just drew all that stuff onto.
J2D::Begin();
J2D::DrawSprite(j2d_render_target, {0, 0}, 0, {0.5, 0.5}, {1,1}, Colors::White);
J2D::DrawSprite(image2_render_target, {300, 500}, 0, {0.5, 0.5}, {1,1}, Colors::White);
J2D::DrawRenderTarget(j2d_render_target, {0, 0});
J2D::DrawSprite(image, image_mask, {0, 0}, 0.25, {0.5, 0.5}, {1,1});
//J2D::DrawSprite(, {0, 0}, 0, {0.5, 0.5}, {1,1}, Colors::White);
//J2D::DrawSprite( {0, 0}, 0, {0.5, 0.5}, {1,1}, Colors::White);
J2D::End();
}
void OnRefresh(float elapsed) override {
if (isKeyDown(Keys::RightArrow))
fps = GetRefreshRate();
if (IsKeyDown(Keys::RightArrow))
camera->angle.y += 45.f * elapsed;
if (isKeyDown(Keys::LeftArrow))
if (IsKeyDown(Keys::LeftArrow))
camera->angle.y -= 45.f * elapsed;
if (isKeyDown(Keys::UpArrow))
if (IsKeyDown(Keys::UpArrow))
camera->angle.x -= 45.f * elapsed;
if (isKeyDown(Keys::DownArrow))
if (IsKeyDown(Keys::DownArrow))
camera->angle.x += 45.f * elapsed;
if (isKeyDown(Keys::Space))
if (IsKeyDown(Keys::Space))
camera->position.y += 1.f * elapsed;
if (isKeyDown(Keys::LeftShift))
if (IsKeyDown(Keys::LeftShift))
camera->position.y -= 1.f * elapsed;
//This is wrong of course. Just for testing purposes.
if (IsKeyDown(Keys::W))
camera->position.z += 1.f * elapsed;
if (IsKeyDown(Keys::S))
camera->position.z -= 1.f * elapsed;
if (IsKeyDown(Keys::A))
camera->position.x += 1.f * elapsed;
if (IsKeyDown(Keys::D))
camera->position.x -= 1.f * elapsed;
auto mouse = GetMouseCoordinates();
a.Update(mouse);
b.Update(mouse);
c.Update(mouse);
d.Update(mouse);
a.Update({(float) mouse.x, (float) mouse.y});
b.Update({(float) mouse.x, (float) mouse.y});
c.Update({(float) mouse.x, (float) mouse.y});
d.Update({(float) mouse.x, (float) mouse.y});
display();
int glError = glGetError();
if (glError != GL_NO_ERROR)
std::cout << glError << std::endl;
glSwapBuffers();
SwapBuffers();
}
void OnMouseButtonDown(const ReWindow::WindowEvents::MouseButtonDownEvent & ev) override
void OnMouseButtonDown(const ReWindow::MouseButtonDownEvent & ev) override
{
RWindow::OnMouseButtonDown(ev);
a.Grab();
@@ -263,7 +291,7 @@ public:
d.Grab();
}
void OnMouseButtonUp(const ReWindow::WindowEvents::MouseButtonUpEvent & ev) override
void OnMouseButtonUp(const ReWindow::MouseButtonUpEvent & ev) override
{
RWindow::OnMouseButtonUp(ev);
a.Release();
@@ -273,25 +301,37 @@ public:
}
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){}
JGLDemoWindow(const std::string& title, int width, int height) : ReWindow::OpenGLWindow(title, width, height, 2, 1) {}
};
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);
window->setVsyncEnabled(false);
while (window->isAlive()) {
std::chrono::high_resolution_clock::time_point start = std::chrono::high_resolution_clock::now();
window->pollEvents();
window->refresh();
std::chrono::high_resolution_clock::time_point stop = std::chrono::high_resolution_clock::now();
std::chrono::duration<float> frame_time = stop - start;
fps = 1.0f / frame_time.count();
}
auto* window = new JGLDemoWindow("JGL Demo Window", 1280, 720);
if (!window->Open())
exit(-1);
window->initGL();
window->SetResizable(true);
window->SetVsyncEnabled(false);
std::ifstream file("assets/models/cube.amo");
if (!file.is_open())
return -1;
/*
std::stringstream buffer;
buffer << file.rdbuf();
std::string file_text = buffer.str();
file.close();
std::cout << file_text << std::endl;
auto result = VertexArray::LoadAMO(file_text);
*/
//ReWindow::Logger::Error.EnableConsole(false);
//ReWindow::Logger::Warning.EnableConsole(false);
//ReWindow::Logger::Debug.EnableConsole(false);
while (window->IsAlive())
window->ManagedRefresh();
return 0;
}

8671
src/Fonts.cpp Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

57
src/ShapeCache.cpp Normal file
View File

@@ -0,0 +1,57 @@
#include <JGL/JGL.h>
void JGL::ShapeCache::Init() {
std::array<Vector3, 8> vertices = {
Vector3(1.0f, 1.0f, -1.0f),
Vector3(1.0f, -1.0f, -1.0f),
Vector3(1.0f, 1.0f, 1.0f),
Vector3(1.0f, -1.0f, 1.0f),
Vector3(-1.0f, 1.0f, -1.0f),
Vector3(-1.0f, -1.0f, -1.0f),
Vector3(-1.0f, 1.0f, 1.0f),
Vector3(-1.0f, -1.0f, 1.0f)
};
std::array<unsigned int, 36> indices = {
4, 2, 0,
2, 7, 3,
6, 5, 7,
1, 7, 5,
0, 3, 1,
4, 1, 5,
4, 6, 2,
2, 6, 7,
6, 4, 5,
1, 3, 7,
0, 2, 3,
4, 0, 1
};
std::array<Vector3, 8> vertex_normals = {
Vector3( 0.816f, 0.408f, -0.408f),
Vector3( 0.333f, -0.667f, -0.667f),
Vector3( 0.333f, 0.667f, 0.667f),
Vector3( 0.816f, -0.408f, 0.408f),
Vector3(-0.333f, 0.667f, -0.667f),
Vector3(-0.816f, -0.408f, -0.408f),
Vector3(-0.816f, 0.408f, 0.408f),
Vector3(-0.333f, -0.667f, 0.667f)
};
if (!cube_vertex_data)
cube_vertex_data = new VRamList(vertices.data(), vertices.size());
if (!cube_index_data)
cube_index_data = new VRamList(indices.data(), indices.size());
if (!cube_normal_data)
cube_normal_data = new VRamList(vertex_normals.data(), vertex_normals.size());
if (!square_origin_topleft_vertex_data) {
std::array<Vector2, 4> square_vertices = { Vector2(0, 0), {1, 0}, {1, -1}, {0, -1} };
square_origin_topleft_vertex_data = new VRamList(square_vertices.data(), square_vertices.size());
}
if (!j2d_default_normal_data) {
std::array<GLfloat, 3> normal {0, 0, 1};
j2d_default_normal_data = new VRamList(normal.data(), normal.size());
}
}

1137
src/renderer/OpenGL/J2D.cpp Normal file

File diff suppressed because it is too large Load Diff

609
src/renderer/OpenGL/J3D.cpp Normal file
View File

@@ -0,0 +1,609 @@
#include <JGL/JGL.h>
#include <JGL/logger/logger.h>
#include <J3ML/Geometry/AABB.hpp>
#include <J3ML/Geometry/OBB.hpp>
#include <J3ML/Algorithm/Bezier.hpp>
#include <JGL/types/Light.h>
#include "internals/internals.h"
std::array<GLfloat, 16> JGL::OpenGLPerspectiveProjectionRH(float fovY, float aspect, float z_near, float z_far) {
std::array<GLfloat, 16> result{};
GLfloat f = 1.0f / std::tan(fovY * 0.5f * Math::Pi / 180.0f);
result[0] = f / aspect;
result[5] = f;
result[10] = (z_far + z_near) / (z_near - z_far);
result[11] = -1.0f;
result[14] = (2.0f * z_far * z_near) / (z_near - z_far);
return result;
}
void JGL::J3D::ChangeFOV(float fov) {
JGL::J3D::fov = fov;
}
void JGL::J3D::ChangeFarPlane(float far_plane) {
JGL::J3D::far_plane = far_plane;
}
void JGL::J3D::RequiredLight(const JGL::LightBase* light) {
bool success = false;
for (auto& i : required_lights)
if (i == nullptr) {
i = light;
success = true;
break;
}
if (!success)
Logger::Error("You cannot specify more than 8 required lights.");
}
void JGL::J3D::OptionalLights(const JGL::LightBase** lights, const size_t& light_count) {
for (size_t i = 0; i < light_count; i++)
optional_lights.push_back(lights[i]);
}
void JGL::J3D::Begin(bool two_pass) {
auto aspect = (float) OpenGLState::window_size.x / (float) OpenGLState::window_size.y;
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glMultMatrixf(OpenGLPerspectiveProjectionRH(fov, aspect, 0.001, far_plane).data());
glMatrixMode(GL_MODELVIEW);
//Get what the draw color was before we did anything.
glGetFloatv(GL_CURRENT_COLOR, OpenGLState::oldColor);
glColor4fv(OpenGLState::baseColor);
if (!glIsEnabled(GL_DEPTH_TEST))
OpenGLState::wasDepthTestEnabled = false,
glEnable(GL_DEPTH_TEST);
else
OpenGLState::wasDepthTestEnabled = true;
OpenGLState::wasVertexArraysEnabled = false;
if (!glIsEnabled(GL_VERTEX_ARRAY))
OpenGLState::wasVertexArraysEnabled = false,
glEnableClientState(GL_VERTEX_ARRAY);
if (glIsEnabled(GL_NORMAL_ARRAY))
OpenGLState::wasNormalArrayEnabled = true,
glDisableClientState(GL_NORMAL_ARRAY);
if (glIsEnabled(GL_TEXTURE_COORD_ARRAY))
OpenGLState::wasTextureCoordArrayEnabled = true,
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
if (glIsEnabled(GL_TEXTURE_2D))
OpenGLState::wasTexture2DEnabled = true,
glDisable(GL_TEXTURE_2D);
OpenGLState::wasCullFaceEnabled = false;
if (glIsEnabled(GL_CULL_FACE))
OpenGLState::wasCullFaceEnabled = true,
glDisable(GL_CULL_FACE);
if (!glIsEnabled(GL_BLEND))
OpenGLState::wasBlendEnabled = false,
glEnable(GL_BLEND),
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
else
OpenGLState::wasBlendEnabled = true;
// Reset the lights.
required_lights = empty_light_array;
optional_lights = {};
glDisable(GL_LIGHTING);
if (!inJ2D)
inJ3D = true;
else
Logger::Error("Beginning J3D context inside of J2D context?");
}
void JGL::J3D::End() {
if (!OpenGLState::wasDepthTestEnabled)
glDisable(GL_DEPTH_TEST);
if (!OpenGLState::wasVertexArraysEnabled)
glDisableClientState(GL_VERTEX_ARRAY);
if (OpenGLState::wasTexture2DEnabled)
glEnable(GL_TEXTURE_2D);
if (!OpenGLState::wasBlendEnabled)
glDisable(GL_BLEND);
if (OpenGLState::wasCullFaceEnabled)
glEnable(GL_CULL_FACE);
if (OpenGLState::wasNormalArrayEnabled)
glEnableClientState(GL_NORMAL_ARRAY);
if (OpenGLState::wasTextureCoordArrayEnabled)
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
if (glIsEnabled(GL_LIGHTING)) {
glDisable(GL_LIGHTING);
glDisable(GL_LIGHT0);
glDisable(GL_LIGHT1);
glDisable(GL_LIGHT2);
glDisable(GL_LIGHT3);
glDisable(GL_LIGHT4);
glDisable(GL_LIGHT5);
glDisable(GL_LIGHT6);
glDisable(GL_LIGHT7);
}
required_lights = empty_light_array;
optional_lights = {};
//Put the draw color back how it was before.
glColor4fv(OpenGLState::oldColor);
inJ3D = false;
}
void JGL::J3D::DrawLine(const Color4& color, const Vector3& A, const Vector3& B, float thickness) {
if (!inJ3D)
Logger::Error("Drawing J3D element before J3D begin.");
Vector3 vertices[] = {A, B};
glLineWidth(thickness);
glColor4ubv(color.ptr());
glVertexPointer(3, GL_FLOAT, sizeof(Vector3), vertices);
glDrawArrays(GL_LINES, 0, 2);
glColor4fv(OpenGLState::baseColor);
}
void JGL::J3D::WireframeSphere(const Color4& color, const Vector3& position, float radius, float thickness, unsigned int sectors, unsigned int stacks) {
Sphere sphere = {position, radius};
BatchWireframeSphere(color,& sphere, 1, thickness, sectors, stacks);
}
void JGL::J3D::WireframeSphere(const Color4& color, const Sphere& sphere, float thickness, unsigned int sectors, unsigned int stacks) {
BatchWireframeSphere(color,& sphere, 1, thickness, sectors, stacks);
}
void JGL::J3D::BatchWireframeSphere(const Color4& color, const Sphere* spheres, const size_t& sphere_count, float thickness, unsigned int sectors, unsigned int stacks) {
if (!inJ3D)
Logger::Error("Drawing J3D element before J3D begin.");
// Create one sphere with a radius of 1 about 0, 0.
float r = 1;
std::vector<Vector3> vertices((sectors + 1) * (stacks + 1));
int index = 0;
for (int i = 0; i <= sectors; i++) {
float lat = J3ML::Math::Pi * (-0.5 + (float) i / sectors);
float z = J3ML::Math::Sin(lat);
float zr = J3ML::Math::Cos(lat);
for (int j = 0; j <= stacks; j++) {
float lng = 2 * J3ML::Math::Pi * (float) (j - 1) / stacks;
float x = J3ML::Math::Cos(lng);
float y = J3ML::Math::Sin(lng);
float pos_x = r * x * zr;
float pos_y = r * y * zr;
float pos_z = r * z;
vertices[index++] = Vector3(pos_x, pos_y, pos_z);
}
}
glLineWidth(thickness);
glColor4ubv(color.ptr());
glVertexPointer(3, GL_FLOAT, sizeof(Vector3), vertices.data());
// Render each sphere in the batch at their given position and radius.
for(size_t i = 0; i < sphere_count; i++) {
glPushMatrix();
glTranslatef(spheres[i].Position.x, spheres[i].Position.y, spheres[i].Position.z);
glScalef(spheres[i].Radius, spheres[i].Radius, spheres[i].Radius);
glDrawArrays(GL_LINE_LOOP, 0, vertices.size());
glPopMatrix();
}
glColor4fv(OpenGLState::baseColor);
}
void JGL::J3D::WireframeRevoSphere(const Color4& color, const Vector3& position, float radius, float thickness, unsigned int sectors, unsigned int revolutions, bool draw_stacks) {
Sphere sphere = {position, radius};
BatchWireframeRevoSphere(color,& sphere, 1, thickness, sectors, revolutions, draw_stacks);
}
void JGL::J3D::WireframeRevoSphere(const Color4& color, const Sphere& sphere, float thickness, unsigned int sectors, unsigned int revolutions, bool draw_stacks) {
BatchWireframeRevoSphere(color,& sphere, 1, thickness, sectors, revolutions, draw_stacks);
}
void JGL::J3D::BatchWireframeRevoSphere(const Color4& color, const Sphere* spheres, const size_t& sphere_count, float thickness, unsigned int sectors, unsigned int revolutions, bool draw_stacks) {
float r = 1;
std::vector<Vector3> vertices;
vertices.reserve((sectors + 1) * (revolutions + 1));
std::vector<Vector3> cross_section(sectors + 1);
for (int i = 0; i <= sectors; i++) {
float lat = J3ML::Math::Pi * (-0.5 + (float)i / sectors);
float z = J3ML::Math::Sin(lat);
float zr = J3ML::Math::Cos(lat);
cross_section[i] = Vector3(0, zr * r, z * r);
}
// Revolve
for (int j = 0; j <= revolutions; j++) {
float lng = 2 * J3ML::Math::Pi * (float)j / revolutions;
float cosLng = J3ML::Math::Cos(lng);
float sinLng = J3ML::Math::Sin(lng);
for (const auto& point : cross_section) {
float pos_x = point.y * cosLng;
float pos_y = point.y * sinLng;
float pos_z = point.z;
vertices.emplace_back(pos_x, pos_y, pos_z);
}
}
glLineWidth(thickness);
glColor4ubv(color.ptr());
VRamList vertex_data(vertices.data(), vertices.size());
glBindBuffer(GL_ARRAY_BUFFER, vertex_data.GetHandle());
glVertexPointer(3, GL_FLOAT, sizeof(Vector3), nullptr);
// Render each sphere in the batch at their given position and radius.
for(size_t i = 0; i < sphere_count; i++) {
glPushMatrix();
glTranslatef(spheres[i].Position.x, spheres[i].Position.y, spheres[i].Position.z);
glScalef(spheres[i].Radius, spheres[i].Radius, spheres[i].Radius);
glDrawArrays(GL_LINE_LOOP, 0, vertex_data.GetLength());
if (draw_stacks)
glRotatef(90, 0, 1, 0),
glDrawArrays(GL_LINE_LOOP, 0, vertex_data.GetLength());
glPopMatrix();
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
glColor4fv(OpenGLState::baseColor);
}
void JGL::J3D::WireframeIcosphere(const Color4& color, const Vector3& position, float radius, float thickness,
unsigned int subdivisions) {
if (!inJ3D)
Logger::Error("Drawing J3D element before J3D begin.");
// NOTE2SELF: Code i'm borrowing this from uses float-packed-arrays rather than discrete Vectors
// working on translating that correctly...
// TODO: Revise this once J3ML::Geometry::Icosahedron is implemented.
const float h_angle = J3ML::Math::Pi / 180.f * 72.f; // 72 degree = 360 / 5;
const float v_angle = J3ML::Math::Atan(1.0f / 2.f); // elevation = 26.565;
std::vector<Vector3> vertices; // array of 12 vertices (x,y,z)
unsigned int index = 0; // indices
float z, xy; // coords
float hAngle1 = -Math::Pi / 2.f - h_angle / 2.f; // start from -126 at 1st row
float hAngle2 = -Math::Pi / 2.f; // start from -90 deg at 2nd row
// the first top vertex at (0,0,r)
vertices[0] = position + Vector3{0,0,radius};
// compute 10 vertices at 1st and 2nd rows
for (int i = 1; i <= 5; ++i)
{
z = radius * Math::Sin(v_angle); // elevation
xy = radius * Math::Cos(v_angle); // length on XY plane
vertices[index++] = position + Vector3{xy * Math::Cos(hAngle1), xy * Math::Sin(hAngle1), z} ;
vertices[index++] = position + Vector3{xy * Math::Cos(hAngle2), xy * Math::Sin(hAngle2), -z};
// next horizontal angles
hAngle1 += h_angle;
hAngle2 += h_angle;
}
// the last bottom vertex at (0, 0, -r)
vertices[11] = position + Vector3{0,0, -radius};
//glLineWidth(thickness);
//glColor4ubv(color.ptr());
//glBindBuffer(GL_ARRAY_BUFFER, vertices.size());
//glVertexPointer(3, GL_FLOAT, sizeof(Vector3), vertices.data());
//glDrawArrays(GL_TRIANGLE_STRIP, 0, vertices.size());
//glColor4fv(baseColor);
std::vector<Vector3> tmpVertices;
std::vector<Vector3> tmpIndices;
std::vector<Vector2> newVs((subdivisions+1) * (subdivisions+2) / 2 * 3);
const Vector3 v1, v2, v3;
Vector3 newV1, newV2, newV3;
int i, j, k;
index = 0; // unsigned int
float a; // lerp alpha
unsigned int i1, i2; // indices
// copy prev arrays
tmpVertices = vertices;
//tmpIndices = indices;
// iterate each triangle of icosahedron
for (i = 0; i < tmpIndices.size(); i++)
{
//v1 = tmpVertices[tmpIndices[i]];
//v2 = tmpVertices[tmpIndices[i+1]];
//v3 = tmpVertices[tmpIndices[i+2]];
}
}
void JGL::J3D::WireframeIcosahedron(const Color4& color, const Vector3& position, float radius, float thickness) {
if (!inJ3D)
Logger::Error("Drawing J3D element before J3D begin.");
// TODO: Revise this once J3ML::Geometry::Icosahedron is implemented.
const float h_angle = J3ML::Math::Pi / 180.f * 72.f; // 72 degree = 360 / 5;
const float v_angle = J3ML::Math::Atan(1.0f / 2.f); // elevation = 26.565;
std::array<Vector3, 12> vertices; // array of 12 vertices (x,y,z)
int index = 0; // indices
float z, xy; // coords
float hAngle1 = -Math::Pi / 2.f - h_angle / 2.f; // start from -126 at 1st row
float hAngle2 = -Math::Pi / 2.f; // start from -90 deg at 2nd row
// the first top vertex at (0,0,r)
vertices[0] = position + Vector3{0,0,radius};
// compute 10 vertices at 1st and 2nd rows
for (int i = 1; i <= 5; ++i)
{
z = radius * Math::Sin(v_angle); // elevation
xy = radius * Math::Cos(v_angle); // length on XY plane
vertices[index++] = position + Vector3{xy * Math::Cos(hAngle1), xy * Math::Sin(hAngle1), z} ;
vertices[index++] = position + Vector3{xy * Math::Cos(hAngle2), xy * Math::Sin(hAngle2), -z};
// next horizontal angles
hAngle1 += h_angle;
hAngle2 += h_angle;
}
// the last bottom vertex at (0, 0, -r)
vertices[11] = position + Vector3{0,0, -radius};
glLineWidth(thickness);
glColor4ubv(color.ptr());
//glBindBuffer(GL_ARRAY_BUFFER, vertices.size());
glVertexPointer(3, GL_FLOAT, sizeof(Vector3), vertices.data());
glDrawArrays(GL_TRIANGLE_STRIP, 0, vertices.size());
glColor4fv(OpenGLState::baseColor);
}
void JGL::J3D::BatchWireframeAABB(const Color4& color, const AABB* boxes, const size_t& box_count, float thickness) {
glColor4ubv(color.ptr());
glLineWidth(thickness);
glBindBuffer(GL_ARRAY_BUFFER, ShapeCache::cube_vertex_data->GetHandle());
glVertexPointer(3, GL_FLOAT, sizeof(Vector3), nullptr);
for (size_t i = 0; i < box_count; i++) {
Vector3 delta = (boxes[i].maxPoint - boxes[i].minPoint) / 2;
Vector3 center = boxes[i].Centroid();
glPushMatrix();
glTranslatef(center.x, center.y, center.z);
glScalef(delta.x, delta.y, delta.z);
glDrawArrays(GL_LINES, 0, ShapeCache::cube_vertex_data->GetLength());
glPopMatrix();
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
glColor4fv(OpenGLState::baseColor);
}
void JGL::J3D::WireframeAABB(const Color4& color, const Vector3& pos, const Vector3& radii, float thickness) {
AABB aabb = {Vector3(pos.x - radii.x, pos.y - radii.y, pos.z - radii.z), Vector3(pos.x + radii.x, pos.y + radii.y, pos.z + radii.z)};
BatchWireframeAABB(color, &aabb, 1, thickness);
}
void JGL::J3D::BatchFillAABB(const Color4& color, const AABB* boxes, const size_t& box_count) {
if (!inJ3D)
Logger::Error("Drawing J3D element before J3D begin.");
glColor4ubv(color.ptr());
glBindBuffer(GL_ARRAY_BUFFER, ShapeCache::cube_vertex_data->GetHandle());
glVertexPointer(3, GL_FLOAT, sizeof(Vector3), nullptr);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ShapeCache::cube_index_data->GetHandle());
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
bool using_lights = false;
if (UsingLighting()) {
using_lights = true;
glEnableClientState(GL_NORMAL_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER, ShapeCache::cube_normal_data->GetHandle());
glNormalPointer(GL_FLOAT, sizeof(float), nullptr);
}
for (size_t i = 0; i < box_count; i++) {
Vector3 delta = (boxes[i].maxPoint - boxes[i].minPoint) / 2;
Vector3 center = boxes[i].Centroid();
glPushMatrix();
glTranslatef(center.x, center.y, center.z);
glScalef(delta.x, delta.y, delta.z);
if (using_lights)
SelectLights(center);
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, nullptr);
if (using_lights)
ResetLights();
glPopMatrix();
}
glColor4fv(OpenGLState::oldColor);
if (using_lights)
glDisableClientState(GL_NORMAL_ARRAY);
glDisable(GL_CULL_FACE);
}
void JGL::J3D::FillAABB(const Color4& color, const Vector3& pos, const Vector3& radii) {
AABB box = {pos - radii, pos + radii};
BatchFillAABB(color, &box, 1);
}
void JGL::J3D::FillAABB(const Color4& color, const AABB& aabb) {
BatchFillAABB(color, &aabb, 1);
}
void JGL::J3D::FillSphere(const Color4& color, const Sphere& sphere, unsigned int sectors, unsigned int stacks) {
BatchFillSphere(color, &sphere, 1, sectors, stacks);
}
void JGL::J3D::BatchFillSphere(const Color4& color, const Sphere* spheres, const size_t& sphere_count, unsigned int sectors, unsigned int stacks) {
if (!inJ3D)
Logger::Error("Drawing J3D element before J3D begin.");
float r = 1;
std::vector<Vector3> vertices((sectors + 1) * (stacks + 1));
std::vector<unsigned int> indices; indices.reserve(sectors * stacks * 6);
float two_pi = 2 * J3ML::Math::Pi;
int index = 0;
for (int i = 0; i <= sectors; i++) {
float lat = J3ML::Math::Pi * (-0.5 + (float) i / sectors);
float z = J3ML::Math::Sin(lat);
float zr = J3ML::Math::Cos(lat);
for (int j = 0; j <= stacks; j++) {
float lng = two_pi * (float) (j - 1) / stacks;
float x = J3ML::Math::Cos(lng);
float y = J3ML::Math::Sin(lng);
float pos_x = r * x * zr;
float pos_y = r * y * zr;
float pos_z = r * z;
vertices[index++] = Vector3(pos_x, pos_y, pos_z);
}
}
for (int i = 0; i < sectors; i++) {
for (int j = 0; j < stacks; j++) {
int first_index = i * (stacks + 1) + j;
int second_index = first_index + stacks + 1;
indices.push_back(first_index);
indices.push_back(second_index);
indices.push_back(first_index + 1);
indices.push_back(second_index);
indices.push_back(second_index + 1);
indices.push_back(first_index + 1);
}
}
VRamList vertex_data(vertices.data(), vertices.size());
VRamList index_data(indices.data(), indices.size());
glColor4ubv(color.ptr());
glBindBuffer(GL_ARRAY_BUFFER, vertex_data.GetHandle());
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_data.GetHandle());
glVertexPointer(3, GL_FLOAT, sizeof(Vector3), nullptr);
for (size_t i = 0; i < sphere_count; i++) {
const Vector3 position = spheres[i].Position;
glPushMatrix();
glTranslatef(position.x, position.y, position.z);
glScalef(spheres[i].Radius, spheres[i].Radius, spheres[i].Radius);
glDrawElements(GL_TRIANGLES, index_data.GetLength(), GL_UNSIGNED_INT, nullptr);
glPopMatrix();
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glColor4fv(OpenGLState::oldColor);
}
void JGL::J3D::FillSphere(const Color4& color, const Vector3& position, float radius, unsigned int sectors, unsigned int stacks) {
Sphere sphere = {position, radius};
BatchFillSphere(color, &sphere, 1, sectors, stacks);
}
void JGL::J3D::WireframeAABB(const Color4& color, const AABB& aabb, float thickness) {
BatchWireframeAABB(color, &aabb, 1, thickness);
}
// TODO Make it work the same as AABB batching. I couldn't get rotation to do anything more than twitch in place :(.
void JGL::J3D::BatchWireframeOBB(const Color4& color, const OBB* boxes, const size_t& box_count, float thickness) {
std::array<Vector3, 8> corner_points;
std::array<GLuint, 24> indices =
{
0, 1, 1, 2, 2, 3, 3, 0,
4, 5, 5, 6, 6, 7, 7, 4,
0, 4, 1, 5, 2, 6, 3, 7
};
glLineWidth(thickness);
glColor4ubv(color.ptr());
glVertexPointer(3, GL_FLOAT, sizeof(Vector3), corner_points.data());
for (size_t i = 0; i < box_count; i++) {
boxes[i].GetCornerPoints(corner_points.data());
glPushMatrix();
glTranslatef(boxes[i].pos.x, boxes[i].pos.y, boxes[i].pos.z);
glDrawElements(GL_LINES, indices.size(), GL_UNSIGNED_INT, indices.data());
glPopMatrix();
}
glColor4fv(OpenGLState::oldColor);
}
void JGL::J3D::WireframeOBB(const Color4& color, const OBB& obb, float thickness) {
BatchWireframeOBB(color, &obb, 1, thickness);
}
/*
void JGL::J3D::WireframeOBB(const Color4& color, const Vector3& position, const Vector3& radii, const Matrix3x3& orientation, float thickness) {
WireframeOBB(color, OBB(position, radii, orientation. * Vector3::UnitX, rotation * Vector3::UnitY, rotation * Vector3::UnitZ), thickness);
}
*/
void JGL::J3D::DrawCubicBezierCurve(const Color4& color, const Vector3& controlA, const Vector3& pointA,
const Vector3& pointB, const Vector3& controlB, int subdivisions, float thickness) {
Vector3 last = controlA;
const Vector3& first = controlB;
for (int i = 0; i < subdivisions; ++i)
{
float alpha = (float) i / (float) subdivisions;
Vector3 step = J3ML::Algorithm::Bezier(alpha, controlA, pointA, pointB, controlB);
DrawLine(color, last, step, thickness);
last = step;
}
// Have to manually draw the last segment of the curve.
DrawLine(color, last, first, thickness);
}
void JGL::J3D::DrawVertexArray(const Color4& color, const VertexArray& vertex_array, const Vector3& position) {
if (!inJ3D)
Logger::Error("Drawing J3D element before J3D begin.");
glColor4ubv(color.ptr());
glEnableClientState(GL_VERTEX_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER, vertex_array.GetVertices()->GetHandle());
glVertexPointer(3, GL_FLOAT, 0, nullptr);
glPushMatrix();
glTranslatef(position.x, position.y, position.z);
//glScalef(1,1,1);
glDrawArrays(GL_TRIANGLES, 0, vertex_array.GetVertices()->GetLength());
glPopMatrix();
//glDrawElements(GL_LINES, vertex_array.GetIndices()->GetLength(), GL_UNSIGNED_INT, nullptr);
//std::cout << vertex_array.GetVertices()->GetLength() << std::endl;
glColor4fv(OpenGLState::oldColor);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}

View File

@@ -1,4 +1,5 @@
#include <JGL/JGL.h>
#include "internals/internals.h"
#if __linux__
@@ -157,12 +158,12 @@ namespace JGL {
glTexCoordPointer(2, GL_FLOAT, sizeof(Vector2), texcoords.data());
glDrawArrays(GL_TRIANGLES, 0, (int) vertices.size() * 6);
glBindTexture(GL_TEXTURE_2D, 0);
glColor4f(1, 1, 1, 1);
glColor4fv(OpenGLState::oldColor);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisable(GL_TEXTURE_2D);
}
void J3D::DrawString(const Color4& color, const std::string& text, const Vector3& pos, float scale, u32 size, const Font& font, const EulerAngle& angle, bool draw_back_face) {
void J3D::DrawString(const Color4& color, const std::string& text, const Vector3& pos, float scale, u32 size, const Font& font, const EulerAngleXYZ& angle, bool draw_back_face) {
// TODO: Determine the proper scale factor mathematically
// This number was arrived at holistically.
scale = scale * 0.002f;
@@ -243,7 +244,7 @@ namespace JGL {
glDisable(GL_CULL_FACE);
glBindTexture(GL_TEXTURE_2D, 0);
glColor4f(1, 1, 1, 1);
glColor4fv(OpenGLState::oldColor);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisable(GL_TEXTURE_2D);
glPopMatrix();

View File

@@ -0,0 +1,119 @@
#include "internals.h"
#include <J3ML/LinearAlgebra/Vector4.hpp>
#include <JGL/types/Light.h>
// TODO handle the case that a required light is in the list of optional lights.
void JGL::J3D::SelectLights(const Vector3& position) {
std::array<const LightBase*, 8> result = required_lights;
unsigned int required_light_count = 0;
for (const auto& i : result)
if (i) required_light_count++;
// If there is optional lights.
if (!optional_lights.empty()) {
// The number of lights we need to solve for
unsigned int remaining_lights = 8 - required_light_count;
std::vector<std::pair<float, const LightBase*>> light_influence;
for (const auto* light: optional_lights)
light_influence.emplace_back(light->GetAttenuationAtPosition(position), light);
// Sort by biggest influence.
std::sort(light_influence.begin(), light_influence.end(),
[](const auto &a, const auto &b) { return a.first > b.first; });
// Add in optional lights.
for (unsigned int i = 0; i < remaining_lights && i < light_influence.size(); i++)
result[required_light_count++] = light_influence[i].second;
}
glEnable(GL_LIGHTING);
for (unsigned int i = 0; i < result.size(); i++) {
// Terminate early if we have less than 8 lights.
if (!result[i])
break;
Vector4 ambient = { result[i]->GetAmbient().RN(), result[i]->GetAmbient().GN(), result[i]->GetAmbient().BN(), result[i]->GetAmbient().AN() };
Vector4 diffuse = { result[i]->GetDiffuse().RN(), result[i]->GetDiffuse().GN(), result[i]->GetDiffuse().BN(), result[i]->GetDiffuse().AN() };
Vector4 specular = { result[i]->GetSpecular().RN(), result[i]->GetSpecular().GN(), result[i]->GetSpecular().BN(), result[i]->GetSpecular().AN() };
GLenum current_light = GL_LIGHT0 + i;
glEnable(GL_LIGHT0 + i);
glLightfv(current_light, GL_POSITION, result[i]->GetPosition().ptr());
// How the light will affect different materials.
glLightfv(current_light, GL_AMBIENT, ambient.ptr());
glLightfv(current_light, GL_DIFFUSE, diffuse.ptr());
glLightfv(current_light, GL_SPECULAR, specular.ptr());
// How far the light goes.
glLightf(current_light, GL_CONSTANT_ATTENUATION, result[i]->GetConstantAttenuation());
glLightf(current_light, GL_LINEAR_ATTENUATION, result[i]->GetLinearAttenuation());
glLightf(current_light, GL_QUADRATIC_ATTENUATION, result[i]->GetQuadraticAttenuation());
}
}
void JGL::J3D::ResetLights() {
Vector4 position = {0, 0, 1, 0};
Vector4 ambient = {0, 0, 0, 1};
Vector4 diffuse_light0 = {1,1,1,1};
Vector4 diffuse = {0,0,0,1};
Vector4 specular_light0 = {1,1,1,1};
Vector4 specular = {0,0,0,1};
Vector3 spot_direction = {0.0, 0.0, -1.0};
for (unsigned int i = 0; i < 8; i++) {
GLenum current_light = GL_LIGHT0 + i;
glLightfv(current_light, GL_POSITION, position.ptr());
glLightfv(current_light, GL_AMBIENT, ambient.ptr());
glLightf(current_light, GL_CONSTANT_ATTENUATION, 1);
glLightf(current_light, GL_LINEAR_ATTENUATION, 0);
glLightf(current_light, GL_QUADRATIC_ATTENUATION, 0);
glLightf(current_light, GL_SPOT_CUTOFF, 180.0);
glLightfv(current_light, GL_SPOT_DIRECTION, spot_direction.ptr());
glLightf(current_light, GL_SPOT_EXPONENT, 0.0);
glLightfv(current_light, GL_DIFFUSE, (i == 0 ? diffuse_light0.ptr() : diffuse.ptr()));
glLightfv(current_light, GL_SPECULAR, (i == 0 ? specular_light0.ptr() : specular.ptr()));
glDisable(current_light);
}
glDisable(GL_LIGHTING);
}
bool JGL::J3D::UsingLighting() {
if (!optional_lights.empty())
return true;
for (unsigned int i = 0; i < required_lights.size(); i++)
if (required_lights[i])
return true;
return false;
}
std::vector<Vector3> JGL::TriangleMeshVertexNormals(const Vector3* vertices, const size_t& vertex_count, const unsigned int* indices, const size_t& index_count) {
std::vector<Vector3> normals(vertex_count, Vector3(0, 0, 0));
for (size_t i = 0; i < index_count; i += 3) {
GLuint i1 = indices[i];
GLuint i2 = indices[i + 1];
GLuint i3 = indices[i + 2];
Vector3 v1 = vertices[i1];
Vector3 v2 = vertices[i2];
Vector3 v3 = vertices[i3];
Vector3 edge1 = v2 - v1;
Vector3 edge2 = v3 - v1;
Vector3 faceNormal = Vector3::Cross(edge1, edge2);
normals[i1] += faceNormal;
normals[i2] += faceNormal;
normals[i3] += faceNormal;
}
for (auto& normal : normals)
normal = normal.Normalized();
return normals;
}

View File

@@ -0,0 +1,57 @@
#pragma once
#include <glad/glad.h>
#include <array>
#include <vector>
#include <J3ML/LinearAlgebra/Vector2.hpp>
#include <JGL/types/RenderTarget.h>
#include <JGL/types/Light.h>
#include <JGL/logger/logger.h>
namespace JGL {
inline constexpr std::array<const LightBase*, 8> empty_light_array = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr };
inline bool inJ2D = false;
inline bool inJ3D = false;
std::vector<Vector3> TriangleMeshVertexNormals(const Vector3* vertices, const size_t& vertex_count, const unsigned int* indices, const size_t& index_count);
}
namespace JGL::OpenGLState {
inline JGL::RenderTarget* render_target = nullptr;
inline Vector2 window_size;
inline GLfloat oldColor[4] = {0, 0, 0, 1};
inline GLfloat baseColor[4] = {1, 1, 1, 1};
inline GLuint current_fbo = 0;
inline GLint viewport[4] = {0, 0, 0, 0};
inline bool wasTexture2DEnabled = false;
inline bool wasTextureCoordArrayEnabled = false;
inline bool wasNormalArrayEnabled = false;
inline bool wasDepthTestEnabled = false;
inline bool wasVertexArraysEnabled = false;
inline bool wasCullFaceEnabled = false;
inline bool wasBlendEnabled = false;
inline bool wasColorArrayEnabled = false;
inline GLint activeTextureUnit = 0;
}
namespace JGL::J2D {
inline std::array<const LightBase*, 8> required_lights;
inline std::vector<const LightBase*> light_array;
}
namespace JGL::J3D {
// List of lights required for each objects in the scene. up-to 8. For example, the sun. Or a flash-light.
inline std::array<const LightBase*, 8> required_lights;
// List of all lights in the scene.
inline std::vector<const LightBase*> optional_lights;
inline float far_plane = 0;
inline float fov = 0;
// Enables lighting and selects the correct lights to use.
void SelectLights(const Vector3& position);
// Resets the GL lights to default and disables them. Then, disables lighting.
void ResetLights();
[[nodiscard]] bool UsingLighting();
}

View File

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

View File

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

View File

@@ -0,0 +1,31 @@
#include <JGL/types/Material.h>
#include <J3ML/LinearAlgebra/Vector4.hpp>
JGL::Material::Material(const Color4& ambient_reflection, const Color4& diffuse_reflection, const Color4& specular_reflection, const Color4& light_emission, float& shine) {
ambient_ref = ambient_reflection;
diffuse_ref = diffuse_reflection;
specular_ref = specular_reflection;
emission = light_emission;
shininess = shine;
}
void JGL::Material::SetActiveMaterial(const Material& material) {
Vector4 ambient = { material.ambient_ref.RN(), material.ambient_ref.BN(), material.ambient_ref.GN(), material.ambient_ref.AN() };
Vector4 diffuse = { material.diffuse_ref.RN(), material.diffuse_ref.BN(), material.diffuse_ref.GN(), material.diffuse_ref.AN() };
Vector4 specular = { material.specular_ref.RN(), material.specular_ref.BN(), material.specular_ref.GN(), material.specular_ref.AN() };
Vector4 emission = { material.emission.RN(), material.emission.BN(), material.emission.GN(), material.emission.AN() };
// Determine which faces we're going to apply this material to.
GLenum face = GL_FRONT;
if (glIsEnabled(GL_CULL_FACE)) {
GLint current; glGetIntegerv(GL_CULL_FACE_MODE, &current);
if (current == GL_FRONT)
face = GL_BACK;
}
glMaterialfv(face, GL_AMBIENT, ambient.ptr());
glMaterialfv(face, GL_DIFFUSE, diffuse.ptr());
glMaterialfv(face, GL_SPECULAR, specular.ptr());
glMaterialfv(face, GL_EMISSION, emission.ptr());
glMaterialf(face, GL_SHININESS, material.shininess);
}

View File

@@ -3,12 +3,12 @@
#include <JGL/logger/logger.h>
#include <stdexcept>
const JGL::Texture* JGL::RenderTarget::GetJGLTexture() const {
const JGL::Texture* JGL::RenderTarget::GetTexture() const {
return texture;
}
GLuint JGL::RenderTarget::GetGLTextureHandle() const {
return texture->GetGLTextureHandle();
GLuint JGL::RenderTarget::GetTextureHandle() const {
return texture->GetHandle();
}
GLuint JGL::RenderTarget::GetGLFramebufferObjectHandle() const {
@@ -30,7 +30,7 @@ void JGL::RenderTarget::SetActiveGLRenderTarget(const RenderTarget& render_targe
glViewport(0,0, render_target.GetDimensions().x, render_target.GetDimensions().y);
}
Vector2 JGL::RenderTarget::GetDimensions() const {
Vector2i JGL::RenderTarget::GetDimensions() const {
return size;
}
@@ -56,6 +56,7 @@ JGL::RenderTarget::RenderTarget(const JGL::Texture* texture, const Color4& clear
if (texture->GetDimensions().x < 1 || texture->GetDimensions().y < 1)
Logger::Fatal("Creating a render target where the color attachment is empty?");
this->size = { static_cast<int>(texture->GetDimensions().x), static_cast<int>(texture->GetDimensions().y) };
GLuint current_fbo = GetActiveGLFramebufferHandle();
GLint viewport[4] = {0, 0, 0, 0};
glGetIntegerv(GL_VIEWPORT, viewport);
@@ -63,7 +64,7 @@ JGL::RenderTarget::RenderTarget(const JGL::Texture* texture, const Color4& clear
glGenFramebuffers(1, &framebuffer_object);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_object);
glViewport(0,0, size.x, size.y);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture->GetGLTextureHandle(), 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture->GetHandle(), 0);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
throw std::runtime_error("A new framebuffer could not be allocated.");
@@ -71,12 +72,11 @@ JGL::RenderTarget::RenderTarget(const JGL::Texture* texture, const Color4& clear
glBindFramebuffer(GL_FRAMEBUFFER, current_fbo);
glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
this->clear_color = clear_color;
this->size = texture->GetDimensions();
this->texture = texture;
texture_created_by_us = false;
}
JGL::RenderTarget::RenderTarget(const Vector2& size, const Color4& clear_color, bool use_depth, MSAA_SAMPLE_RATE sample_rate) {
JGL::RenderTarget::RenderTarget(const Vector2i& size, const Color4& clear_color, bool use_depth, MSAA_SAMPLE_RATE sample_rate) {
if (size.x < 1 || size.y < 1)
Logger::Fatal("Creating a render target where the color attachment is empty?");
@@ -84,22 +84,18 @@ JGL::RenderTarget::RenderTarget(const Vector2& size, const Color4& clear_color,
GLint viewport[4] = {0, 0, 0, 0};
glGetIntegerv(GL_VIEWPORT, viewport);
//Textures behave strangely if they're not square aaaaaaaaaaaaa.
unsigned int biggest;
if (size.x >= size.y)
biggest = size.x;
else biggest = size.y;
texture = new Texture(Vector2(biggest, biggest));
texture = new Texture(size);
glGenFramebuffers(1, &framebuffer_object);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_object);
glViewport(0,0, size.x, size.y);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture->GetGLTextureHandle(), 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture->GetHandle(), 0);
if (use_depth) {
GLuint depthBuffer;
glGenRenderbuffers(1, &depthBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, depthBuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, biggest, biggest);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, size.x, size.y);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthBuffer);
glClear(GL_DEPTH_BUFFER_BIT);
using_depth = true;
@@ -124,7 +120,7 @@ JGL::RenderTarget::RenderTarget(const Vector2& size, const Color4& clear_color,
SetMSAAEnabled(sample_rate);
}
std::vector<GLfloat> JGL::RenderTarget::GetData() const {
std::vector<GLfloat> JGL::RenderTarget::GetPixels() const {
std::vector<GLfloat> data(GetDimensions().x * GetDimensions().y * 4);
GLuint current_fbo = GetActiveGLFramebufferHandle();
@@ -132,12 +128,11 @@ std::vector<GLfloat> JGL::RenderTarget::GetData() const {
glReadPixels(0, 0, GetDimensions().x, GetDimensions().y, GL_RGBA, GL_FLOAT, data.data());
glBindFramebuffer(GL_FRAMEBUFFER, current_fbo);
return data;
}
void JGL::RenderTarget::Resize(const Vector2& new_size) {
void JGL::RenderTarget::Resize(const Vector2i& new_size) {
if (!texture_created_by_us)
Logger::Fatal("Resizing a texture that already existed?");
Logger::Error("Resizing a Render Target that does not own it's texture?");
GLuint current_fbo = GetActiveGLFramebufferHandle();
GLfloat old_clear_color[4];
@@ -145,8 +140,8 @@ void JGL::RenderTarget::Resize(const Vector2& new_size) {
glGetIntegerv(GL_VIEWPORT, old_viewport);
glGetFloatv(GL_COLOR_CLEAR_VALUE, old_clear_color);
/* If what was previously not part of the renderable area is big enough to
* just set the new size without reading data back */
/* If what was previously not part of the renderable area is big enough
* to just set the new size. */
if (new_size.x <= texture->GetDimensions().x && new_size.y <= texture->GetDimensions().y) {
size = new_size;
@@ -172,18 +167,12 @@ void JGL::RenderTarget::Resize(const Vector2& new_size) {
// Erase it.
delete texture;
unsigned int biggest;
if (new_size.x >= new_size.y)
biggest = new_size.x;
else
biggest = new_size.y;
auto cc = GetClearColor();
glClearColor(cc.RedChannelNormalized(), cc.GreenChannelNormalized(), cc.BlueChannelNormalized(), cc.AlphaChannelNormalized());
glViewport(0,0, size.x, size.y);
texture = new Texture(Vector2((float) biggest, (float) biggest));
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture->GetGLTextureHandle(), 0);
texture = new Texture(new_size);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture->GetHandle(), 0);
glBindFramebuffer(GL_FRAMEBUFFER, current_fbo);
glClearColor(old_clear_color[0], old_clear_color[1], old_clear_color[2], old_clear_color[3]);
@@ -204,7 +193,7 @@ JGL::RenderTarget::~RenderTarget() {
delete texture;
}
bool JGL::RenderTarget::TextureCreatedByRenderTarget() const {
bool JGL::RenderTarget::OwnsTexture() const {
return texture_created_by_us;
}
@@ -216,19 +205,20 @@ bool JGL::RenderTarget::MSAAEnabled() const {
return msaa_sample_rate != MSAA_SAMPLE_RATE::MSAA_NONE;
}
void JGL::RenderTarget::SetMSAAEnabled(JGL::MSAA_SAMPLE_RATE sample_rate) {
bool JGL::RenderTarget::SetMSAAEnabled(JGL::MSAA_SAMPLE_RATE sample_rate) {
// If we'd be setting the same sample_rate we already have.
if (sample_rate == msaa_sample_rate)
return;
return false;
// If we'd be rendering onto a texture and not a plain render target we don't want this.
if (!TextureCreatedByRenderTarget())
return;
if (!OwnsTexture())
return false;
// Remove it if they request no msaa or if what they requested is different than what they already have.
if (sample_rate == MSAA_SAMPLE_RATE::MSAA_NONE || msaa_sample_rate != MSAA_SAMPLE_RATE::MSAA_NONE) {
if(using_depth)
glDeleteRenderbuffers(1, &msaa_depth_buffer);
glDeleteRenderbuffers(1, &msaa_render_buffer);
glDeleteFramebuffers(1, &msaa_framebuffer_object);
@@ -239,7 +229,7 @@ void JGL::RenderTarget::SetMSAAEnabled(JGL::MSAA_SAMPLE_RATE sample_rate) {
// Only return here if they specifically requested no MSAA. else continue to change mode.
if (sample_rate == MSAA_SAMPLE_RATE::MSAA_NONE)
return;
return true;
}
GLuint current_fbo = GetActiveGLFramebufferHandle();
@@ -271,11 +261,11 @@ void JGL::RenderTarget::SetMSAAEnabled(JGL::MSAA_SAMPLE_RATE sample_rate) {
if (failure)
SetMSAAEnabled(MSAA_SAMPLE_RATE::MSAA_NONE);
return failure;
}
void JGL::RenderTarget::Blit() const {
if (MSAAEnabled() && TextureCreatedByRenderTarget()) {
void JGL::RenderTarget::MSAABlit() const {
if (MSAAEnabled() && OwnsTexture()) {
// Save the GL state.
GLuint current_fbo = GetActiveGLFramebufferHandle();
GLint current_draw_fbo = 0;
@@ -295,23 +285,22 @@ void JGL::RenderTarget::Blit() const {
}
// Fixes using render targets on a texture that has mipmaps.
if (GetJGLTexture()->GetFilteringMode() == TextureFilteringMode::MIPMAP_NEAREST
|| GetJGLTexture()->GetFilteringMode() == TextureFilteringMode::MIPMAP_BILINEAR ||
GetJGLTexture()->GetFilteringMode() == TextureFilteringMode::MIPMAP_TRILINEAR) {
if (GetTexture()->GetFilteringMode() == FilteringMode::MIPMAP_NEAREST
|| GetTexture()->GetFilteringMode() == FilteringMode::MIPMAP_BILINEAR ||
GetTexture()->GetFilteringMode() == FilteringMode::MIPMAP_TRILINEAR) {
GLint current_texture = 0;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &current_texture);
glBindTexture(GL_TEXTURE_2D, GetJGLTexture()->GetGLTextureHandle());
glBindTexture(GL_TEXTURE_2D, GetTexture()->GetHandle());
glGenerateMipmap(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, current_texture);
}
}
void JGL::RenderTarget::Blit(const JGL::RenderTarget& source, JGL::RenderTarget* destination) {
//TODO allow blitting onto an FBO that is bigger but not smaller.
if (source.size != destination->size)
Logger::Warning("Blitting a render target but they're not the same size?");
void JGL::RenderTarget::Blit(const JGL::RenderTarget& source, JGL::RenderTarget* destination, const Vector2i& position) {
if (source.GetDimensions().x > destination->GetDimensions().x || source.GetDimensions().y > destination->GetDimensions().y)
Logger::Warning("Blitting a Render Target onto another Render Target but the destination Render Target is too small?");
// Save the GL state.
GLuint current_fbo = GetActiveGLFramebufferHandle();
@@ -323,7 +312,7 @@ void JGL::RenderTarget::Blit(const JGL::RenderTarget& source, JGL::RenderTarget*
// Draw the contents of one into the other.
glBindFramebuffer(GL_READ_FRAMEBUFFER, source.framebuffer_object);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, destination->framebuffer_object);
glBlitFramebuffer(0, 0, source.size.x, source.size.y, 0, 0, source.size.x, source.size.y, GL_COLOR_BUFFER_BIT, GL_NEAREST);
glBlitFramebuffer(0, 0, source.size.x, source.size.y, position.x, position.y, position.x + source.size.x, position.y + source.size.y, GL_COLOR_BUFFER_BIT, GL_NEAREST);
// Put the GL state back.
glBindFramebuffer(GL_READ_FRAMEBUFFER, current_read_fbo);
@@ -331,12 +320,20 @@ void JGL::RenderTarget::Blit(const JGL::RenderTarget& source, JGL::RenderTarget*
glBindFramebuffer(GL_FRAMEBUFFER, current_fbo);
}
void JGL::RenderTarget::Blit(const Color4& color, const Vector2& position, JGL::RenderTarget* destination) {
void JGL::RenderTarget::Blit(const Texture* source, RenderTarget* destination, const Vector2i& position) {
auto temp = new RenderTarget(source);
Blit(*temp, destination);
delete temp;
}
// To avoid repeatedly allocating and deallocating.
JGL::RenderTarget* pixel = nullptr;
void JGL::RenderTarget::Blit(const Color4& color, const Vector2i& position, JGL::RenderTarget* destination) {
if (position.x > destination->size.x || position.y > destination->size.y)
Logger::Warning("Blitting outside of the renderable area of the destination.");
Texture texture(Vector2(1,1));
RenderTarget source(&texture,color);
if (pixel == nullptr)
pixel = new RenderTarget({1,1});
GLint current_draw_fbo = 0;
GLint current_read_fbo = 0;
@@ -348,13 +345,14 @@ void JGL::RenderTarget::Blit(const Color4& color, const Vector2& position, JGL::
glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &current_read_fbo);
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &current_draw_fbo);
SetActiveGLRenderTarget(source);
SetActiveGLRenderTarget(*pixel);
glClearColor(color.RedChannelNormalized(), color.GreenChannelNormalized(), color.BlueChannelNormalized(), color.AlphaChannelNormalized());
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glClear(GL_COLOR_BUFFER_BIT);
// Invert so it's relative to the top left corner.
int target_y = destination->size.y - position.y - 1;
glBindFramebuffer(GL_READ_FRAMEBUFFER, source.framebuffer_object);
glBindFramebuffer(GL_READ_FRAMEBUFFER, pixel->framebuffer_object);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, destination->framebuffer_object);
glBlitFramebuffer(0, 0, 1, 1, position.x, target_y, position.x + 1, target_y + 1, GL_COLOR_BUFFER_BIT, GL_NEAREST);

View File

@@ -1,192 +1,274 @@
#include <JGL/types/Texture.h>
#include <iostream>
#include <JGL/types/RenderTarget.h>
#include <JGL/logger/logger.h>
#include <glad/glad.h>
#include <fstream>
using namespace ReImage;
#define STB_IMAGE_IMPLEMENTATION
#include "internals/stb_image.h"
namespace JGL
{
Texture::Texture(const std::string& file, TextureFilteringMode filtering_mode, TextureWrappingMode wrapping_mode, const TextureFlag& flags)
{
auto* t = new ReImage::Image(file, flags);
GLuint previous_texture;
glGetIntegerv(GL_TEXTURE_BINDING_2D, (GLint*) &previous_texture);
using namespace JGL;
load(t, {(float) t->getWidth(), (float) t->getHeight()}, t->getTextureFormat(), filtering_mode, wrapping_mode);
texture_flags = flags;
Texture::Texture(const std::filesystem::path& file, FilteringMode filtering_mode, WrappingMode wrapping_mode, bool invert_y) :
invert_y(invert_y), filtering_mode(filtering_mode), wrapping_mode(wrapping_mode) {
std::vector<unsigned char> pixels{};
delete t;
std::ifstream ifStream(file, std::ios::in | std::ios::binary);
if (!ifStream.is_open())
Logger::Fatal("Trying to load a texture from: " + file.string() + "but we couldn't read the file.");
unsigned char bmpFileHeader[14];
ifStream.read(reinterpret_cast<char*>(bmpFileHeader), 14);
if (bmpFileHeader[0] == 'B' && bmpFileHeader[1] == 'M')
pixels = bmp(file);
//TODO determine if it's a PNG using the file header instead.
else if (file.string().ends_with(".png"))
pixels = png(file);
ifStream.close();
if (invert_y) {
unsigned int row_size = size.x * 4;;
if (format == ColorFormat::RGB)
row_size = size.x * 3;
std::vector<unsigned char> temp(row_size);
for (unsigned int y = 0; y < size.y / 2; ++y) {
unsigned char *topRow = &pixels[y * row_size];
unsigned char *bottomRow = &pixels[(size.y - y - 1) * row_size];
std::copy(bottomRow, bottomRow + row_size, temp.begin());
std::copy(topRow, topRow + row_size, bottomRow);
std::copy(temp.begin(), temp.end(), topRow);
}
}
Texture::Texture(const Vector2& size) {
GLuint previous_texture;
glGetIntegerv(GL_TEXTURE_BINDING_2D, (GLint*) &previous_texture);
load(pixels.data());
}
glGenTextures(1, &texture_handle);
glBindTexture(GL_TEXTURE_2D, texture_handle);
//NEAREST
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
//Clamp
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
std::vector<unsigned char> Texture::png(const std::filesystem::path& file) {
std::vector<unsigned char> result{};
int channels;
unsigned char* image_data = stbi_load(file.c_str(), &size.x, &size.y, &channels, 0);
if (!image_data)
Logger::Fatal("Trying to load a texture from: " + file.string() + "but we couldn't read the file.");
if (channels == 4)
format = ColorFormat::RGBA;
else if (channels == 3)
format = ColorFormat::RGB;
if (image_data)
result.assign(image_data, image_data + size.x * size.y * channels);
stbi_image_free(image_data);
return result;
}
std::vector<unsigned char> Texture::bmp(const std::filesystem::path& file) {
std::vector<unsigned char> result{};
std::ifstream bmp_file(file, std::ios::in | std::ios::binary);
unsigned char bmpFileHeader[14];
unsigned char bmpInfoHeader[40];
bmp_file.read(reinterpret_cast<char *>(bmpFileHeader), 14);
bmp_file.read(reinterpret_cast<char *>(bmpInfoHeader), 40);
size.x = bmpInfoHeader[4] + (bmpInfoHeader[5] << 8) + (bmpInfoHeader[6] << 16) + (bmpInfoHeader[7] << 24);
size.y = bmpInfoHeader[8] + (bmpInfoHeader[9] << 8) + (bmpInfoHeader[10] << 16) + (bmpInfoHeader[11] << 24);
int row_padded = (size.x * 3 + 3) & (~3);
result.resize(size.x * size.y * 3);
std::vector<unsigned char> rowData(row_padded);
for (int y = size.y - 1; y >= 0; --y) {
bmp_file.read(reinterpret_cast<char *>(rowData.data()), row_padded);
for (int x = 0; x < size.x; ++x) {
result[(y * size.x + x) * 3 + 2] = rowData[x * 3 + 0];
result[(y * size.x + x) * 3 + 1] = rowData[x * 3 + 1];
result[(y * size.x + x) * 3 + 0] = rowData[x * 3 + 2];
}
}
bmp_file.close();
format = ColorFormat::RGB;
return result;
}
Texture::Texture(const Vector2i& size) : invert_y(true), format(ColorFormat::RGBA), size(size), filtering_mode(FilteringMode::NEAREST), wrapping_mode(WrappingMode::CLAMP_TO_EDGE) {
GLuint previous_texture;
glGetIntegerv(GL_TEXTURE_BINDING_2D, (GLint*) &previous_texture);
glGenTextures(1, &texture_handle);
glBindTexture(GL_TEXTURE_2D, texture_handle);
//NEAREST
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
//Clamp
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size.x, size.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glBindTexture(GL_TEXTURE_2D, previous_texture);
}
void Texture::load(const unsigned char* pixels) {
GLuint previous_texture;
glGetIntegerv(GL_TEXTURE_BINDING_2D, (GLint*) &previous_texture);
glGenTextures(1, &texture_handle);
glBindTexture(GL_TEXTURE_2D, texture_handle);
if (format == ColorFormat::RGBA)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size.x, size.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
else if (format == ColorFormat::RGB)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, size.x, size.y, 0, GL_RGB, GL_UNSIGNED_BYTE, pixels);
if (wrapping_mode == WrappingMode::CLAMP_TO_EDGE)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE),
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (int) size.x, (int) size.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
else if (wrapping_mode == WrappingMode::REPEAT)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT),
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
texture_format = TextureFormat::RGBA;
texture_size = size;
texture_filtering_mode = TextureFilteringMode::NEAREST;
texture_wrapping_mode = TextureWrappingMode::CLAMP_TO_EDGE;
// Because in vram it'll be right side up.
texture_flags = TextureFlag::INVERT_Y;
else if (wrapping_mode == WrappingMode::MIRRORED_REPEAT)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT),
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
glBindTexture(GL_TEXTURE_2D, previous_texture);
else if (wrapping_mode == WrappingMode::CLAMP_TO_BORDER)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER),
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
if (filtering_mode == FilteringMode::NEAREST)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST),
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
else if (filtering_mode == FilteringMode::BILINEAR)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR),
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
else if (filtering_mode == FilteringMode::MIPMAP_NEAREST ||
filtering_mode == FilteringMode::MIPMAP_BILINEAR ||
filtering_mode == FilteringMode::MIPMAP_TRILINEAR) {
glGenerateMipmap(GL_TEXTURE_2D);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
if (filtering_mode == FilteringMode::MIPMAP_NEAREST)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST),
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
else if (filtering_mode == FilteringMode::MIPMAP_BILINEAR)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST),
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
else if (filtering_mode == FilteringMode::MIPMAP_TRILINEAR)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR),
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}
glBindTexture(GL_TEXTURE_2D, previous_texture);
}
void Texture::load(Image* software_texture, const Vector2& size, const TextureFormat& format,
TextureFilteringMode filtering_mode, TextureWrappingMode wrapping_mode) {
std::vector<Color4> JGL::Texture::GetPixelData() const {
GLint current_texture;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &current_texture);
GLuint previous_texture;
glGetIntegerv(GL_TEXTURE_BINDING_2D, (GLint*) &previous_texture);
glGenTextures(1, &texture_handle);
glBindTexture(GL_TEXTURE_2D, texture_handle);
if (format == TextureFormat::RGBA)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (int) size.x, (int) size.y, 0, GL_RGBA, GL_UNSIGNED_BYTE,
software_texture->pixel_data.data());
else if (format == TextureFormat::RGB)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, (int) size.x, (int) size.y, 0, GL_RGB, GL_UNSIGNED_BYTE,
software_texture->pixel_data.data());
if (wrapping_mode == TextureWrappingMode::CLAMP_TO_EDGE)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE),
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
else if (wrapping_mode == TextureWrappingMode::REPEAT)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT),
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
else if (wrapping_mode == TextureWrappingMode::MIRRORED_REPEAT)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT),
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
else if (wrapping_mode == TextureWrappingMode::CLAMP_TO_BORDER)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER),
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
if (filtering_mode == TextureFilteringMode::NEAREST)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST),
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
else if (filtering_mode == TextureFilteringMode::BILINEAR)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR),
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
else if (filtering_mode == TextureFilteringMode::MIPMAP_NEAREST ||
filtering_mode == TextureFilteringMode::MIPMAP_BILINEAR ||
filtering_mode == TextureFilteringMode::MIPMAP_TRILINEAR) {
glGenerateMipmap(GL_TEXTURE_2D);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
if (filtering_mode == TextureFilteringMode::MIPMAP_NEAREST)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST),
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
else if (filtering_mode == TextureFilteringMode::MIPMAP_BILINEAR)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST),
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
else if (filtering_mode == TextureFilteringMode::MIPMAP_TRILINEAR)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR),
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}
texture_size = size;
texture_format = format;
texture_filtering_mode = filtering_mode;
glBindTexture(GL_TEXTURE_2D, previous_texture);
}
std::vector<Color4> JGL::Texture::GetPixelData() const {
GLint current_texture;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &current_texture);
std::vector<Color4> result((size_t) (texture_size.x * texture_size.y));
glBindTexture(GL_TEXTURE_2D, texture_handle);
if (texture_format == TextureFormat::RGBA) {
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, result.data());
glBindTexture(GL_TEXTURE_2D, current_texture);
return result;
}
//if RGB
std::vector<Color3> color3((size_t) (texture_size.x * texture_size.y));
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGB, GL_UNSIGNED_BYTE, color3.data());
for (const auto &c: color3)
result.emplace_back(c);
std::vector<Color4> result((size_t) (size.x * size.y));
glBindTexture(GL_TEXTURE_2D, texture_handle);
if (format == ColorFormat::RGBA) {
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, result.data());
glBindTexture(GL_TEXTURE_2D, current_texture);
return result;
}
void Texture::Erase() {
if (texture_handle != 0)
glDeleteTextures(1, &texture_handle);
texture_handle = 0;
//if RGB
std::vector<Color3> color3((size_t) (size.x * size.y));
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGB, GL_UNSIGNED_BYTE, color3.data());
for (const auto &c: color3)
result.emplace_back(c);
glBindTexture(GL_TEXTURE_2D, current_texture);
return result;
}
GLuint Texture::GetHandle() const {
return texture_handle;
}
Vector2i Texture::GetDimensions() const {
return size;
}
bool Texture::Inverted() const {
return invert_y;
}
ColorFormat Texture::GetFormat() const {
return format;
}
FilteringMode Texture::GetFilteringMode() const {
return filtering_mode;
}
WrappingMode Texture::GetWrappingMode() const {
return wrapping_mode;
}
Texture::Texture(const Color4* pixels, const Vector2i& size, const ColorFormat& format, FilteringMode filtering_mode, WrappingMode wrapping_mode) :
size(size), format(format), filtering_mode(filtering_mode), wrapping_mode(wrapping_mode) {
load((unsigned char*) pixels);
}
Texture::Texture(const Color3* pixels, const Vector2i& size, const ColorFormat& format, FilteringMode filtering_mode, WrappingMode wrapping_mode) :
size(size), format(format), filtering_mode(filtering_mode), wrapping_mode(wrapping_mode) {
load((unsigned char*) pixels);
}
Texture::~Texture() {
if (texture_handle != 0)
glDeleteTextures(1, &texture_handle);
texture_handle = 0;
}
Texture::Texture(const Texture& rhs) {
auto* this_texture = new Texture(rhs.GetDimensions());
auto this_render_target = RenderTarget(this_texture);
auto rhs_render_target = RenderTarget(&rhs);
RenderTarget::Blit(rhs_render_target, &this_render_target);
this->texture_handle = this_texture->texture_handle;
this->size = this_texture->size;
this->invert_y = this_texture->invert_y;
this->format = this_texture->format;
this->filtering_mode = this_texture->filtering_mode;
this->wrapping_mode = this_texture->wrapping_mode;
operator delete(this_texture);
}
Texture::Texture(const Texture* textures, const size_t& texture_count) {
int length = 0;
int biggest_y = 0;
for (int i = 0; i < texture_count; i++) {
if (biggest_y < textures[i].size.y)
biggest_y = textures[i].size.y;
length += textures[i].size.x;
}
GLuint Texture::GetGLTextureHandle() const {
return texture_handle;
auto* result = new Texture(Vector2i(length, biggest_y));
auto render_target = RenderTarget(result);
int next_x = 0;
for (int i = 0; i < texture_count; i++) {
RenderTarget::Blit(&textures[i], &render_target, Vector2i(next_x, 0));
next_x += textures[i].GetDimensions().x;
}
Vector2 Texture::GetDimensions() const {
return texture_size;
}
TextureFlag Texture::GetFlags() const {
return texture_flags;
}
TextureFormat Texture::GetFormat() const {
return texture_format;
}
TextureFilteringMode Texture::GetFilteringMode() const {
return texture_filtering_mode;
}
TextureWrappingMode Texture::GetWrappingMode() const {
return texture_wrapping_mode;
}
Texture::Texture(Image* software_texture, const Vector2& size, const TextureFormat& format,
TextureFilteringMode filtering_mode, TextureWrappingMode wrapping_mode) {
load(software_texture, size, format, filtering_mode, wrapping_mode);
}
Texture::~Texture() {
Erase();
}
Texture::Texture(const Texture& rhs) {
auto* this_texture = new Texture(rhs.GetDimensions());
auto this_render_target = RenderTarget(this);
auto rhs_render_target = RenderTarget(&rhs);
RenderTarget::Blit(rhs_render_target, &this_render_target);
this->texture_handle = this_texture->texture_handle;
this->texture_size = this_texture->texture_size;
this->texture_flags = this_texture->texture_flags;
this->texture_format = this_texture->texture_format;
this->texture_filtering_mode = this_texture->texture_filtering_mode;
this->texture_wrapping_mode = this_texture->texture_wrapping_mode;
// Free the memory of "this_texture" without calling the destructor.
// In 99% of cases you wouldn't want this. But in this scenario we do.
operator delete(this_texture);
}
}
}

View File

@@ -170,6 +170,10 @@ std::vector<GLfloat> JGL::VRamList::GetDataF() const {
if (element_array_buffer)
JGL::Logger::Warning("Getting data as GLfloat but the buffer data is GLuint?");
bool vertex_array_enabled = glIsEnabled(GL_VERTEX_ARRAY);
if (!vertex_array_enabled)
glEnableClientState(GL_VERTEX_ARRAY);
GLint current_buffer = 0;
glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &current_buffer);
glBindBuffer(GL_ARRAY_BUFFER, list_handle);
@@ -184,6 +188,8 @@ std::vector<GLfloat> JGL::VRamList::GetDataF() const {
glUnmapBuffer(GL_ARRAY_BUFFER);
glBindBuffer(GL_ARRAY_BUFFER, current_buffer);
if (!vertex_array_enabled)
glDisableClientState(GL_VERTEX_ARRAY);
return data;
}
@@ -289,7 +295,6 @@ JGL::VRamList::VRamList(const JGL::VRamList& rhs) {
if (rhs.element_array_buffer) {
auto data_array = rhs.GetDataUI();
this->load(data_array.data(), data_array.size());
return;
}
auto data_array = rhs.GetDataF();

View File

@@ -1,20 +1,21 @@
#include <JGL/types/VertexArray.h>
#include <JGL/logger/logger.h>
using namespace JGL;
VRamList VertexArray::GetVertices() const {
const VRamList* VertexArray::GetVertices() const {
return vertices;
}
VRamList VertexArray::GetIndices() const {
const VRamList* VertexArray::GetIndices() const {
return indices;
}
JGL::VRamList VertexArray::GetNormals() const {
const VRamList* VertexArray::GetNormals() const {
return normals;
}
VRamList VertexArray::GetTextureCoordinates() const {
const VRamList* VertexArray::GetTextureCoordinates() const {
return texture_coordinates;
}
@@ -144,46 +145,33 @@ VertexArray::VertexArray(const Vector3* vertex_positions, const long& vp_length,
const Normal* vertex_normals, const long& vn_length, const TextureCoordinate* texture_coordinates, const long& vt_length) {
// TODO decimator. This is a total waste of memory as it sits.
vertices = VRamList(vertex_positions, vp_length);
this->vertices = new VRamList(vertex_positions, vp_length);
local_vertices.resize(vp_length);
memcpy(local_vertices.data(), vertex_positions, sizeof(Vector3) * vp_length);
if (vertex_indices && vi_length) {
indices = VRamList(vertex_indices, vi_length);
indices = new VRamList(vertex_indices, vi_length);
local_indices.resize(vi_length);
memcpy(local_indices.data(), vertex_indices, sizeof(unsigned int) * vi_length);
}
if (vertex_normals && vn_length) {
normals = VRamList(vertex_normals, vn_length);
normals = new VRamList(vertex_normals, vn_length);
local_normals.resize(vn_length);
memcpy(local_normals.data(), vertex_indices, sizeof(Normal) * vn_length);
}
if (texture_coordinates && vt_length) {
this->texture_coordinates = VRamList(texture_coordinates, vt_length);
local_normals.resize(vt_length);
this->texture_coordinates = new VRamList(texture_coordinates, vt_length);
local_texture_coordinates.resize(vt_length);
memcpy(local_texture_coordinates.data(), texture_coordinates, sizeof(TextureCoordinate) * vt_length);
}
}
VertexArray::VertexArray(const std::vector<Vertex>& vertex_positions, const std::vector<unsigned int>& vertex_indices,
const std::vector<Normal>& vertex_normals, const std::vector<TextureCoordinate>& texture_coordinates) {
vertices = VRamList(vertex_positions.data(), vertex_positions.size());
local_vertices = vertex_positions;
if (!vertex_indices.empty()) {
indices = VRamList(vertex_indices.data(), vertex_indices.size());
local_indices = vertex_indices;
}
if (!vertex_normals.empty()) {
normals = VRamList(vertex_normals.data(), vertex_normals.size());
local_normals = vertex_normals;
}
if (!texture_coordinates.empty()){
this->texture_coordinates = VRamList(texture_coordinates.data(), texture_coordinates.size());
local_texture_coordinates = texture_coordinates;
}
VertexArray::VertexArray(const std::vector<Vertex>& vertex_positions, const std::vector<unsigned int>& vertex_indices, const std::vector<Normal>& vertex_normals, const std::vector<TextureCoordinate>& texture_coordinates) {
*this = VertexArray(vertex_positions.data(), vertex_positions.size(), vertex_indices.data(), vertex_indices.size(), vertex_normals.data(), vertex_normals.size(), texture_coordinates.data(), texture_coordinates.size());
}
bool VertexArray::Static() {

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,334 @@
#include <JGL/types/VertexArray.h>
#include <JGL/logger/logger.h>
std::pair<float, unsigned long> ParseNumber(const std::string& file_text, const unsigned long& offset) {
std::string number;
unsigned long new_offset = offset;
bool decimal_used = false;
if (offset >= file_text.size())
return {0.0f, offset};
for (; new_offset < file_text.size(); new_offset++) {
if (file_text[new_offset] == '-') {
if (number.empty()) {
number.push_back(file_text[new_offset]);
continue;
}
Logger::Error("Error while parsing number, Index: " + std::to_string(new_offset) + " Extra negative sign.");
return {0.0f, offset};
}
else if (file_text[new_offset] == '.') {
if (!decimal_used) {
number.push_back(file_text[new_offset]);
decimal_used = true;
continue;
}
Logger::Error("Error parsing number at: " + std::to_string(new_offset) + " Extra decimal point.");
return {0.0f, offset};
}
else if (isdigit(file_text[new_offset]))
number.push_back(file_text[new_offset]);
else
break;
}
return {std::stof(number), new_offset};
}
std::pair<Vector2, unsigned long> ParseVector2(const std::string& file_text, const unsigned long& offset) {
auto x_result = ParseNumber(file_text, offset);
auto y_result = ParseNumber(file_text, x_result.second + 1);
// If the new offset is the same as the offset we passed in then we know it didn't work.
if (x_result.second == offset || y_result.second == x_result.second)
return {Vector2(0, 0), offset};
return {Vector2(x_result.first, y_result.first), y_result.second + 1};
}
std::pair<Vector3, unsigned long> ParseVector3(const std::string& file_text, const unsigned long& offset) {
auto x_result = ParseNumber(file_text, offset);
auto y_result = ParseNumber(file_text, x_result.second + 1);
auto z_result = ParseNumber(file_text, y_result.second + 1);
// If the new offset is the same as the offset we passed in then we know it didn't work.
if (x_result.second == offset || y_result.second == x_result.second || z_result.second == y_result.second)
return {Vector3(0,0,0), offset};
return {Vector3(x_result.first, y_result.first, z_result.first), z_result.second + 1};
}
std::pair<Vector4, unsigned long> ParseVector4(const std::string& file_text, const unsigned long& offset) {
auto x_result = ParseNumber(file_text, offset);
auto y_result = ParseNumber(file_text, x_result.second + 1);
auto z_result = ParseNumber(file_text, y_result.second + 1);
auto w_result = ParseNumber(file_text, z_result.second + 1);
if (x_result.second == offset || y_result.second == x_result.second || z_result.second == y_result.second || w_result.second == z_result.second)
return {Vector4(0, 0, 0, 0), offset};
return {Vector4(x_result.first, y_result.first, z_result.first, w_result.first), w_result.second + 1};
}
std::pair<std::array<Vector3, 3>, unsigned long> ParseWavefrontFaceData(const std::string& file_text, const unsigned long& offset) {
unsigned long new_offset = offset;
std::array<Vector3, 3> face_data;
for (unsigned int i = 0; i < 3; i++) {
Vector3 vertex = {-1, -1, -1};
// Vertex
if (std::isdigit(file_text[new_offset])) {
auto v_result = ParseNumber(file_text, new_offset);
vertex.x = v_result.first;
new_offset = v_result.second;
}
// UV
if (new_offset < file_text.size() && file_text[new_offset] == '/') {
new_offset++;
if (new_offset < file_text.size() && (std::isdigit(file_text[new_offset]))) {
auto vt_result = ParseNumber(file_text, new_offset);
vertex.y = vt_result.first;
new_offset = vt_result.second;
}
}
// Normal
if (new_offset < file_text.size() && file_text[new_offset] == '/') {
new_offset++;
if (new_offset < file_text.size() && (std::isdigit(file_text[new_offset]))) {
auto vn_result = ParseNumber(file_text, new_offset);
vertex.z = vn_result.first;
new_offset = vn_result.second;
}
}
face_data[i] = vertex;
new_offset++;
}
return {face_data, new_offset};
}
VertexArray VertexArray::LoadWavefrontOBJ(const std::string &file_text) {
std::vector<std::array<Vector3, 3>> faceline_data;
std::vector<TextureCoordinate> temp_uvs;
std::vector<Normal> temp_normals;
std::vector<Vertex> temp_vertices;
unsigned long offset = 0;
while (offset < file_text.size()) {
char c = file_text[offset];
// TODO the entire line a comment is on should be skipped.
// Skip forward to the character after the next newline.
if (c == '#' || c == '\n' || c == '\r') {
offset++;
continue;
}
// Vertices
if (c == 'v' && offset + 1 < file_text.size() && file_text[offset + 1] == ' ') {
offset += 2;
auto vertex_result = ParseVector3(file_text, offset);
if (vertex_result.second != offset) {
temp_vertices.push_back(vertex_result.first);
offset = vertex_result.second;
}
}
// Normals.
else if (c == 'v' && offset + 2 < file_text.size() && file_text[offset + 1] == 'n' &&
file_text[offset + 2] == ' ') {
offset += 3;
auto normal_result = ParseVector3(file_text, offset);
if (normal_result.second != offset) {
temp_normals.push_back(normal_result.first);
offset = normal_result.second;
}
}
// Texture Coordinates
else if (c == 'v' && offset + 2 < file_text.size() && file_text[offset + 1] == 't' &&
file_text[offset + 2] == ' ') {
offset += 3;
auto uv_result = ParseVector2(file_text, offset);
if (uv_result.second != offset) {
temp_uvs.push_back(uv_result.first);
offset = uv_result.second;
}
}
// Face lines.
else if (c == 'f' && offset + 1 < file_text.size() && file_text[offset + 1] == ' ') {
offset += 2;
auto faceline_result = ParseWavefrontFaceData(file_text, offset);
if (faceline_result.second != offset) {
faceline_data.push_back(faceline_result.first);
offset = faceline_result.second;
}
} else
offset++;
}
// Pick everything out of the face lines and set up how OpenGL expects.
std::vector<Vertex> final_vertices;
std::vector<TextureCoordinate> final_uvs;
std::vector<Normal> final_normals;
std::vector<unsigned int> final_indices;
for (const auto &face: faceline_data) {
for (const auto &vp: face) {
Vertex vertex;
TextureCoordinate uv;
Normal normal;
if (vp.x != -1)
vertex = temp_vertices[vp.x - 1];
if (vp.y != -1)
uv = temp_uvs[vp.y - 1];
if (vp.z != -1)
normal = temp_normals[vp.z - 1];
final_vertices.push_back(vertex);
final_uvs.push_back(uv);
final_normals.push_back(normal);
final_indices.push_back((unsigned int) final_vertices.size() - 1);
}
}
return VertexArray(
final_vertices.data(), final_vertices.size(),
final_indices.data(), final_indices.size(),
final_normals.data(), final_normals.size(),
final_uvs.data(), final_uvs.size());
}
/*
VertexArray JGL::LoadAMO(const std::string& file_text) {
std::vector<Vector3> temp_vertices;
std::vector<Vector3> temp_normals;
std::vector<Vector2> temp_uvs;
std::vector<std::array<Vector3, 3>> faceline_data;
std::string name;
unsigned long offset = 0;
while (offset < file_text.size()) {
char c = file_text[offset];
// If we've dropped on a newline character somehow then just skip passed it.
if (c == '\n' || c == '\r') {
offset++; continue;
}
// File name.
else if (offset + 2 < file_text.size() && c == 'a' && file_text[offset + 1] == 'o' && file_text[offset + 2] == ' ') {
offset += 3;
while (offset < file_text.size()) {
if (file_text[offset] != '\n' && file_text[offset] != '\r')
name.push_back(file_text[offset]), offset++;
else
break;
}
}
// Vertices
else if (offset + 1 < file_text.size() && c == 'v' && file_text[offset + 1] == ' ') {
offset += 2; auto parsed_number = ParseNumber(file_text, offset);
if (parsed_number.second == offset)
Logger::Fatal("We couldn't interpret the Vertex count at: " + std::to_string(offset));
unsigned long vertex_count = parsed_number.first;
offset = parsed_number.second;
for (unsigned long i = 0; i < vertex_count; i++) {
// Skip by newlines.
while (file_text[offset] == '\n' || file_text[offset] == '\r')
offset++;
auto parsed_vector3 = ParseVector3(file_text, offset);
if (parsed_vector3.second == offset)
Logger::Fatal("We couldn't interpret the Vertex at: " + std::to_string(offset));
temp_vertices.push_back(parsed_vector3.first);
offset = parsed_vector3.second;
}
}
// UVs
else if (offset + 2 < file_text.size() && c == 'v' && file_text[offset + 1] == 't' && file_text[offset + 2] == ' ') {
offset += 3; auto parsed_number = ParseNumber(file_text, offset);
if (parsed_number.second == offset)
Logger::Fatal("We couldn't interpret the UV count at: " + std::to_string(offset));
unsigned long uv_count = parsed_number.first;
offset = parsed_number.second;
for (unsigned long i = 0; i < uv_count; i++) {
// Skip by newlines.
while (file_text[offset] == '\n' || file_text[offset] == '\r')
offset++;
auto parsed_vector2 = ParseVector2(file_text, offset);
if (parsed_vector2.second == offset)
Logger::Fatal("We couldn't interpret the UV at: " + std::to_string(offset));
temp_uvs.push_back(parsed_vector2.first);
offset = parsed_vector2.second;
}
}
// Normals
else if (offset + 2 < file_text.size() && c == 'v' && file_text[offset + 1] == 'n' && file_text[offset + 2] == ' ') {
offset += 3; auto parsed_number = ParseNumber(file_text, offset);
if (parsed_number.second == offset)
Logger::Fatal("We couldn't interpret the Normal count at: " + std::to_string(offset));
unsigned long uv_count = parsed_number.first;
offset = parsed_number.second;
for (unsigned long i = 0; i < uv_count; i++) {
// Skip by newlines.
while (file_text[offset] == '\n' || file_text[offset] == '\r')
offset++;
auto parsed_vector3 = ParseVector3(file_text, offset);
if (parsed_vector3.second == offset)
Logger::Fatal("We couldn't interpret the Normal at: " + std::to_string(offset));
temp_normals.push_back(parsed_vector3.first);
offset = parsed_vector3.second;
}
}
// Face Lines
else if (offset + 1 < file_text.size() && c == 'f' && file_text[offset + 1] == ' ') {
offset += 2; auto parsed_number = ParseNumber(file_text, offset);
if (parsed_number.second == offset)
Logger::Fatal("We couldn't interpret the Face Line count at: " + std::to_string(offset));
unsigned long faceline_count = parsed_number.first;
for (unsigned long i = 0; i < faceline_count; i++) {
// Skip by newlines.
while (file_text[offset] == '\n' || file_text[offset] == '\r')
offset++;
}
}
}
}
*/