Compare commits

...

104 Commits

Author SHA1 Message Date
db7a37d647 Allow the user to have J2D states inside of J2D states.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 2m23s
2025-02-03 15:30:05 -05:00
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
dbcc3e11e1 Update CMakeLists.txt
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 5m23s
2024-11-13 15:16:29 -05:00
b2bc1170df Blit single pixel onto target RenderTarget (cave-game)
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 5m25s
2024-11-13 11:51:20 -05:00
5d7fe84bd0 BatchWireframeRevoSphere
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 7m11s
2024-11-13 10:04:32 -05:00
627a047a9b Batching for WireframeSphere.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m48s
2024-11-12 22:50:18 -05:00
f0e2cd151f cleanup
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m43s
2024-11-12 21:41:42 -05:00
bf60c14d5e Improve thread safety.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m42s
2024-11-12 14:31:43 -05:00
83a9baea34 Cleanup
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 7m9s
2024-11-12 11:31:18 -05:00
Redacted
95de887499 Update README.md
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m48s
2024-11-01 15:13:02 -04:00
81ebc175e2 Structures for skeletons
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 4m57s
2024-11-01 13:09:32 -04:00
82669b43bc Wireframe OBB + VertexArray
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 7m47s
2024-10-31 12:50:56 -04:00
779f212a29 Implemented J3D::DrawCubicBezierCurve
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m42s
2024-10-25 13:17:07 -04:00
0cc18cfdad J3D Framework Progress
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 2m19s
2024-10-25 11:35:31 -04:00
da35075735 Work-in-progress documentation for J3D x2
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 2m21s
2024-10-24 13:33:32 -04:00
d29ea018c3 Work-in-progress documentation for J3D
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 3m55s
2024-10-24 13:22:51 -04:00
72c256f907 FillSphere is completely fucked.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m46s
2024-10-24 12:42:38 -04:00
7687c0d873 fix winding order
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m51s
2024-10-24 12:23:55 -04:00
ad34e9ffc0 Implement FillAABB
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 4m1s
2024-10-24 12:09:59 -04:00
5a9716af02 Implementation of WireframeAABB (Still validating?)
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 7m1s
2024-10-24 11:24:25 -04:00
ce5c4d4eb1 Sending up work
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m44s
2024-10-23 14:16:33 -04:00
0302c43f5d Merge branch 'master' of https://git.redacted.cc/Josh/JGL
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m41s
2024-10-22 22:32:27 -04:00
9a5554e447 Update README.md 2024-10-22 22:32:21 -04:00
7dada0e51f This Dick
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m47s
2024-10-22 20:32:32 -04:00
807eef59bc a word
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m47s
2024-10-22 19:04:49 -04:00
4d97c6dead Make it go faster
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Has been cancelled
2024-10-22 19:02:01 -04:00
686a4be0c9 Visible Wireframe Sphere, need some camera rotation to see it properly though.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 6m44s
2024-10-22 13:46:59 -04:00
68ba438433 Fix Prior issue with circle render.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 5m17s
2024-10-22 13:30:52 -04:00
e4a4f01b28 Un-commenting Line 166 stops all rendering, also, it appears circles are gone?
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Has been cancelled
2024-10-22 13:26:04 -04:00
aa4a29961c Implement FillEllipse and OutlineEllipse
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 4m30s
2024-10-22 12:26:40 -04:00
55b67ab850 Update RenderTarget.cpp
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 5m38s
crash related to making a render target with a color attachment 0x0
2024-10-22 10:49:56 -04:00
68e98e6c43 Implement documentation + A special case check on DrawArc.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 6m50s
2024-10-21 13:03:44 -04:00
4e9645436e Implement documentation + A special case check on DrawArc.
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Has been cancelled
2024-10-21 12:57:20 -04:00
ea99a96e64 Doxyfile
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m47s
2024-10-18 15:00:51 -04:00
6866ba828b Outline of Documentation work
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m45s
2024-10-18 14:50:20 -04:00
4893233301 Outline of Documentation work
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m51s
2024-10-18 13:25:48 -04:00
7b5ef6045b CachedFont destructor.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 6m59s
2024-10-18 10:01:21 -04:00
5998bec833 Cleanup.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 4m1s
Preparing for Release-1, J2D lighting is all that's left and then I can work on J3D.

Added helper functions to get rid of the constant need to dereference things.

Wrote a few functions that were defined but never implemented.
2024-10-17 12:48:28 -04:00
0f4ada563e Fix for Nvidia driver being exceedingly picky.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 5m24s
2024-10-17 00:40:55 -04:00
b9bae43cf3 Documentation work
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 4m51s
2024-10-16 14:30:42 -04:00
7f1794e48f Naiive & Slow (But correct) implementation of OutlineRoundedRect. Needs to be performance-improved to a contiguous line loop.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 7m7s
2024-10-16 13:19:42 -04:00
cc504c65ec copy construct RenderTarget.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m36s
2024-10-13 16:37:51 -04:00
bb4a80e36d Copy texture without readbacks.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 2m5s
2024-10-13 09:45:24 -04:00
5d981e64fc Merge remote-tracking branch 'origin/master'
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 6m6s
2024-10-11 13:29:59 -04:00
5b19d26b79 Implement rounding on DrawString input coordinates (Crisp text rendering even when users pass in non-integer values, however, still need to account for non-integer scaling). 2024-10-11 13:29:53 -04:00
8e834f9c5a glad update
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 6m51s
Only use the GL_ARB extension because it'll be more widely supported than EXT.
2024-10-10 22:09:21 -04:00
dbdb4f7ec1 I'll try this I guess idk.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m40s
2024-10-10 19:34:34 -04:00
39c7c7ac0d Fix using RenderTargets on a texture that has mipmaps.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 5m40s
Also better mipmap generation.
2024-10-10 13:12:18 -04:00
e155d272bb Update to latest everything
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 6m37s
2024-10-10 12:28:45 -04:00
2ee5015d61 Update RenderTarget.cpp
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m47s
Whoopsies
2024-10-09 23:08:17 -04:00
4484fd482f Update JGL.cpp
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m47s
Fix memory error
2024-10-09 22:54:41 -04:00
97573e28a9 Multi-Sample-Anti-Alias.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 2m4s
2024-10-09 22:22:24 -04:00
0417c37460 Render Targets Update.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m54s
Allow rendering onto a texture that's been loaded already.

Make DrawSprite commands work regardless of if the texture was loaded in inverted or not.

You however cannot draw onto a texture which is upside-down in vram because your draw commands would be positioned incorrectly.
2024-10-08 18:25:31 -04:00
0a757407d8 Update CMakeLists.txt
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 6m5s
Don't link libJGL.so to event.
2024-10-08 12:13:42 -04:00
308b0dc854 Improve memory safety.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m44s
Also fixed a case where we didn't reset the GL state correctly 🤷
2024-10-06 23:03:50 -04:00
5f367efc28 Ability to resize render targets.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m48s
2024-10-06 00:01:06 -04:00
b4c29315f4 Improve memory safety of VRamList
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 2m5s
Copying it around is slow and you wouldn't do it. But incase some idiot actually does so it doesn't break everything.
2024-10-05 20:14:46 -04:00
Redacted
a568faa701 Update .gitea/workflows/buildtest.yml
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m35s
2024-10-04 23:30:47 -04:00
9d89abb2b8 UpdateData in VRamList & QOL changes.
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 1m45s
2024-10-04 22:50:57 -04:00
43 changed files with 24693 additions and 1415 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 && 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 }}."

2
.gitignore vendored
View File

@@ -1,2 +1,4 @@
/cmake-build-debug
/.idea
/assets/test_files

View File

@@ -17,45 +17,30 @@ 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.1.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-15.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_fbo.zip
URL https://git.redacted.cc/Redacted/glad/archive/v2.1ext_fbo_depthtexture_shadow.zip
)
CPMAddPackage(
NAME jlog
URL https://git.redacted.cc/josh/jlog/Prerelease-12.zip
)
CPMAddPackage(
NAME Event
URL https://git.redacted.cc/josh/Event/archive/Release-6.zip
)
CPMAddPackage(
NAME ReTexture
URL https://git.redacted.cc/Redacted/ReTexture/archive/Release-1.2.zip
URL https://git.redacted.cc/josh/jlog/Prerelease-16.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
@@ -66,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)
@@ -83,32 +66,32 @@ endif()
set_target_properties(JGL PROPERTIES LINKER_LANGUAGE CXX)
#Don't expose these ones.
include_directories(${ReWindow_SOURCE_DIR}/include)
include_directories(
${ReWindow_SOURCE_DIR}/include
${Event_SOURCE_DIR}/include
)
target_include_directories(JGL PUBLIC
${PROJECT_SOURCE_DIR}/include
${OPENGL_INCLUDE_DIRS}
${ReTexture_SOURCE_DIR}/include
${mcolor_SOURCE_DIR}/include
${J3ML_SOURCE_DIR}/include
${Event_SOURCE_DIR}/include
${glad_SOURCE_DIR}/include
${jlog_SOURCE_DIR}/include
${glad_SOURCE_DIR}/include
)
add_executable(JGL_Demo main.cpp)
#set_target_properties(JGL_Demo PROPERTIES LINK_FLAGS "-Wl,-rpath,./lib")
if (UNIX AND NOT APPLE)
target_include_directories(JGL PRIVATE ${FREETYPE_INCLUDE_DIRS})
target_include_directories(JGL PRIVATE ${FREETYPE_INCLUDE_DIRS} )
target_link_libraries(JGL PRIVATE ${FREETYPE_LIBRARIES})
target_link_libraries(JGL PUBLIC ${OPENGL_LIBRARIES} mcolor J3ML glad jlog Event ReTexture)
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 Event ReTexture)
target_link_libraries(JGL PUBLIC ${OPENGL_LIBRARIES} mcolor J3ML glad jlog glad)
endif()
target_link_libraries(JGL_Demo PUBLIC JGL ReWindowLibrary)
target_link_libraries(JGL_Demo PUBLIC JGL ReWindow Event glad)

2743
Doxyfile Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,30 +1,31 @@
# Josh Graphics Library
# Josh's Graphics Library
Yet Another C++ Rendering Toolkit
![Static Badge](https://img.shields.io/badge/Lit-Based-%20)
[![License: Unlicense](https://img.shields.io/badge/license-Unlicense-blue.svg)](http://unlicense.org/) ![Static Badge](https://img.shields.io/badge/Lit-Based-%20)
## Features
* Modern C++ (20)
* Provides single-function-calls to render various graphics primitives in 2D and 3D.
* Integrates directly with our other toolkits (ReWindow, J3ML)
* Quick Rendering of Debug Text, Geometric Widgets, Textures, and so forth.
* Little-to-no overhead.
* Modern C++ (20).
* Cross Platform (Linux & Windows 10 1909 or newer).
* Provides single-function-calls to render various graphics in 2D and 3D.
* Integrates right into an existing OpenGL rendering system.
* Quick Rendering of Text, Shapes, Sprites / Textures, etc.
* High-performance text rendering.
* Little-to-no overhead.
## API Overview
### J2D
* DrawPoint
* DrawLine / DrawGradientLine
* DrawSprite
* DrawSprite / DrawPartialSprite
* OutlineRect / FillRect / FillGradientRect / FillRoundedRect
* OutlineCircle / FillCircle
* OutlineTriangle / FillTriangle
* DrawString
### J3D
* DrawLine
* DrawString
@@ -33,27 +34,44 @@ Yet Another C++ Rendering Toolkit
* DrawQuaternionGizmo (WIP)
### Types
* RenderTarget
* VRamList
* Texture
* Font
* Sprite
* Color4/Color3
* Gradient
## Usage
```cpp
// Makes sure the glViewport will be the correct size.
// Typically done once per frame.
JGL::Update(window_size);
Install instructions and code samples coming soon :tm: !
JGL::J2D::Begin();
JGL::J2D::FillRect(Colors::Blue, {0,0}, {16,16});
JGL::J2D::End();
// See the example program in main.cpp for more in-depth usage.
```
## Requirements
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
* AMD ✓
* NVIDIA ✓
* Intel ✓
* Zink (OpenGL-on-Vulkan) ✓
* GL4ES ✓
## Documentation
Documentation is (sic: will be) automatically generated from latest commit and is hosted at https://doc.redacted.cc/jgl .
Documentation is available [here](https://doc.redacted.cc/JGL/d3/dcc/md__r_e_a_d_m_e.html).
## Contributing
Contributions to JGL are welcome! If you find a bug, have a feature request, or would like to contribute code, please submit an issue or pull request to our repository!
## License
JGL is licensed under the Public Domain. See the LICENSE file for details.
## Acknowledgements
JGL is developed and maintained by Josh O'Leary @ Co from Redacted Software and contributors. Special thanks to Redacted.

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

View File

@@ -7,7 +7,7 @@
/// @file JGL.h
/// @desc All JGL usable functions are defined here. This is the public API.
/// @edit 2024-07-16
/// @edit 2024-10-24
#pragma once
@@ -26,166 +26,669 @@
#include <J3ML/Geometry/Sphere.hpp>
#include <J3ML/Geometry/Capsule.hpp>
#include <J3ML/Geometry/Triangle2D.hpp>
#include <JGL/types/Font.h>
#include <JGL/types/VRamList.h>
#include <JGL/types/VertexArray.h>
// Fonts that are included by default.
namespace JGL::Fonts {
void Init();
// Built in fonts.
inline Font Jupiteroid;
}
// Simple shapes that are pre-computed and used in some draw functions.
namespace JGL::ShapeCache {
inline VRamList* cube_vertex_data = nullptr;
inline VRamList* cube_index_data = nullptr;
inline VRamList* cube_normal_data = nullptr;
// Facing straight out.
inline VRamList* j2d_default_normal_data = nullptr;
inline VRamList* square_origin_topleft_vertex_data = nullptr;
void Init();
}
/// OpenGL Wrapper for rendering 2D & 3D graphics in both a 2D and 3D context.
namespace JGL {
using namespace J3ML::LinearAlgebra;
using namespace J3ML::Geometry;
void Update(const Vector2& window_size);
inline void PurgeFontCache() { fontCache.purgeCache(); }
[[nodiscard]] bool Init(const Vector2i& window_size, float fovY, float far_plane);
void Update(const Vector2i& window_size);
inline void PurgeFontCache() { JGL::fontCache.purgeCache(); }
std::array<GLfloat, 16> OpenGLPerspectiveProjectionRH(float fovY, float aspect, float z_near, float z_far);
/// Returns true if the graphics driver meets the requirements (GL Version & Extensions).
/// @returns true if the graphics driver meets the requirements (GL Version & Extensions).
bool MeetsRequirements();
/// Drawing functions for primitive 2D Shapes.
namespace J2D {
/// Open a 2-D rendering context with the underlying graphics system (In this case& by default OpenGL).
/// @note This call may not strictly be necessary on some setups, but is provided to keep the API constant.
/// It is recommended to always open a JGL 2D context to render your content, then close when completed.
/// This keeps our code from, say, clobbering the OpenGL rendering context driving 3D content in between our calls.
void Begin(RenderTarget* render_target = nullptr, bool clear_buffers = false);
/// Closes a 2-D rendering context with the underlying graphics system (In this case& by default OpenGL).
/// @see Begin().
void End();
/// Provide a list of lights to be used in 2D space. Typically directly after J2D::Begin();
/// 8 lights maximum for now. Some kind of light sorting will eventually be needed per j2d element.
void LightArray(Light*, size_t size);
void LightArray(std::vector<Light> lights);
}
/// Plots a single pixel on the screen.
/// @param color A 3-or-4 channel color value. @see class Color3, class Color4
/// @param coordinates The pixel-point on-screen at which to plot the pixel.
void DrawPoint(const Color4& color, const Vector2& coordinates, float radius = 1.f);
void DrawPoint(const Color4& color, float x, float y, float radius = 1.f);
/// Drawing functions for 2D objects.
namespace JGL::J2D {
/// Open a 2-D rendering context with the underlying graphics system (In this case& by default OpenGL).
/// @note This call may not strictly be necessary on some setups, but is provided to keep the API constant.
/// It is recommended to always open a JGL 2D context to render your content, then close when completed.
/// This keeps our code from, say, clobbering the OpenGL rendering context driving 3D content in between our calls.
/// @param render_target
/// @param clear_buffers
void Begin(RenderTarget* render_target = nullptr, bool clear_buffers = false);
/// Plots a line (segment) on the screen.
/// @param color A 3-or-4 channel color value. @see classes Color3, Color4.
/// @param A The starting point of the line segment.
/// @param B The end point of the line segment.
/// @param thickness The width at which to render the line.
void DrawLine(const Color4& color, const Vector2& A, const Vector2& B, float thickness = 1);
void DrawLine(const Color4& color, float x1, float y1, float x2, float y2, float thickness = 1);
/// Closes a 2-D rendering context with the underlying graphics system (In this case& by default OpenGL).
/// @see Begin().
void End();
///Draws a line with a gradient that transitions across it.
void DrawGradientLine(const Color4& color_a, const Color4& color_b, const Vector2& A, const Vector2& B, float thickness = 1);
void DrawGradientLine(const Color4& color_a, const Color4& color_b, float x, float y, float w, float h, float thickness = 1);
/// Provide a list of lights to be used in 2D space. Typically directly after J2D::Begin();
/// 8 lights maximum for now. Some kind of light sorting will eventually be needed per j2d element.
void OptionalLights(const LightBase** lights, const size_t& light_count);
/// Draws an outline of a rectangle on the screen.
void OutlineRect(const Color4& color, const Vector2& pos, const Vector2& size, float thickness = 1);
/// Specifies a light which is required for every object in the scene.
void RequiredLight(const LightBase* light);
/// Draws a filled rectangle on the screen.
void FillRect(const Color4& color, const Vector2& pos, const Vector2& size);
/// Plots a single pixel on the screen.
/// @param color A 3-or-4 channel color value. @see class Color3, class Color4
/// @param coordinates The pixel-point on-screen at which to plot the pixel.
/// @param radius The size of the point to plot. By default, a single pixel.
void DrawPoint(const Color4& color, const Vector2& coordinates, float radius = 1.f);
void DrawPoint(const Color4& color, float x, float y, float radius = 1.f);
/// Draws a filled rectangle where the color transitions across it.
void FillGradientRect(const Color4& color1, const Color4& color2, const Direction& gradient, const Vector2& pos, const Vector2& size);
/// Plots a series of pixel-points on the screen, in a batch.
/// @note This is more performant for multiple points than plotting them individually.
/// @param color A 3-or-4 channel color value. @see class Color3, class Color4
/// @param points A set of x,y points to render.
/// @param radius The size of the point to plot. By default, a single pixel.
void DrawPoints(const Color4& color, const Vector2* points, int point_count, float radius = 1.f);
/// Draws a filled rectangle with rounded corners on the screen.
void FillRoundedRect(const Color4& color, const Vector2& pos, const Vector2& size, float radius = 5, unsigned int subdivisions = 8);
/// Plots a line (segment) on the screen.
/// @param color A 3-or-4 channel color value. @see classes Color3, Color4.
/// @param A The starting point of the line segment.
/// @param B The end point of the line segment.
/// @param thickness The width at which to render the line.
void DrawLine(const Color4& color, const Vector2& A, const Vector2& B, float thickness = 1);
void DrawLine(const Color4& color, float x1, float y1, float x2, float y2, float thickness = 1);
void DrawRenderTargetAsSprite(const RenderTarget& render_target, const Vector2& position, float rad_rotation = 0, const Vector2& origin = Vector2(0 , 0),
const Vector2& scale = Vector2(1, 1), const Color4& color = Colors::White, Direction inversion = Direction::None);
/// Draws a sprite to the screen by passing a G̶L̶u̶i̶n̶t̶ JGL Texture that represents a handle to a loaded texture.
/// @param texture
/// @param position
/// @param origin The center point around which the image should have all transformations applied to it.
/// @param scale The scale transformation for the image. X and Y axis are independently-scalable.
/// @param rad_rotation A float representing the rotation of the sprite where 0 is no rotation and 1 is the maximum rotation (would look the same as 0).
/// @param color A 32-bit RGBA value represented as four unsigned 8-bit integers.
/// @param inversion inverts the texture only.
/// @see class Texture
void DrawSprite(const Texture& texture, const Vector2& position, float rad_rotation = 0, const Vector2& origin = Vector2(0,0),
const Vector2& scale = Vector2(1,1), const Color4& color = Colors::White, Direction inversion = Direction::None);
void DrawSprite(const Texture& texture,
float positionX, float positionY,
float rad_rotation = 0,
float originX = 0, float originY = 0,
float scaleX = 1, float scaleY = 1,
const Color4& color = Colors::White,
Direction inversion = Direction::None);
/// Plots several line segments defined by a series of points to be connected together.
/// @param color A 3-or-4 channel color value. @see classes Color3, Color4.
/// @param points pointer to the first point in the list.
/// @param point_count the number of points to draw.
/// @param thickness The width at which to render the line.
void DrawLines(const Color4& color, const Vector2* points, const size_t& point_count, float thickness = 1);
/// Draws a piece of a sprite to the screen, similar to DrawSprite.
/// @param texture
/// @param position
/// @param sub_texture_position The top left corner of the sub-texture to be drawn.
/// @param sub_texture_size The size of the sub-texture in px.
/// @param origin
/// @param scale
/// @param color
/// @param inversion
void DrawPartialSprite(const Texture& texture, const Vector2& position, const Vector2& sub_texture_position, const Vector2& sub_texture_size, float rad_rotation = 0,
const Vector2& origin = Vector2(0,0), const Vector2& scale = Vector2(1, 1), const Color4& color = Colors::White, Direction inversion = Direction::None);
void DrawPartialSprite(const Texture& texture, float positionX, float positionY, float sub_texture_positionX, float sub_texture_positionY, unsigned int sub_texture_sizeX, unsigned int sub_texture_sizeY,
float rad_rotation = 0, float originX = 0, float originY = 0, float scaleX = 1, float scaleY = 1, const Color4& color = Colors::White, Direction inversion = Direction::None);
/// Plots a line segment using a series of points separated by a given distance.
/// @param color A 3-or-4 channel color value. @see classes Color3, Color4.
/// @param A The starting point of the line segment.
/// @param B The end point of the line segment.
/// @param spacing The distance between each point (px)
/// @param thickness The width at which to render the line.
/// @note With diagonal lines, the distance between points can differ by one px.
void DrawDottedLine(const Color4& color, const Vector2& A, const Vector2& B, float spacing = 1.f, float thickness = 1.f);
void DrawDottedLine(const Color4& color, float x1, float y1, float x2, float y2, float spacing = 1.f, float thickness = 1.f);
/// To save v-ram, Use if a sprite would be identical if mirrored horizontally, vertically, or both. For example, a circle.
/// Assumes the input texture is the top left quadrant. You can use "SoftwareTexture" to invert it correctly so that's the case.
/// @param texture
/// @param position
/// @param mirror_axis The axes to mirror across, Vertical and Horizontal or both only.
/// @param rad_rotation The rotation of the final result.
/// @param origin The point at which transformations are done about.
/// @param scale
/// @param color
void DrawMirrorSprite(const Texture& texture, const Vector2& position, Direction mirror_axis = Direction::Horizontal | Direction::Vertical, float rad_rotation = 0, const Vector2& origin = Vector2(0,0), const Vector2& scale = Vector2(1,1), const Color4& color = Colors::White);
/// Plots a line segment using a series of points separated by a given distance.
/// @param color A 3-or-4 channel color value. @see classes Color3, Color4.
/// @param A The starting point of the line segment.
/// @param B The end point of the line segment.
/// @param spacing The distance between each point (px)
/// @param dash_length The length of each dash making up the line.
/// @param thickness The width at which to render the line.
/// @note With diagonal lines, the distance between dashes can differ by one px.
void DrawDashedLine(const Color4& color, const Vector2& A, const Vector2& B, float spacing = 4.f, float dash_length = 6.f, float thickness = 1.f);
void DrawDashedLine(const Color4& color, float x1, float y1, float x2, float y2, float spacing = 4.f, float dash_length = 6.f, float thickness = 1.f);
/// Draws an outline of a circle on the screen.
void OutlineCircle(const Color4& color, const Vector2& center, float radius, unsigned int subdivisions = 16, float thickness = 1);
/// Draws a line with a color gradient that transitions across it.
/// @param color_a A 3-or-4 channel color value. @see class Color3, class Color4
/// @param color_b A 3-or-4 channel color value. @see class Color3, class Color4
/// @param A The starting point of the line segment.
/// @param B The end point of the line segment.
/// @param thickness The width at which to render the line.
void DrawGradientLine(const Color4& color_a, const Color4& color_b, const Vector2& A, const Vector2& B, float thickness = 1);
void DrawGradientLine(const Color4& color_a, const Color4& color_b, float x1, float y1, float x2, float y2, float thickness = 1);
/// Draws a filled circle on the screen.
void FillCircle(const Color4& color, const Vector2& center, float radius, unsigned int subdivisions = 8);
/// Draws an outline of a rectangle on the screen.
/// @param color A 3-or-4 channel color value. @see class Color3, class Color4
/// @param pos The top-left corner of the rectangle.
/// @param size The width and height of the rectangle.
/// @param thickness The width at which to render the lines.
void OutlineRect(const Color4& color, const Vector2& pos, const Vector2& size, float thickness = 1);
/// Draws an outline of a triangle on the screen.
/// @param color
/// @param tri
/// @param thickness
void OutlineTriangle(const Color4& color, const Triangle2D& tri, float thickness = 1);
void OutlineTriangle(const Color4& color, const Vector2& triA, const Vector2& triB, const Vector2& triC, float thickness = 1);
/// Draws an outline of a rectangle with rounded corners onto the screen.
/// @param color A 3-or-4 channel color value. @see class Color3, class Color4
/// @param pos The top-left corner of the rectangle.
/// @param size The width and height of the rectangle.
/// @param radius The corner-rounding radius (in radians).
/// @param thickness The width at which to render the lines.
void OutlineRoundedRect(const Color4& color, const Vector2& pos, const Vector2& size, float radius = 5, float thickness = 1);
/// Draws a filled triangle on the screen.
void FillTriangle(const Color4& color, const Triangle2D& tri);
void FIllTriangle(const Color4& color, const Vector2& triA, const Vector2& triB, const Vector2& triC);
/// Draws an outline of a rectangle with chamfered corners onto the screen.
/// @param color A 3-or-4 channel color value. @see class Color3, class Color4
/// @param pos The top-left corner of the rectangle.
/// @param size The width and height of the rectangle.
/// @param radius The corner-rounding radius (in radians).
/// @param thickness The width at which to render the lines.
void OutlineChamferRect(const Color4& color, const Vector2& pos, const Vector2& size, float radius = 5, float thickness = 1);
/// Draws a triangle where each corner is defined by a given color, Smoothly transitioning between them.
void FillGradientTriangle(const Color4& a_color, const Color4& b_color, const Color4& c_color, const Triangle2D& tri);
/// Draws a filled rectangle on the screen.
/// @param color A 3-or-4 channel color value. @see class Color3, class Color4
/// @param pos The top-left corner of the rectangle.
/// @param size The width and height of the rectangle.
/// @see FillRoundedRect, FillGradientRect, FillChamferRect.
void FillRect(const Color4& color, const Vector2& pos, const Vector2& size);
void DrawCubicBezierCurve(const Color4& color, const Vector2& controlA, const Vector2& pointA, const Vector2& pointB, const Vector2& controlB,
/// Draws a filled rectangle where the color transitions across it.
/// @param color1 A 3-or-4 channel color value. @see class Color3, class Color4
/// @param color2 A 3-or-4 channel color value. @see class Color3, class Color4
/// @param gradient See enum Direction
/// @param pos The top-left corner of the rectangle.
/// @param size The width and height of the rectangle.
void FillGradientRect(const Color4& color1, const Color4& color2, const Direction& gradient, const Vector2& pos, const Vector2& size);
/// Draws a filled rectangle with rounded corners on the screen.
/// @param color A 3-or-4 channel color value. @see class Color3, class Color4
/// @param pos The top-left corner of the rectangle.
/// @param size The width and height of the rectangle.
/// @param radius The corner-rounding radius (in radians).
/// @param subdivisions The amount of sub-divisions (and calculations) to be performed per-arc rounding corner.
void FillRoundedRect(const Color4& color, const Vector2& pos, const Vector2& size, float radius = 5, unsigned int subdivisions = 8);
/// Draws a filled rectangle with chamfered (beveled) corners on the screen.
/// @param color A 3-or-4 channel color value. @see class Color3, class Color4
/// @param pos The top-left corner of the rectangle.
/// @param size The width and height of the rectangle.
/// @param radius The corner-rounding radius (in radians).
void FillChamferRect(const Color4& color, const Vector2& pos, const Vector2& size, float radius = 5);
/// Draws a render-target (runtime-modifiable texture) to the screen.
/// @param render_target A RenderTarget instance to be displayed.
/// @param position The position at which to render this object from it's center-point, defined by the origin parameter.
/// @param rad_rotation The amount of radians to rotate this render-target about it's center-point.
/// @param origin The center-point in the image to use for rendering, rotation, and scaling. Top-left is {0,0} and bottom right is {1, 1}.
/// @param scale The amount (in both x, and y axis) to scale the image, with {1,1} being default scaling.
/// @param color A 3-or-4 channel color value. @see class Color3, class Color4
/// @param inversion @see Direction
void DrawRenderTarget(const RenderTarget& render_target, const Vector2& position, float rad_rotation = 0, const Vector2& origin = Vector2(0 , 0),
const Vector2& scale = Vector2(1, 1), const Color4& color = Colors::White, Direction inversion = Direction::None);
void DrawRenderTarget(const RenderTarget* render_target, const Vector2& position, float rad_rotation = 0, const Vector2& origin = Vector2(0 , 0),
const Vector2& scale = Vector2(1, 1), const Color4& color = Colors::White, Direction inversion = Direction::None);
void DrawPartialRenderTarget(const RenderTarget&, const Vector2& position, const Vector2& sub_texture_position, const Vector2& sub_texture_size, float rad_rotation = 0,
const Vector2& origin = Vector2(0,0), const Vector2& scale = Vector2(1, 1), const Color4& color = Colors::White, Direction inversion = Direction::None);
void DrawPartialRenderTarget(const RenderTarget*, const Vector2& position, const Vector2& sub_texture_position, const Vector2& sub_texture_size, float rad_rotation = 0,
const Vector2& origin = Vector2(0,0), const Vector2& scale = Vector2(1, 1), const Color4& color = Colors::White, Direction inversion = Direction::None);
/// Draws a sprite (technically, actually a render target) to the screen.
/// @note This similar overload exists because we expect someone will be an idiot and turn all of their sprites into RenderTargets. ~william
/// @param render_target A RenderTarget instance to be displayed.
/// @param position The position at which to render this object from it's center-point, defined by the origin parameter.
/// @param rad_rotation The amount of radians to rotate this render-target about it's center-point.
/// @param origin The center-point in the image to use for rendering, rotation, and scaling. Top-left is {0,0} and bottom right is {1, 1}.
/// @param scale The amount (in both x, and y axis) to scale the image, with {1,1} being default scaling.
/// @param color A 3-or-4 channel color value. @see class Color3, class Color4
/// @param inversion @see Direction
/// @see DrawSprite
void DrawSprite(const RenderTarget& render_target, const Vector2& position, float rad_rotation = 0, const Vector2& origin = Vector2(0 , 0),
const Vector2& scale = Vector2(1, 1), const Color4& color = Colors::White, Direction inversion = Direction::None);
void DrawSprite(const RenderTarget* render_target, const Vector2& position, float rad_rotation = 0, const Vector2& origin = Vector2(0 , 0),
const Vector2& scale = Vector2(1, 1), const Color4& color = Colors::White, Direction inversion = Direction::None);
/// Draws a sprite to the screen by passing a G̶L̶u̶i̶n̶t̶ JGL Texture that represents a handle to a loaded texture.
/// @param texture A texture instance to be displayed.
/// @param position The point at which to draw the sprite (from the top-left down).
/// @param origin The center point around which the image should have all transformations applied to it.
/// @param scale The scale transformation for the image. X and Y axis are independently-scalable.
/// @param rad_rotation A float representing the rotation of the sprite where 0 is no rotation and 1 is the maximum rotation (would look the same as 0).
/// @param color A 3-or-4 channel color value. @see class Color3, class Color4
/// @param inversion @see Direction
/// @see class Texture
void DrawSprite(const Texture& texture, const Vector2& position, float rad_rotation = 0, const Vector2& origin = Vector2(0,0),
const Vector2& scale = Vector2(1,1), const Color4& color = Colors::White, Direction inversion = Direction::None);
void DrawSprite(const Texture* texture, const Vector2& position, float rad_rotation = 0, const Vector2& origin = Vector2(0,0),
const Vector2& scale = Vector2(1,1), const Color4& color = Colors::White, Direction inversion = Direction::None);
void DrawSprite(const Texture& texture, float positionX, float positionY, float rad_rotation = 0, float originX = 0, float originY = 0,
float scaleX = 1, float scaleY = 1, const Color4& color = Colors::White, Direction inversion = Direction::None);
void DrawSprite(const Texture* texture, float positionX, float positionY, float rad_rotation = 0,
float originX = 0, float originY = 0,float scaleX = 1, float scaleY = 1,
const Color4& color = Colors::White, Direction inversion = Direction::None);
/// Draws a sprite to the screen by passing a G̶L̶u̶i̶n̶t̶ JGL Texture that represents a handle to a loaded texture.
/// @param texture A texture instance to be displayed.
/// @param alpha_mask A texture which determines how much of the sprite you can see. Grayscale image exported as "8bpc RGBA".
/// @param position The point at which to draw the sprite (from the top-left down).
/// @param origin The center point around which the image should have all transformations applied to it.
/// @param scale The scale transformation for the image. X and Y axis are independently-scalable.
/// @param rad_rotation A float representing the rotation of the sprite where 0 is no rotation and 1 is the maximum rotation (would look the same as 0).
/// @param color A 3-or-4 channel color value. @see class Color3, class Color4
/// @param inversion @see Direction
/// @see class Texture
void DrawSprite(const Texture& texture, const Texture& alpha_mask, const Vector2& position, float rad_rotation = 0, const Vector2& origin = Vector2(0,0),
const Vector2& scale = Vector2(1,1), const Color4& color = Colors::White, Direction inversion = Direction::None);
void DrawSprite(const Texture* texture, const Texture* alpha_mask, const Vector2& position, float rad_rotation = 0, const Vector2& origin = Vector2(0,0),
const Vector2& scale = Vector2(1,1), const Color4& color = Colors::White, Direction inversion = Direction::None);
void DrawSprite(const Texture& texture, const Texture& alpha_mask, float positionX, float positionY, float rad_rotation = 0, float originX = 0, float originY = 0,
float scaleX = 1, float scaleY = 1, const Color4& color = Colors::White, Direction inversion = Direction::None);
void DrawSprite(const Texture* texture, const Texture* alpha_mask, float positionX, float positionY, float rad_rotation = 0, float originX = 0, float originY = 0,
float scaleX = 1, float scaleY = 1, const Color4& color = Colors::White, Direction inversion = Direction::None);
/// Draws a piece of a sprite to the screen, similar to DrawSprite.
/// @param texture A texture instance to be displayed.
/// @param position The point at which to draw the sprite (from the top-left down).
/// @param sub_texture_position The top left corner of the sub-texture to be drawn.
/// @param sub_texture_size The size of the sub-texture in px.
/// @param origin The center point around which the image should have all transformations applied to it.
/// @param scale The scale transformation for the image. X and Y axis are independently-scalable.
/// @param color A 3-or-4 channel color value. @see class Color3, class Color4
/// @param inversion @see Direction
void DrawPartialSprite(const Texture& texture, const Vector2& position, const Vector2& sub_texture_position, const Vector2& sub_texture_size, float rad_rotation = 0,
const Vector2& origin = Vector2(0,0), const Vector2& scale = Vector2(1, 1), const Color4& color = Colors::White, Direction inversion = Direction::None);
void DrawPartialSprite(const Texture* texture, const Vector2& position, const Vector2& sub_texture_position, const Vector2& sub_texture_size, float rad_rotation = 0,
const Vector2& origin = Vector2(0,0), const Vector2& scale = Vector2(1, 1), const Color4& color = Colors::White, Direction inversion = Direction::None);
void DrawPartialSprite(const Texture& texture, float positionX, float positionY, float sub_texture_positionX, float sub_texture_positionY, unsigned int sub_texture_sizeX, unsigned int sub_texture_sizeY,
float rad_rotation = 0, float originX = 0, float originY = 0, float scaleX = 1, float scaleY = 1, const Color4& color = Colors::White, Direction inversion = Direction::None);
void DrawPartialSprite(const Texture* texture, float positionX, float positionY, float sub_texture_positionX, float sub_texture_positionY, unsigned int sub_texture_sizeX, unsigned int sub_texture_sizeY,
float rad_rotation = 0, float originX = 0, float originY = 0, float scaleX = 1, float scaleY = 1, const Color4& color = Colors::White, Direction inversion = Direction::None);
/// To save v-ram, Use if a sprite would be identical if mirrored horizontally, vertically, or both. For example, a circle.
/// Assumes the input texture is the top left quadrant. You can use "SoftwareTexture" to invert it correctly so that's the case.
/// @param texture A texture instance to be displayed.
/// @param position The point at which to draw the sprite (from the top-left down).
/// @param mirror_axis The axes to mirror across, Vertical and Horizontal or both only.
/// @param rad_rotation The rotation of the final result.
/// @param origin The point at which transformations are done about.
/// @param scale The scale transformation for the image. X and Y axis are independently-scalable.
/// @param color A 3-or-4 channel color value. @see class Color3, class Color4
void DrawMirrorSprite(const Texture& texture, const Vector2& position, Direction mirror_axis = Direction::Horizontal | Direction::Vertical, float rad_rotation = 0, const Vector2& origin = Vector2(0,0), const Vector2& scale = Vector2(1,1), const Color4& color = Colors::White);
void DrawMirrorSprite(const Texture* texture, const Vector2& position, Direction mirror_axis = Direction::Horizontal | Direction::Vertical, float rad_rotation = 0, const Vector2& origin = Vector2(0,0), const Vector2& scale = Vector2(1,1), const Color4& color = Colors::White);
/// Draws an outline of a circle on the screen.
/// @param color A 3-or-4 channel color value. @see class Color3, class Color4
/// @param center The point in cartesian space at which to draw the circle. This will by-definition be the centroid of this circle.
/// @param radius The radius of the circle to be drawn. AKA Half the diameter.
/// @param subdivisions The accuracy of the approximation of the circle, measured in iteration steps taken.
/// @param thickness The line-width of the circle to be rendered at.
void OutlineCircle(const Color4& color, const Vector2& center, float radius, unsigned int subdivisions = 16, float thickness = 1);
/// Draws a filled circle on the screen.
/// @param color A 3-or-4 channel color value. @see class Color3, class Color4
/// @param center The point in cartesian space at which to draw the circle. This will by-definition be the centroid of this circle.
/// @param radius The radius of the circle to be drawn. AKA Half the diameter.
/// @param subdivisions The accuracy of the approximation of the circle, measured in iteration steps taken.
void FillCircle(const Color4& color, const Vector2& center, float radius, unsigned int subdivisions = 8);
/// Draws an outline of a triangle on the screen.
/// @param color A 3-or-4 channel color value. @see class Color3, class Color4
/// @param tri The triangle defined by its vertices (A, B, and C).
/// @param thickness The line-width of the triangle to be rendered at.
void OutlineTriangle(const Color4& color, const Triangle2D& tri, float thickness = 1);
void OutlineTriangle(const Color4& color, const Vector2& triA, const Vector2& triB, const Vector2& triC, float thickness = 1);
/// Draws a filled triangle on the screen.
/// @param color A 3-or-4 channel color value. @see class Color3, class Color4
/// @param tri The triangle defined by its vertices (A, B, and C).
void FillTriangle(const Color4& color, const Triangle2D& tri);
void FIllTriangle(const Color4& color, const Vector2& triA, const Vector2& triB, const Vector2& triC);
/// Fills a triangle defined by the provided vertices with a gradient that transitions smoothly between the three specified colors at each corner.
/// @param a_color The color at vertex A of the triangle.
/// @param b_color The color at vertex B of the triangle.
/// @param c_color The color at vertex C of the triangle.
/// @param tri The triangle defined by its vertices (A, B, and C).
void FillGradientTriangle(const Color4& a_color, const Color4& b_color, const Color4& c_color, const Triangle2D& tri);
void FillGradientTriangle(const Color4& a_color, const Color4& b_color, const Color4& c_color, const Vector2& tri_a, const Vector2& tri_b, const Vector2& tri_c);
/// Draws a smooth, curved line segment between two control points, with the curve controlled by the two inner points.
/// @param color A 3-or-4 channel color value. @see class Color3, class Color4
/// @param controlA The first control point, which can be considered the start of the line.
/// @param pointA The first inner point, which controls the contour of the curve.
/// @param pointB The second inner point, which controls the contour of the curve.
/// @param controlB The second control point, which can be considered the end of the line.
/// @param subdivisions The accuracy of the approximation of the curve, measured in iteration steps taken.
/// @param thickness The line-width to draw the curve with.
/// @see J3ML::Algorithm::Bezier
void DrawCubicBezierCurve(const Color4& color, const Vector2& controlA, const Vector2& pointA, const Vector2& pointB, const Vector2& controlB,
int subdivisions = 10, float thickness = 1);
/// Draws a series of points where the last point always connects to the first point.
/// @param color A 3-or-4 channel color value. @see class Color3, class Color4
/// @param points The array of vector2's to draw as a polygon.
/// @param point_count The number of points
/// @param thickness The line-width of the polygon
void OutlinePolygon(const Color4& color, const Vector2* points, int point_count, float thickness = 1);
/// Draws a text string on the screen with a given point-size and font.
/// @param color A 3-or-4 channel color value. @see class Color3, class Color4
/// @param text The text to be rendered.
/// @param x The position on the screen at which to draw the text, from the top-left.
/// @param y The position on the screen at which to draw the text, from the top-left.
/// @param scale The value (in both axes) to scale the text by. Defaults to {1,1}.
/// @param size The point-size at which to render the font out. Re-using the same point-size allows efficient glyph caching.
/// @param font The font to use for rendering. @see Font.
void DrawString(const Color4& color, const std::string& text, float x, float y, float scale, u32 size, const Font& font = Fonts::Jupiteroid);
/// Draws an Arc (section of a circle) to the screen.
/// @param color A 3-or-4 channel color value. @see class Color3, class Color4
/// @param center The point in cartesian space at which to draw the arc. This will by-definition be the centroid of this partial circle.
/// @param radius The radius of the partial circle to be drawn. AKA Half the diameter.
/// @param arc_begin The point (0 - 2pi) around a unit-circle of which to start the arc.
/// @param arc_end The point (0 - 2pi) around a unit-circle of which to start the arc.
/// @param subdivisions The accuracy of the approximation of the circle, measured in iteration steps taken.
/// @param thickness The line-width to draw the arc with.
void DrawArc(const Color4& color, const Vector2& center, float radius, float arc_begin, float arc_end,
unsigned int subdivisions, float thickness);
/// TODO Implement the following. These ones are going to be extremely annoying.
void FillPolygon(const Color4& color, const std::vector<Vector2>& points);
void OutlineEllipse(const Color4& color, const Vector2& position, float radius_x, float radius_y, float thickness = 1, int subdivisions = 8);
void FillEllipse(const Color4& color, const Vector2& position, float radius_x, float radius_y, int subdivisions = 8);
}
/// Drawing functions for 3D objects.
namespace JGL::J3D {
/// A light for this 3D render that should never be culled out. up-to 8.
/// The more you put here, The less we will solve for if you're also using LightArray.
/// @note More than 8 lights will cause an error to be printed.
void RequiredLight(const LightBase* light);
/// When each 3D object is drawn, We'll do our best to determine which lights would effect it the most and use those ones.
void OptionalLights(const LightBase** lights, const size_t& light_count);
/// Helper function to conveniently change the Field-Of-View.
void ChangeFOV(float fov);
/// Helper function to change the far-plane, which is the cutoff distance for rendering.
void ChangeFarPlane(float far_plane);
/// Open a 3-D rendering context with the underlying graphics system (In this case& by default OpenGL).
/// It is recommended to always open a JGL 3D context to render your content, then close when completed.
/// @param two_pass Whether or not we'll use two-pass rendering for occlusion.
void Begin(bool two_pass = false);
/// Closes a 3-D rendering context with the underlying graphics system (In this case& by default OpenGL).
/// @see Begin().
void End();
void SetMatrix(const std::vector<GLfloat>& matrix, const Vector2& window_size);
/// Draws a line in 3D space.
/// @param color A 3-or-4 channel color value. @see class Color3, class Color4
/// @param A The start-point of the line segment.
/// @param B The end-point of the line segment.
/// @param thickness The line-width to draw the line segment with.
void DrawLine(const Color4& color, const Vector3& A, const Vector3& B, float thickness = 1.f);
/// Draws a smooth, curved line segment between two control points, with the curve controlled by the two inner points.
/// @param color A 3-or-4 channel color value. @see class Color3, class Color4
/// @param controlA The first control point, which can be considered the start of the line.
/// @param pointA The first inner point, which controls the contour of the curve.
/// @param pointB The second inner point, which controls the contour of the curve.
/// @param controlB The second control point, which can be considered the end of the line.
/// @param subdivisions The accuracy of the approximation of the curve, measured in iteration steps taken.
/// @param thickness The line-width to draw the curve with.
/// @see J3ML::Algorithm::Bezier
void DrawCubicBezierCurve(const Color4& color, const Vector3& controlA, const Vector3& pointA, const Vector3& pointB, const Vector3& controlB,
int subdivisions = 10, float thickness = 1);
/// Draws a series of points where the last point always connects to the first point.
void OutlinePolygon(const Color4& color, const std::vector<Vector2>& points, float thickness = 1);
/// Draws the outline of an Icosahedron in 3D space.
/// @param color A 3-or-4 channel color value. @see class Color3, class Color4
/// @param position The point in 3D space at which to draw the Icosahedron.
/// @param radius The size to draw the Icosahedron at.
/// @param thickness The line-width to draw the Icosahedron outline with.
void WireframeIcosahedron(const Color4& color, const Vector3& position, float radius, float thickness = 1.f);
/// Draws a text string on the screen with a given point-size and font.
void DrawString(const Color4& color, const std::string& text, float x, float y, float scale, u32 size, const Font& font);
/// Draws a solid Icosahedron in 3D space.
/// @param color A 3-or-4 channel color value. @see class Color3, class Color4
/// @param position The point in 3D space at which to draw the Icosahedron.
/// @param radius The size to draw the Icosahedron at.
void FillIcosahedron(const Color4& color, const Vector3& position, float radius);
/// TODO Implement the following. These ones are going to be extremely annoying.
void FillPolygon(const Color4& color, const std::vector<Vector2>& points);
void FillTexturedPolygon();
void OutlineRoundedRect(const Color4& color, const Vector2& pos, const Vector2& size, float radius = 5, float thickness = 1);
void FillTexturedTriangle();
}
/// Draws the outline of a Sphere in 3D space.
/// @param color A 3-or-4 channel color value. @see class Color3, class Color4
/// @param position The point in 3D space at which to draw the Sphere.
/// @param radius The size to draw the Sphere at.
/// @param thickness The line-width to draw the Icosahedron outline with.
/// @param sectors The amount of latitudinal subdivisions to perform when computing the sphere.
/// @param stacks The amount of longitudinal subdivisions to perform when computing the sphere.
void WireframeSphere(const Color4& color, const Vector3& position, float radius, float thickness = 1.f, unsigned int sectors = 10, unsigned int stacks = 10);
/// Drawing functions for primitive 3D Shapes.
namespace J3D {
void Init(const Vector2& window_size, float fov, float far_plane);
void ChangeFOV(float fov);
void ChangeFarPlane(float far_plane);
void Begin();
void End();
void SetMatrix(const std::vector<GLfloat>& matrix, const Vector2& window_size);
void DrawLine(const Color4& color, const Vector3& A, const Vector3& B, float thickness = 1);
void FillSphere(const Color3& color, const Sphere& sphere);
void WireframeSphere(const Color3& color, const Sphere& sphere, float thickness = 1);
void FillOBB(const Color3& color, const OBB& obb);
void WireframeOBB(const Color3& color, const OBB& obb, float thickness = 1);
void FillCapsule(const Color3& color, const Capsule& capsule);
void WireframeCapsule(const Color3& color, const Capsule& cap, float thickness = 1);
void FillTriangleMesh(const Color3& color, const TriangleMesh& mesh);
void WireframeTriangleMesh(const Color3& color, const TriangleMesh& mesh, float thickness = 1);
void DrawString(const Color4& color, const std::string& text, const Vector3& pos, float scale, u32 size, const Font& font, const EulerAngle& angle = {0, 0, 0}, bool draw_back_face = false);
void DrawSprite();
void DrawMatrixGizmo (const Matrix3x3&, const Vector3&);
void DrawMatrixGizmo (const Matrix4x4&);
void DrawAxisAngleGizmo (const AxisAngle&, const Vector3&);
void DrawQuaternionGizmo (const Quaternion&, const Vector3&);
}
/// Draws the outline of a Sphere in 3D space.
/// @param color A 3-or-4 channel color value. @see class Color3, class Color4
/// @param sphere The mathematically-defined sphere object to be rendered.
/// @param thickness The line-width to draw the Icosahedron outline with.
/// @param sectors The amount of latitudinal subdivisions to perform when computing the sphere.
/// @param stacks The amount of longitudinal subdivisions to perform when computing the sphere.
void WireframeSphere(const Color4& color, const Sphere& sphere, float thickness = 1.f, unsigned int sectors = 10, unsigned int stacks = 10);
/// Draws outlines of multiple spheres in 3D space.
/// @param color A 3-or-4 channel color value. @see class Color3, class Color4
/// @param spheres The mathematically-defined sphere objects to be rendered.
/// @param sphere_count The number of spheres.
/// @param thickness The line-width to draw the Icosahedron outline with.
/// @param sectors The amount of latitudinal subdivisions to perform when computing the sphere.
/// @param stacks The amount of longitudinal subdivisions to perform when computing the sphere.
/// @note The "Position" of the spheres is expected to be in world space.
void BatchWireframeSphere(const Color4& color, const Sphere* spheres, const size_t& sphere_count, float thickness = 1.f, unsigned int sectors = 10, unsigned int stacks = 10);
/// Draws outlines of a sphere in 3D space. Calculates a cross section and revolves it around the center.
/// @param color A 3-or-4 channel color value. @see class Color3, class Color4
/// @param position The point in 3D space at which to draw the Sphere.
/// @param radius The size to draw the Sphere at.
/// @param thickness The line-width to draw the Icosahedron outline with.
/// @param sectors The amount of latitudinal subdivisions to perform when computing the sphere.
/// @param revolutions The number of times to revolve the cross section about the center.
/// @param draw_stacks Whether or not to draw the stacks of the sphere.
void WireframeRevoSphere(const Color4& color, const Vector3& position, float radius, float thickness = 1.f, unsigned int sectors = 10, unsigned int revolutions = 10, bool draw_stacks = false);
/// Draws the outline of a Sphere in 3D space.
/// @param color A 3-or-4 channel color value. @see class Color3, class Color4
/// @param sphere The mathematically-defined sphere object to be rendered.
/// @param thickness The line-width to draw the Icosahedron outline with.
/// @param sectors The amount of latitudinal subdivisions to perform when computing the sphere.
/// @param revolutions The number of times to revolve the cross section about the center.
/// @param draw_stacks Whether or not to draw the stacks of the sphere.
void WireframeRevoSphere(const Color4& color, const Sphere& sphere, float thickness = 1.f, unsigned int sectors = 10, unsigned int revolutions = 10, bool draw_stacks = false);
/// Draws outlines of multiple spheres in 3D space. Calculates a cross section and revolves it around the center.
/// @param color A 3-or-4 channel color value. @see class Color3, class Color4
/// @param spheres The mathematically-defined sphere objects to be rendered.
/// @param sphere_count The number of spheres.
/// @param thickness The line-width to draw the Icosahedron outline with.
/// @param sectors The amount of latitudinal subdivisions to perform when computing the sphere.
/// @param revolutions The number of times to revolve the cross section about the center.
/// @param draw_stacks Whether or not to draw the stacks of the sphere.
/// @note The "Position" of the spheres is expected to be in world space.
/// @note This method of drawing a sphere is *probably* the fastest out of all of them.
void BatchWireframeRevoSphere(const Color4& color, const Sphere* spheres, const size_t& sphere_count, float thickness = 1.f, unsigned int sectors = 10, unsigned int revolutions = 10, bool draw_stacks = false);
/// Draws the outline of an Icosphere in 3D space.
/// @note An Icosphere is an approximation of a sphere that is generated by recursively subdividing an Icosahedron.
/// @param color A 3-or-4 channel color value. @see class Color3, class Color4
/// @param position The point in 3D space at which to draw the Sphere.
/// @param radius The size to draw the Sphere at.
/// @param thickness The line-width to draw the Icosahedron outline with.
/// @param subdivisions The accuracy of the approximation of the Icosphere, measured in iteration steps taken.
void WireframeIcosphere(const Color4& color, const Vector3& position, float radius, float thickness = 1.f, unsigned int subdivisions = 10);
/// Draws the outline of an Icosphere in 3D space.
/// @note An Icosphere is an approximation of a sphere that is generated by recursively subdividing an Icosahedron.
/// @param color A 3-or-4 channel color value. @see class Color3, class Color4
/// @param sphere The mathematically-defined sphere object to be rendered.
/// @param thickness The line-width to draw the Icosphere outline with.
/// @param subdivisions The accuracy of the approximation of the Icosphere, measured in iteration steps taken.
void WireframeIcosphere(const Color4& color, const Sphere& sphere, float thickness = 1.f, unsigned int subdivisions = 10);
/// Draws the outline of a Cubesphere in 3D space.
/// @note A Cubesphere is an approximation of a sphere that is generated by recursively subdividing a Cube.
/// @param color A 3-or-4 channel color value. @see class Color3, class Color4
/// @param position The point in 3D space at which to draw the Sphere.
/// @param radius The size to draw the Sphere at.
/// @param thickness The line-width to draw the Cubesphere outline with.
/// @param subdivisions The accuracy of the approximation of the Cubesphere, measured in iteration steps taken.
void WireframeCubesphere(const Color4& color, const Vector3& position, float radius, float thickness = 1.f, unsigned int subdivisions = 10);
/// Draws the outline of a Cubesphere in 3D space.
/// @note A Cubesphere is an approximation of a sphere that is generated by recursively subdividing a Cube.
/// @param color A 3-or-4 channel color value. @see class Color3, class Color4
/// @param sphere The mathematically-defined sphere object to be rendered.
/// @param thickness The line-width to draw the Cubesphere outline with.
/// @param subdivisions The accuracy of the approximation of the Cubesphere, measured in iteration steps taken.
void WireframeCubesphere(const Color4& color, const Sphere& sphere, float thickness = 1.f, unsigned int subdivisions = 10);
/// Draws a solid Sphere in 3D space.
/// @param color A 3-or-4 channel color value. @see class Color3, class Color4
/// @param position The point in 3D space at which to draw the Sphere.
/// @param radius The size to draw the Sphere at.
/// @param sectors The amount of latitudinal subdivisions to perform when computing the sphere.
/// @param stacks The amount of longitudinal subdivisions to perform when computing the sphere.
void FillSphere(const Color4& color, const Vector3& position, float radius, unsigned int sectors = 10, unsigned int stacks = 10);
/// Draws a solid Sphere in 3D space.
/// @param color A 3-or-4 channel color value. @see class Color3, class Color4
/// @param sphere The mathematically-defined sphere object to be rendered.
/// @param sectors The amount of latitudinal subdivisions to perform when computing the sphere.
/// @param stacks The amount of longitudinal subdivisions to perform when computing the sphere.
void FillSphere(const Color4& color, const Sphere& sphere, unsigned int sectors = 10, unsigned int stacks = 10);
/// Draws multiple solid Spheres in 3D space.
/// @param color A 3-or-4 channel color value. @see class Color3, class Color4
/// @param spheres The mathematically-defined sphere objects to be rendered.
/// @param sphere_count The number of spheres to be rendered.
/// @param sectors The amount of latitudinal subdivisions to perform when computing the sphere.
/// @param stacks The amount of longitudinal subdivisions to perform when computing the sphere.
void BatchFillSphere(const Color4& color, const Sphere* spheres, const size_t& sphere_count, unsigned int sectors = 10, unsigned int stacks = 10);
/// Draws a solid Icosphere in 3D space.
/// @note An Icosphere is an approximation of a sphere that is generated by recursively subdividing an Icosahedron.
/// @param color A 3-or-4 channel color value. @see class Color3, class Color4
/// @param position The point in 3D space at which to draw the Sphere.
/// @param radius The size to draw the Sphere at.
/// @param subdivisions The accuracy of the approximation of the Cubesphere, measured in iteration steps taken.
void FillIcosphere(const Color4& color, const Vector3& position, float radius, unsigned int subdivisions = 10);
/// Draws a solid Icosphere in 3D space.
/// @note An Icosphere is an approximation of a sphere that is generated by recursively subdividing an Icosahedron.
/// @param color A 3-or-4 channel color value. @see class Color3, class Color4
/// @param sphere The mathematically-defined sphere object to be rendered.
/// @param subdivisions The accuracy of the approximation of the Cubesphere, measured in iteration steps taken.
void FillIcosphere(const Color4& color, const Sphere& sphere, unsigned int subdivisions = 10);
/// Draws a solid Cubesphere in 3D space.
/// @note A Cubesphere is an approximation of a sphere that is generated by recursively subdividing a Cube.
/// @param color A 3-or-4 channel color value. @see class Color3, class Color4
/// @param position The point in 3D space at which to draw the Sphere.
/// @param radius The size to draw the Sphere at.
/// @param subdivisions The accuracy of the approximation of the Cubesphere, measured in iteration steps taken.
void FillCubesphere(const Color4& color, const Vector3& position, float radius, unsigned int subdivisions = 10);
/// Draws a solid Cubesphere in 3D space.
/// @note A Cubesphere is an approximation of a sphere that is generated by recursively subdividing a Cube.
/// @param color A 3-or-4 channel color value. @see class Color3, class Color4
/// @param sphere The mathematically-defined sphere object to be rendered.
/// @param subdivisions The accuracy of the approximation of the Cubesphere, measured in iteration steps taken.
void FillCubesphere(const Color4& color, const Sphere& sphere, unsigned int subdivisions = 10);
/// Draws an outline of an axis-aligned bounding box in 3D space.
/// @param color A 3-or-4 channel color value. @see class Color3, class Color4
/// @param pos The point in 3D space that is the center of the AABB.
/// @param radii The radii along x,y,z axes to size the bounding box.
/// @param thickness The line-width to draw the Icosphere outline with.
void WireframeAABB(const Color4& color, const Vector3& pos, const Vector3& radii, float thickness = 1.f);
/// Draws an outline of an axis-aligned bounding box in 3D space.
/// @param color A 3-or-4 channel color value. @see class Color3, class Color4
/// @param aabb The AABB object to render.
/// @param thickness The line-width to draw the Icosphere outline with.
void WireframeAABB(const Color4& color, const AABB& aabb, float thickness = 1.f);
/// Draws outlines of multiple axis-aligned bounding-boxes in 3D space.
/// @param color A 3-or-4 channel color value. @see class Color3, class Color4
/// @param boxes The boxes to be drawn.
/// @param box_count The number of boxes to be drawn.
/// @param thickness The line-width to draw the Icosahedron outline with.
void BatchWireframeAABB(const Color4& color, const AABB* boxes, const size_t& box_count, float thickness = 1.f);
/// Draws a solid axis-aligned bounding box in 3D space.
/// @param color A 3-or-4 channel color value. @see class Color3, class Color4
/// @param pos The point in 3D space that is the center of the AABB.
/// @param radii The radii along x,y,z axes to size the bounding box.
void FillAABB(const Color4& color, const Vector3& pos, const Vector3& radii);
/// Draws a solid axis-aligned bounding box in 3D space.
/// @param color A 3-or-4 channel color value. @see class Color3, class Color4
/// @param aabb The AABB object to visualize.
void FillAABB(const Color4& color, const AABB& aabb);
/// Draws solid axis-aligned bounding boxes in 3D space.
/// @param color A 3-or-4 channel color value. @see class Color3, class Color4
/// @param aabb The AABB objects to visualize.
/// @param box_count The number of AABBs to draw.
void BatchFillAABB(const Color4& color, const AABB* boxes, const size_t& box_count);
/// Draws an outline of an oriented bounding box in 3D space.
/// @param color A 3-or-4 channel color value. @see class Color3, class Color4
/// @param position The point in 3D space that is the center of the AABB.
/// @param radii The radii along x,y,z axes to size the bounding box.
/// @param orientation The rotation in 3D space of the OBB.
/// @param thickness The line-width to draw the OBB outline with.
void WireframeOBB(const Color4& color, const Vector3& position, const Vector3& radii, const Matrix3x3& orientation, float thickness = 1.f);
/// Draws an outline of an oriented bounding box in 3D space.
/// @param color A 3-or-4 channel color value. @see class Color3, class Color4
/// @param obb The OBB object to visualize.
/// @param thickness The line-width to draw the OBB outline with.
void WireframeOBB(const Color4& color, const OBB& obb, float thickness = 1.f);
void BatchWireframeOBB(const Color4& color, const OBB* boxes, const size_t& box_count, float thickness = 1.f);
/// Draws a solid oriented bounding box in 3D space.
/// @param color A 3-or-4 channel color value. @see class Color3, class Color4
/// @param position The center-position of the oriented bounding box.
/// @param radii The radii along x,y,z axes to size the bounding box.
/// @param orientation The rotation in 3D space of the OBB.
void FillOBB(const Color4& color, const Vector3& position, const Vector3& radii, const EulerAngleXYZ& orientation);
/// Draws a solid oriented bounding box in 3D space.
/// @param color A 3-or-4 channel color value. @see class Color3, class Color4
/// @param obb The oriented-bounding-box object to visualize.
void FillOBB(const Color4& color, const OBB& obb);
void WireframeCylinder();
void FillCylinder();
void WireframePrism();
void FillPrism();
void WireframePipe();
void FillPipe();
void WireframeCone();
void FillCone();
void WireframeTorus();
void FillTorus();
void FillCapsule(const Color3& color, const Capsule& capsule);
void WireframeCapsule(const Color3& color, const Capsule& cap, float thickness = 1.f);
void FillTriangleMesh(const Color3& color, const TriangleMesh& mesh);
void WireframeTriangleMesh(const Color3& color, const TriangleMesh& mesh, float thickness = 1.f);
/// Draws a string of text in 3D space, with an arbitrary rotation.
/// @param color A 3-or-4 channel color value. @see class Color3, class Color4
/// @param text The content to display on the screen.
/// @param pos The position in 3D space to display the text.
/// @param scale The scaling factor to render the text with, 1 being default. TODO: Vector2 scaling?
/// @param size The pixel size to rasterize the font with.
/// @param font The font object to use when drawing.
/// @param angle The orientation in 3D space.
/// @param draw_back_face
void DrawString(const Color4& color, const std::string& text, const Vector3& pos, float scale, u32 size, const Font& font = Fonts::Jupiteroid, const EulerAngleXYZ& angle = {0, 0, 0}, bool draw_back_face = false);
void DrawVertexArray(const Color4& color, const VertexArray& vertex_array, const Vector3& position);
/// Draws a string of text in 3D space that is always facing the exact direction of the camera projection.
void DrawBillboardString();
/// Draws a texture sprite in 3D space that is always facing the exact direction of the camera projection.
void DrawBillboardSprite();
void DrawSprite();
void DrawMatrixGizmo (const Matrix3x3&, const Vector3&);
void DrawMatrixGizmo (const Matrix4x4&);
void DrawAxisAngleGizmo (const AxisAngle&, const Vector3&);
void DrawQuaternionGizmo (const Quaternion&, const Vector3&);
}

View File

@@ -17,7 +17,7 @@ namespace JGL {
return (u8)a & (u8)b;
}
static std::string to_string(JGL::Direction direction) {
static std::string to_string(const JGL::Direction& direction) {
switch (direction) {
case JGL::Direction::None:
return "None";
@@ -33,4 +33,40 @@ namespace JGL {
return "Unknown";
}
}
enum class MSAA_SAMPLE_RATE : u8 {
MSAA_NONE = 0,
MSAA_2X = 1,
MSAA_4X = 2,
MSAA_8X = 3
};
static std::string to_string(const JGL::MSAA_SAMPLE_RATE& sample_rate) {
switch (sample_rate) {
case MSAA_SAMPLE_RATE::MSAA_NONE:
return "No MSAA";
case MSAA_SAMPLE_RATE::MSAA_2X:
return "MSAA 2x";
case MSAA_SAMPLE_RATE::MSAA_4X:
return "MSAA 4x";
case MSAA_SAMPLE_RATE::MSAA_8X:
return "MSAA 8x";
default:
return "Unknown";
}
}
static int to_int(const JGL::MSAA_SAMPLE_RATE& sample_rate) {
switch (sample_rate) {
case MSAA_SAMPLE_RATE::MSAA_NONE:
return 0;
case MSAA_SAMPLE_RATE::MSAA_2X:
return 2;
case MSAA_SAMPLE_RATE::MSAA_4X:
return 4;
case MSAA_SAMPLE_RATE::MSAA_8X:
return 8;
default:
return 0;
}
}
}

View File

@@ -13,8 +13,6 @@ extern "C" typedef struct FT_LibraryRec_* FT_Library;
namespace JGL
{
/// Initializes FreeType engine. Remember to call this during program initialization.
bool InitTextEngine();
/// A Font class implementation.
@@ -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

@@ -26,7 +26,7 @@ public:
//CachedGlyph(GLuint texture_id, char c);
CachedGlyph(char c, std::array<GLfloat, 12> texcoords, float x2o, float y2o, float w, float h, float advX, float advY);
char getCharacter();
char getCharacter() const;
[[nodiscard]] std::array<GLfloat, 12> getTexCoords() const;
};
@@ -38,16 +38,19 @@ private:
GLsizei texture_width = 0, texture_height = 0;
unsigned int font_size = 0;
unsigned int font_index = 0;
void Erase();
public:
void appendGlyph(CachedGlyph* glyph);
unsigned int getFontSize();
unsigned int getFontIndex();
unsigned int getFontSize() const;
unsigned int getFontIndex() const;
CachedGlyph* getGlyph(char c);
std::unordered_map<char, CachedGlyph*> getGlyphs();
const GLuint* getTexture();
const GLuint* getTextureHandle();
[[nodiscard]] GLsizei getTextureWidth() const;
[[nodiscard]] GLsizei getTextureHeight() const;
public:
CachedFont(GLuint texture_id, GLsizei texture_width, GLsizei texture_height, unsigned int font_size, unsigned int font_index);
~CachedFont();
};
class JGL::FontCache {

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,39 +1,123 @@
#pragma once
#include <vector>
#include <glad/glad.h>
#include <JGL/types/Texture.h>
#include <Color4.hpp>
#include <Colors.hpp>
#include <JGL/types/Enums.h>
#include <J3ML/LinearAlgebra/Vector2i.hpp>
namespace JGL {
class RenderTarget;
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;
GLuint depth_buffer = 0;
Texture* texture = nullptr;
public:
static GLuint GetActiveGLFramebufferHandle();
static void SetActiveGLRenderTarget(const RenderTarget& render_target);
public:
[[nodiscard]] Vector2 GetDimensions() const;
[[nodiscard]] Texture* GetJGLTexture() const;
[[nodiscard]] GLuint GetGLTextureHandle() const;
[[nodiscard]] GLuint GetGLFramebufferObjectHandle() const;
[[nodiscard]] GLuint GetGLDepthBufferHandle() const;
[[nodiscard]] Color4 GetClearColor() const;
/// Get the data back from the FBO. This is *not* async friendly.
[[nodiscard]] std::vector<GLfloat> GetData() const;
public:
/// Create a render target for a texture that already exists. For decals.
explicit RenderTarget(const Texture& texture, const Color4& clear_color = Colors::Black, bool use_depth = false);
/// 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);
const Texture* texture = nullptr;
MSAA_SAMPLE_RATE msaa_sample_rate = MSAA_SAMPLE_RATE::MSAA_NONE;
GLuint msaa_framebuffer_object = 0;
GLuint msaa_depth_buffer = 0;
GLuint msaa_render_buffer = 0;
void Erase();
public:
/// @returns The Render Target currently in use by OpenGL.
/// @note Zero is the screen.
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);
/// 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);
/// 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);
/// 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 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:
/// @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 this Render Target is using MSAA.
[[nodiscard]] bool MSAAEnabled() 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;
/// @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:
/// 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.
/// @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

@@ -0,0 +1,124 @@
#pragma once
#include <vector>
#include <array>
#include <J3ML/LinearAlgebra/Vector3.hpp>
#include <J3ML/LinearAlgebra/Matrix4x4.hpp>
#include <unordered_map>
namespace JGL {
class Bone;
class SkeletalVertexAttribute;
class Skeleton;
class KeyFrame;
class Animation;
class AnimationState;
}
class JGL::SkeletalVertexAttribute {
private:
std::array<int, 4> bone_ids = { 0, 0, 0, 0 };
std::array<float, 4> bone_weights = { 0, 0, 0, 0 };
public:
[[nodiscard]] std::array<int, 4> GetAffectingBoneIDs() const;
[[nodiscard]] std::array<float, 4> GetAffectingBoneWeights() const;
public:
SkeletalVertexAttribute() = default;
/// These cannpt be longer than 4.
SkeletalVertexAttribute(const std::vector<int>& ids, const std::vector<float>& weights);
};
class JGL::Bone {
private:
int id = 0;
// Not every gltf2 model includes names, but I'll still include it.
std::string name;
Matrix4x4 inverse_bind_matrix = Matrix4x4::Identity;
Matrix4x4 offset_matrix = Matrix4x4::Identity;
Matrix4x4 final_transform = Matrix4x4::Identity;
int parent_id = -1;
std::vector<int> children{};
public:
[[nodiscard]] int GetID() const;
[[nodiscard]] std::string GetName() const;
[[nodiscard]] Matrix4x4 GetInverseBindMatrix() const;
[[nodiscard]] Matrix4x4 GetOffsetMatrix() const;
[[nodiscard]] Matrix4x4 GetFinalTransform() const;
[[nodiscard]] bool IsRootBone() const;
public:
[[nodiscard]] int GetParentID() const;
[[nodiscard]] std::vector<int> GetChildren() const;
public:
void SetParent(int parent_id);
void AppendChild(int new_child);
void SetID(int numeric_id);
void SetName(const std::string& string_id);
void SetInverseBindMatrix(const Matrix4x4& inverse_bind);
void SetOffsetMatrix(const Matrix4x4& offset);
void SetFinalTransformMatrix(const Matrix4x4& final);
public:
~Bone() = default;
Bone() = default;
explicit Bone(int numeric_id, const std::string& string_id = "", int parent_id = -1, const std::vector<int>& children_ids = {},
const Matrix4x4& inverse_bind = Matrix4x4::Identity, const Matrix4x4& offset = Matrix4x4::Identity, const Matrix4x4& final = Matrix4x4::Identity);
};
class JGL::Skeleton {
private:
Bone root;
std::vector<Bone> bones;
public:
[[nodiscard]] Bone* GetRootBone();
[[nodiscard]] Bone* FindBone(int id);
[[nodiscard]] Bone* FindBone(const std::string& string_id);
public:
void AppendBone(const Bone& bone);
public:
explicit Skeleton(const Bone& root_bone, const std::vector<Bone>& children = {});
~Skeleton() = default;
Skeleton() = default;
};
class JGL::KeyFrame {
private:
float time_stamp = 0;
Skeleton pose;
public:
[[nodiscard]] float GetTimeStamp() const;
[[nodiscard]] Skeleton GetSkeleton() const;
public:
KeyFrame(const Skeleton& pose, float time_stamp);
};
class JGL::Animation {
private:
int id = -1;
// Not all animations have names.
std::string name;
float length = 0;
std::vector<KeyFrame> key_frames;
std::vector<SkeletalVertexAttribute> vertex_attributes{};
public:
[[nodiscard]] float GetDuratrion() const;
[[nodiscard]] std::vector<KeyFrame> GetKeyFrames() const;
[[nodiscard]] int GetID() const;
[[nodiscard]] std::string GetName() const;
[[nodiscard]] std::vector<SkeletalVertexAttribute> GetSkeletalVertexAttributes() const;
public:
void AppendKeyFrame(const KeyFrame& new_key);
void SetDuration(float duration);
void SetID(int identifier);
void SetName(const std::string& name_id);
public:
~Animation() = default;
Animation(int id, float duration, const std::vector<KeyFrame>& key_frames, const std::vector<SkeletalVertexAttribute>& skeletal_vertex_attributes,
const std::string& name = "");
};
class JGL::AnimationState {
private:
int animation_id = -1;
float animation_time = 0;
public:
};

View File

@@ -1,57 +1,75 @@
#pragma once
#include <ReTexture/Texture.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 ReTexture;
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 {
protected:
GLuint texture_handle = 0;
Vector2 texture_size = {0, 0};
ReTexture::TextureFlag texture_flags;
ReTexture::TextureFormat texture_format;
TextureFilteringMode texture_filtering_mode;
TextureWrappingMode texture_wrapping_mode;
void load(SoftwareTexture* 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);
Texture(const std::string& file, const TextureFlag& flags, TextureFilteringMode filtering_mode = TextureFilteringMode::BILINEAR, TextureWrappingMode wrapping_mode = TextureWrappingMode::CLAMP_TO_EDGE);
/* Initialize a texture filled with trash data
this is primarily for the RenderTarget */
explicit Texture(const Vector2& size);
Texture() = default;
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);
void SetTextureHandle(GLuint handle);
void Erase();
};
}
Texture(const Texture& rhs);
~Texture();
};

View File

@@ -1,72 +1,68 @@
#pragma once
#include <vector>
#include <glad/glad.h>
#include <J3ML/LinearAlgebra/Vector2.hpp>
#include <J3ML/LinearAlgebra/Vector2i.hpp>
#include <J3ML/LinearAlgebra/Vector3.hpp>
#include <J3ML/LinearAlgebra/Vector4.hpp>
#include <JGL/logger/logger.h>
#include <type_traits>
#include <vector>
#include <cstring>
#include <string>
namespace JGL {
class VRamList;
}
/// A wrapper for VBO, Storing texture coordinates or vertices or indices in vram.
/// A wrapped for "Vertex Buffer Object" In OpenGL, Store things in VRam.
class JGL::VRamList {
private:
GLuint list_handle = 0;
long size = 0;
long num_elements = 0;
bool element_array_buffer = false;
/// "Spin Locking" fix for multi-threading.
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& count);
void UpdateData(void* data, const long& offset, const long& count);
void Erase();
public:
VRamList() = default;
VRamList(const GLuint* data, const long& size);
VRamList(const GLfloat* data, const long& size);
VRamList(Vector2* data, const long& size);
VRamList(Vector3* data, const long& size);
VRamList(Vector4* data, const long& size);
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() : list_handle(0), num_elements(0), element_array_buffer(false), spin_lock(false) {}
public:
[[nodiscard]] GLuint GetHandle() const;
/// Returns the number of GLfloat or GLuint in the list.
[[nodiscard]] long GetSize() const;
/// Returns the number of elements in the list.
[[nodiscard]] long GetLength() const;
/// Returns the size of the data in bytes.
[[nodiscard]] long GetDataSize() const;
[[nodiscard]] bool IsIntegerArray() const;
[[nodiscard]] size_t GetDataSize() const;
/** Get VBO data back from the GPU. This is *bad* because the CPU is going to wait
* for the transfer to finish. Has limited use other than testing. */
[[nodiscard]] std::vector<GLfloat> GetDataF() const;
[[nodiscard]] std::vector<GLuint> GetDataUI() const;
[[nodiscard]] bool IsFloatArray() const;
void Erase();
/** 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);
/// Update the data of an existing VBO. Data would be the same type as the list already is.
/// "size" refers to the number of elements in data. Not the number of bytes.
void SetData(void* data, const long& size);
void SetData(const GLuint* data, const long& count);
void SetData(const Vector2i* data, const long& count);
/* Get VBO data back from the GPU. This is *very* slow because the CPU is going to wait
for the transfer to finish. I don't know why you'd want to do this outside of testing reasons.*/
template <typename T>
[[nodiscard]] std::vector<T> GetData() const {
GLenum buffer_type;
GLint current_buffer = 0;
if constexpr (std::is_same<T, GLfloat>::value)
buffer_type = GL_ARRAY_BUFFER,
glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &current_buffer);
else if constexpr (std::is_same<T, GLuint>::value)
buffer_type = GL_ELEMENT_ARRAY_BUFFER,
glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &current_buffer);
else
jlog::Fatal("Typename T must be either GLfloat or GLuint.");
/** Update only a portion of the data in a VBO. Must be same type.
* "offset" refers the number of Typename T into the buffer the data you want to change is.
* For ex, offset 0 and length of 1 overwrites the first value. Offset 1 the second etc */
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);
if ((element_array_buffer && buffer_type == GL_ARRAY_BUFFER) || (!element_array_buffer && buffer_type == GL_ELEMENT_ARRAY_BUFFER))
jlog::Fatal("Returning the contents of a VRamList using the incorrect Typename T?");
glBindBuffer(buffer_type, list_handle);
std::vector<T> data(size);
memcpy(data.data(), glMapBuffer(buffer_type, GL_READ_ONLY), size * sizeof(T));
glUnmapBuffer(buffer_type);
glBindBuffer(buffer_type, current_buffer);
return data;
}
void UpdateData(const GLuint* data, const long& offset, const long& count);
void UpdateData(const Vector2i* data, const long& offset, const long& count);
};

View File

@@ -0,0 +1,103 @@
#pragma once
#include <vector>
#include <JGL/types/VRamList.h>
#include <JGL/types/Skeleton.h>
#include <J3ML/LinearAlgebra/Vector3.hpp>
#include <J3ML/Geometry/Sphere.hpp>
#include <J3ML/Geometry/AABB.hpp>
#include <J3ML/Geometry/OBB.hpp>
namespace JGL {
/// A point that is part of an object in 3D space.
typedef Vector3 Vertex;
/// A direction vector which describes which way a triangle is facing.
typedef Vector3 Normal;
/// 2D positions that describe how a texture is to be wrapped around a 3D object.
typedef Vector2 TextureCoordinate;
/// Container for storing 3D models in v-ram, system memory, or both.
class VertexArray;
}
class JGL::VertexArray {
private:
Sphere me_sphere;
OBB me_obb;
protected:
virtual void CreateMESphere();
virtual void CreateMEOBB();
protected:
std::vector<Animation> animations{};
protected:
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.
*/
std::vector<Vertex> local_vertices{};
std::vector<unsigned int> local_indices{};
std::vector<TextureCoordinate> local_texture_coordinates{};
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]] 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;
[[nodiscard]] std::vector<unsigned int> GetLocalIndices() const;
[[nodiscard]] std::vector<TextureCoordinate> GetLocalTextureCoordinates() const;
[[nodiscard]] std::vector<Normal> GetLocalNormals() const;
public:
/// Returns true if the VertexArray does not have any animations.
bool Static();
public:
/// Provides the minimally enclosing bounding sphere of the vertex array given information from the instance.
/// @param scale The scale of the instance.
/// @param translate_part The center of the sphere would be shifted in 3D space by this. Primarily for world space.
[[nodiscard]] Sphere GetMESphere(const Vector3& scale = Vector3::One, const Vector3& translate_part = Vector3::Zero) const;
/// Provides the minimally enclosing bounding sphere of the vertex array given information from the instance.
/// @param instance_matrix A Matrix4x4 which contains scale, rotation, and translation.
/// @param translate Whether or not to translate to world space by the translate part of the Matrix.
[[nodiscard]] Sphere GetMESphere(const Matrix4x4& instance_matrix, bool translate = false) const;
/// Provides the minimally enclosing oriented bounding box of the vertex array given information from the instance.
/// @param rotation_matrix A Matrix3x3 representing rotation in 3D space.
/// @param scale The scale of the instance.
/// @param translate_part The center of the box would be shifted in 3D space by this. Primarily for world space.
[[nodiscard]] OBB GetMEOBB(const Matrix3x3& rotation_matrix, const Vector3& scale = Vector3::One, const Vector3& translate_part = Vector3::Zero) const;
/// Provides the minimally enclosing oriented bounding box of the vertex array given information from the instance.
/// @param instance_matrix A Matrix4x4 which contains scale, rotation, and translation.
/// @param translate Whether or not to translate to world space by the translate part of the Matrix.
[[nodiscard]] OBB GetMEOBB(const Matrix4x4& instance_matrix, bool translate = false) const;
/// Provides the minimally enclosing axis-aligned bounding box of the vertex array given information from the instance.
/// @param rotation_matrix A Matrix3x3 representing rotation in 3D space.
/// @param scale The scale of the instance.
/// @param translate_part The center of the box would be shifted in 3D space by this. Primarily for world space.
[[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);
/// 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);

198
main.cpp
View File

@@ -1,18 +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:
@@ -22,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)
@@ -33,23 +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::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);
J2D::DrawString(Colors::White, std::format("{:.1f},{:.1f}", position.x, position.y), position.x, position.y, 1.f, 10, FreeSans);
}
};
/// A 3D Camera Controller.
class Camera {
public:
Vector3 position = {0,0,0};
@@ -93,53 +107,60 @@ Gizmo b({200, 250});
Gizmo c({350, 300});
Gizmo d({450, 250});
JGL::Font FreeSans;
Texture* image;
Texture* image_mask;
RenderTarget* j2d_render_target;
class JGLDemoWindow : public ReWindow::RWindow
class JGLDemoWindow : public ReWindow::OpenGLWindow
{
public:
void initGL() {
camera = new Camera;
gladLoadGL();
if (!JGL::MeetsRequirements()) {
Logger::Fatal("The graphics driver does not meet the minimum requirements to run this program.");
exit(-1);
}
JGL::InitTextEngine();
JGL::Update(getSize());
J3D::Init(getSize(), 90, 100);
if (!JGL::Init({ GetSize().x, 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({500, 500}, {255,0,0,0});
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);
//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() {
JGL::Update(getSize());
pulse++;
float dt = GetDeltaTime();
JGL::Update({ GetSize().x, GetSize().y });
if (fov_increasing)
fov += 0.25;
fov += 0.025;
else
fov -= 0.50;
fov -= 0.050;
if (fov >= 120)
fov_increasing = false;
else if (fov <= 75)
fov_increasing = true;
J3D::ChangeFOV(fov);
//J3D::ChangeFOV(fov);
sprite_radians += 0.05;
textAngle.y += 2.0f;
sprite_radians += 0.005;
textAngle.yaw += 1;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
@@ -147,39 +168,50 @@ 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, true);
//J3D::WireframeSphere(Colors::Green, {0,0,0.5f}, 0.25f, 1, 128, 128);
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});
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::End();
J2D::Begin();
J2D::Begin(j2d_render_target, true);
J2D::FillRect(Colors::Blue, {0,52}, {100,100});
J2D::DrawSprite(*image, {300, 300}, 0, {0,0}, {1, 1}, Colors::White, Direction::Vertical | Direction::Horizontal);
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});
J2D::DrawSprite(image, {300, 400}, sprite_radians * 0.10f, {0.5,0.5}, {1, 1}, Colors::White);
J2D::DrawMirrorSprite(image, {400, 300}, Direction::Horizontal | Direction::Vertical, sprite_radians, {0.5,0.5}, {1, 1}, Colors::White);
J2D::DrawPartialSprite(image, Vector2(225, 300), Vector2(image->GetDimensions()) * 0.25, Vector2(image->GetDimensions()) * 0.75, sprite_radians, {0.5, 0.5}, {1,1}, Colors::White);
J2D::FillRect(Colors::Pinks::HotPink, {68, 120}, {32, 32});
J2D::FillGradientRect(Colors::Red, Colors::Blue, Direction::Diagonal_SWNE, {100,52}, {100,100});
J2D::FillRoundedRect(Colors::Red, {200, 52}, {100, 100}, 8, 8);
J2D::FillRoundedRect(Colors::Purples::BlueViolet, {300, 52}, {100, 100}, 8, 4);
J2D::FillCircle(Colors::White, {52, 204}, 50, 24);
J2D::OutlineCircle(Colors::White, {153, 204}, 50, 24);
J2D::FillChamferRect(Colors::Reds::LightSalmon, {150, 400}, {64, 64}, 5);
J2D::OutlineRoundedRect(Colors::Reds::LightCoral, {250, 350}, {128, 128}, 10, 2);
std::vector<Vector2> points = {{1,1}, {4,4}, {8,8}, {16,16}, {32,32}};
J2D::FillGradientTriangle(Color4(Colors::Red), Color4(Colors::Green), Color4(Colors::Blue), {{0, 275}, {0, 375}, {100, 375}});
J2D::OutlineTriangle(Colors::Blue, {{100, 275}, {0, 275}, {100, 375}});
J2D::DrawGradientLine(Colors::Red, Colors::Blue, {105, 375}, {200, 275}, 2);
auto result = Jupiteroid.MeasureString("Jupiteroid Font", 16);
J2D::FillRect(Colors::Gray, {0, 0}, result);
J2D::DrawString(Colors::Green, "Jupteroid Font", 0.f, 0, 1.f, 16, Jupiteroid);
J2D::DrawString(Colors::White, "Position: " + std::to_string(camera->position.x) + " " + std::to_string(camera->position.y) + " " + std::to_string(camera->position.z), 0, 16, 1,16, Jupiteroid);
J2D::DrawString(Colors::White, "ViewAngle: " + std::to_string(camera->angle.x) + " " + std::to_string(camera->angle.y) + " " + std::to_string(camera->angle.z), 0, 33, 1,16, Jupiteroid);
J2D::DrawString(Colors::White, "Framerate: " + std::to_string((int) fps), 0, 48, 1, 16, Jupiteroid);
J2D::OutlinePolygon(Colors::White, {{200, 400}, {220, 420}, {220, 430}, {230, 410}, {200, 400}});
//J2D::FillPolygon(Colors::White, {{200, 400}, {220, 420}, {220, 430}, {230, 410}, {200, 400}});
std::array<Vector2, 5> polygon = {Vector2(200, 400), {220, 420}, {220, 430}, {230, 410}, {200, 400}};
J2D::OutlinePolygon(Colors::White, polygon.data(), polygon.size());
J2D::DrawCubicBezierCurve(Colors::Blues::CornflowerBlue,
a.position,
b.position,
@@ -193,29 +225,55 @@ public:
d.Draw();
J2D::End();
//Draw the Render Target that we just drew all that stuff onto.
/*
J2D::Begin();
J2D::DrawRenderTargetAsSprite(*j2d_render_target, {0, 0}, 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::End();
*/
}
void OnRefresh(float elapsed) override {
fps = GetRefreshRate();
if (IsKeyDown(Keys::RightArrow))
camera->angle.y += 45.f * elapsed;
if (IsKeyDown(Keys::LeftArrow))
camera->angle.y -= 45.f * elapsed;
if (IsKeyDown(Keys::UpArrow))
camera->angle.x -= 45.f * elapsed;
if (IsKeyDown(Keys::DownArrow))
camera->angle.x += 45.f * elapsed;
if (IsKeyDown(Keys::Space))
camera->position.y += 1.f * elapsed;
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();
@@ -224,7 +282,7 @@ public:
d.Grab();
}
void OnMouseButtonUp(const ReWindow::WindowEvents::MouseButtonUpEvent & ev) override
void OnMouseButtonUp(const ReWindow::MouseButtonUpEvent & ev) override
{
RWindow::OnMouseButtonUp(ev);
a.Release();
@@ -234,25 +292,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

View File

@@ -2,772 +2,47 @@
// Created by dawsh on 1/17/24.
//
#include <JGL/JGL.h>
#include <glad/glad.h>
#include <J3ML/Algorithm/Bezier.hpp>
#include <JGL/JGL.h>
#include <JGL/logger/logger.h>
JGL::RenderTarget* render_target = nullptr;
GLfloat oldColor[4] = {0, 0, 0, 1};
GLfloat baseColor[4] = {1, 1, 1, 1};
GLuint current_fbo = 0;
GLint viewport[4] = {0, 0, 0, 0};
bool inJ2D = false;
bool inJ3D = false;
bool wasTexture2DEnabled = false;
bool wasTextureCoordArrayEnabled = false;
bool wasDepthTestEnabled = false;
bool wasVertexArraysEnabled = false;
bool wasCullFaceEnabled = false;
bool wasBlendEnabled = false;
bool wasColorArrayEnabled = false;
GLint activeTextureUnit = 0;
#include "renderer/OpenGL/internals/internals.h"
namespace JGL {
using namespace J3ML;
Vector2 wS;
void Update(const Vector2& window_size) {
wS = window_size;
glViewport(0, 0, (int) wS.x, (int) wS.y);
bool Init(const Vector2i& ws, float fovY, float far_plane) {
gladLoadGL();
if (!MeetsRequirements()) {
Logger::Fatal("The graphics driver does not meet the minimum requirements to run this program.");
return false;
}
InitTextEngine();
Fonts::Init();
ShapeCache::Init();
JGL::window_size = ws;
J3D::fov = fovY;
J3D::far_plane = far_plane;
return true;
}
void Update(const Vector2i& ws) {
if (state_stack.Size())
Logger::Error("You shouldn't be running JGL::Update while inside a J2D or J3D context.");
JGL::window_size = ws;
glViewport(0, 0, window_size.x, window_size.y);
}
bool MeetsRequirements() {
if (!GLAD_GL_VERSION_2_1)
return false;
if (!GLAD_GL_EXT_framebuffer_object)
return false;
if (!GLAD_GL_ARB_framebuffer_object)
return false;
if (!GLAD_GL_ARB_depth_texture)
return false;
if (!GLAD_GL_ARB_shadow)
return false;
return true;
}
#pragma region J2D
void J2D::Begin(RenderTarget* rt, bool clear_buffers) {
GLfloat old_clear_color[4];
if (rt != nullptr) {
render_target = rt;
glGetFloatv(GL_COLOR_CLEAR_VALUE, old_clear_color);
glGetIntegerv(GL_VIEWPORT, viewport);
current_fbo = JGL::RenderTarget::GetActiveGLFramebufferHandle();
JGL::RenderTarget::SetActiveGLRenderTarget(*rt);
}
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
if (rt == nullptr)
glViewport(0, 0, (int) wS.x, (int) wS.y),
glOrtho(0, wS.x, wS.y, 0, -1, 1);
else
glOrtho(0, rt->GetDimensions().x, rt->GetDimensions().y, 0, -1, 1);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
//Get what the draw color was before we did anything.
glGetFloatv(GL_CURRENT_COLOR, oldColor);
glColor4fv(baseColor);
glGetIntegerv(GL_ACTIVE_TEXTURE, &activeTextureUnit);
activeTextureUnit = activeTextureUnit - GL_TEXTURE0;
if (activeTextureUnit != 0)
glActiveTexture(GL_TEXTURE0);
if (glIsEnabled(GL_DEPTH_TEST))
wasDepthTestEnabled = true,
glDisable(GL_DEPTH_TEST);
else
wasDepthTestEnabled = false;
if (!glIsEnabled(GL_VERTEX_ARRAY))
wasVertexArraysEnabled = false,
glEnableClientState(GL_VERTEX_ARRAY);
else
wasVertexArraysEnabled = true;
if (!glIsEnabled(GL_CULL_FACE))
wasCullFaceEnabled = false,
glEnable(GL_CULL_FACE),
glCullFace(GL_BACK);
else
wasCullFaceEnabled = true;
if (!glIsEnabled(GL_BLEND))
wasBlendEnabled = false,
glEnable(GL_BLEND),
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
else
wasBlendEnabled = true;
if (!glIsEnabled(GL_TEXTURE_2D))
wasTexture2DEnabled = false,
glEnable(GL_TEXTURE_2D);
else
wasTexture2DEnabled = true;
if (!glIsEnabled(GL_TEXTURE_COORD_ARRAY))
wasTextureCoordArrayEnabled = false,
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
else
wasTextureCoordArrayEnabled = true;
if (glIsEnabled(GL_COLOR_ARRAY))
wasColorArrayEnabled = true,
glDisableClientState(GL_COLOR_ARRAY);
else
wasColorArrayEnabled = false;
if (!inJ3D)
inJ2D = true;
else { Logger::Error("Attempt to Begin J2D inside of J3D context."); }
if (rt != nullptr && clear_buffers) {
glClearColor(rt->GetClearColor().R(), rt->GetClearColor().G(), rt->GetClearColor().B(), rt->GetClearColor().A());
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glClearColor(old_clear_color[0], old_clear_color[1], old_clear_color[2], old_clear_color[3]);
}
}
void J2D::End() {
//Change back to the previous projection (The 3D one in Re3D's case.)
glPopMatrix();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
if (wasDepthTestEnabled)
glEnable(GL_DEPTH_TEST);
if (!wasVertexArraysEnabled)
glDisableClientState(GL_VERTEX_ARRAY);
if (!wasCullFaceEnabled)
glDisable(GL_CULL_FACE);
if (!wasBlendEnabled)
glDisable(GL_BLEND);
if (!wasTexture2DEnabled)
glDisable(GL_TEXTURE_2D);
if (!wasTextureCoordArrayEnabled)
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
if (wasColorArrayEnabled)
glEnableClientState(GL_COLOR_ARRAY);
//Select whatever texture_handle unit was selected before.
glActiveTexture(GL_TEXTURE0 + activeTextureUnit);
//Put the draw color back how it was before.
glColor4fv(oldColor);
if (render_target != nullptr) {
render_target = nullptr;
glBindFramebuffer(GL_FRAMEBUFFER, current_fbo);
glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
}
inJ2D = false;
}
void J2D::DrawPoint(const Color4& color, const Vector2& coordinates, float radius) {
if (!inJ2D)
Logger::Error("Drawing J2D element before J2D begin.");
glPointSize(radius);
glColor4ubv(color.ptr());
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), coordinates.ptr());
glDrawArrays(GL_POINTS, 0, 1);
glColor4fv(baseColor);
}
void J2D::DrawPoint(const Color4& color, float x, float y, float radius) {
DrawPoint(color, {x, y}, radius);
}
void J2D::DrawLine(const Color4& color, const Vector2& A, const Vector2& B, float thickness) {
if (!inJ2D)
Logger::Error("Drawing J2D element before J2D begin.");
Vector2 vertices[] = {A, B};
glLineWidth(thickness);
glColor4ubv(color.ptr());
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices);
glDrawArrays(GL_LINES, 0, 2);
glColor4fv(baseColor);
}
void J2D::DrawLine(const Color4& color, float x, float y, float w, float h, float thickness) {
J2D::DrawLine(color, {x, y}, {w, h}, thickness);
}
void J2D::DrawGradientLine(const Color4& color1, const Color4& color2, const Vector2& A, const Vector2& B, float thickness) {
if (!inJ2D)
Logger::Error("Drawing J2D element before J2D begin.");
Vector2 vertices[] = {A, B};
GLfloat colors[8] = {color1.RedChannelNormalized(), color1.GreenChannelNormalized(), color1.BlueChannelNormalized(), color1.AlphaChannelNormalized(),
color2.RedChannelNormalized(), color2.GreenChannelNormalized(), color2.BlueChannelNormalized(), color2.AlphaChannelNormalized() };
glEnableClientState(GL_COLOR_ARRAY);
glLineWidth(thickness);
glColorPointer(4,GL_FLOAT,sizeof(Color4), colors);
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices);
glDrawArrays(GL_LINES, 0, 2);
glDisableClientState(GL_COLOR_ARRAY);
glColor4fv(baseColor);
}
void DrawGradientLine(const Color4& color1, const Color4& color2, float x, float y, float w, float h, float thickness) {
J2D::DrawGradientLine(color1, color2, {x, y}, {w, h}, thickness);
}
void J2D::OutlineRect(const Color4& color, const Vector2& pos, const Vector2& size, float thickness) {
if (!inJ2D)
Logger::Error("Drawing J2D element before J2D begin.");
Vector2 vertices[] = {{pos.x, pos.y}, {pos.x, pos.y + size.y}, {pos.x + size.x, pos.y + size.y}, {pos.x + size.x, pos.y}};
glLineWidth(thickness);
glColor4ubv(color.ptr());
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices);
glDrawArrays(GL_LINE_LOOP, 0, 4);
glColor4fv(baseColor);
}
void J2D::FillRect(const Color4& color, const Vector2& pos, const Vector2& size) {
if (!inJ2D)
Logger::Error("Drawing J2D element before J2D begin.");
Vector2 vertices[] = {{pos.x, pos.y}, {pos.x, pos.y + size.y}, {pos.x + size.x, pos.y + size.y}, {pos.x + size.x, pos.y}};
glColor4ubv(color.ptr());
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices);
glDrawArrays(GL_QUADS, 0, 4);
glColor4fv(baseColor);
}
void J2D::FillGradientRect(const Color4& color1, const Color4& color2, const Direction& gradient, const Vector2& pos, const Vector2& size) {
if (!inJ2D)
Logger::Error("Drawing J2D element before J2D begin.");
Vector2 vertices[] = {{pos.x, pos.y}, {pos.x, pos.y + size.y}, {pos.x + size.x, pos.y + size.y}, {pos.x + size.x, pos.y}};
std::vector<GLfloat> colors{};
if (gradient == Direction::Horizontal)
colors = {color1.RedChannelNormalized(), color1.GreenChannelNormalized(), color1.BlueChannelNormalized(), color1.AlphaChannelNormalized(), color1.RedChannelNormalized(), color1.GreenChannelNormalized(), color1.BlueChannelNormalized(), color1.AlphaChannelNormalized(),
color2.RedChannelNormalized(), color2.GreenChannelNormalized(), color2.BlueChannelNormalized(), color2.AlphaChannelNormalized(), color2.RedChannelNormalized(), color2.GreenChannelNormalized(), color2.BlueChannelNormalized(), color2.AlphaChannelNormalized()};
else if (gradient == Direction::Vertical)
colors = {color1.RedChannelNormalized(), color1.GreenChannelNormalized(), color1.BlueChannelNormalized(), color1.AlphaChannelNormalized(), color2.RedChannelNormalized(), color2.GreenChannelNormalized(), color2.BlueChannelNormalized(), color2.AlphaChannelNormalized(),
color2.RedChannelNormalized(), color2.GreenChannelNormalized(), color2.BlueChannelNormalized(), color2.AlphaChannelNormalized(), color1.RedChannelNormalized(), color1.GreenChannelNormalized(), color1.BlueChannelNormalized(), color1.AlphaChannelNormalized()};
else if (gradient == Direction::Diagonal_SWNE)
colors = {(color1.RedChannelNormalized() + color2.RedChannelNormalized()) / 2.f, (color1.GreenChannelNormalized() + color2.GreenChannelNormalized()) / 2.f, (color1.BlueChannelNormalized() + color2.BlueChannelNormalized()) / 2.f, (color1.AlphaChannelNormalized() + color2.AlphaChannelNormalized()) / 2.f,
color1.RedChannelNormalized(), color1.GreenChannelNormalized(), color1.BlueChannelNormalized(), color1.AlphaChannelNormalized(), (color1.RedChannelNormalized() + color2.RedChannelNormalized()) / 2.f, (color1.GreenChannelNormalized() + color2.GreenChannelNormalized()) / 2.f,
(color1.BlueChannelNormalized() + color2.BlueChannelNormalized()) / 2.f, (color1.AlphaChannelNormalized() + color2.AlphaChannelNormalized()) / 2.f, color2.RedChannelNormalized(), color2.GreenChannelNormalized(), color2.BlueChannelNormalized(), color2.AlphaChannelNormalized()};
else if (gradient == Direction::Diagonal_NWSE)
colors = {color1.RedChannelNormalized(), color1.GreenChannelNormalized(), color1.BlueChannelNormalized(), color1.AlphaChannelNormalized(),(color1.RedChannelNormalized() + color2.RedChannelNormalized()) / 2.f, (color1.GreenChannelNormalized() + color2.GreenChannelNormalized()) / 2.f,
(color1.BlueChannelNormalized() + color2.BlueChannelNormalized()) / 2.f, (color1.AlphaChannelNormalized() + color2.AlphaChannelNormalized()) / 2.f, color2.RedChannelNormalized(), color2.GreenChannelNormalized(), color2.BlueChannelNormalized(), color2.AlphaChannelNormalized(),
(color1.RedChannelNormalized() + color2.RedChannelNormalized()) / 2.f, (color1.GreenChannelNormalized() + color2.GreenChannelNormalized()) / 2.f, (color1.BlueChannelNormalized() + color2.BlueChannelNormalized()) / 2.f,(color1.AlphaChannelNormalized() + color2.AlphaChannelNormalized()) / 2.f};
glEnableClientState(GL_COLOR_ARRAY);
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices);
glColorPointer(4, GL_FLOAT, 0, colors.data());
glDrawArrays(GL_QUADS, 0, 4);
glDisableClientState(GL_COLOR_ARRAY);
glColor4fv(baseColor);
}
void J2D::FillRoundedRect(const Color4& color, const Vector2& pos, const Vector2& size, float radius, unsigned int subdivisions) {
if (!inJ2D)
Logger::Error("Drawing J2D element before J2D begin.");
J2D::FillRect(color, {pos.x + radius, pos.y}, {size.x - 2 * radius, size.y});
J2D::FillRect(color, {pos.x, pos.y + radius}, {size.x, size.y - 2 * radius});
J2D::FillCircle(color, {pos.x + radius, pos.y + radius}, radius, subdivisions);
J2D::FillCircle(color, {pos.x + size.x - radius, pos.y + radius}, radius, subdivisions);
J2D::FillCircle(color, {pos.x + radius, pos.y + size.y - radius}, radius, subdivisions);
J2D::FillCircle(color, {pos.x + size.x - radius, pos.y + size.y - radius}, radius, subdivisions);
}
void
J2D::DrawRenderTargetAsSprite(const JGL::RenderTarget& rt, const Vector2& position, float rad_rotation, const Vector2& origin,
const Vector2& scale, const Color4& color, Direction inversion) {
//Correct for the render-target being upside-down.
Direction d{};
if (inversion == Direction::None)
d = Direction::Vertical;
else if (inversion == Direction::Horizontal)
d = Direction::Horizontal | Direction::Vertical;
else if (inversion& Direction::Horizontal && inversion& Direction::Vertical)
d = Direction::Horizontal;
//Change the blending mode such that the alpha doesn't get multiplied again.
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
//J2D::DrawSprite(*rt.GetJGLTexture(), position, rad_rotation, origin, scale, color, d);
J2D::DrawPartialSprite(*rt.GetJGLTexture(), position, {0, 0}, rt.GetDimensions(), rad_rotation, origin, scale, color, d);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
void J2D::DrawSprite(const Texture& texture, const Vector2& pos, float rad_rotation, const Vector2& origin,
const Vector2& scale, const Color4& color, Direction inversion) {
if (!inJ2D)
Logger::Error("Drawing J2D element before J2D begin.");
const Vector2 size = texture.GetDimensions();
std::array<Vector2, 4> textureCoordinates = {Vector2(0, 0), Vector2(0, 1), Vector2(1, 1), Vector2(1, 0)};
// TODO: Kind of a mess, refactor to be more sensible later.
// Factors in scaling and origin correctly.
// i.e. to render at 2x size, from the center, at coords XY, use {2, 2} scale, and {0.5, 0.5} offset.
const Vector2 offset = origin * size;
Vector2 pos2 = pos;
Vector2 scaled_size = scale * size;
Vector2 size2 = scaled_size;
float cos_theta = std::cos(rad_rotation);
float sin_theta = std::sin(rad_rotation);
std::array<Vector2, 4> vertices =
{
pos2, // Top-left vertex
{pos2.x, pos2.y + size2.y}, // Bottom-left
{pos2.x + size2.x, pos2.y + size2.y}, // Bottom-right
{pos2.x + size2.x, pos2.y} // Top-right
};
//Rotate the vertices about the origin by float rad_rotation.
if (rad_rotation != 0)
for (auto& v: vertices)
v = {(v.x - pos2.x - offset.x * scale.x) * cos_theta - (v.y - pos2.y - offset.y * scale.y) * sin_theta +
pos2.x + offset.x * scale.x,
(v.x - pos2.x - offset.x * scale.x) * sin_theta + (v.y - pos2.y - offset.y * scale.y) * cos_theta +
pos2.y + offset.y * scale.y
};
if (inversion == Direction::Vertical)
textureCoordinates = {Vector2(0, 1), Vector2(0, 0), Vector2(1, 0), Vector2(1, 1)};
else if (inversion == Direction::Horizontal)
textureCoordinates = {Vector2(1, 0), Vector2(1, 1), Vector2(0, 1), Vector2(0, 0)};
else if ((inversion& Direction::Horizontal) && (inversion& Direction::Vertical))
textureCoordinates = {Vector2(1, 1), Vector2(1, 0), Vector2(0, 0), Vector2(0, 1)};
glColor4ubv(color.ptr());
glBindTexture(GL_TEXTURE_2D, texture.GetGLTextureHandle());
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices.data());
glTexCoordPointer(2, GL_FLOAT, sizeof(Vector2), textureCoordinates.data());
glDrawArrays(GL_QUADS, 0, 4);
glBindTexture(GL_TEXTURE_2D, 0);
glColor4fv(baseColor);
}
void J2D::DrawSprite(const Texture& texture, float positionX, float positionY, float rad_rotation, float originX,
float originY, float scaleX, float scaleY, const Color4& color, Direction inversion) {
DrawSprite(texture,
{positionX, positionY},
rad_rotation,
{originX, originY},
{scaleX, scaleY},
color, inversion);
}
void J2D::DrawPartialSprite(const Texture& texture, const Vector2 &position, const Vector2& sub_texture_position,
const Vector2& sub_texture_size, float rad_rotation, const Vector2& origin,
const Vector2& scale, const Color4& color, Direction inversion) {
if (!inJ2D)
Logger::Error("Drawing J2D element before J2D begin.");
const Vector2 textureSize = texture.GetDimensions();
// Calculate texture coordinates (relative to the whole texture)
std::array<GLfloat, 8> textureCoordinates = {
sub_texture_position.x / textureSize.x,
sub_texture_position.y / textureSize.y,
sub_texture_position.x / textureSize.x,
(sub_texture_position.y + sub_texture_size.y) / textureSize.y,
(sub_texture_position.x + sub_texture_size.x) / textureSize.x,
(sub_texture_position.y + sub_texture_size.y) / textureSize.y,
(sub_texture_position.x + sub_texture_size.x) / textureSize.x,
sub_texture_position.y / textureSize.y
};
if (inversion& Direction::Vertical)
std::swap(textureCoordinates[1], textureCoordinates[3]),
std::swap(textureCoordinates[5], textureCoordinates[7]);
if (inversion& Direction::Horizontal)
std::swap(textureCoordinates[0], textureCoordinates[6]),
std::swap(textureCoordinates[2], textureCoordinates[4]);
const Vector2 offset = origin * sub_texture_size;
Vector2 pos2 = position;
Vector2 scaled_size = scale * sub_texture_size;
Vector2 size2 = scaled_size;
float cos_theta = std::cos(rad_rotation);
float sin_theta = std::sin(rad_rotation);
std::array<Vector2, 4> vertices =
{
pos2, // Top-left
{pos2.x, pos2.y + size2.y}, // Bottom-left
{pos2.x + size2.x, pos2.y + size2.y},// Bottom-right
{pos2.x + size2.x, pos2.y} // Top-right
};
//Rotate the vertices about the origin by float rad_rotation.
if (rad_rotation != 0)
for (auto& v: vertices)
v = {(v.x - pos2.x - offset.x * scale.x) * cos_theta - (v.y - pos2.y - offset.y * scale.y) * sin_theta +
pos2.x + offset.x * scale.x,
(v.x - pos2.x - offset.x * scale.x) * sin_theta + (v.y - pos2.y - offset.y * scale.y) * cos_theta +
pos2.y + offset.y * scale.y};
glColor4ubv(color.ptr());
glBindTexture(GL_TEXTURE_2D, texture.GetGLTextureHandle());
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices.data());
glTexCoordPointer(2, GL_FLOAT, 0, textureCoordinates.data());
glDrawArrays(GL_QUADS, 0, 4);
glBindTexture(GL_TEXTURE_2D, 0);
glColor4fv(baseColor);
}
void
J2D::DrawPartialSprite(const JGL::Texture& texture, float positionX, float positionY, float sub_texture_positionX,
float sub_texture_positionY, unsigned int sub_texture_sizeX,
unsigned int sub_texture_sizeY, float originX, float originY, float rad_rotation,
float scaleX, float scaleY, const Color4& color, JGL::Direction inversion) {
J2D::DrawPartialSprite(texture, {positionX, positionY}, {sub_texture_positionX, sub_texture_positionY},
{(float) sub_texture_sizeX, (float) sub_texture_sizeY}, rad_rotation, {originX, originY},
{scaleX, scaleY}, color, inversion);
}
void J2D::DrawMirrorSprite(const Texture& texture, const Vector2& position, Direction mirror_axis, float rad_rotation, const Vector2& origin, const Vector2& scale, const Color4& color) {
if (!inJ2D)
Logger::Error("Drawing J2D element before J2D begin.");
if (mirror_axis == Direction::None)
Logger::Warning("Drawing non-mirrored sprite with J2D::DrawMirrorSprite?");
glBindTexture(GL_TEXTURE_2D, texture.GetGLTextureHandle());
Vector2 size = texture.GetDimensions();
std::array<Vector2, 4> textureCoordinates = {Vector2(0, 0), Vector2(0, 1), Vector2(1, 1), Vector2(1, 0)};
if (mirror_axis == Direction::Horizontal)
size.x *= 2,
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT),
textureCoordinates = {Vector2(0, 0), Vector2(0, 1), Vector2(2, 1), Vector2(2, 0)};
else if (mirror_axis == Direction::Vertical)
size.y *= 2,
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT),
textureCoordinates = {Vector2(0, 0), Vector2(0, 2), Vector2(1, 2), Vector2(1, 0)};
else if ((mirror_axis& Direction::Horizontal) && (mirror_axis& Direction::Vertical))
size *= 2,
textureCoordinates = {Vector2(0, 0), Vector2(0, 2), Vector2(2, 2), Vector2(2, 0)};
const Vector2 offset = origin * size;
Vector2 pos2 = position;
Vector2 size2 = scale * size;
float cos_theta = std::cos(rad_rotation);
float sin_theta = std::sin(rad_rotation);
std::array<Vector2, 4> vertices =
{
pos2,
{pos2.x, pos2.y + size2.y},
{pos2.x + size2.x, pos2.y + size2.y},
{pos2.x + size2.x, pos2.y}
};
if (rad_rotation != 0)
for (auto& v : vertices)
v = {
(v.x - pos2.x - offset.x * scale.x) * cos_theta - (v.y - pos2.y - offset.y * scale.y) * sin_theta + pos2.x + offset.x * scale.x,
(v.x - pos2.x - offset.x * scale.x) * sin_theta + (v.y - pos2.y - offset.y * scale.y) * cos_theta + pos2.y + offset.y * scale.y
};
glColor4ubv(color.ptr());
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices.data());
glTexCoordPointer(2, GL_FLOAT, sizeof(Vector2), textureCoordinates.data());
glDrawArrays(GL_QUADS, 0, 4);
//Reset the wrapping mode.
if (texture.GetWrappingMode() == 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 (texture.GetWrappingMode() == TextureWrappingMode::REPEAT)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT),
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
else if (texture.GetWrappingMode() == 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);
glBindTexture(GL_TEXTURE_2D, 0);
glColor4fv(baseColor);
}
void J2D::OutlineCircle(const Color4& color, const Vector2& center, float radius, unsigned int subdivisions, float thickness) {
if (!inJ2D)
Logger::Error("Drawing J2D element before J2D begin.");
float step = (2.f * Math::Pi) / (float) subdivisions;
std::vector<Vector2> vertices(subdivisions);
GLfloat angle, x, y;
int i = 0;
for (angle = 0.0f; angle < (2.f * Math::Pi); angle += step) {
x = radius * std::sin(angle) + center.x;
y = radius * std::cos(angle) + center.y;
if (i <= subdivisions)
vertices[i] = {x, y};
else
vertices.emplace_back(x, y);
i++;
}
glLineWidth(thickness);
glColor4ubv(color.ptr());
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices.data());
glDrawArrays(GL_LINE_LOOP, 0, (int) vertices.size());
glColor4fv(baseColor);
}
void J2D::FillCircle(const Color4& color, const Vector2& center, float radius, unsigned int subdivisions) {
if (!inJ2D)
Logger::Error("Drawing J2D element before J2D begin.");
GLfloat angle, x, y;
float step = (2.f * Math::Pi) / (float) subdivisions;
std::vector<Vector2> vertices(subdivisions);
/* Most of the time the vector "vertices" is either the same size or size + 1 of the number of subdivisions.
* Because a float is a decimal, It'd take too long to get rid of the emplace-back making us
* wait around for the container to resize. This gets rid of it for what we can guarantee. */
int i = 0;
for (angle = 0.0f; angle < (2.f * Math::Pi); angle += step) {
x = radius * std::sin(angle) + center.x;
y = radius * std::cos(angle) + center.y;
if (i <= subdivisions)
vertices[i] = {x, y};
else
vertices.emplace_back(x, y);
i++;
}
glColor4ubv(color.ptr());
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices.data());
glDrawArrays(GL_TRIANGLE_FAN, 0, (int) vertices.size());
glColor4fv(baseColor);
}
void J2D::OutlineTriangle(const Color4& color, const Triangle2D& tri, float thickness) {
if (!inJ2D)
Logger::Error("Drawing J2D element before J2D begin.");
Vector2 vertices[] = {{tri.A.x, tri.A.y}, {tri.B.x, tri.B.y}, {tri.C.x, tri.C.y}};
glLineWidth(thickness);
glColor4ubv(color.ptr());
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices);
glDrawArrays(GL_LINE_LOOP, 0, 3);
glColor4fv(baseColor);
}
void J2D::FillTriangle(const Color4& color, const Triangle2D& tri) {
if (!inJ2D)
Logger::Error("Drawing J2D element before J2D begin.");
Vector2 vertices[] = {{tri.A.x, tri.A.y}, {tri.B.x, tri.B.y}, {tri.C.x, tri.C.y}};
glColor4ubv(color.ptr());
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices);
glDrawArrays(GL_TRIANGLES, 0, 3);
glColor4fv(baseColor);
}
void J2D::FillGradientTriangle(const Color4& a_color, const Color4& b_color, const Color4& c_color, const Triangle2D& tri) {
if (!inJ2D)
Logger::Error("Drawing J2D element before J2D begin.");
Vector2 vertices[] = {{tri.A.x, tri.A.y}, {tri.B.x, tri.B.y}, {tri.C.x, tri.C.y}};
GLfloat colors[] = {a_color.RedChannelNormalized(), a_color.GreenChannelNormalized(), a_color.BlueChannelNormalized(), a_color.AlphaChannelNormalized(),
b_color.RedChannelNormalized(),b_color.GreenChannelNormalized(), b_color.BlueChannelNormalized(), b_color.AlphaChannelNormalized(),
c_color.RedChannelNormalized(), c_color.GreenChannelNormalized(), c_color.BlueChannelNormalized(),c_color.AlphaChannelNormalized()};
glEnableClientState(GL_COLOR_ARRAY);
glColorPointer(4, GL_FLOAT, sizeof(Color4), colors);
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices);
glDrawArrays(GL_TRIANGLES, 0, 3);
glDisableClientState(GL_COLOR_ARRAY);
glColor4fv(baseColor);
}
//TODO render all in once pass with GL_LINE_LOOP instead of separate lines.
void J2D::DrawCubicBezierCurve(const Color4 &color, const Vector2& controlA, const Vector2& pointA, const Vector2& pointB, const Vector2& controlB,
int subdivisions, float thickness) {
Vector2 last = controlA;
const Vector2& first = controlB;
for (int i = 0; i < subdivisions; ++i)
{
float alpha = (float) i / (float) subdivisions;
Vector2 step = J3ML::Algorithm::Bezier(alpha, controlA, pointA, pointB, controlB);
DrawLine(color, last, step, thickness);
last = step;
}
// Have to manually draw the last segment of the curve.
DrawLine(color, last, first, thickness);
}
void J2D::OutlinePolygon(const Color4 &color, const std::vector<Vector2>& points, float thickness) {
if (!inJ2D)
Logger::Error("Drawing J2D element before J2D begin.");
if (points.front() != points.back())
throw std::runtime_error("J2D::OutlinePolygon: The first point and the last point must connect.");
glLineWidth(thickness);
glColor4ubv(color.ptr());
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), points.data());
glDrawArrays(GL_LINE_LOOP, 0, (int) points.size());
glColor4fv(baseColor);
}
#pragma endregion
#pragma region J3D
std::array<GLfloat, 16> OpenGLPerspectiveProjectionRH(float fovY, float aspect, float z_near, float z_far) {
std::array<GLfloat, 16> result{};
GLfloat f = 1.0f / std::tan(fovY * 0.5f * Math::Pi / 180.0f);
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;
}
bool j3d_initialized = false;
float j3d_far_plane = 0;
float j3d_fov = 0;
void J3D::Init(const J3ML::LinearAlgebra::Vector2& window_size, float fov, float far_plane) {
wS = window_size;
j3d_far_plane = far_plane;
j3d_fov = fov;
j3d_initialized = true;
}
void J3D::ChangeFOV(float fov) {
j3d_fov = fov;
}
void J3D::ChangeFarPlane(float far_plane) {
j3d_far_plane = far_plane;
}
void J3D::Begin() {
if (!j3d_initialized)
throw std::runtime_error("You have to run J3D::Init before rendering 3D elements.");
auto aspect = (float) wS.x / (float) wS.y;
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glMultMatrixf(OpenGLPerspectiveProjectionRH(j3d_fov, aspect, 0.001, j3d_far_plane).data());
glMatrixMode(GL_MODELVIEW);
//Get what the draw color was before we did anything.
glGetFloatv(GL_CURRENT_COLOR, oldColor);
glColor4fv(baseColor);
wasDepthTestEnabled = false;
if (glIsEnabled(GL_DEPTH_TEST))
wasDepthTestEnabled = true,
glDisable(GL_DEPTH_TEST);
wasVertexArraysEnabled = false;
if (!glIsEnabled(GL_VERTEX_ARRAY))
wasVertexArraysEnabled = false,
glEnableClientState(GL_VERTEX_ARRAY);
if (!glIsEnabled(GL_TEXTURE_COORD_ARRAY))
wasTextureCoordArrayEnabled = false,
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
else
wasTextureCoordArrayEnabled = true;
wasTexture2DEnabled = true;
if (!glIsEnabled(GL_TEXTURE_2D))
wasTexture2DEnabled = false,
glEnable(GL_TEXTURE_2D);
wasCullFaceEnabled = false;
if (glIsEnabled(GL_CULL_FACE))
wasCullFaceEnabled = true,
glDisable(GL_CULL_FACE);
if (!glIsEnabled(GL_BLEND))
wasBlendEnabled = false,
glEnable(GL_BLEND),
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
else
wasBlendEnabled = true;
if (!inJ2D)
inJ3D = true;
else
Logger::Error("Can't begin J3D context inside J2D context.");
}
void J3D::End() {
if (wasDepthTestEnabled)
glEnable(GL_DEPTH_TEST);
if (!wasVertexArraysEnabled)
glDisableClientState(GL_VERTEX_ARRAY);
if (wasTexture2DEnabled)
glDisable(GL_TEXTURE_2D);
if (!wasBlendEnabled)
glDisable(GL_BLEND);
if (wasCullFaceEnabled)
glEnable(GL_CULL_FACE);
if (!wasTextureCoordArrayEnabled)
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
//Put the draw color back how it was before.
glColor4fv(oldColor);
inJ3D = false;
}
void J3D::DrawLine(const Color4& color, const Vector3& A, const Vector3& B, float thickness) {
if (!inJ3D)
Logger::Error("Attempt to Render 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(baseColor);
}
#pragma endregion
}

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());
}
}

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

File diff suppressed because it is too large Load Diff

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

@@ -0,0 +1,583 @@
#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 : current_state.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++)
current_state.optional_lights.push_back(lights[i]);
}
void JGL::J3D::Begin(bool two_pass) {
auto aspect = (float) window_size.x / (float) window_size.y;
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glMultMatrixf(OpenGLPerspectiveProjectionRH(fov, aspect, 0.001, far_plane).data());
glMatrixMode(GL_MODELVIEW);
//Get what the draw color was before we did anything.
glGetFloatv(GL_CURRENT_COLOR, current_state.draw_color);
glColor4fv(current_state.draw_color);
if (!glIsEnabled(GL_DEPTH_TEST))
current_state.depth_test = false,
glEnable(GL_DEPTH_TEST);
else
current_state.depth_test = true;
current_state.vertex_array = false;
if (!glIsEnabled(GL_VERTEX_ARRAY))
current_state.vertex_array = false,
glEnableClientState(GL_VERTEX_ARRAY);
if (glIsEnabled(GL_NORMAL_ARRAY))
current_state.normal_array = true,
glDisableClientState(GL_NORMAL_ARRAY);
if (glIsEnabled(GL_TEXTURE_COORD_ARRAY))
current_state.texture_coordinate_array = true,
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
if (glIsEnabled(GL_TEXTURE_2D))
current_state.texture_2D = true,
glDisable(GL_TEXTURE_2D);
current_state.cull_face = false;
if (glIsEnabled(GL_CULL_FACE))
current_state.cull_face = true,
glDisable(GL_CULL_FACE);
if (!glIsEnabled(GL_BLEND))
current_state.blend = false,
glEnable(GL_BLEND),
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
else
current_state.blend = true;
// Reset the lights.
current_state.required_lights = empty_light_array;
current_state.optional_lights = {};
glDisable(GL_LIGHTING);
}
void JGL::J3D::End() {
if (!current_state.depth_test)
glDisable(GL_DEPTH_TEST);
if (!current_state.vertex_array)
glDisableClientState(GL_VERTEX_ARRAY);
if (current_state.texture_2D)
glEnable(GL_TEXTURE_2D);
if (!current_state.blend)
glDisable(GL_BLEND);
if (current_state.cull_face)
glEnable(GL_CULL_FACE);
if (current_state.normal_array)
glEnableClientState(GL_NORMAL_ARRAY);
if (current_state.texture_coordinate_array)
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);
}
current_state.required_lights = empty_light_array;
current_state.optional_lights = {};
//Put the draw color back how it was before.
glColor4fv(current_state.draw_color);
}
void JGL::J3D::DrawLine(const Color4& color, const Vector3& A, const Vector3& B, float thickness) {
Vector3 vertices[] = {A, B};
glLineWidth(thickness);
glColor4ubv(color.ptr());
glVertexPointer(3, GL_FLOAT, sizeof(Vector3), vertices);
glDrawArrays(GL_LINES, 0, 2);
glColor4fv(current_state.draw_color);
}
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) {
// 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(current_state.draw_color);
}
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(current_state.draw_color);
}
void JGL::J3D::WireframeIcosphere(const Color4& color, const Vector3& position, float radius, float thickness, unsigned int subdivisions) {
// 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) {
// 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(current_state.draw_color);
}
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(current_state.draw_color);
}
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) {
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(current_state.draw_color);
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) {
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(current_state.draw_color);
}
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(current_state.draw_color);
}
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) {
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(current_state.draw_color);
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__
@@ -15,13 +16,15 @@
#include <JGL/types/Font.h>
#include <JGL/types/FontCache.h>
#include "JGL/logger/logger.h"
#include <JGL/logger/logger.h>
namespace JGL {
CachedFont* CacheFont(const Font& font, u32 size) {
glEnable(GL_TEXTURE_2D);
CachedFont* cachedFont;
FT_Set_Pixel_Sizes(font.face, 0, size);
jlog::Debug("Caching font data...");
Logger::Debug("Caching font data...");
GLuint texture_id;
glGenTextures(1, &texture_id);
glBindTexture(GL_TEXTURE_2D, texture_id);
@@ -88,6 +91,7 @@ namespace JGL {
xoffset += g->bitmap.width;
charcode = FT_Get_Next_Char(font.face, charcode, &gindex);
}
glDisable(GL_TEXTURE_2D);
return cachedFont;
}
@@ -95,18 +99,30 @@ namespace JGL {
// Offset by height to render at "correct" location.
y += size;
bool round_text_coords_for_crisp_rendering = true;
// TODO: This currently does not account for non-integer scale factors.
if (round_text_coords_for_crisp_rendering)
{
x = J3ML::Math::Floor(x);
y = J3ML::Math::Floor(y);
}
CachedFont* cachedFont = fontCache.getFont(size, font.index);
if (font.face == nullptr)
jlog::Fatal("Drawing a string with an uninitialized font?");
Logger::Fatal("Drawing a string with an uninitialized font?");
//If the font doesn't exist in the cache yet.
if (!cachedFont)
cachedFont = CacheFont(font, size);
glColor4ubv(color.ptr());
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnable(GL_TEXTURE_2D);
//Texture parameters are restored when the texture_handle is bound
glBindTexture(GL_TEXTURE_2D, *cachedFont->getTexture());
glBindTexture(GL_TEXTURE_2D, *cachedFont->getTextureHandle());
std::vector<std::array<GLfloat, 12>> vertices(text.size());
std::vector<std::array<GLfloat, 12>> texcoords(text.size());
@@ -142,17 +158,33 @@ 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(default_state.draw_color);
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;
scale = -scale;
float x = pos.x;
float y = pos.y;
float z = pos.z;
// TODO: this is broken because it causes the text to be shifted downwards.
/*
bool round_text_coords_for_crisp_rendering = true;
// TODO: This currently does not account for non-integer scale factors.
if (round_text_coords_for_crisp_rendering)
{
x = J3ML::Math::Floor(x);
y = J3ML::Math::Floor(y);
z = J3ML::Math::Floor(z);
}
*/
CachedFont* cachedFont = fontCache.getFont(size, font.index);
if (font.face == nullptr)
jlog::Fatal("Drawing a string with an uninitialized font?");
@@ -160,8 +192,10 @@ namespace JGL {
if (!cachedFont)
cachedFont = CacheFont(font, size);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnable(GL_TEXTURE_2D);
glColor4ubv(color.ptr());
glBindTexture(GL_TEXTURE_2D, *cachedFont->getTexture());
glBindTexture(GL_TEXTURE_2D, *cachedFont->getTextureHandle());
std::vector<std::array<GLfloat, 18>> vertices(text.size());
std::vector<std::array<GLfloat, 12>> texcoords(text.size());
@@ -210,7 +244,9 @@ namespace JGL {
glDisable(GL_CULL_FACE);
glBindTexture(GL_TEXTURE_2D, 0);
glColor4f(1, 1, 1, 1);
glColor4fv(current_state.draw_color);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisable(GL_TEXTURE_2D);
glPopMatrix();
}
}

View File

@@ -0,0 +1,210 @@
#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 = current_state.required_lights;
unsigned int required_light_count = 0;
for (const auto& i : result)
if (i) required_light_count++;
// If there is optional lights.
if (!current_state.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: current_state.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 (!current_state.optional_lights.empty())
return true;
for (unsigned int i = 0; i < current_state.required_lights.size(); i++)
if (current_state.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;
}
void JGL::StateStack::Push(const JGL::State& state) {
states.push_back(state);
}
void JGL::StateStack::Pop() {
states.pop_back();
}
JGL::State* JGL::StateStack::PreviousState() {
if (states.empty())
return nullptr;
return &states.back();
}
JGL::State JGL::State::SaveState() {
State result;
result.depth_test = glIsEnabled(GL_DEPTH_TEST);
result.vertex_array = glIsEnabled(GL_VERTEX_ARRAY);
result.normal_array = glIsEnabled(GL_NORMAL_ARRAY);
result.cull_face = glIsEnabled(GL_CULL_FACE);
result.blend = glIsEnabled(GL_BLEND);
result.texture_2D = glIsEnabled(GL_TEXTURE_2D);
result.texture_coordinate_array = glIsEnabled(GL_TEXTURE_COORD_ARRAY);
result.color_array = glIsEnabled(GL_COLOR_ARRAY);
glGetIntegerv(GL_ACTIVE_TEXTURE, &result.selected_texture_unit);
result.selected_texture_unit -= GL_TEXTURE0;
glGetFloatv(GL_COLOR_CLEAR_VALUE, result.clear_color);
glGetFloatv(GL_CURRENT_COLOR, result.draw_color);
glGetIntegerv(GL_VIEWPORT, result.viewport);
glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&result.current_fbo);
glGetIntegerv(GL_BLEND_SRC, &result.blend_func[0]);
glGetIntegerv(GL_BLEND_DST, &result.blend_func[1]);
return result;
}
void JGL::State::RestoreState(const State& state) {
if (state.depth_test)
glEnable(GL_DEPTH_TEST);
else
glDisable(GL_DEPTH_TEST);
if (state.vertex_array)
glEnableClientState(GL_VERTEX_ARRAY);
else
glDisableClientState(GL_VERTEX_ARRAY);
if (state.normal_array)
glEnableClientState(GL_NORMAL_ARRAY);
else
glDisableClientState(GL_NORMAL_ARRAY);
if (state.cull_face)
glEnable(GL_CULL_FACE);
else
glDisable(GL_CULL_FACE);
if (state.blend)
glEnable(GL_BLEND);
else
glDisable(GL_BLEND);
glActiveTexture(GL_TEXTURE0 + state.selected_texture_unit);
glClientActiveTexture(GL_TEXTURE0 + state.selected_texture_unit);
if (state.texture_2D)
glEnable(GL_TEXTURE_2D);
else
glDisable(GL_TEXTURE_2D);
if (state.texture_coordinate_array)
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
else
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
if (state.color_array)
glEnable(GL_COLOR_ARRAY);
else
glDisable(GL_COLOR_ARRAY);
glBindFramebuffer(GL_FRAMEBUFFER, state.current_fbo);
glViewport(state.viewport[0], state.viewport[1], state.viewport[2], state.viewport[3]);
glClearColor(state.clear_color[0], state.clear_color[1], state.clear_color[2], state.clear_color[3]);
glColor4f(state.draw_color[0], state.draw_color[1], state.draw_color[2], state.draw_color[3]);
glBlendFunc(state.blend_func[0], state.blend_func[1]);
}

View File

@@ -0,0 +1,86 @@
#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 {
class State;
class StateStack;
}
class JGL::State {
public:
GLfloat clear_color[4] = {0, 0, 0, 1};
GLfloat draw_color[4] = {1, 1, 1, 1};
GLint viewport[4] = {0, 0, 0, 0};
GLint blend_func[2];
GLuint current_fbo = 0;
bool texture_2D = false;
bool texture_coordinate_array = false;
bool normal_array = false;
bool depth_test = false;
bool vertex_array = false;
bool cull_face = false;
bool blend = false;
bool color_array = false;
GLint selected_texture_unit = 0;
// List of lights required for each object in the scene. up-to 8. For example, the sun. Or a flash-light.
std::array<const LightBase*, 8> required_lights{};
// List of all lights in the scene.
std::vector<const LightBase*> optional_lights{};
public:
static State SaveState();
static void RestoreState(const State& state);
};
class JGL::StateStack {
private:
std::vector<State> states{};
public:
size_t Size() { return states.size(); }
void Push(const State& state);
void Pop();
State* PreviousState();
public:
StateStack() = default;
~StateStack() = default;
};
namespace JGL {
inline constexpr std::array<const LightBase*, 8> empty_light_array = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr };
std::vector<Vector3> TriangleMeshVertexNormals(const Vector3* vertices, const size_t& vertex_count, const unsigned int* indices, const size_t& index_count);
inline StateStack state_stack;
inline Vector2i window_size;
//inline State current_state;
}
namespace JGL::J2D {
inline State default_state
{
{0, 0, 0, 1},
{1, 1, 1, 1},
{0, 0, 0, 0},
{GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA},
0, false, false,
false, false, true, true,
true, false, 0,
{}, {}
};
}
namespace JGL::J3D {
inline State current_state;
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

@@ -3,7 +3,8 @@
#include <string>
#include <iostream>
#include <glad/glad.h>
#include <jlog/Logger.hpp>
#include <JGL/logger/logger.h>
#include <JGL/JGL.h>
#if __linux__
#include <freetype2/ft2build.h>
@@ -32,7 +33,7 @@ namespace JGL::Detail
// Keep note of this, might cause problems later?
if (ft != nullptr)
throw std::runtime_error("Error::FREETYPE: FT_Library was initialized but is already initialized.");
Logger::Fatal("Error::FREETYPE: FT_Library was initialized but is already initialized.");
if (FT_Init_FreeType(&ft))
return true;
@@ -52,38 +53,47 @@ namespace JGL::Detail
}
}
namespace JGL
{
namespace JGL {
bool InitTextEngine()
{
bool InitTextEngine() {
return Detail::InitTextEngine();
}
Font::Font(const std::filesystem::path& path)
{
Font::Font(const unsigned char* data, const size_t& size) {
if (Detail::ft == nullptr)
throw new std::runtime_error("Error::FREETYPE: FT_Library was not initialized before attempting to load a font!");
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;
throw new std::runtime_error("Error::FREETYPE: Failed to load font!");
//return -1;
}
unsigned int newIndex = 0;
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 >= newIndex)
newIndex = f.index + 1;
if (f.index >= new_index)
new_index = f.index + 1;
index = new_index;
index = newIndex;
Detail::fonts.push_back(font);
std::cout << "Loaded font from " << path << " with index " << newIndex << std::endl;
//return newIndex;
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()
{
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!");
if (FT_New_Face(Detail::ft, path.string().c_str(), 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 " << path << " with index " << new_index << std::endl;
}
Font::~Font() {
//Detail::UnloadFont(this->index);
}
@@ -123,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;
@@ -150,9 +164,6 @@ namespace JGL
if (extents.y < ptSize)
extents.y = ptSize;
}
return extents;
}
}

View File

@@ -2,7 +2,7 @@
using namespace JGL;
char CachedGlyph::getCharacter() {
char CachedGlyph::getCharacter() const {
return character;
}
@@ -25,11 +25,11 @@ void JGL::CachedFont::appendGlyph(JGL::CachedGlyph* glyph) {
glyphs.emplace(glyph->getCharacter(), glyph);
}
unsigned int JGL::CachedFont::getFontSize() {
unsigned int JGL::CachedFont::getFontSize() const {
return font_size;
}
unsigned int JGL::CachedFont::getFontIndex() {
unsigned int JGL::CachedFont::getFontIndex() const {
return font_index;
}
@@ -52,7 +52,7 @@ std::unordered_map<char, CachedGlyph*> CachedFont::getGlyphs() {
return glyphs;
}
const GLuint* CachedFont::getTexture() {
const GLuint* CachedFont::getTextureHandle() {
return &texture;
}
@@ -64,6 +64,15 @@ GLsizei CachedFont::getTextureHeight() const {
return texture_height;
}
void CachedFont::Erase() {
if (texture != 0)
glDeleteTextures(1, &texture);
}
CachedFont::~CachedFont() {
Erase();
}
void FontCache::appendFont(CachedFont* font) {
cachedFonts.push_back(font);
}

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

@@ -1,13 +1,14 @@
#include <JGL/types/RenderTarget.h>
#include <jlog/Logger.hpp>
#include <JGL/types/Texture.h>
#include <JGL/logger/logger.h>
#include <stdexcept>
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 {
@@ -25,52 +26,76 @@ GLuint JGL::RenderTarget::GetActiveGLFramebufferHandle() {
}
void JGL::RenderTarget::SetActiveGLRenderTarget(const RenderTarget& render_target) {
RenderTarget rt = render_target;
glBindFramebuffer(GL_FRAMEBUFFER, rt.GetGLFramebufferObjectHandle());
glViewport(0,0, rt.GetDimensions().x, rt.GetDimensions().y);
glBindFramebuffer(GL_FRAMEBUFFER, render_target.MSAAEnabled() ? render_target.msaa_framebuffer_object : render_target.GetGLFramebufferObjectHandle());
glViewport(0,0, render_target.GetDimensions().x, render_target.GetDimensions().y);
}
Vector2 JGL::RenderTarget::GetDimensions() const {
Vector2i JGL::RenderTarget::GetDimensions() const {
return size;
}
void JGL::RenderTarget::Erase() {
if (GetActiveGLFramebufferHandle() == framebuffer_object)
jlog::Warning("Deleting the framebuffer that's currently in use?");
texture->Erase();
Logger::Warning("Deleting the framebuffer that's currently in use?");
if (using_depth)
glDeleteRenderbuffers(1, &depth_buffer);
glDeleteFramebuffers(1, &framebuffer_object);
if (MSAAEnabled())
SetMSAAEnabled(MSAA_SAMPLE_RATE::MSAA_NONE);
}
Color4 JGL::RenderTarget::GetClearColor() const {
return clear_color;
}
JGL::RenderTarget::RenderTarget(const Vector2& size, const Color4& clear_color, bool use_depth) {
/// Idk why you'd ever want to clear it out if you're rendering onto a texture you passed in :shrug:.
JGL::RenderTarget::RenderTarget(const JGL::Texture* texture, const Color4& clear_color) {
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);
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->GetHandle(), 0);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
throw std::runtime_error("A new framebuffer could not be allocated.");
glBindFramebuffer(GL_FRAMEBUFFER, current_fbo);
glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
this->clear_color = clear_color;
this->texture = texture;
texture_created_by_us = false;
}
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?");
GLuint current_fbo = GetActiveGLFramebufferHandle();
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;
@@ -89,9 +114,13 @@ JGL::RenderTarget::RenderTarget(const Vector2& size, const Color4& clear_color,
glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
this->clear_color = clear_color;
this->size = size;
texture_created_by_us = true;
if (sample_rate != MSAA_SAMPLE_RATE::MSAA_NONE)
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();
@@ -99,5 +128,256 @@ 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 Vector2i& new_size) {
if (!texture_created_by_us)
Logger::Error("Resizing a Render Target that does not own it's texture?");
GLuint current_fbo = GetActiveGLFramebufferHandle();
GLfloat old_clear_color[4];
GLint old_viewport[4] = {0, 0, 0, 0};
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. */
if (new_size.x <= texture->GetDimensions().x && new_size.y <= texture->GetDimensions().y) {
size = new_size;
// Clear.
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_object);
auto cc = GetClearColor();
glClearColor(cc.RedChannelNormalized(), cc.GreenChannelNormalized(), cc.BlueChannelNormalized(), cc.AlphaChannelNormalized());
glViewport(0,0, size.x, size.y);
glClear(GL_COLOR_BUFFER_BIT);
if (using_depth)
glClear(GL_DEPTH_BUFFER_BIT);
glBindFramebuffer(GL_FRAMEBUFFER, current_fbo);
glClearColor(old_clear_color[0], old_clear_color[1], old_clear_color[2], old_clear_color[3]);
glViewport(old_viewport[0], old_viewport[1], old_viewport[2], old_viewport[3]);
return;
}
//If we have to remake the texture.
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_object);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
// Erase it.
delete texture;
auto cc = GetClearColor();
glClearColor(cc.RedChannelNormalized(), cc.GreenChannelNormalized(), cc.BlueChannelNormalized(), cc.AlphaChannelNormalized());
glViewport(0,0, size.x, size.y);
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]);
glViewport(old_viewport[0], old_viewport[1], old_viewport[2], old_viewport[3]);
size = new_size;
//Disable & Re-enable MSAA so the msaa buffer is remade with the correct dimensions.
if (MSAAEnabled()) {
MSAA_SAMPLE_RATE current_sample_rate = msaa_sample_rate;
SetMSAAEnabled(MSAA_SAMPLE_RATE::MSAA_NONE);
SetMSAAEnabled(current_sample_rate);
}
}
JGL::RenderTarget::~RenderTarget() {
Erase();
if (texture_created_by_us)
delete texture;
}
bool JGL::RenderTarget::OwnsTexture() const {
return texture_created_by_us;
}
JGL::MSAA_SAMPLE_RATE JGL::RenderTarget::GetMSAASampleRate() const {
return msaa_sample_rate;
}
bool JGL::RenderTarget::MSAAEnabled() const {
return msaa_sample_rate != MSAA_SAMPLE_RATE::MSAA_NONE;
}
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 false;
// If we'd be rendering onto a texture and not a plain render target we don't want this.
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);
msaa_framebuffer_object = 0;
msaa_depth_buffer = 0;
msaa_render_buffer = 0;
msaa_sample_rate = MSAA_SAMPLE_RATE::MSAA_NONE;
// Only return here if they specifically requested no MSAA. else continue to change mode.
if (sample_rate == MSAA_SAMPLE_RATE::MSAA_NONE)
return true;
}
GLuint current_fbo = GetActiveGLFramebufferHandle();
glGenFramebuffers(1, &msaa_framebuffer_object);
glBindFramebuffer(GL_FRAMEBUFFER, msaa_framebuffer_object);
GLint current_renderbuffer = 0;
glGetIntegerv(GL_RENDERBUFFER_BINDING, &current_renderbuffer);
glGenRenderbuffers(1, &msaa_render_buffer);
glBindRenderbuffer(GL_RENDERBUFFER, msaa_render_buffer);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, JGL::to_int(sample_rate), GL_RGBA, size.x, size.y);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, msaa_render_buffer);
if (using_depth) {
glGenRenderbuffers(1, &msaa_depth_buffer);
glBindRenderbuffer(GL_RENDERBUFFER, msaa_depth_buffer);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, JGL::to_int(sample_rate), GL_DEPTH_COMPONENT, size.x, size.y);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, msaa_depth_buffer);
}
bool failure = false;
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
failure = true,
Logger::Fatal("A new MSAA " + std::to_string(to_int(sample_rate)) + "x framebuffer couldn't be allocated.");
glBindRenderbuffer(GL_RENDERBUFFER, current_renderbuffer);
glBindFramebuffer(GL_FRAMEBUFFER, current_fbo);
msaa_sample_rate = sample_rate;
if (failure)
SetMSAAEnabled(MSAA_SAMPLE_RATE::MSAA_NONE);
return failure;
}
void JGL::RenderTarget::MSAABlit() const {
if (MSAAEnabled() && OwnsTexture()) {
// Save the GL state.
GLuint current_fbo = GetActiveGLFramebufferHandle();
GLint current_draw_fbo = 0;
GLint current_read_fbo = 0;
glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &current_read_fbo);
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &current_draw_fbo);
// Draw the contents of one into the other.
glBindFramebuffer(GL_READ_FRAMEBUFFER, msaa_framebuffer_object);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer_object);
glBlitFramebuffer(0, 0, size.x, size.y, 0, 0, size.x, size.y, GL_COLOR_BUFFER_BIT, GL_NEAREST);
// Put the GL state back.
glBindFramebuffer(GL_READ_FRAMEBUFFER, current_read_fbo);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, current_draw_fbo);
glBindFramebuffer(GL_FRAMEBUFFER, current_fbo);
}
// Fixes using render targets on a texture that has mipmaps.
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, GetTexture()->GetHandle());
glGenerateMipmap(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, current_texture);
}
}
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();
GLint current_draw_fbo = 0;
GLint current_read_fbo = 0;
glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &current_read_fbo);
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &current_draw_fbo);
// 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, 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);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, current_draw_fbo);
glBindFramebuffer(GL_FRAMEBUFFER, current_fbo);
}
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.");
if (pixel == nullptr)
pixel = new RenderTarget({1,1});
GLint current_draw_fbo = 0;
GLint current_read_fbo = 0;
GLint viewport[4];
GLfloat clear_color[4];
GLuint current_fbo = GetActiveGLFramebufferHandle();
glGetIntegerv(GL_VIEWPORT, viewport);
glGetFloatv(GL_COLOR_CLEAR_VALUE, clear_color);
glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &current_read_fbo);
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &current_draw_fbo);
SetActiveGLRenderTarget(*pixel);
glClearColor(color.RedChannelNormalized(), color.GreenChannelNormalized(), color.BlueChannelNormalized(), color.AlphaChannelNormalized());
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, 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);
glBindFramebuffer(GL_READ_FRAMEBUFFER, current_read_fbo);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, current_draw_fbo);
glBindFramebuffer(GL_FRAMEBUFFER, current_fbo);
glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
glClearColor(clear_color[0], clear_color[1], clear_color[2], clear_color[3]);
}
JGL::RenderTarget::RenderTarget(const JGL::RenderTarget& rhs) {
auto* this_render_target = new RenderTarget(rhs.size, rhs.clear_color, rhs.using_depth, rhs.msaa_sample_rate);
RenderTarget::Blit(rhs, this_render_target);
this->clear_color = this_render_target->clear_color;
this->size = this_render_target->size;
this->using_depth = this_render_target->using_depth;
this->texture_created_by_us = true;
this->texture = this_render_target->texture;
this->framebuffer_object = this_render_target->framebuffer_object;
this->depth_buffer = this_render_target->depth_buffer;
this->msaa_sample_rate = this_render_target->msaa_sample_rate;
this->msaa_framebuffer_object = this_render_target->msaa_framebuffer_object;
this->msaa_depth_buffer = this_render_target->msaa_depth_buffer;
this->msaa_render_buffer = this_render_target->msaa_render_buffer;
operator delete(this_render_target);
}

186
src/types/Skeleton.cpp Normal file
View File

@@ -0,0 +1,186 @@
#include <JGL/types/Skeleton.h>
#include <JGL/logger/logger.h>
#include <cstring>
using namespace JGL;
std::array<int, 4> JGL::SkeletalVertexAttribute::GetAffectingBoneIDs() const {
return bone_ids;
}
std::array<float, 4> JGL::SkeletalVertexAttribute::GetAffectingBoneWeights() const {
return bone_weights;
}
JGL::SkeletalVertexAttribute::SkeletalVertexAttribute(const std::vector<int>& ids, const std::vector<float>& weights) {
if (bone_ids.size() > 4 || bone_weights.size() > 4)
Logger::Fatal("Initialization of a skeletal vertex that is effected by more than 4 bones.");
memcpy(this->bone_ids.data(), bone_ids.data(), sizeof(int) * 4);
memcpy(this->bone_weights.data(), bone_weights.data(), sizeof(float) * 4);
}
bool JGL::Bone::IsRootBone() const {
if (parent_id == -1)
return true;
return false;
}
int JGL::Bone::GetID() const {
return id;
}
std::string JGL::Bone::GetName() const {
return name;
}
Matrix4x4 JGL::Bone::GetInverseBindMatrix() const {
return inverse_bind_matrix;
}
Matrix4x4 JGL::Bone::GetOffsetMatrix() const {
return offset_matrix;
}
Matrix4x4 JGL::Bone::GetFinalTransform() const {
return final_transform;
}
std::vector<int> JGL::Bone::GetChildren() const {
return children;
}
void Bone::SetParent(int parent_identifier) {
parent_id = parent_identifier;
}
void Bone::AppendChild(int new_child) {
children.push_back(new_child);
}
void Bone::SetID(int numeric_id) {
id = numeric_id;
}
void Bone::SetName(const std::string& string_id) {
name = string_id;
}
void Bone::SetInverseBindMatrix(const Matrix4x4& inverse_bind) {
inverse_bind_matrix = inverse_bind;
}
void Bone::SetOffsetMatrix(const Matrix4x4& offset) {
offset_matrix = offset;
}
void Bone::SetFinalTransformMatrix(const Matrix4x4& final) {
final_transform = final;
}
int Bone::GetParentID() const {
return parent_id;
}
Bone::Bone(int numeric_id, const std::string& string_id, int parent_bone, const std::vector<int>& children_ids, const Matrix4x4& inverse_bind, const Matrix4x4& offset, const Matrix4x4& final) {
id = numeric_id;
name = string_id;
parent_id = parent_bone;
inverse_bind_matrix = inverse_bind;
offset_matrix = offset;
final_transform = final;
children = children_ids;
}
Bone* Skeleton::GetRootBone() {
return &root;
}
Bone* Skeleton::FindBone(int id) {
if (root.GetID() == id)
return &root;
for (auto& bone : bones)
if (bone.GetID() == id)
return &bone;
// If we couldn't find it.
return nullptr;
}
Bone* Skeleton::FindBone(const std::string& string_id) {
if (root.GetName() == string_id)
return &root;
for (auto& bone : bones)
if (bone.GetName() == string_id)
return &bone;
return nullptr;
}
void Skeleton::AppendBone(const Bone& new_bone) {
bones.push_back(new_bone);
}
Skeleton::Skeleton(const Bone& root_bone, const std::vector<Bone>& children) {
root = root_bone;
bones = children;
}
float KeyFrame::GetTimeStamp() const {
return time_stamp;
}
Skeleton KeyFrame::GetSkeleton() const {
return pose;
}
KeyFrame::KeyFrame(const Skeleton& pose, float time_stamp) {
this->pose = pose;
this->time_stamp = time_stamp;
}
Animation::Animation(int id, float duration, const std::vector<KeyFrame>& key_frames, const std::vector<SkeletalVertexAttribute>& skeletal_vertex_attributes,
const std::string& name) {
length = duration;
this->key_frames = key_frames;
this->id = id;
this->name = name;
this->vertex_attributes = skeletal_vertex_attributes;
}
float Animation::GetDuratrion() const {
return length;
}
std::vector<KeyFrame> Animation::GetKeyFrames() const {
return key_frames;
}
void Animation::AppendKeyFrame(const KeyFrame& new_key) {
key_frames.push_back(new_key);
}
void Animation::SetDuration(float duration) {
length = duration;
}
int Animation::GetID() const {
return id;
}
std::string Animation::GetName() const {
return name;
}
void Animation::SetID(int identifier) {
id = identifier;
}
void Animation::SetName(const std::string& name_id) {
name = name_id;
}
std::vector<SkeletalVertexAttribute> Animation::GetSkeletalVertexAttributes() const {
return vertex_attributes;
}

View File

@@ -1,195 +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 ReTexture;
#define STB_IMAGE_IMPLEMENTATION
#include "internals/stb_image.h"
namespace JGL
{
Texture::Texture(const std::string& file, const ReTexture::TextureFlag& flags, TextureFilteringMode filtering_mode, TextureWrappingMode wrapping_mode)
{
auto *t = new ReTexture::SoftwareTexture(file, flags);
using namespace JGL;
GLuint previous_texture;
glGetIntegerv(GL_TEXTURE_BINDING_2D, (GLint*) &previous_texture);
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{};
load(t, {(float) t->getWidth(), (float) t->getHeight()}, t->getTextureFormat(), filtering_mode, wrapping_mode);
texture_flags = flags;
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.");
delete t;
unsigned char bmpFileHeader[14];
glBindTexture(GL_TEXTURE_2D, previous_texture);
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 std::string& file, TextureFilteringMode filtering_mode, TextureWrappingMode wrapping_mode) {
GLuint previous_texture;
glGetIntegerv(GL_TEXTURE_BINDING_2D, (GLint*) &previous_texture);
load(pixels.data());
}
auto* t = new SoftwareTexture(file);
std::vector<unsigned char> Texture::png(const std::filesystem::path& file) {
std::vector<unsigned char> result{};
int channels;
load(t, {(float) t->getWidth(), (float) t->getHeight()}, t->getTextureFormat(), filtering_mode, wrapping_mode);
texture_flags = TextureFlag::NONE;
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.");
delete t;
glBindTexture(GL_TEXTURE_2D, previous_texture);
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];
}
}
Texture::Texture(const Vector2& size) {
GLuint previous_texture;
glGetIntegerv(GL_TEXTURE_BINDING_2D, (GLint*) &previous_texture);
bmp_file.close();
format = ColorFormat::RGB;
return result;
}
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);
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;
texture_flags = TextureFlag::NONE;
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);
}
std::vector<Color4> JGL::Texture::GetPixelData() const {
GLint current_texture;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &current_texture);
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::load(SoftwareTexture* software_texture, const Vector2& size, const TextureFormat& format,
TextureFilteringMode filtering_mode, TextureWrappingMode wrapping_mode) {
glGenTextures(1, &texture_handle);
glBindTexture(GL_TEXTURE_2D, texture_handle);
//if 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);
if (format == TextureFormat::RGBA)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (int) size.x, (int) size.y, 0, GL_RGBA, GL_UNSIGNED_BYTE,
software_texture->pixelData.data());
glBindTexture(GL_TEXTURE_2D, current_texture);
return result;
}
else if (format == TextureFormat::RGB)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, (int) size.x, (int) size.y, 0, GL_RGB, GL_UNSIGNED_BYTE,
software_texture->pixelData.data());
GLuint Texture::GetHandle() const {
return texture_handle;
}
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);
Vector2i Texture::GetDimensions() const {
return size;
}
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);
bool Texture::Inverted() const {
return invert_y;
}
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);
ColorFormat Texture::GetFormat() const {
return format;
}
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);
FilteringMode Texture::GetFilteringMode() const {
return filtering_mode;
}
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);
WrappingMode Texture::GetWrappingMode() const {
return wrapping_mode;
}
else if (filtering_mode == TextureFilteringMode::MIPMAP_NEAREST ||
filtering_mode == TextureFilteringMode::MIPMAP_BILINEAR ||
filtering_mode == TextureFilteringMode::MIPMAP_TRILINEAR) {
//3 mipmap levels.
auto *m1 = new SoftwareTexture(software_texture->downscale(2));
auto *m2 = new SoftwareTexture(software_texture->downscale(4));
auto *m3 = new SoftwareTexture(software_texture->downscale(8));
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);
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 3);
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);
}
if (format == TextureFormat::RGBA) {
glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, m1->getWidth(), m1->getHeight(), 0, GL_RGBA,
GL_UNSIGNED_BYTE, m1->pixelData.data());
glTexImage2D(GL_TEXTURE_2D, 2, GL_RGBA, m2->getWidth(), m2->getHeight(), 0, GL_RGBA,
GL_UNSIGNED_BYTE, m2->pixelData.data());
glTexImage2D(GL_TEXTURE_2D, 3, GL_RGBA, m3->getWidth(), m3->getHeight(), 0, GL_RGBA,
GL_UNSIGNED_BYTE, m3->pixelData.data());
} else if (format == TextureFormat::RGB) {
glTexImage2D(GL_TEXTURE_2D, 1, GL_RGB, m1->getWidth(), m1->getHeight(), 0, GL_RGB, GL_UNSIGNED_BYTE,
m1->pixelData.data());
glTexImage2D(GL_TEXTURE_2D, 2, GL_RGB, m2->getWidth(), m2->getHeight(), 0, GL_RGB, GL_UNSIGNED_BYTE,
m2->pixelData.data());
glTexImage2D(GL_TEXTURE_2D, 3, GL_RGB, m3->getWidth(), m3->getHeight(), 0, GL_RGB, GL_UNSIGNED_BYTE,
m3->pixelData.data());
}
Texture::~Texture() {
if (texture_handle != 0)
glDeleteTextures(1, &texture_handle);
texture_handle = 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);
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);
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);
RenderTarget::Blit(rhs_render_target, &this_render_target);
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);
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);
}
delete m1;
delete m2;
delete m3;
}
texture_size = size;
texture_format = format;
texture_filtering_mode = filtering_mode;
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;
}
std::vector<Color4> JGL::Texture::GetPixelData() const {
std::vector<Color4> result((size_t) (texture_size.x * texture_size.y));
glBindTexture(GL_TEXTURE_2D, texture_handle);
auto* result = new Texture(Vector2i(length, biggest_y));
auto render_target = RenderTarget(result);
if (texture_format == TextureFormat::RGBA) {
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, result.data());
return result;
}
//if RGB
std::vector<Color3> color3((size_t) (texture_size.x * texture_size.y));
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGB, GL_UNSIGNED_BYTE, color3.data());
for (const auto &c: color3)
result.emplace_back(c);
return result;
}
void Texture::Erase() {
if (texture_handle != 0)
glDeleteTextures(1, &texture_handle);
}
GLuint Texture::GetGLTextureHandle() const {
return texture_handle;
}
Vector2 Texture::GetDimensions() const {
return texture_size;
}
TextureFlag Texture::GetFlags() const {
return texture_flags;
}
TextureFormat Texture::GetFormat() const {
return texture_format;
}
TextureFilteringMode Texture::GetFilteringMode() const {
return texture_filtering_mode;
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;
}
void Texture::SetTextureHandle(GLuint handle) {
texture_handle = handle;
}
TextureWrappingMode Texture::GetWrappingMode() const {
return texture_wrapping_mode;
}
}
}

View File

@@ -1,52 +1,45 @@
#include <JGL/types/VRamList.h>
#include <jlog/Logger.hpp>
#include <JGL/logger/logger.h>
#include <cstring>
void JGL::VRamList::load(const GLfloat* data, const long& s) {
void JGL::VRamList::load(const GLfloat* data, const long& size) {
while (spin_lock) {}
spin_lock = true;
GLint current_array_buffer = 0;
glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &current_array_buffer);
glGenBuffers(1, &list_handle);
glBindBuffer(GL_ARRAY_BUFFER, list_handle);
glBufferData(GL_ARRAY_BUFFER, s, data, GL_STATIC_DRAW);
glBufferData(GL_ARRAY_BUFFER, size, data, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, current_array_buffer);
element_array_buffer = false;
size = s / sizeof(GLfloat);
num_elements = size / sizeof(GLfloat);
spin_lock = false;
}
void JGL::VRamList::load(const GLuint* data, const long& s) {
void JGL::VRamList::load(const GLuint* data, const long& size) {
while (spin_lock) {}
spin_lock = true;
GLint current_element_array_buffer = 0;
glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &current_element_array_buffer);
glGenBuffers(1, &list_handle);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, list_handle);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, s, data, GL_STATIC_DRAW);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, data, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, current_element_array_buffer);
element_array_buffer = true;
size = s / sizeof(GLuint);
}
num_elements = size / sizeof(GLuint);
JGL::VRamList::VRamList(const GLfloat* data, const long& size) {
load(data, (long) sizeof(GLfloat) * size);
}
JGL::VRamList::VRamList(const GLuint* data, const long& size) {
load(data, (long) sizeof(GLuint) * size);
}
JGL::VRamList::VRamList(Vector2* data, const long& size) {
load(reinterpret_cast<GLfloat*>(data), (long) sizeof(Vector2) * size);
}
JGL::VRamList::VRamList(Vector3* data, const long& size) {
load(reinterpret_cast<GLfloat*>(data), (long) sizeof(Vector3) * size);
}
JGL::VRamList::VRamList(Vector4* data, const long& size) {
load(reinterpret_cast<GLfloat*>(data), (long) sizeof(Vector4) * size);
spin_lock = false;
}
void JGL::VRamList::Erase() {
if (list_handle == 0)
jlog::Warning("Erasing an uninitialized array buffer?");
return;
while (spin_lock) {}
spin_lock = true;
GLint current_element_array_buffer = 0;
glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &current_element_array_buffer);
@@ -54,50 +47,49 @@ void JGL::VRamList::Erase() {
glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &current_array_buffer);
if (element_array_buffer && current_element_array_buffer == list_handle)
jlog::Warning("Erasing an element array buffer while it's in use?");
JGL::Logger::Warning("Erasing an element array buffer while it's in use?");
else if (!element_array_buffer && current_array_buffer == list_handle)
jlog::Warning("Erasing an array buffer while it's in use?");
JGL::Logger::Warning("Erasing an array buffer while it's in use?");
glDeleteBuffers(1, &list_handle);
list_handle = 0;
spin_lock = false;
}
GLuint JGL::VRamList::GetHandle() const {
return list_handle;
}
bool JGL::VRamList::IsIntegerArray() const {
return element_array_buffer;
}
bool JGL::VRamList::IsFloatArray() const {
return !element_array_buffer;
}
long JGL::VRamList::GetSize() const {
return size;
long JGL::VRamList::GetLength() const {
return num_elements;
}
long JGL::VRamList::GetDataSize() const {
size_t JGL::VRamList::GetDataSize() const {
if (element_array_buffer)
return (long) sizeof(GLuint) * size;
return (long) sizeof(GLfloat) * size;
return sizeof(GLuint) * num_elements;
return sizeof(GLfloat) * num_elements;
}
void JGL::VRamList::SetData(void* data, const long& size) {
bool should_resize = (this->size != size);
void JGL::VRamList::SetData(void* data, const long& length) {
while (spin_lock) {}
spin_lock = true;
bool should_resize = (this->num_elements != length);
if (should_resize) {
glDeleteBuffers(1, &list_handle);
list_handle = 0;
if (!element_array_buffer)
load((GLfloat*) data, sizeof(GLfloat) * size);
else
load((GLuint*) data, sizeof(GLuint) * size);
spin_lock = false;
element_array_buffer ? load((GLuint*) data, sizeof(GLuint) * length) : load((GLfloat*) data, sizeof(GLfloat) * length);
return;
}
// if (!should resize)
GLint current_buffer = 0;
GLenum buffer_type = GL_ARRAY_BUFFER;
GLenum buffer_binding = GL_ARRAY_BUFFER_BINDING;
@@ -108,13 +100,203 @@ void JGL::VRamList::SetData(void* data, const long& size) {
glGetIntegerv(buffer_binding, &current_buffer);
glBindBuffer(buffer_type, list_handle);
void* buffer_data = glMapBuffer(buffer_type, GL_WRITE_ONLY);
if (buffer_data == nullptr)
jlog::Fatal("Mapping in a VBO that doesn't exist?");
void* vram = glMapBuffer(buffer_type, GL_WRITE_ONLY);
if (!vram)
JGL::Logger::Fatal("Mapping in a VBO that doesn't exist?");
memcpy(buffer_data, data, (element_array_buffer ? sizeof(GLuint) : sizeof(GLfloat)) * size);
memcpy(vram, data, (element_array_buffer ? sizeof(GLuint) : sizeof(GLfloat)) * length);
if (!glUnmapBuffer(buffer_type))
jlog::Fatal("We couldn't unmap the buffer?");
JGL::Logger::Fatal("We couldn't unmap the buffer?");
glBindBuffer(buffer_type, current_buffer);
spin_lock = false;
}
void JGL::VRamList::UpdateData(void* data, const long& offset, const long& length) {
while (spin_lock) {}
spin_lock = true;
if (offset + length > num_elements) {
JGL::Logger::Warning("Using UpdateData to out-of-bounds write the VRamList? I'll resize it for you, But this is slow.");
unsigned long oob_delta = (offset + length) - num_elements;
if (element_array_buffer) {
auto list_data = GetDataUI();
list_data.resize(list_data.size() + oob_delta);
memcpy(list_data.data() + (offset * sizeof(GLuint)), data, length * sizeof(GLuint));
spin_lock = false; // This is going unlock and relock really fast, But this code fixes something considered wrong anyway - Redacted.
return SetData(list_data.data(), list_data.size());
}
// if (!element_array_buffer)
auto list_data = GetDataF();
list_data.resize(list_data.size() + oob_delta);
memcpy(list_data.data() + (offset * sizeof(GLfloat)), data, length * sizeof(GLfloat));
spin_lock = false;
return SetData(list_data.data(), list_data.size());
}
GLint current_buffer = 0;
GLenum buffer_type = GL_ARRAY_BUFFER;
GLenum buffer_binding = GL_ARRAY_BUFFER_BINDING;
if (element_array_buffer)
buffer_type = GL_ELEMENT_ARRAY_BUFFER,
buffer_binding = GL_ELEMENT_ARRAY_BUFFER_BINDING;
glGetIntegerv(buffer_binding, &current_buffer);
glBindBuffer(buffer_type, list_handle);
void* vram = glMapBuffer(buffer_type, GL_WRITE_ONLY);
if (!vram)
JGL::Logger::Fatal("Mapping in a VBO that doesn't exist?");
size_t element_size = element_array_buffer ? sizeof(GLuint) : sizeof(GLfloat);
memcpy(reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(vram) + (offset * element_size)), data, length * element_size);
if (!glUnmapBuffer(buffer_type))
JGL::Logger::Fatal("We couldn't unmap the buffer?");
glBindBuffer(buffer_type, current_buffer);
spin_lock = false;
}
std::vector<GLfloat> JGL::VRamList::GetDataF() const {
while (spin_lock) {}
if (element_array_buffer)
JGL::Logger::Warning("Getting data as GLfloat but the buffer data is GLuint?");
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);
std::vector<GLfloat> data(num_elements);
void* vram = glMapBuffer(GL_ARRAY_BUFFER, GL_READ_ONLY);
if (vram == nullptr)
JGL::Logger::Fatal("Mapping in a VBO that doesn't exist?");
memcpy(data.data(), vram, num_elements * sizeof(GLfloat));
glUnmapBuffer(GL_ARRAY_BUFFER);
glBindBuffer(GL_ARRAY_BUFFER, current_buffer);
if (!vertex_array_enabled)
glDisableClientState(GL_VERTEX_ARRAY);
return data;
}
std::vector<GLuint> JGL::VRamList::GetDataUI() const {
while (spin_lock) {}
if (!element_array_buffer)
JGL::Logger::Warning("Getting data as GLuint but the buffer data is GLfloat?");
GLint current_buffer = 0;
glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &current_buffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, list_handle);
std::vector<GLuint> data(num_elements);
void* vram = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_READ_ONLY);
if (vram == nullptr)
JGL::Logger::Fatal("Mapping in a VBO that doesn't exist?");
memcpy(data.data(), vram, num_elements * sizeof(GLuint));
glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, current_buffer);
return data;
}
JGL::VRamList::VRamList(const GLfloat* data, const long& length) {
load(data, (long) sizeof(GLfloat) * length);
}
JGL::VRamList::VRamList(const GLuint* data, const long& length) {
load(data, (long) sizeof(GLuint) * length);
}
JGL::VRamList::VRamList(const Vector2* data, const long& length) {
load(reinterpret_cast<const GLfloat*>(data), (long) sizeof(Vector2) * length);
}
JGL::VRamList::VRamList(const Vector3* data, const long& length) {
load(reinterpret_cast<const GLfloat*>(data), (long) sizeof(Vector3) * length);
}
JGL::VRamList::VRamList(const Vector4* data, const long& length) {
load(reinterpret_cast<const GLfloat*>(data), (long) sizeof(Vector4) * length);
}
void JGL::VRamList::SetData(const GLfloat* data, const long& length) {
SetData((void*) data, length);
}
void JGL::VRamList::SetData(const Vector2* data, const long& length) {
SetData((void*) data, length * 2);
}
void JGL::VRamList::SetData(const Vector3* data, const long& length) {
SetData((void*) data, length * 3);
}
void JGL::VRamList::SetData(const Vector4* data, const long& length) {
SetData((void*) data, length * 4);
}
void JGL::VRamList::SetData(const GLuint* data, const long& length) {
SetData((void*) data, length);
}
void JGL::VRamList::SetData(const Vector2i* data, const long& length) {
SetData((void*) data, length * 2);
}
void JGL::VRamList::UpdateData(const GLfloat* data, const long& offset, const long& length) {
UpdateData((void*) data, offset, length);
}
void JGL::VRamList::UpdateData(const Vector2* data, const long& offset, const long& length) {
UpdateData((void*) data, offset, length * 2);
}
void JGL::VRamList::UpdateData(const Vector3* data, const long& offset, const long& length) {
UpdateData((void*) data, offset, length * 3);
}
void JGL::VRamList::UpdateData(const Vector4* data, const long& offset, const long& length) {
UpdateData((void*) data, offset, length * 4);
}
void JGL::VRamList::UpdateData(const GLuint* data, const long& offset, const long& length) {
UpdateData((void*) data, offset, length);
}
void JGL::VRamList::UpdateData(const Vector2i* data, const long& offset, const long& length) {
UpdateData((void*) data, offset, length * 2);
}
JGL::VRamList::~VRamList() {
Erase();
}
JGL::VRamList::VRamList(const JGL::VRamList& rhs) {
while (rhs.spin_lock) {}
if (rhs.element_array_buffer) {
auto data_array = rhs.GetDataUI();
this->load(data_array.data(), data_array.size());
}
auto data_array = rhs.GetDataF();
this->load(data_array.data(), data_array.size());
}

179
src/types/VertexArray.cpp Normal file
View File

@@ -0,0 +1,179 @@
#include <JGL/types/VertexArray.h>
#include <JGL/logger/logger.h>
using namespace JGL;
const VRamList* VertexArray::GetVertices() const {
return vertices;
}
const VRamList* VertexArray::GetIndices() const {
return indices;
}
const VRamList* VertexArray::GetNormals() const {
return normals;
}
const VRamList* VertexArray::GetTextureCoordinates() const {
return texture_coordinates;
}
std::vector<Vertex> VertexArray::GetLocalVertices() const {
return local_vertices;
}
std::vector<unsigned int> VertexArray::GetLocalIndices() const {
return local_indices;
}
std::vector<TextureCoordinate> VertexArray::GetLocalTextureCoordinates() const {
return local_texture_coordinates;
}
std::vector<Normal> VertexArray::GetLocalNormals() const {
return local_normals;
}
Sphere VertexArray::GetMESphere(const Vector3& scale, const Vector3& translate_part) const {
if (scale == Vector3::One)
return { me_sphere.Position + translate_part, me_sphere.Radius };
float scale_factor = 0;
if (scale.x > scale_factor)
scale_factor = scale.x;
if (scale.y > scale_factor)
scale_factor = scale.y;
if (scale.z > scale_factor)
scale_factor = scale.z;
return { me_sphere.Position + translate_part, me_sphere.Radius * scale_factor };
}
Sphere VertexArray::GetMESphere(const Matrix4x4& instance_matrix, bool translate) const {
Vector3 scale = instance_matrix.GetScale();
Vector3 origin = Vector3::Zero;
if (translate)
origin = instance_matrix.GetTranslatePart();
return GetMESphere(scale, origin);
}
void VertexArray::CreateMESphere() {
Sphere result = {Vector3::Zero, 0};
Vector3 minimum = {std::numeric_limits<float>::max(), std::numeric_limits<float>::max(), std::numeric_limits<float>::max() };
Vector3 maximum = { std::numeric_limits<float>::lowest(), std::numeric_limits<float>::lowest(), std::numeric_limits<float>::lowest()};
for (const auto& vertex : local_vertices) {
if (vertex.x < minimum.x)
minimum.x = vertex.x;
if (vertex.x > maximum.x)
maximum.x = vertex.x;
if (vertex.y < minimum.y)
minimum.y = vertex.y;
if (vertex.y > maximum.y)
maximum.y = vertex.y;
if (vertex.z < minimum.z)
minimum.z = vertex.z;
if (vertex.z > maximum.z)
maximum.z = vertex.z;
}
result.Position = (minimum + maximum) * 0.5f;
for (const auto& vertex : local_vertices) {
float distance_squared = Vector3::DistanceSquared(result.Position, vertex);
if (distance_squared > result.Radius)
result.Radius = distance_squared;
}
result.Radius = std::sqrt(result.Radius);
me_sphere = result;
}
void VertexArray::CreateMEOBB() {
Vector3 minimum(std::numeric_limits<float>::max());
Vector3 maximum (std::numeric_limits<float>::min());
for (const auto& vertex : local_vertices) {
if (vertex.x < minimum.x)
minimum.x = vertex.x;
if (vertex.x > maximum.x)
maximum.x = vertex.x;
if (vertex.y < minimum.y)
minimum.y = vertex.y;
if (vertex.y > maximum.y)
maximum.y = vertex.y;
if (vertex.z < minimum.z)
minimum.z = vertex.z;
if (vertex.z > maximum.z)
maximum.z = vertex.z;
}
Vector3 position = (minimum + maximum) * 0.5f;
Vector3 half_extents = (maximum - minimum) * 0.5f;
me_obb = { position, half_extents, Vector3::UnitX, Vector3::UnitY, Vector3::UnitZ };
}
OBB VertexArray::GetMEOBB(const Matrix3x3& rotation_matrix, const Vector3& scale, const Vector3& translate_part) const {
Vector3 half_extents = me_obb.r.Mul(scale);
Vector3 axis0 = rotation_matrix * me_obb.axis[0];
Vector3 axis1 = rotation_matrix * me_obb.axis[1];
Vector3 axis2 = rotation_matrix * me_obb.axis[2];
Vector3 position = me_obb.pos + translate_part;
return { position, half_extents, axis0, axis1, axis2 };
}
OBB VertexArray::GetMEOBB(const Matrix4x4& instance_matrix, bool translate) const {
return GetMEOBB(instance_matrix.GetRotatePart(), instance_matrix.GetScale(), translate ? instance_matrix.GetTranslatePart() : Vector3::Zero);
}
AABB VertexArray::GetMEAABB(const Matrix3x3& rotation_matrix, const Vector3& scale, const Vector3& translate_part) const {
/* kind-of a cheat. It's faster to calculate the oriented bounding box and then put the axis-aligned one around that
* than to loop over the whole model like an idiot. You gain back all the speed with the simplicity of the physics anyways. - Redacted */
return GetMEOBB(rotation_matrix, scale, translate_part).MinimalEnclosingAABB();
}
AABB VertexArray::GetMEAABB(const Matrix4x4& instance_matrix, bool translate) const {
return GetMEAABB(instance_matrix.GetRotatePart(), instance_matrix.GetScale(), translate ? instance_matrix.GetTranslatePart() : Vector3::Zero);
}
VertexArray::VertexArray(const Vector3* vertex_positions, const long& vp_length, const unsigned int* vertex_indices, const long& vi_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.
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 = 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 = 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 = 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) {
*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() {
return animations.empty();
}

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++;
}
}
}
}
*/