Compare commits

...

84 Commits

Author SHA1 Message Date
819539247e Merge pull request 'shader_preprocessor' (#43) from shader_preprocessor into shaders_again
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 26m34s
Reviewed-on: #43
2025-06-06 13:38:29 -04:00
ee90e7f95b Fix measure string.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 2m16s
2025-05-29 22:58:25 -04:00
ad3b451659 BatchFillOutlineRect
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 2m19s
2025-05-25 16:33:58 -04:00
74ab9d25db BatchFillRoundedRect
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 3m44s
2025-05-18 00:07:58 -04:00
a5424eb370 BatchFillCircle Instanced Rendering.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 2m51s
Added JGL::ClearScreen as-well to clear fbo 0 at any time.
2025-05-17 17:04:27 -04:00
6f99689add Performance optimization.
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Has been cancelled
2025-05-12 11:38:28 -04:00
7ea4b8696e cleanup & performance optimization.
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Has been cancelled
2025-05-11 13:49:07 -04:00
6d37cd93e3 BatchFillRect instanced rendering.
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Has been cancelled
during shader creation you can now also specify your attributes.
2025-05-09 16:41:29 -04:00
7875b777e5 Demonstrate GLSL custom preprocessor.
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Has been cancelled
2025-05-09 13:50:18 -05:00
ede11131fe Change gradient rect to accept 4x color4
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Has been cancelled
2025-05-06 11:55:01 -04:00
347b9cb278 Performance optimization
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Has been cancelled
2025-05-05 11:25:49 -04:00
1a2f7627d3 custom behavior per draw function
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Has been cancelled
Add support for doing custom behavior per draw function for JGL.

You must define uniform int J2D_RENDERING_ROUTINE in any shader to be used by JGL even if you're not doing per draw function behavior.
2025-05-04 23:19:49 -04:00
d2497d64a2 Performance Optimization
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 2m35s
Draw points can accept multiple colors.

Draw points now uses vbos.

Allowed the user to set the usage hint for the VBO.

When changing data in a vbo, the data will be orphaned if the mode is not Fixed.
2025-04-20 04:30:29 -04:00
f7eff123be Fix alpha masking when using shader.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 5m11s
2025-04-18 12:00:51 -04:00
a1ca7ace77 Flip order of J2D::DrawString argument, so that we can default scale to 1, and add vector2 overload.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m59s
2025-04-17 13:57:03 -04:00
02370c6bfa work on the default shader.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 2m59s
2025-04-17 13:33:08 -04:00
2f13be9dd8 Merge branch 'shader_preprocessor' into shaders_again
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m56s
2025-04-16 01:44:27 -04:00
4c798ea76a shader with state stack.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 2m26s
2025-04-16 01:43:00 -04:00
af31b41782 Implement testing GLSL preprocessing and #include support.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 2m10s
2025-04-16 01:36:39 -04:00
4374b83464 Add Shader::OnCompilationErrorMessag
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 2m6s
2025-04-15 19:11:07 -05:00
147123b202 Working on the default shader
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 5m16s
( Need a system for accessing texture units & sampling etc )
2025-04-15 12:18:14 -04:00
2a2410e9bf Merge pull request 'Shader Implementation' (#42) from shaders_again into master
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 2m29s
Reviewed-on: #42
2025-04-15 01:21:42 -04:00
fdabbe866f Implement doxygen annotation for Shader class.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m55s
2025-04-14 22:12:28 -04:00
dac830fc7c Implement ineffectual shader for basis testing.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 6m44s
2025-04-14 21:45:42 -04:00
019b4aa5ae Implement Shader::UseDefault()
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Has been cancelled
2025-04-14 21:44:33 -04:00
3a293a2e0c Even Epicer
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m56s
2025-04-14 21:37:27 -04:00
73de143ec5 Epic Color Transition Fragment Shader
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Has been cancelled
2025-04-14 21:36:08 -04:00
5068b4660e Josh Attempts Shaders
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 3m24s
2025-04-14 21:28:06 -04:00
fd656cd543 Upgrade mcolor version.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 3m11s
2025-02-26 15:19:46 -05:00
291b3f3778 Fix the wireframe AABB not rendering correctly.
Some checks are pending
Run ReCI Build Test / Explore-Gitea-Actions (push) Waiting to run
2025-02-23 22:04:20 -05:00
4ee84d6442 Fix OpenGL 1280 on Windoze
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 2m2s
2025-02-19 21:06:38 -05:00
59df950e11 Add draw commands to support the texture atlas.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 2m33s
2025-02-18 14:10:59 -05:00
03a687179c Texture Atlas constructors. 2025-02-18 13:52:43 -05:00
ae84a68e11 Fix typo
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 2m27s
2025-02-13 21:49:32 -05:00
c5490cb321 Made a naming choice for will <3.
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Has been cancelled
2025-02-13 21:47:53 -05:00
04b7cc9544 Anisotropic filtering.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 2m19s
2025-02-13 03:18:23 -05:00
7498390180 Add the ability to use mipmaps with RenderTargets.
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 50s
2025-02-11 18:48:15 -05:00
4ac28a2c10 Fixed a regression that causes MSAA to not work.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 2m14s
2025-02-11 16:25:59 -05:00
61c1c3245c Fixed a case where resizing the render target was no good
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 2m16s
2025-02-07 01:56:34 -05:00
cb9fe4e5c9 Update J2D.cpp
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 2m26s
Dramatically improve the speed of J2D::DrawCubicBezierCurve
2025-02-06 12:01:49 -05:00
c7e7aa6fb5 Batch FillRect & Batch FillCircle.
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Has been cancelled
2025-02-06 11:43:03 -05:00
1261321992 Merge branch 'master' of https://git.redacted.cc/Josh/JGL
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 6m11s
2025-02-06 10:26:35 -05:00
6d1ddad428 Add a way to query the max texture size. 2025-02-06 10:26:34 -05:00
eae3c794fa Update README.md
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 2m46s
Added extended J2D member functions. (TODO: Document new usability of J2D contexts)
2025-02-04 15:51:52 -05:00
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
48 changed files with 21890 additions and 2268 deletions

View File

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

View File

@@ -17,40 +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-6.2.zip
)
CPMAddPackage(
NAME J3ML
URL https://git.redacted.cc/josh/j3ml/archive/Release-3.4.zip
URL https://git.redacted.cc/josh/j3ml/archive/3.4.5.zip
)
CPMAddPackage(
NAME ReWindow
URL https://git.redacted.cc/Redacted/ReWindow/archive/Prerelease-21.zip
URL https://git.redacted.cc/Redacted/ReWindow/archive/Prerelease-32.zip
)
CPMAddPackage(
NAME GLAD
URL https://git.redacted.cc/Redacted/glad/archive/v2.1ext_fboV2.zip
URL https://git.redacted.cc/Redacted/glad/archive/v2.1ext_fbo_depthtexture_shadow_anisotropic_instanced.zip
)
CPMAddPackage(
NAME jlog
URL https://git.redacted.cc/josh/jlog/Prerelease-16.zip
)
CPMAddPackage(
NAME ReImage
URL https://git.redacted.cc/Redacted/ReImage/archive/Release-2.0.zip
URL https://git.redacted.cc/josh/jlog/Prerelease-17.zip
)
if (WIN32)
#CPMAddPackage(
#NAME harfbuzz
#URL https://github.com/harfbuzz/harfbuzz/archive/refs/tags/9.0.0.zip
#)
CPMAddPackage(
NAME freetype
URL https://github.com/freetype/freetype/archive/refs/tags/VER-2-13-2.zip
@@ -61,10 +51,8 @@ endif()
file(COPY "assets" DESTINATION "${PROJECT_BINARY_DIR}")
file(GLOB_RECURSE ASSETS "assets/*")
file(GLOB_RECURSE HEADERS "include/*.h" "include/*.hpp")
file(GLOB_RECURSE SOURCES "src/*.c" "src/*.cpp" )
find_package(OpenGL REQUIRED)
file(GLOB_RECURSE HEADERS "include/*.h" "include/*.hpp" )
file(GLOB_RECURSE SOURCES "src/*.c" "src/*.cpp" "src/*.h")
if (UNIX AND NOT APPLE)
find_package(Freetype REQUIRED)
@@ -85,8 +73,6 @@ include_directories(
target_include_directories(JGL PUBLIC
${PROJECT_SOURCE_DIR}/include
${OPENGL_INCLUDE_DIRS}
${ReImage_SOURCE_DIR}/include
${mcolor_SOURCE_DIR}/include
${J3ML_SOURCE_DIR}/include
${jlog_SOURCE_DIR}/include
@@ -99,13 +85,13 @@ add_executable(JGL_Demo main.cpp)
if (UNIX AND NOT APPLE)
target_include_directories(JGL PRIVATE ${FREETYPE_INCLUDE_DIRS} )
target_link_libraries(JGL PRIVATE ${FREETYPE_LIBRARIES})
target_link_libraries(JGL PUBLIC ${OPENGL_LIBRARIES} mcolor J3ML jlog ReImage glad)
target_link_libraries(JGL PUBLIC mcolor J3ML jlog glad)
endif()
if (WIN32)
target_include_directories(JGL PRIVATE ${freetype_SOURCE_DIR}/include)
target_link_libraries(JGL PRIVATE freetype)
target_link_libraries(JGL PUBLIC ${OPENGL_LIBRARIES} mcolor J3ML glad jlog ReImage glad)
target_link_libraries(JGL PUBLIC ${OPENGL_LIBRARIES} mcolor J3ML glad jlog glad)
endif()
target_link_libraries(JGL_Demo PUBLIC JGL ReWindowLibrary Event glad)
target_link_libraries(JGL_Demo PUBLIC JGL ReWindow Event glad)

View File

@@ -19,12 +19,17 @@ Yet Another C++ Rendering Toolkit
### J2D
* DrawPoint
* DrawLine / DrawGradientLine
* DrawLine / DrawGradientLine / DrawDottedLine / DrawDashedLine / DrawLines
* DrawSprite / DrawPartialSprite
* OutlineRect / FillRect / FillGradientRect / FillRoundedRect
* DrawRenderTarget / DrawPartialRenderTarget
* OutlineRect / OutlineRoundedRect / OutlineChamferRect
* FillRect / FillGradientRect / FillRoundedRect / FillChamferRect
* OutlineCircle / FillCircle
* OutlineTriangle / FillTriangle
* OutlineTriangle / FillTriangle / FillGradientTriangle
* DrawString
* DrawCubicBezierCurve
* DrawArc
* OutlineEllipse / FillEllipse
### J3D
* DrawLine
@@ -53,7 +58,7 @@ JGL::J2D::End();
```
## Requirements
An OpenGL 2.1 or newer accelerator that supports the `GL_ARB_framebuffer_object` extension or
An OpenGL 2.1 or newer accelerator with at-least two texture mappers that supports the `GL_ARB_framebuffer_object` extension or
an implementation that can provide those features through alternative means (common on ArmSoC and Risc-V).
## Compatability

Binary file not shown.

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

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

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

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

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

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

View File

@@ -0,0 +1,30 @@
#ifndef include_shared
#define include_shared
#version 120
vec3 rgb2hsb( in vec3 c ){
vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
vec4 p = mix(vec4(c.bg, K.wz),
vec4(c.gb, K.xy),
step(c.b, c.g));
vec4 q = mix(vec4(p.xyw, c.r),
vec4(c.r, p.yzx),
step(p.x, c.r));
float d = q.x - min(q.w, q.y);
float e = 1.0e-10;
return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)),
d / (q.x + e),
q.x);
}
// Function from Iñigo Quiles
// https://www.shadertoy.com/view/MsS3Wc
vec3 hsb2rgb( in vec3 c ){
vec3 rgb = clamp(abs(mod(c.x*6.0+vec3(0.0,4.0,2.0),
6.0)-3.0)-1.0,
0.0,
1.0 );
rgb = rgb*rgb*(3.0-2.0*rgb);
return c.z * mix(vec3(1.0), rgb, c.y);
}
#endif

View File

@@ -1,5 +1,154 @@
#version 120
#define J2D_DrawPoint 1
#define J2D_DrawPoints 2
#define J2D_DrawLine 3
#define J2D_DrawLines 4
#define J2D_DrawDottedLine 5
#define J2D_DrawDashedLine 6
#define J2D_DrawGradientLine 7
#define J2D_OutlineRect 8
#define J2D_OutlineRoundedRect 9
#define J2D_OutlineChamferRect 10
#define J2D_FillRect 11
#define J2D_FillGradientRect 12
#define J2D_FillRoundedRect 13
#define J2D_FillChamferRect 14
#define J2D_DrawRenderTarget 15
#define J2D_DrawPartialRenderTarget 16
#define J2D_DrawSprite 17
#define J2D_DrawAlphaMaskSprite 18
#define J2D_DrawPartialSprite 19
#define J2D_DrawMirrorSprite 20
#define J2D_OutlineCircle 21
#define J2D_FillCircle 22
#define J2D_OutlineTriangle 23
#define J2D_FillTriangle 24
#define J2D_FillGradientTriangle 25
#define J2D_DrawCubicBezierCurve 26
#define J2D_OutlinePolygon 27
#define J2D_DrawString 28
#define J2D_DrawArc 29
uniform int JGL_RENDERING_ROUTINE;
uniform bool JGL_INSTANCED_RENDERING;
// The number of texture units that have been set.
uniform int TEXTURE_UNIT_SET_COUNT;
// The color manually set with glColor4f, glColor4ubv etc.
varying vec4 v_color;
// Texture unit 0 - 7 (8 - 31 will come later).
uniform sampler2D GL_TEXTURE0;
uniform sampler2D GL_TEXTURE1;
uniform sampler2D GL_TEXTURE2;
uniform sampler2D GL_TEXTURE3;
uniform sampler2D GL_TEXTURE4;
uniform sampler2D GL_TEXTURE5;
uniform sampler2D GL_TEXTURE6;
uniform sampler2D GL_TEXTURE7;
// Texture coordinates.
varying vec2 GL_TEXTURE0_COORD;
varying vec2 GL_TEXTURE1_COORD;
varying vec2 GL_TEXTURE2_COORD;
varying vec2 GL_TEXTURE3_COORD;
varying vec2 GL_TEXTURE4_COORD;
varying vec2 GL_TEXTURE5_COORD;
varying vec2 GL_TEXTURE6_COORD;
varying vec2 GL_TEXTURE7_COORD;
void DrawColorOnly() {
gl_FragColor = v_color;
}
void SampleTextureUnits() {
if (JGL_RENDERING_ROUTINE == J2D_DrawString)
gl_FragColor = vec4(v_color.rgb, v_color.a * texture2D(GL_TEXTURE0, GL_TEXTURE0_COORD).a);
// Draw sprite, partial sprite, mirror sprite, render target, partial render target.
else if (TEXTURE_UNIT_SET_COUNT == 1)
gl_FragColor = v_color * texture2D(GL_TEXTURE0, GL_TEXTURE0_COORD);
// Draw alpha masked sprite.
else if (TEXTURE_UNIT_SET_COUNT == 2) {
vec4 color_texture = texture2D(GL_TEXTURE0, GL_TEXTURE0_COORD);
float alpha_mask = texture2D(GL_TEXTURE1, GL_TEXTURE1_COORD).a;
gl_FragColor = vec4(v_color.rgb * color_texture.rgb, v_color.a * alpha_mask);
}
}
void Default() {
if (TEXTURE_UNIT_SET_COUNT == 0) {
DrawColorOnly(); return;
}
SampleTextureUnits();
}
void main() {
gl_FragColor = vec4(1, 1, 1, 1);
/* If you want behavior per JGL draw function, Or for specific ones. The order here matters because some JGL functions call others.
if (JGL_RENDERING_ROUTINE == J2D_DrawRenderTarget)
Default();
else if (JGL_RENDERING_ROUTINE == J2D_DrawPartialRenderTarget)
Default();
else if (JGL_RENDERING_ROUTINE == J2D_DrawAlphaMaskSprite)
Default();
else if (JGL_RENDERING_ROUTINE == J2D_DrawMirrorSprite)
Default();
else if (JGL_RENDERING_ROUTINE == J2D_DrawSprite)
Default();
else if (JGL_RENDERING_ROUTINE == J2D_DrawPartialSprite)
Default();
else if (JGL_RENDERING_ROUTINE == J2D_DrawPartialSprite)
Default();
else if (JGL_RENDERING_ROUTINE == J2D_DrawString)
Default();
else if (JGL_RENDERING_ROUTINE == J2D_DrawPoint)
Default();
else if (JGL_RENDERING_ROUTINE == J2D_DrawPoints)
Default();
else if (JGL_RENDERING_ROUTINE == J2D_DrawCubicBezierCurve)
Default();
else if (JGL_RENDERING_ROUTINE == J2D_DrawLine)
Default();
else if (JGL_RENDERING_ROUTINE == J2D_DrawLines)
Default();
else if (JGL_RENDERING_ROUTINE == J2D_DrawDottedLine)
Default();
else if (JGL_RENDERING_ROUTINE == J2D_DrawDashedLine)
Default();
else if (JGL_RENDERING_ROUTINE == J2D_DrawGradientLine)
Default();
else if (JGL_RENDERING_ROUTINE == J2D_OutlineRoundedRect)
Default();
else if (JGL_RENDERING_ROUTINE == J2D_FillChamferRect)
Default();
else if (JGL_RENDERING_ROUTINE == J2D_FillRoundedRect)
Default();
else if (JGL_RENDERING_ROUTINE == J2D_FillGradientRect)
Default();
else if (JGL_RENDERING_ROUTINE == J2D_OutlineRect)
Default();
else if (JGL_RENDERING_ROUTINE == J2D_FillRect)
Default();
else if (JGL_RENDERING_ROUTINE == J2D_OutlineCircle)
Default();
else if (JGL_RENDERING_ROUTINE == J2D_FillCircle)
Default();
else if (JGL_RENDERING_ROUTINE == J2D_OutlineTriangle)
Default();
else if (JGL_RENDERING_ROUTINE == J2D_FillTriangle)
Default();
else if (JGL_RENDERING_ROUTINE == J2D_FillGradientTriangle)
Default();
else if (JGL_RENDERING_ROUTINE == J2D_OutlinePolygon)
Default();
else if (JGL_RENDERING_ROUTINE == J2D_DrawArc)
Default();
else { Default(); }
*/
Default();
}

View File

@@ -1,7 +1,155 @@
#version 120
// TODO this will fail if we don't have the extension.
#extension GL_ARB_instanced_arrays : enable
attribute vec2 position;
#define J2D_DrawPoint 1
#define J2D_DrawPoints 2
#define J2D_DrawLine 3
#define J2D_DrawLines 4
#define J2D_DrawDottedLine 5
#define J2D_DrawDashedLine 6
#define J2D_DrawGradientLine 7
#define J2D_OutlineRect 8
#define J2D_OutlineRoundedRect 9
#define J2D_OutlineChamferRect 10
#define J2D_FillRect 11
#define J2D_FillGradientRect 12
#define J2D_FillRoundedRect 13
#define J2D_FillChamferRect 14
#define J2D_DrawRenderTarget 15
#define J2D_DrawPartialRenderTarget 16
#define J2D_DrawSprite 17
#define J2D_DrawAlphaMaskSprite 18
#define J2D_DrawPartialSprite 19
#define J2D_DrawMirrorSprite 20
#define J2D_OutlineCircle 21
#define J2D_FillCircle 22
#define J2D_OutlineTriangle 23
#define J2D_FillTriangle 24
#define J2D_FillGradientTriangle 25
#define J2D_DrawCubicBezierCurve 26
#define J2D_OutlinePolygon 27
#define J2D_DrawString 28
#define J2D_DrawArc 29
uniform int JGL_RENDERING_ROUTINE;
uniform bool JGL_INSTANCED_RENDERING;
// The color manually set with glColor4f, glColor4ubv etc etc.
varying vec4 v_color;
// Local space vertices for instanced rendering.
attribute vec2 a_vertex_position; // 0
// The position at which to render the instance.
attribute vec2 a_instance_position; // 1
// The scale of the instance.
attribute vec2 a_instance_size; // 2
// The color of the instance.
attribute vec4 a_instance_color; // 3
// The texture coordinates in each texture unit.
varying vec2 GL_TEXTURE0_COORD;
varying vec2 GL_TEXTURE1_COORD;
varying vec2 GL_TEXTURE2_COORD;
varying vec2 GL_TEXTURE3_COORD;
varying vec2 GL_TEXTURE4_COORD;
varying vec2 GL_TEXTURE5_COORD;
varying vec2 GL_TEXTURE6_COORD;
varying vec2 GL_TEXTURE7_COORD;
vec4 Default() {
v_color = gl_Color;
return gl_ModelViewProjectionMatrix * gl_Vertex;
}
vec4 DefaultInstanced() {
v_color = a_instance_color;
vec2 scaled = a_vertex_position * a_instance_size;
vec2 world_pos = scaled + a_instance_position;
return gl_ModelViewProjectionMatrix * vec4(world_pos, 0.0, 1.0);
}
#include "shared.glsl"
void main() {
gl_Position = vec4(position.x, position.y, 1.0, 1.0);
GL_TEXTURE0_COORD = gl_MultiTexCoord0.xy;
GL_TEXTURE1_COORD = gl_MultiTexCoord1.xy;
GL_TEXTURE2_COORD = gl_MultiTexCoord2.xy;
GL_TEXTURE3_COORD = gl_MultiTexCoord3.xy;
GL_TEXTURE4_COORD = gl_MultiTexCoord4.xy;
GL_TEXTURE5_COORD = gl_MultiTexCoord5.xy;
GL_TEXTURE6_COORD = gl_MultiTexCoord6.xy;
GL_TEXTURE7_COORD = gl_MultiTexCoord7.xy;
/* If you want behavior per JGL draw function, Or for specific ones. The order here matters because some JGL functions call others.
if (JGL_RENDERING_ROUTINE == J2D_DrawRenderTarget)
gl_Position = Default();
else if (JGL_RENDERING_ROUTINE == J2D_DrawPartialRenderTarget)
gl_Position = Default();
else if (JGL_RENDERING_ROUTINE == J2D_DrawAlphaMaskSprite)
gl_Position = Default();
else if (JGL_RENDERING_ROUTINE == J2D_DrawMirrorSprite)
gl_Position = Default();
else if (JGL_RENDERING_ROUTINE == J2D_DrawSprite)
gl_Position = Default();
else if (JGL_RENDERING_ROUTINE == J2D_DrawPartialSprite)
gl_Position = Default();
else if (JGL_RENDERING_ROUTINE == J2D_DrawPartialSprite)
gl_Position = Default();
else if (JGL_RENDERING_ROUTINE == J2D_DrawString)
gl_Position = Default();
else if (JGL_RENDERING_ROUTINE == J2D_DrawPoint)
gl_Position = Default();
else if (JGL_RENDERING_ROUTINE == J2D_DrawPoints)
gl_Position = Default();
else if (JGL_RENDERING_ROUTINE == J2D_DrawCubicBezierCurve)
gl_Position = Default();
else if (JGL_RENDERING_ROUTINE == J2D_DrawLine)
gl_Position = Default();
else if (JGL_RENDERING_ROUTINE == J2D_DrawLines)
gl_Position = Default();
else if (JGL_RENDERING_ROUTINE == J2D_DrawDottedLine)
gl_Position = Default();
else if (JGL_RENDERING_ROUTINE == J2D_DrawDashedLine)
gl_Position = Default();
else if (JGL_RENDERING_ROUTINE == J2D_DrawGradientLine)
gl_Position = Default();
else if (JGL_RENDERING_ROUTINE == J2D_OutlineRoundedRect)
gl_Position = Default();
else if (JGL_RENDERING_ROUTINE == J2D_FillChamferRect)
gl_Position = Default();
else if (JGL_RENDERING_ROUTINE == J2D_FillRoundedRect)
gl_Position = Default();
else if (JGL_RENDERING_ROUTINE == J2D_FillGradientRect)
gl_Position = Default();
else if (JGL_RENDERING_ROUTINE == J2D_OutlineRect)
gl_Position = Default();
else if (JGL_RENDERING_ROUTINE == J2D_FillRect)
gl_Position = Default();
else if (JGL_RENDERING_ROUTINE == J2D_OutlineCircle)
gl_Position = Default();
else if (JGL_RENDERING_ROUTINE == J2D_FillCircle)
gl_Position = Default();
else if (JGL_RENDERING_ROUTINE == J2D_OutlineTriangle)
gl_Position = Default();
else if (JGL_RENDERING_ROUTINE == J2D_FillTriangle)
gl_Position = Default();
else if (JGL_RENDERING_ROUTINE == J2D_FillGradientTriangle)
gl_Position = Default();
else if (JGL_RENDERING_ROUTINE == J2D_OutlinePolygon)
gl_Position = Default();
else if (JGL_RENDERING_ROUTINE == J2D_DrawArc)
gl_Position = Default();
else { gl_Position = Default(); }
*/
if (JGL_INSTANCED_RENDERING)
gl_Position = DefaultInstanced();
else
gl_Position = Default();
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 942 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

@@ -16,6 +16,7 @@
#include <Color4.hpp>
#include <JGL/types/Texture.h>
#include <JGL/types/Enums.h>
#include <JGL/types/Instance.h>
#include <JGL/types/FontCache.h>
#include <JGL/types/Font.h>
#include <JGL/types/RenderTarget.h>
@@ -26,29 +27,51 @@
#include <J3ML/Geometry/Sphere.hpp>
#include <J3ML/Geometry/Capsule.hpp>
#include <J3ML/Geometry/Triangle2D.hpp>
#include <J3ML/J3ML.hpp>
#include <JGL/types/Font.h>
#include <JGL/types/VRamList.h>
#include <JGL/types/VertexArray.h>
#include <JGL/types/TextureAtlas.h>
#include <JGL/types/Shader.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;
inline VRamList* draw_points_positions = nullptr;
inline VRamList* draw_points_colors = 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;
[[nodiscard]] bool Init(const Vector2& window_size, float fovY, float far_plane);
/// @param window_size
void Update(const Vector2& window_size);
[[nodiscard]] bool Init(const Vector2i& window_size, float fovY, float far_plane);
void Update(const Vector2i& window_size);
/// Clear the default framebuffer for the OpenGL context (0).
void ClearScreen(const Color4& clear_color);
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.
}
/// 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.
@@ -56,7 +79,7 @@ namespace JGL::J2D {
/// 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);
void Begin(RenderTarget* render_target = nullptr, Shader* shader = nullptr, bool clear_buffers = false);
/// Closes a 2-D rendering context with the underlying graphics system (In this case& by default OpenGL).
/// @see Begin().
@@ -64,8 +87,10 @@ namespace JGL::J2D {
/// 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);
void OptionalLights(const LightBase** lights, const size_t& light_count);
/// Specifies a light which is required for every object in the scene.
void RequiredLight(const LightBase* light);
/// Plots a single pixel on the screen.
/// @param color A 3-or-4 channel color value. @see class Color3, class Color4
@@ -76,10 +101,17 @@ namespace JGL::J2D {
/// 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 colors A set of 4 channel color values. @see 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 num_points, float radius = 1.f);
void DrawPoints(const Color4* color, const Vector2* points, int point_count, float radius = 1.f);
/// 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);
/// Plots a line (segment) on the screen.
/// @param color A 3-or-4 channel color value. @see classes Color3, Color4.
@@ -89,6 +121,34 @@ namespace JGL::J2D {
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);
/// 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);
/// 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);
/// 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 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
@@ -96,7 +156,7 @@ namespace JGL::J2D {
/// @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 x, float y, float w, float h, 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 an outline of a rectangle on the screen.
/// @param color A 3-or-4 channel color value. @see class Color3, class Color4
@@ -105,6 +165,8 @@ namespace JGL::J2D {
/// @param thickness The width at which to render the lines.
void OutlineRect(const Color4& color, const Vector2& pos, const Vector2& size, float thickness = 1);
void BatchOutlineRect(const Instance2D* instances, float thickness, const size_t& instance_count);
/// 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.
@@ -129,12 +191,13 @@ namespace JGL::J2D {
void FillRect(const Color4& color, const Vector2& pos, const Vector2& size);
/// 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 top_left_color A 3-or-4 channel color value. @see class Color3, class Color4
/// @param bottom_left_color A 3-or-4 channel color value. @see class Color3, class Color4
/// @param bottom_right_color A 3-or-4 channel color value. @see class Color3, class Color4
/// @param top_right_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.
void FillGradientRect(const Color4& color1, const Color4& color2, const Direction& gradient, const Vector2& pos, const Vector2& size);
void FillGradientRect(const Color4& top_left_color, const Color4& bottom_left_color, const Color4& bottom_right_color, const Color4& top_right_color, 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
@@ -144,6 +207,8 @@ namespace JGL::J2D {
/// @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);
void BatchFillRoundedRect(const Color4* colors, const Vector2* positions, const Vector2* sizes, float radius, unsigned int subdivisions, const size_t& count);
/// 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.
@@ -184,6 +249,10 @@ namespace JGL::J2D {
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 TextureAtlas* texture_atlas, const AtlasRegion& atlas_region, const Vector2& position, float rad_rotation, const Vector2& origin = Vector2(0, 0),
const Vector2& scale = Vector2(1, 1), const Color4& color = Colors::White);
void DrawSprite(const TextureAtlas& texture_atlas, const AtlasRegion& atlas_region, const Vector2& position, float rad_rotation, const Vector2& origin = Vector2(0, 0),
const Vector2& scale = Vector2(1, 1), const Color4& color = Colors::White);
/// 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).
@@ -203,6 +272,26 @@ namespace JGL::J2D {
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).
@@ -259,7 +348,7 @@ namespace JGL::J2D {
/// @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);
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.
@@ -296,8 +385,9 @@ namespace JGL::J2D {
/// @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);
void DrawString(const Color4& color, const std::string& text, float x, float y, u32 size, float scale = 1.f, const Font& font = Fonts::Jupiteroid);
void DrawString(const Color4& color, const std::string& text, const Vector2& pos, u32 size, float scale = 1.f, 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
@@ -315,10 +405,20 @@ namespace JGL::J2D {
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);
void BatchFillRect(const Instance2D* instances, const size_t& instance_count);
void BatchFillCircle(const Instance2D* instances, float subdivisions, const size_t& instance_count);
}
/// Drawing functions for primitive 3D Shapes.
/// 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);
@@ -327,10 +427,9 @@ namespace JGL::J3D {
void ChangeFarPlane(float far_plane);
/// Open a 3-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 3D context to render your content, then close when completed.
/// This keeps our code from, say, clobbering the OpenGL rendering context driving 2D content in between our calls.
void Begin();
/// @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().
@@ -573,7 +672,7 @@ namespace JGL::J3D {
/// @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 EulerAngle& orientation);
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
@@ -605,8 +704,9 @@ namespace JGL::J3D {
/// @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, const EulerAngle& angle = {0, 0, 0}, bool draw_back_face = false);
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();

View File

@@ -1,9 +0,0 @@
#pragma once
#include <JGL/types/VRamList.h>
#include <array>
namespace JGL::ShapeCache {
inline VRamList* cube_vertex_data = nullptr;
inline VRamList* cube_index_data = nullptr;
void Init();
}

View File

@@ -1,6 +1,9 @@
#pragma once
namespace JGL {
#define stringify(name) # name
enum class Direction : u8 {
None = 0,
Vertical = 1,
@@ -18,55 +21,61 @@ namespace JGL {
}
static std::string to_string(const JGL::Direction& direction) {
switch (direction) {
case JGL::Direction::None:
return "None";
case JGL::Direction::Vertical:
return "Vertical";
case JGL::Direction::Horizontal:
return "Horizontal";
case JGL::Direction::Diagonal_NWSE:
return "Diagonal_NWSE";
case JGL::Direction::Diagonal_SWNE:
return "Diagonal_SWNE";
default:
return "Unknown";
}
return stringify(direction);
}
enum class MSAA_SAMPLE_RATE : u8 {
MSAA_NONE = 0,
// TODO come up with a cleaner way to type out sample-rates before release.
// :shrug:, I'm not very creative. - Redacted.
/*enum class MSAA_SAMPLE_RATE : u8 {
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";
}
enum class ANISOTROPY_SAMPLE_RATE : u8 {
NONE = 0,
ANISOTROPY_2X = 2,
ANISOTROPY_4X = 4,
ANISOTROPY_8X = 8,
ANISOTROPY_16X = 16
};*/
/// This enumeration is used by both MSAA and Anisotropic Filtering.
/// However, higher rates may not be supported by one, or the other.
/// This additionally depends on what your graphics supports.
enum class SampleRate : u8 {
NONE = 0, X0 = 0,
X2 = 2,
X4 = 4,
X8 = 8,
X16 = 16,
};
enum class FilteringMode : u8 {
NEAREST = 0, // Fastest for 2D, Sometimes causes graphical issues.
BILINEAR = 1, // Fast and pretty, The best for 2D.
// Mipmapping uses approx 30% more v-ram.
MIPMAP_NEAREST = 2, // Nearest with mipmaps. The fastest for 3D, Sometimes causes graphical issues.
MIPMAP_BILINEAR = 3, // Bilinear with mipmaps, Fast and pretty. Uses more vram.
MIPMAP_TRILINEAR = 4 // Prettier, But still looks weird at very steep angles.
};
enum class WrappingMode : u8 {
REPEAT = 0,
MIRRORED_REPEAT = 1,
CLAMP_TO_EDGE = 2,
CLAMP_TO_BORDER = 3 // Effectively the same as clamp_to_edge
};
enum class ColorFormat : bool { RGB = false, RGBA = true };
static std::string to_string(const JGL::SampleRate& sample_rate) {
return stringify(sample_rate);
}
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;
}
static int to_int(const JGL::SampleRate& sample_rate) {
return (int)sample_rate;
}
}

View File

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

View File

@@ -22,11 +22,11 @@ private:
std::array<GLfloat, 12> texcoords;
public:
int x2offset = 0, y2offset = 0, w = 0, h = 0;
float advanceX = 0, advanceY = 0;
float advanceX = 0, advanceY = 0, ascent = 0, descent = 0;
//CachedGlyph(GLuint texture_id, char c);
CachedGlyph(char c, std::array<GLfloat, 12> texcoords, float x2o, float y2o, float w, float h, float advX, float advY);
char getCharacter() const;
CachedGlyph(char c, std::array<GLfloat, 12> texcoords, float x2o, float y2o, float w, float h, float advX, float advY, float asc, float desc);
[[nodiscard]] char getCharacter() const;
[[nodiscard]] std::array<GLfloat, 12> getTexCoords() const;
};

View File

@@ -0,0 +1,49 @@
#pragma once
#include <Colors.hpp>
#include <J3ML/LinearAlgebra/Vector2.hpp>
#include <J3ML/LinearAlgebra/Matrix4x4.hpp>
namespace JGL {
struct Instance2D;
struct Instance3D;
}
/// Information provided to JGL for each instance of an object you're rendering in a batch.
/// @note This is non-negotiable. This information is interleaved in *the same buffer* in v-ram for performance reasons - Redacted.
struct JGL::Instance2D {
public:
Color4 color;
Vector2 position;
Vector2 size;
Vector2 scale;
float rotation;
public:
/// @param position The position of the instance in world space.
/// @param size The size of the instance.
/// @param scale A multiplier to be applied to the size.
/// @param rotation The rotation in radians.
/// @param color A 3-or-4 channel color value. @see class Color3, class Color4.
/// @note If the thing you're drawing is textured you probably want Colors::White
Instance2D(const Color4& color, const Vector2& position, const Vector2& size, const Vector2& scale = Vector2::One, float rotation = 0.0f) :
color(color), position(position), size(size), scale(scale), rotation(rotation) {};
Instance2D() = default;
~Instance2D() = default;
};
struct JGL::Instance3D {
public:
Matrix4x4 instance_matrix;
Color4 color;
public:
/// @param instance_matrix A matrix containing rotation matrix, position, and scale.
/// @param color A 3-or-4 channel color value. @see class Color3, class Color4.
/// @note If the thing you're drawing is textured you probably want Colors::White
Instance3D(const Matrix4x4& instance_matrix, const Color4& color) : instance_matrix(instance_matrix), color(color) {};
Instance3D() = default;
~Instance3D() = default;
};

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

121
include/JGL/types/Shader.h Normal file
View File

@@ -0,0 +1,121 @@
#pragma once
#include <filesystem>
#include <J3ML/LinearAlgebra.hpp>
#include <fstream>
#include <iostream>
#include "glad/glad.h"
#include <Event.h>
// LearnOpenGL::Shader
// OpenGL Shader Class Wrapper
// Updated by dawsh
// https://learnopengl.com/code_viewer_gh.php?code=includes/learnopengl/shader.h
namespace JGL {
using namespace J3ML::LinearAlgebra;
class Shader;
}
/// TODO: Eventually support Hot-reloading shaders like in shadertoy. Should help tremendously with debugging.
class JGL::Shader {
public:
static inline Event<std::string, std::string> OnCompilationErrorMessage;
static bool HasFile(const std::filesystem::path& path)
{
return std::filesystem::exists(path);
}
static std::string ReadFile(const std::filesystem::path& path);
/// The default constructor does not initialize any member values.
Shader() = default;
/// Creates a shader by compiling a vertex and fragment program from the sources in the respective filesystem paths.
Shader(const std::filesystem::path& vertex_source_path, const std::filesystem::path& fragment_source_path, const std::vector<std::pair<std::string, GLint>>& attribute_bindings = {});
/// Creates a shader by compiling a vertex and fragment program from the respective GLSL source-strings.
Shader(const std::string& vertex_code, const std::string& fragment_code, const std::vector<std::pair<std::string, GLint>>& attribute_bindings = {});
/// @return True if the shader program successfully loaded and compiled.
[[nodiscard]] bool Loaded() const;
/// @return The integer handle that OpenGL links to this shader program.
[[nodiscard]] unsigned int Handle() const;
/// Enable this shader. All rendering performed thereafter will be affected by this shader code.
/// @see UseDefault.
// TODO: Implement for hot-reloading.
void Reload();
// TODO: Implement for hot-reloading.
void Unload();
/// @return The Uniform variable linked to the specified name.
/// A Uniform is global a variable that is unique per shader program object, and can be accessed from any shader at any stage in the shader program.
GLint Uniform(const std::string& name) const;
/// @return The Attribute variable linked to the specified name.
/// Attributes differ from Uniforms in that their value is different for each instance of the shader-program as it is running.
[[nodiscard]] GLint Attribute(const std::string& name) const;
/// Sets a `uniform bool name = value` in the shader program.
void SetBool (const std::string& name, bool value) const;
/// Sets a `uniform int name = value` in the shader program.
void SetInt (const std::string& name, int value) const;
/// Sets a `uniform float name = value` in the shader program.
void SetFloat(const std::string& name, float value) const;
/// Sets a `uniform vec2 name = value` in the shader program.
/// @note GLSL has builtin vec2, while we implement a Vector2 type. Please be aware there may be differences in implementation between them!
/// @see class J3ML::LinearAlgebra::Vector2.
void SetVector2(const std::string& name, const Vector2& value) const;
/// Sets a `uniform vec2 name = value` in the shader program.
/// @note GLSL has builtin vec2, while we implement a Vector2 type. Please be aware there may be differences in implementation between them!
/// @see class J3ML::LinearAlgebra::Vector2.
void SetVector2(const std::string& name, float x, float y) const;
/// Sets a `uniform vec3 name = value` in the shader program.
/// @note GLSL has builtin vec3, while we implement a Vector3 type. Please be aware there may be differences in implementation between them!
/// @see class J3ML::LinearAlgebra::Vector3.
void SetVector3(const std::string& name, const Vector3& value) const;
/// Sets a `uniform vec3 name = value` in the shader program.
/// @note GLSL has builtin vec3, while we implement a Vector3 type. Please be aware there may be differences in implementation between them!
/// @see class J3ML::LinearAlgebra::Vector3.
void SetVector3(const std::string& name, float x, float y, float z) const;
/// Sets a `uniform vec4 name = value` in the shader program.
/// @note GLSL has builtin vec4, while we implement aVector4 type. Please be aware there may be differences in implementation between them!
/// @see class J3ML::LinearAlgebra::Vector4.
void SetVector4(const std::string& name, const Vector4& value) const;
/// Sets a `uniform vec4 name = value` in the shader program.
/// @note GLSL has builtin vec4, while we implement a Vector4 type. Please be aware there may be differences in implementation between them!
/// @see class J3ML::LinearAlgebra::Vector4.
void SetVector4(const std::string& name, float x, float y, float z, float w) const;
/// Sets a `uniform mat2 name = value` in the shader program.
/// @note GLSL has builtin mat2, while we implement a Matrix2x2 type. Please be aware there may be differences in implementation between them!
/// @see class J3ML::LinearAlgebra::Matrix2x2
void SetMatrix2x2(const std::string& name, const Matrix2x2& value) const;
/// Sets a `uniform mat3 name = value` in the shader program.
/// @note GLSL has builtin mat3, while we implement a Matrix3x3 type. Please be aware there may be differences in implementation between them!
/// @see class J3ML::LinearAlgebra::Matrix3x3
void SetMatrix3x3(const std::string& name, const Matrix3x3& value) const;
/// Sets a `uniform mat4 name = value` in the shader program.
/// @note GLSL has builtin mat4, while we implement a Matrix4x4 type. Please be aware there may be differences in implementation between them!
/// @see class J3ML::LinearAlgebra::Matrix4x4
void SetMatrix4x4(const std::string& name, const Matrix4x4& value) const;
// TODO: Implement Uniform-Setters for GLSL types that do not have a J3ML corollary (dmat3x4, dvec4, etc).
protected:
private:
unsigned int id = 0;
std::string vertexPath;
std::string vertexSource;
std::string fragmentSource;
std::string fragmentPath;
mutable std::unordered_map<std::string, GLint> uniform_location_cache;
static void checkCompileErrors(GLuint shader, const std::string& type);
};

View File

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

View File

@@ -1,57 +1,69 @@
#pragma once
#include <ReImage/Image.h>
#include <J3ML/LinearAlgebra.hpp>
#include<vector>
#include <filesystem>
#include <J3ML/LinearAlgebra/Vector2i.hpp>
#include <Color3.hpp>
#include <Color4.hpp>
#include <glad/glad.h>
#include <JGL/types/Enums.h>
namespace JGL {
using namespace ReImage;
enum class TextureFilteringMode : u8 {
NEAREST = 0, //Fastest for 2D, Sometimes causes graphical issues.
BILINEAR = 1, //Fast and pretty, The best for 2D.
using J3ML::LinearAlgebra::Vector2i;
class Texture;
}
MIPMAP_NEAREST = 2, //Nearest with mipmaps. The fastest for 3D, Sometimes causes graphical issues. Uses more vram.
MIPMAP_BILINEAR = 3, //Bilinear with mipmaps, Fast and pretty. Uses more vram.
MIPMAP_TRILINEAR = 4 //The prettiest. Still decent speed. Uses more vram.
};
enum class TextureWrappingMode : u8 {
REPEAT = 0,
MIRRORED_REPEAT = 1,
CLAMP_TO_EDGE = 2,
CLAMP_TO_BORDER = 3 //Effectively the same as clamp_to_edge
};
/// Represents texture data loaded on the GPU. Contains a handle that can be passed to OpenGL draw calls.
class Texture {
private:
void Erase();
protected:
GLuint texture_handle = 0;
Vector2 texture_size = {0, 0};
ReImage::TextureFlag texture_flags;
ReImage::TextureFormat texture_format;
TextureFilteringMode texture_filtering_mode;
TextureWrappingMode texture_wrapping_mode;
void load(Image* software_texture, const Vector2& size, const TextureFormat& format, TextureFilteringMode filtering_mode, TextureWrappingMode wrapping_mode);
public:
/// Load a texture from a file,
explicit Texture(const std::string& file, TextureFilteringMode filtering_mode = TextureFilteringMode::BILINEAR, TextureWrappingMode wrapping_mode = TextureWrappingMode::CLAMP_TO_EDGE, const TextureFlag& flags = TextureFlag::INVERT_Y);
Texture(Image* software_texture, const Vector2& size, const TextureFormat& format, TextureFilteringMode filtering_mode, TextureWrappingMode wrapping_mode);
/* Initialize a texture filled with trash data
this is primarily for the RenderTarget */
explicit Texture(const Vector2& size);
Texture(const Texture& rhs);
~Texture();
public:
[[nodiscard]] GLuint GetGLTextureHandle() const;
[[nodiscard]] Vector2 GetDimensions() const;
[[nodiscard]] TextureFilteringMode GetFilteringMode() const;
[[nodiscard]] TextureWrappingMode GetWrappingMode() const;
[[nodiscard]] TextureFlag GetFlags() const;
[[nodiscard]] TextureFormat GetFormat() const;
[[nodiscard]] std::vector<Color4> GetPixelData() const;
};
}
/// 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;
SampleRate anisotropy;
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);
[[nodiscard]] bool SizeExceedsMaximum(const Vector2i& size);
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 current level of anisotropic filtering for this texture.
[[nodiscard]] SampleRate GetAnisotropySampleRate() 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,
SampleRate anisotropy = SampleRate::NONE, WrappingMode wrapping_mode = WrappingMode::CLAMP_TO_EDGE, bool invert_y = true);
/// Load a texture from raw pixels.
Texture(const Color4* pixels, const Vector2i& size, FilteringMode filtering_mode = FilteringMode::BILINEAR,
SampleRate anisotropy = SampleRate::NONE, WrappingMode wrapping_mode = WrappingMode::CLAMP_TO_EDGE);
/// Load a texture from raw pixels.
Texture(const Color3* pixels, const Vector2i& size, FilteringMode filtering_mode = FilteringMode::BILINEAR,
SampleRate anisotropy = SampleRate::NONE, WrappingMode wrapping_mode = WrappingMode::CLAMP_TO_EDGE);
/// Initialize a texture filled with trash data.
/// @see RenderTarget
explicit Texture(const Vector2i& size, FilteringMode filtering_mode = FilteringMode::NEAREST);
Texture(const Texture& rhs);
Texture() = default;
~Texture();
public:
/// @returns True if this system supports anisotropy.
// TODO add a similar mechanism for MSAA so the extension isn't required.
static enum SampleRate MaxAnisotropySampleRate();
/// @returns The biggest size for a texture on this system.
/// @note on modern systems this is *usually* ridiculous.
[[nodiscard]] static Vector2i MaxSize();
};

View File

@@ -0,0 +1,35 @@
#pragma once
#include <JGL/types/Texture.h>
namespace JGL {
struct AtlasRegion {
Vector2i position = { 0, 0 };
Vector2i size = { 0, 0 };
};
class TextureAtlas;
}
// A texture which actually contains multiple such that you can pass it in to draw sprite or draw partial sprite.
// It also makes for more efficient sprite batch because you don't have to swap textures between.
class JGL::TextureAtlas : public Texture {
protected:
std::vector<AtlasRegion> regions;
public:
unsigned int RegionCount() { return regions.size(); }
[[nodiscard]] AtlasRegion GetRegion(unsigned int region) const;
public:
/// Create a texture atlas from multiple separate images.
/// @param pixels The array(s) of pixels.
/// @param sizes The size of each image.
/// @param texture_count The number of textures this atlas will contain.
TextureAtlas(const Color4** pixels, const Vector2i** sizes, unsigned int texture_count, FilteringMode filtering_mode = FilteringMode::NEAREST);
/// Create a texture atlas from a single image
/// @param pixels The array of pixels.
/// @param size The size of the image.
/// @param regions The individual regions that will make up your atlas.
/// @param region_count The number of regions there are.
TextureAtlas(const Color4* pixels, const Vector2i& size, AtlasRegion** regions, unsigned int region_count, FilteringMode filtering_mode = FilteringMode::NEAREST, SampleRate anisotropy = SampleRate::NONE);
~TextureAtlas() = default;
};

View File

@@ -5,52 +5,68 @@
#include <J3ML/LinearAlgebra/Vector2i.hpp>
#include <J3ML/LinearAlgebra/Vector3.hpp>
#include <J3ML/LinearAlgebra/Vector4.hpp>
#include <Color4.hpp>
namespace JGL {
class VRamList;
/// A hint for OpenGL on how the VBO is to be used.
enum VRamUsageHint : GLenum {
// Never updated after creation.
Fixed = GL_STATIC_DRAW,
// Modified occasionally.
Dynamic = GL_DYNAMIC_DRAW,
// Constantly modified and used one or a few times.
Stream = GL_STREAM_DRAW
};
}
/// A wrapped for "Vertex Buffer Object" In OpenGL, Store things in VRam.
/// A wrapped for "Vertex Buffer Object" In OpenGL, Store things in V-ram.
class JGL::VRamList {
private:
VRamUsageHint usage_hint = Fixed;
GLuint list_handle = 0;
long num_elements = 0;
long byte_count = 0;
bool element_array_buffer = false;
/// "Spin Locking" fix for multi-threading.
bool spin_lock = false;
// TODO mutex lock.
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(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(const GLuint* data, const long& count, VRamUsageHint hint = Fixed);
VRamList(const GLfloat* data, const long& count, VRamUsageHint hint = Fixed);
VRamList(const Vector2* data, const long& count, VRamUsageHint hint = Fixed);
VRamList(const Vector3* data, const long& count, VRamUsageHint hint = Fixed);
VRamList(const Vector4* data, const long& count, VRamUsageHint hint = Fixed);
VRamList(const Color4* data, const long& count, VRamUsageHint hint = Fixed);
/// Allocate an empty VBO
/// @param byte_count the size of the buffer in bytes.
/// @param element_array_buffer if applicable, whether the buffer is to be used for GLuint indices.
/// @param hint A hint to the graphics driver for what the buffer is to be used for.
VRamList(const size_t& byte_count, bool element_array_buffer, VRamUsageHint hint = Fixed);
~VRamList();
/** Copying around the VBO data to a new VBO like this is slow.
* Pass to function by const reference or pointer always. */
VRamList(const VRamList& rhs);
VRamList() = default;
VRamList() : list_handle(0), byte_count(0), element_array_buffer(false) {}
public:
[[nodiscard]] GLuint GetHandle() const;
[[nodiscard]] GLuint Handle() const;
/// Returns the number of elements in the list.
[[nodiscard]] long GetLength() const;
[[nodiscard]] long Length() const;
/// Returns the size of the data in bytes.
[[nodiscard]] size_t GetDataSize() const;
[[nodiscard]] size_t Size() 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;
/** Replace the data of an existing VBO in it's entirety. Must be same type. */
void SetData(const GLfloat* data, const long& count);
void SetData(const Vector2* data, const long& count);
void SetData(const Vector3* data, const long& count);
void SetData(const Vector4* data, const long& count);
void SetData(const Color4* data, const long& count);
void SetData(const GLuint* data, const long& count);
void SetData(const Vector2i* data, const long& count);
@@ -58,11 +74,17 @@ public:
/** 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 */
// TODO provide a bool to specify whether the current buffer should be orphaned.
void UpdateData(const GLfloat* data, const long& offset, const long& count);
void UpdateData(const Vector2* data, const long& offset, const long& count);
void UpdateData(const Vector3* data, const long& offset, const long& count);
void UpdateData(const Vector4* data, const long& offset, const long& count);
void UpdateData(const Color4* data, const long& offset, const long& count);
void UpdateData(const GLuint* data, const long& offset, const long& count);
void UpdateData(const Vector2i* data, const long& offset, const long& count);
// Update only a portion of the data in a VBO using bytes.
// TODO This version of the function has no protection for out of bounds writes.
void UpdateData(const uint8_t* data, const long& offset, const long& count);
};

View File

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

229
main.cpp
View File

@@ -1,18 +1,22 @@
#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>
#include <JGL/types/Shader.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;
std::vector<Instance2D> rect_instances;
/// A draggable 2D point that highlights when moused over and when clicked.
class Gizmo
{
public:
@@ -22,6 +26,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,27 +46,25 @@ public:
void Update(const Vector2& mouse) {
if (dragging)
position = position.Lerp(mouse, 0.25f);
position = position.Lerp(mouse, lerp_rate);
hovered = mouse.Distance(position) < range;
}
void Draw() {
if (dragging)
J2D::DrawPoint(Colors::White, position, 4.f);
J2D::DrawPoint(drag_color, position, drag_radius);
else if (hovered)
J2D::DrawPoint(Colors::Reds::Crimson, position, 6.f);
J2D::DrawPoint(hover_color, position, hover_radius);
else
J2D::DrawPoint(Colors::Reds::Salmon, position, 3.f);
J2D::DrawString(Colors::White, std::format("{:.1f},{:.1f}", position.x, position.y), position.x, position.y, 1.f, 10, FreeSans);
J2D::DrawPoint(base_color, position, base_radius);
J2D::DrawString(Colors::White, std::format("{:.1f},{:.1f}", position.x, position.y), position.x, position.y, text_scale, text_size);
}
};
/// A 3D Camera Controller.
class Camera {
public:
Vector3 position = {0,0,0};
@@ -97,48 +108,56 @@ Gizmo b({200, 250});
Gizmo c({350, 300});
Gizmo d({450, 250});
JGL::Font FreeSans;
Texture* image;
Texture* image2;
Texture* image_mask;
RenderTarget* j2d_render_target;
RenderTarget* image2_render_target;
Shader* shader;
Vector2 result;
class JGLDemoWindow : public ReWindow::RWindow
class JGLDemoWindow : public ReWindow::OpenGLWindow
{
public:
void initGL() {
camera = new Camera;
if (!JGL::Init(getSize(), 75, 100))
if (!JGL::Init({ 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({540, 540}, {0,0,0,0}, false, MSAA_SAMPLE_RATE::MSAA_NONE);
image2 = image;
image2_render_target = new RenderTarget(image2);
image = new Texture("assets/sprites/Re3D.png", FilteringMode::MIPMAP_NEAREST);
image_mask = new Texture("assets/sprites/alpha_mask_2.png");
j2d_render_target = new RenderTarget({540, 500}, {0,0,0,0}, false,
SampleRate::NONE, FilteringMode::MIPMAP_NEAREST);
J2D::Begin(image2_render_target);
J2D::DrawString(Colors::Red, "TEST", 0, 16, 1, 16, FreeSans);
J2D::FillRect(Colors::Blue, {0,0}, {4,4});
J2D::End();
//Texture::MultiplyByAlphaMask(*image, *image_mask);
shader = new Shader(std::filesystem::path("assets/shader_programs/test_vertex.glsl"), std::filesystem::path("assets/shader_programs/test_fragment.glsl"),
{{"a_vertex_position", 0}, {"a_instance_position", 1}, {"a_instance_size", 2}, {"a_instance_color", 3}});
result = Jupiteroid.MeasureString("The quick black fox jumps over the lazy dog.", 16);
for (unsigned int i = 0; i < 100; i++)
rect_instances.emplace_back(Colors::Red, Vector2(420, 420), Vector2(20, 20));
}
Vector3 textAngle = {0,0,0};
EulerAngleXYZ textAngle = {0,0,0};
float fov = 90;
float pulse = 0;
float sprite_radians = 0;
bool fov_increasing = true;
int blit_pos = 0;
void display() {
float dt = 1.f / fps;
JGL::Update(getSize());
pulse += 20 * delta_time;
float dt = GetDeltaTime();
JGL::Update({ GetSize().x, GetSize().y });
if (fov_increasing)
fov += 0.025;
@@ -152,47 +171,45 @@ public:
//J3D::ChangeFOV(fov);
sprite_radians += 0.005;
textAngle.y += (1);
textAngle.yaw += 1;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
camera->render();
// All 3D elements of the scene and JGL elements *must* be rendered before the 2D stuff
/* if rendering to screen space directly. */
auto test_light = PointLight({2,1,2}, {(u8) pulse,(u8) pulse,(u8) pulse, 255}, {(u8) pulse, (u8) pulse, (u8) 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, 16, 16, true);
J3D::FillAABB(Colors::Whites::AliceBlue, {0,0,0.5f}, {0.1f, 0.1f, 0.1f});
J3D::WireframeAABB(Colors::Gray, {0,0,0.5f}, {0.11f, 0.06f, 0.11f});
AABB boxes[1] = {{Vector3(-0.2125, -0.2125,0.28750), Vector3(0.2125,0.2125,0.7125)}};
J3D::BatchWireframeAABB(Colors::Yellow, boxes, 1, 1);
//J3D::WireframeOBB(Colors::Red, {0, 0, 1.5f}, {0.40f, 0.10f, 0.10f}, {0,textAngle.y, 0});
//J3D::FillSphere({0,255,0,120}, sphere);
//J3D::DrawCubicBezierCurve(Colors::Blue, {0,0,0.3}, {0,0,0.5}, {0.2,0,0.3}, {0.2, 0.3, 0.1}, 30);
//J3D::WireframeIcosahedron(Colors::Green, {0,0,0.5f}, 0.125f, 1.f);
Sphere sphere = {{1,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::Yellow, {0.5, 0, 0.5}, {0.125, 0.125, 0.125}, 1);
J3D::End();
J2D::Begin(j2d_render_target, true);
//JGL::ClearScreen(Colors::Red);
J2D::Begin(j2d_render_target, shader, true);
J2D::FillRect(Colors::Blue, {0,52}, {100,100});
J2D::DrawSprite(image2, {300, 400}, sprite_radians * 0.10f, {0.5,0.5}, {1, 1}, Colors::White);
J2D::BatchFillRect(rect_instances.data(), rect_instances.size());
J2D::DrawSprite(image, {300, 400}, sprite_radians * 0.10f, {0.5,0.5}, {1, 1}, Colors::White);
J2D::DrawMirrorSprite(image, {400, 300}, Direction::Horizontal | Direction::Vertical, sprite_radians, {0.5,0.5}, {1, 1}, Colors::White);
J2D::DrawPartialSprite(image, {225, 300}, image->GetDimensions() * 0.25, image->GetDimensions() * 0.75, sprite_radians, {0.5, 0.5}, {1,1}, Colors::White);
J2D::DrawPartialSprite(image, Vector2(225, 300), Vector2(image->GetDimensions()) * 0.25, Vector2(image->GetDimensions()) * 0.75, sprite_radians, {0.5, 0.5}, {1,1}, Colors::White);
J2D::FillRect(Colors::Pinks::HotPink, {68, 120}, {32, 32});
J2D::FillGradientRect(Colors::Red, Colors::Blue, Direction::Diagonal_SWNE, {100,52}, {100,100});
J2D::FillGradientRect(Colors::Red, Colors::Green, Colors::Blue, Colors::White, {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);
auto box = JGL::Fonts::Jupiteroid.MeasureString("Hello g", 16);
J2D::FillChamferRect(Colors::Reds::LightSalmon, {150, 400}, {64, 64}, 5);
J2D::OutlineRoundedRect(Colors::Reds::LightCoral, {250, 350}, {128, 128}, 10, 2);
@@ -200,16 +217,15 @@ public:
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::DrawString(Colors::Green, "The quick black fox jumps over the lazy dog.", 0, 20, 1, 16);
J2D::OutlineRect(Colors::Red, {0, 20}, result, 1);
//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::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);
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::FillPolygon(Colors::White, {{200, 400}, {220, 420}, {220, 430}, {230, 410}, {200, 400}});
J2D::DrawCubicBezierCurve(Colors::Blues::CornflowerBlue,
a.position,
b.position,
@@ -221,58 +237,57 @@ public:
b.Draw();
c.Draw();
d.Draw();
J2D::End();
RenderTarget::Blit(Colors::Red, {0, 0}, j2d_render_target);
//Draw the Render Target that we just drew all that stuff onto.
J2D::Begin();
J2D::DrawPartialRenderTarget(j2d_render_target, {0, 0}, {0,0}, {512, 512});
//J2D::DrawSprite(j2d_render_target, {0, 0}, 0, {0.5, 0.5}, {1,1}, Colors::White);
J2D::DrawSprite(image2_render_target, {300, 500}, 0, {0.5, 0.5}, {1,1}, Colors::White);
J2D::End();
J2D::Begin(nullptr, nullptr, true);
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 {
if (isKeyDown(Keys::RightArrow))
fps = GetRefreshRate();
if (IsKeyDown(Keys::RightArrow))
camera->angle.y += 45.f * elapsed;
if (isKeyDown(Keys::LeftArrow))
if (IsKeyDown(Keys::LeftArrow))
camera->angle.y -= 45.f * elapsed;
if (isKeyDown(Keys::UpArrow))
if (IsKeyDown(Keys::UpArrow))
camera->angle.x -= 45.f * elapsed;
if (isKeyDown(Keys::DownArrow))
if (IsKeyDown(Keys::DownArrow))
camera->angle.x += 45.f * elapsed;
if (isKeyDown(Keys::Space))
if (IsKeyDown(Keys::Space))
camera->position.y += 1.f * elapsed;
if (isKeyDown(Keys::LeftShift))
if (IsKeyDown(Keys::LeftShift))
camera->position.y -= 1.f * elapsed;
/* This is wrong of course. Just for testing purposes.
if (isKeyDown(Keys::W))
//This is wrong of course. Just for testing purposes.
if (IsKeyDown(Keys::W))
camera->position.z += 1.f * elapsed;
if (isKeyDown(Keys::S))
if (IsKeyDown(Keys::S))
camera->position.z -= 1.f * elapsed;
if (isKeyDown(Keys::A))
if (IsKeyDown(Keys::A))
camera->position.x += 1.f * elapsed;
if (isKeyDown(Keys::D))
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();
@@ -281,7 +296,7 @@ public:
d.Grab();
}
void OnMouseButtonUp(const ReWindow::WindowEvents::MouseButtonUpEvent & ev) override
void OnMouseButtonUp(const ReWindow::MouseButtonUpEvent & ev) override
{
RWindow::OnMouseButtonUp(ev);
a.Release();
@@ -291,25 +306,41 @@ 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;
Shader::OnCompilationErrorMessage += [] (std::string type, std::string info) {
std::cout << type << ", " << info << std::endl;
};
/*
std::stringstream buffer;
buffer << file.rdbuf();
std::string file_text = buffer.str();
file.close();
std::cout << file_text << std::endl;
auto result = VertexArray::LoadAMO(file_text);
*/
//ReWindow::Logger::Error.EnableConsole(false);
//ReWindow::Logger::Warning.EnableConsole(false);
//ReWindow::Logger::Debug.EnableConsole(false);
while (window->IsAlive())
window->ManagedRefresh();
return 0;
}

8671
src/Fonts.cpp Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,38 +1,68 @@
#include <JGL/ShapeCache.h>
#include <JGL/JGL.h>
void JGL::ShapeCache::Init() {
if (!cube_vertex_data) {
std::array<Vector3, 24> vertices {
Vector3(-1, 1, -1), Vector3(1, 1, -1),
Vector3(1, 1, -1), Vector3(1, 1, 1),
Vector3(1, 1, 1), Vector3(-1, 1, 1),
Vector3(-1, 1, 1), Vector3(-1, 1, -1),
Vector3(-1, -1, -1), Vector3(1, -1, -1),
Vector3(1, -1, -1), Vector3(1, -1, 1),
Vector3(1, -1, 1), Vector3(-1, -1, 1),
Vector3(-1, -1, 1), Vector3(-1, -1, -1),
Vector3(-1, -1, -1), Vector3(-1, 1, -1),
Vector3(1, -1, -1), Vector3(1, 1, -1),
Vector3(1, -1, 1), Vector3(1, 1, 1),
Vector3(-1, -1, 1), Vector3(-1, 1, 1)
};
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} };
std::array<Vector2, 4> square_vertices = { Vector2(0, 0), {0, 1}, {1, 1}, {1, 0} };
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());
}
if (!cube_index_data) {
std::array<GLuint, 36> indices {
0, 1, 3, 0, 3, 5,
if (!draw_points_colors) {
std::array<Color4, 1> color = { Colors::Transparent };
draw_points_colors = new VRamList(color.data(), color.size(), Stream);
}
8, 9, 11, 8, 11, 13,
5, 3, 11, 5, 11, 13,
0, 1, 9, 0, 9, 8,
0, 5, 13, 0, 13, 8,
1, 3, 11, 1, 11, 9
};
cube_index_data = new VRamList(indices.data(), indices.size());
if (!draw_points_positions) {
std::array<Vector2, 1> position = { Vector2::Zero };
draw_points_positions = new VRamList(position.data(), position.size(), Stream);
}
}

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

File diff suppressed because it is too large Load Diff

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

@@ -0,0 +1,585 @@
#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();
glLoadMatrixf(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());
// TODO allocate once.
VRamList vertex_data(vertices.data(), vertices.size());
glBindBuffer(GL_ARRAY_BUFFER, vertex_data.Handle());
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.Length());
if (draw_stacks)
glRotatef(90, 0, 1, 0),
glDrawArrays(GL_LINE_LOOP, 0, vertex_data.Length());
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->Handle());
glVertexPointer(3, GL_FLOAT, sizeof(Vector3), nullptr);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ShapeCache::cube_index_data->Handle());
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);
glDrawElements(GL_LINE_LOOP, ShapeCache::cube_index_data->Length(), GL_UNSIGNED_INT, nullptr);
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->Handle());
glVertexPointer(3, GL_FLOAT, sizeof(Vector3), nullptr);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ShapeCache::cube_index_data->Handle());
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->Handle());
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.Handle());
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_data.Handle());
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.Length(), 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()->Handle());
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()->Length());
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__
@@ -85,7 +86,10 @@ namespace JGL {
u1, v0
};
cachedFont->appendGlyph(new CachedGlyph((char)charcode, texcoords, g->bitmap_left, g->bitmap_top, g->bitmap.width, g->bitmap.rows, (g->advance.x >> 6), (g->advance.y >> 6)));
float ascent = font.face->size->metrics.ascender / 64.0f;
float descent = -font.face->size->metrics.descender / 64.0f;
cachedFont->appendGlyph(new CachedGlyph((char) charcode, texcoords, g->bitmap_left, g->bitmap_top, g->bitmap.width, g->bitmap.rows, (g->advance.x >> 6), (g->advance.y >> 6), ascent, descent));
xoffset += g->bitmap.width;
charcode = FT_Get_Next_Char(font.face, charcode, &gindex);
@@ -94,7 +98,7 @@ namespace JGL {
return cachedFont;
}
void J2D::DrawString(const Color4& color, const std::string& text, float x, float y, float scale, u32 size, const Font& font) {
void J2D::DrawString(const Color4& color, const std::string& text, float x, float y, u32 size, float scale, const Font& font) {
// Offset by height to render at "correct" location.
y += size;
@@ -133,7 +137,7 @@ namespace JGL {
continue;
x2 = x + glyph->x2offset * scale;
y2 = y - glyph->y2offset * scale; // Adjust y-coordinate
y2 = y - glyph->y2offset * scale;
w = glyph->w * scale;
h = glyph->h * scale;
x += glyph->advanceX * scale;
@@ -155,14 +159,24 @@ namespace JGL {
}
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices.data());
glTexCoordPointer(2, GL_FLOAT, sizeof(Vector2), texcoords.data());
if (current_state.current_shader)
current_state.current_shader->SetInt("TEXTURE_UNIT_SET_COUNT", 1),
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_DrawString);
glDrawArrays(GL_TRIANGLES, 0, (int) vertices.size() * 6);
if (current_state.current_shader)
current_state.current_shader->SetInt("TEXTURE_UNIT_SET_COUNT", 0),
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0);
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;
@@ -237,13 +251,23 @@ namespace JGL {
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
/*
if (current_state.current_shader)
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_DrawString);
*/
glDrawArrays(GL_TRIANGLES, 0, (int) vertices.size() * 6);
/*
if (current_state.current_shader)
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0);
*/
if (!draw_back_face)
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,217 @@
#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(const State& state) {
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_CURRENT_PROGRAM, &result.current_shader_handle);
result.current_shader = state.current_shader;
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)
glEnableClientState(GL_COLOR_ARRAY);
else
glDisableClientState(GL_COLOR_ARRAY);
if (state.current_shader)
glUseProgram(state.current_shader->Handle());
else
glUseProgram(0);
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,128 @@
#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/types/Shader.h>
#include <JGL/logger/logger.h>
namespace JGL {
class State;
class StateStack;
enum RENDERING_ROUTINE_ID : int32_t;
}
enum JGL::RENDERING_ROUTINE_ID : int32_t {
J2D_DrawPoint = 1,
J2D_DrawPoints = 2,
J2D_DrawLine = 3,
J2D_DrawLines = 4,
J2D_DrawDottedLine = 5,
J2D_DrawDashedLine = 6,
J2D_DrawGradientLine = 7,
J2D_OutlineRect = 8,
J2D_OutlineRoundedRect = 9,
J2D_OutlineChamferRect = 10,
J2D_FillRect = 11,
J2D_FillGradientRect = 12,
J2D_FillRoundedRect = 13,
J2D_FillChamferRect = 14,
J2D_DrawRenderTarget = 15,
J2D_DrawPartialRenderTarget = 16,
J2D_DrawSprite = 17,
J2D_DrawAlphaMaskSprite = 18,
J2D_DrawPartialSprite = 19,
J2D_DrawMirrorSprite = 20,
J2D_OutlineCircle = 21,
J2D_FillCircle = 22,
J2D_OutlineTriangle = 23,
J2D_FillTriangle = 24,
J2D_FillGradientTriangle = 25,
J2D_DrawCubicBezierCurve = 26,
J2D_OutlinePolygon = 27,
J2D_DrawString = 28,
J2D_DrawArc = 29,
};
class JGL::State {
public:
//Matrix4x4 transformation = Matrix4x4::Identity;
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;
RenderTarget* current_render_target = nullptr;
GLint current_shader_handle = 0;
Shader* current_shader = nullptr;
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(const State& state);
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 bool supports_instanced = true;
}
namespace JGL::J2D {
// On windoze this can't be constexpr?
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, nullptr, 0, nullptr, false, false,
false, false, true, true,
true, false, 0,
{}, {}
};
inline State current_state;
}
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

@@ -4,6 +4,7 @@
#include <iostream>
#include <glad/glad.h>
#include <JGL/logger/logger.h>
#include <JGL/JGL.h>
#if __linux__
#include <freetype2/ft2build.h>
@@ -58,25 +59,38 @@ namespace JGL {
return Detail::InitTextEngine();
}
Font::Font(const unsigned char* data, const size_t& size) {
if (Detail::ft == nullptr)
throw std::runtime_error("Error::FREETYPE: FT_Library was not initialized before attempting to load a font!");
if (FT_New_Memory_Face(Detail::ft, data, size, 0, &face))
throw std::runtime_error("Error::FREETYPE: Failed to load font!");
unsigned int new_index = 0;
for (const auto& f : Detail::fonts)
if (f.index >= new_index)
new_index = f.index + 1;
index = new_index;
Detail::fonts.push_back(*this);
std::cout << "Loaded font from memory at " << static_cast<const void*>(data) << " with index " << new_index << std::endl;
}
Font::Font(const std::filesystem::path& path) {
if (Detail::ft == nullptr)
throw std::runtime_error("Error::FREETYPE: FT_Library was not initialized before attempting to load a font!");
Font font;
if (FT_New_Face(Detail::ft, path.string().c_str(), 0, &face)) {
std::cout << "Error::FREETYPE: Failed to load font!" << std::endl;
if (FT_New_Face(Detail::ft, path.string().c_str(), 0, &face))
throw std::runtime_error("Error::FREETYPE: Failed to load font!");
//return -1;
}
unsigned int newIndex = 0;
for (const auto& f : Detail::fonts)
if (f.index >= newIndex)
newIndex = f.index + 1;
index = newIndex;
Detail::fonts.push_back(font);
std::cout << "Loaded font from " << path << " with index " << newIndex << std::endl;
//return newIndex;
unsigned int new_index = 0;
for (const auto& f : Detail::fonts)
if (f.index >= new_index)
new_index = f.index + 1;
index = new_index;
Detail::fonts.push_back(*this);
std::cout << "Loaded font from " << path << " with index " << new_index << std::endl;
}
Font::~Font() {
@@ -94,61 +108,33 @@ namespace JGL {
return Font(path);
}
Vector2 Font::MeasureString(const std::string &text, unsigned int ptSize) {
Vector2 extents = Vector2(0,0);
bool font_of_size_in_cache = false;
Vector2 Font::MeasureString(const std::string& text, unsigned int ptSize) {
Vector2 extents = Vector2::Zero;
for(const auto& f : fontCache.getFonts()) {
if (f->getFontSize() == ptSize) {
font_of_size_in_cache = true;
break;
for(auto& f : fontCache.getFonts()) {
if (f->getFontIndex() == this->index) {
for (const char &c: text) {
auto glyph = f->getGlyph(c);
extents.x += glyph->advanceX;
extents.y = glyph->ascent + glyph->descent;
}
return extents;
}
}
if (font_of_size_in_cache) {
CachedFont* font;
for (auto* f: fontCache.getFonts())
if (f->getFontSize() == ptSize)
font = f;
for (const char& c : text)
extents.x += font->getGlyph(c)->advanceX;
extents.y = ptSize;
return extents;
}
jlog::Warning("Measuring a font size that is not cached, This is *super* slow.");
// No cache
FT_Set_Pixel_Sizes(this->face, ptSize, ptSize);
for (const char& c : text) {
FT_GlyphSlot slot = face->glyph;
auto glyph_index = FT_Get_Char_Index(this->face, c);
auto error = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT);
auto error = FT_Load_Glyph(this->face, glyph_index, FT_LOAD_DEFAULT);
if (error)
continue;
Vector2 advance = {static_cast<float>(slot->advance.x >> 6),
static_cast<float>(slot->advance.y >> 6)};
extents += advance;
// Gives smaller results than we'd want.
if (extents.y < slot->metrics.height / 64)
extents.y = slot->metrics.height / 64;
// Just fucking hardcode it, we know the glyph height is roughly always the ptSize anyway.
if (extents.y < ptSize)
extents.y = ptSize;
extents.x += static_cast<float>(slot->advance.x >> 6);
extents.y = (face->size->metrics.ascender / 64.0f) + (-face->size->metrics.descender / 64.0f);
}
return extents;
}
}

View File

@@ -10,7 +10,7 @@ std::array<GLfloat, 12> CachedGlyph::getTexCoords() const {
return texcoords;
}
CachedGlyph::CachedGlyph(char c, std::array<GLfloat, 12> texcoords, float x2offset, float y2offset, float w, float h, float advanceX, float advanceY) {
CachedGlyph::CachedGlyph(char c, std::array<GLfloat, 12> texcoords, float x2offset, float y2offset, float w, float h, float advanceX, float advanceY, float asc, float desc) {
character = c;
this->x2offset = x2offset;
this->y2offset = y2offset;
@@ -19,6 +19,8 @@ CachedGlyph::CachedGlyph(char c, std::array<GLfloat, 12> texcoords, float x2offs
this->advanceX = advanceX;
this->advanceY = advanceY;
this->texcoords = texcoords;
this->ascent = asc;
this->descent = desc;
}
void JGL::CachedFont::appendGlyph(JGL::CachedGlyph* glyph) {

View File

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

View File

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

View File

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

269
src/types/Shader.cpp Normal file
View File

@@ -0,0 +1,269 @@
#include <JGL/types/Shader.h>
#include <glad/glad.h>
namespace JGL {
class ShaderPreprocessor {
public:
std::string include_keyword = "#include";
ShaderPreprocessor() {}
std::string LoadShaderFile(const std::filesystem::path& filepath)
{
// if size is 0, it indicates, that we are at the top of the recursive load stack
bool stack_top = (already_included.size() == 0);
if (stack_top) {
already_included.emplace_back(filepath);
}
std::string ret_data = "";
std::ifstream file(filepath);
if (!file.good())
{
std::cout << "ERROR [ ShaderLoader::load_shader ]: Failed to start fstream, check if '" << filepath << "' exists\n";
return ret_data;
}
if (file.is_open()) {
std::string line;
while (getline(file, line)) {
if (line.find(include_keyword) == 0)
{
// Get path between double quotes
std::string rel_include_path = extract_first_between(line.substr(include_keyword.length()), '"', '"');
// Modify path according to os
// TODO: Use std::filesystem dummy...
// later, let me get this stolen code to work first.
#ifdef _WIN32
std::replace(rel_include_path.begin(), rel_include_path.end(), '/', '\\');
#elif __linux__
std::replace(rel_include_path.begin(), rel_include_path.end(), '\\', '/');
#endif
std::string full_include_path = extract_path(filepath) + rel_include_path;
// Avoid including self
if (filepath == full_include_path) {
std::cout << "WARNING [ ShaderLoader::load_shader ]: '"<< filepath <<"' tried to include itself\n";
continue;
} else {
bool include_flag = true;
// check if the current file is already included
for (const auto& file_to_check : already_included) {
// Avoid duplicate includes
if (file_to_check == full_include_path) {
include_flag = false;
break;
}
}
// If not yet included, push path to include vector and replace line with file contents
if (include_flag) {
already_included.push_back(full_include_path);
// Repeat recursively.
ret_data += LoadShaderFile(full_include_path) + '\n';
}
}
} else {
ret_data += line + "\n";
}
}
file.close();
} else {
std::cout << "ERROR [ ShaderLoader::load_shader ]: Unable to open file '" << filepath << "'\n";
}
// We are back to the first call
if (stack_top) {
already_included.clear();
}
return ret_data;
}
std::string PreprocessShaderSource(const std::string& source);
private:
std::vector<std::string> already_included;
/// Helper function that strips filename from a path, basically getting the path to the directory containing the file.
std::string extract_path(const std::string& path)
{
// find the position of the last directory separator.
std::size_t pos = path.find_last_of("\\/");
// strip fname from path
if (pos != std::string::npos) {
return path.substr(0, pos+1);
}
return "";
}
std::string extract_first_between(const std::string& input, char start_symbol, char end_symbol)
{
size_t start_index = input.find(start_symbol);
size_t end_index = input.find(end_symbol, start_index+1);
std::string extracted = "";
if (start_index != std::string::npos && end_index != std::string::npos) {
extracted = input.substr(start_index + 1, end_index - start_index - 1);
} else {
std::cout << "ERROR [ ShaderLoader::extract_first_between ]: Start '" << start_symbol << "' or end symbol '" << end_symbol << "' not found" << std::endl;
}
return extracted;
}
};
Shader::Shader(const std::filesystem::path& vertex_source_path, const std::filesystem::path& fragment_source_path, const std::vector<std::pair<std::string, GLint>>& attribute_bindings) :
Shader(ReadFile(vertex_source_path), ReadFile(fragment_source_path), attribute_bindings) {
vertexPath = vertex_source_path;
fragmentPath = fragment_source_path;
}
void Shader::checkCompileErrors(GLuint shader, const std::string& type) {
GLint success;
GLchar infoLog[1024];
if (type != "PROGRAM") {
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
if (!success) {
glGetShaderInfoLog(shader, 1024, nullptr, infoLog);
OnCompilationErrorMessage.Invoke( std::format("COMPILATION_ERROR: {}", type), infoLog);
}
} else {
glGetProgramiv(shader, GL_LINK_STATUS, &success);
if (!success) {
glGetProgramInfoLog(shader, 1024, nullptr, infoLog);
OnCompilationErrorMessage.Invoke(std::format("COMPILATION_ERROR: {}", type), infoLog);
}
}
if (success)
std::cout << "Shader compiled successfully" << std::endl;
}
GLint Shader::Uniform(const std::string &name) const {
auto it = uniform_location_cache.find(name);
if (it == uniform_location_cache.end()) {
auto location = glGetUniformLocation(id, name.c_str());
uniform_location_cache[name] = location;
return location;
}
return it->second;
}
GLint Shader::Attribute(const std::string &name) const {
return glGetAttribLocation(id, name.c_str());
}
Shader::Shader(const std::string &vertex_code, const std::string &fragment_code, const std::vector<std::pair<std::string, GLint>>& attribute_bindings) {
vertexSource = vertex_code;
fragmentSource = fragment_code;
const char* vShaderCode = vertex_code.c_str();
const char* fShaderCode = fragment_code.c_str();
// 2. Compile shaders.
unsigned int vertex, fragment;
// vertex shader.
vertex = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex, 1, &vShaderCode, nullptr);
glCompileShader(vertex);
checkCompileErrors(vertex, "VERTEX");
// fragment shader
fragment = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment, 1, &fShaderCode, nullptr);
glCompileShader(fragment);
checkCompileErrors(fragment, "FRAGMENT");
// shader Program
id = glCreateProgram();
// bind attribute locations.
for (auto& a: attribute_bindings)
glBindAttribLocation(id, a.second, a.first.c_str());
glAttachShader(id, vertex);
glAttachShader(id, fragment);
glLinkProgram(id);
checkCompileErrors(id, "PROGRAM");
// delete the shaders as they're linked into our program now and are no longer necessary
glDeleteShader(vertex);
glDeleteShader(fragment);
}
bool Shader::Loaded() const { return id != 0; }
unsigned int Shader::Handle() const { return id;}
void Shader::SetBool(const std::string &name, bool value) const { glUniform1i(Uniform(name), (int)value); }
void Shader::SetInt(const std::string &name, int value) const { glUniform1i(Uniform(name), value); }
void Shader::SetFloat(const std::string &name, float value) const { glUniform1f(Uniform(name), value); }
void Shader::SetVector2(const std::string &name, const Vector2 &value) const { glUniform2f(Uniform(name), value.x, value.y); }
void Shader::SetVector2(const std::string &name, float x, float y) const { glUniform2f(Uniform(name), x, y); }
void Shader::SetVector3(const std::string &name, const Vector3 &value) const { glUniform3f(Uniform(name), value.x, value.y, value.z); }
void Shader::SetVector3(const std::string &name, float x, float y, float z) const { glUniform3f(Uniform(name), x, y, z); }
void Shader::SetVector4(const std::string &name, const Vector4 &value) const { glUniform4f(Uniform(name), value.x, value.y, value.z, value.w); }
void Shader::SetVector4(const std::string &name, float x, float y, float z, float w) const { glUniform4f(Uniform(name), x, y, z, w); }
void Shader::SetMatrix2x2(const std::string &name, const Matrix2x2 &value) const {
/// TODO: Verify if glsl expects row-major or col-major!!
bool transpose = false;
glUniformMatrix2fv(Uniform(name), 4, transpose, value.ptr());
}
void Shader::SetMatrix3x3(const std::string &name, const Matrix3x3 &value) const {
/// TODO: Verify if glsl expects row-major or col-major!!
bool transpose = false;
glUniformMatrix3fv(Uniform(name), 9, transpose, value.ptr());
}
void Shader::SetMatrix4x4(const std::string &name, const Matrix4x4 &value) const {
/// TODO: Verify if glsl expects row-major or col-major!!
bool transpose = false;
glUniformMatrix4fv(Uniform(name), 16, transpose, value.ptr());
}
std::string Shader::ReadFile(const std::filesystem::path &path) {
/*std::string contents;
std::ifstream file;
file.exceptions(std::ifstream::failbit | std::ifstream::badbit);
try {
// Open Files
file.open(path);
std::stringstream stream;
// Read file's buffer contents into streams
stream << file.rdbuf();
// close file handlers
file.close();
// convert stream into string
contents = stream.str();
return contents;
} catch (std::ifstream::failure& e) {
std::cout << "ERROR::FILE_READ_FAILURE: " << e.what() << std::endl;
return "";
}*/
ShaderPreprocessor processor;
return processor.LoadShaderFile(path);
}
}

View File

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

View File

@@ -0,0 +1,63 @@
#include <JGL/types/TextureAtlas.h>
#include <JGL/types/RenderTarget.h>
#include <JGL/logger/logger.h>
using namespace JGL;
AtlasRegion TextureAtlas::GetRegion(unsigned int region) const {
if (region >= regions.size()) {
Logger::Error("Requesting region " + std::to_string(region) + "but it's outside of the texture atlas?");
return { {0, 0},{0, 0} };
}
return regions[region];
}
TextureAtlas::TextureAtlas(const Color4** pixels, const Vector2i** sizes, unsigned int texture_count, FilteringMode filtering_mode) {
std::vector<Texture> textures(texture_count);
int x_length = 0; int largest_height = 0;
for (unsigned int i = 0; i < texture_count; i++) {
textures[i] = Texture(pixels[i], *sizes[i], FilteringMode::NEAREST);
x_length += sizes[i]->x;
if (sizes[i]->y > largest_height)
largest_height = sizes[i]->y;
}
int rows = 1;
while (x_length > Texture::MaxSize().x)
x_length /= 2, rows += 1;
auto* result = new Texture(Vector2i(x_length, largest_height * rows), filtering_mode);
auto* render_target = new RenderTarget(result);
int current_x = 0; int current_y = 0;
for (unsigned int i = 0; i < texture_count; i++) {
if (current_x + sizes[i]->x > x_length)
current_x = 0, current_y += largest_height;
RenderTarget::Blit(&textures[i], render_target, Vector2i(current_x, current_y));
regions.emplace_back(AtlasRegion({ current_x, current_y }, { sizes[i]->x, sizes[i]->y }));
current_x += sizes[i]->x;
}
render_target->RegenerateMipMaps();
delete render_target;
this->texture_handle = result->GetHandle();
this->size = result->GetDimensions();
this->invert_y = result->Inverted();
this->filtering_mode = result->GetFilteringMode();
this->wrapping_mode = result->GetWrappingMode();
this->anisotropy = result->GetAnisotropySampleRate();
this->format = result->GetFormat();
// Don't call destructor so the v-ram isn't cleared.
operator delete(result);
}
TextureAtlas::TextureAtlas(const Color4* pixels, const Vector2i& size, AtlasRegion** regions, unsigned int region_count, FilteringMode filtering_mode, SampleRate anisotropy) : Texture(pixels, size, filtering_mode, anisotropy) {
this->regions.resize(region_count);
for (unsigned int i = 0; i < region_count; ++i)
this->regions[i] = *regions[i];
}

View File

@@ -2,44 +2,39 @@
#include <JGL/logger/logger.h>
#include <cstring>
// TODO combine the two load functions.
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, size, data, GL_STATIC_DRAW);
glBufferData(GL_ARRAY_BUFFER, size, data, usage_hint);
glBindBuffer(GL_ARRAY_BUFFER, current_array_buffer);
element_array_buffer = false;
num_elements = size / sizeof(GLfloat);
byte_count = size;
spin_lock = false;
}
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, size, data, GL_STATIC_DRAW);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, data, usage_hint);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, current_element_array_buffer);
element_array_buffer = true;
num_elements = size / sizeof(GLuint);
byte_count = size;
spin_lock = false;
}
void JGL::VRamList::Erase() {
if (list_handle == 0)
return;
while (spin_lock) {}
spin_lock = true;
GLint current_element_array_buffer = 0;
glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &current_element_array_buffer);
@@ -55,36 +50,27 @@ void JGL::VRamList::Erase() {
glDeleteBuffers(1, &list_handle);
list_handle = 0;
spin_lock = false;
}
GLuint JGL::VRamList::GetHandle() const {
GLuint JGL::VRamList::Handle() const {
return list_handle;
}
bool JGL::VRamList::IsFloatArray() const {
return !element_array_buffer;
long JGL::VRamList::Length() const {
// sizeof(GLfloat) & sizeof(GLuint) are both 4 - Redacted.
return byte_count / 4;
}
long JGL::VRamList::GetLength() const {
return num_elements;
}
size_t JGL::VRamList::GetDataSize() const {
if (element_array_buffer)
return sizeof(GLuint) * num_elements;
return sizeof(GLfloat) * num_elements;
size_t JGL::VRamList::Size() const {
return byte_count;
}
void JGL::VRamList::SetData(void* data, const long& length) {
while (spin_lock) {}
spin_lock = true;
bool should_resize = (this->num_elements != length);
bool should_resize = (this->byte_count != length * 4);
if (should_resize) {
glDeleteBuffers(1, &list_handle);
list_handle = 0;
spin_lock = false;
element_array_buffer ? load((GLuint*) data, sizeof(GLuint) * length) : load((GLfloat*) data, sizeof(GLfloat) * length);
return;
}
@@ -100,33 +86,22 @@ void JGL::VRamList::SetData(void* data, const long& length) {
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?");
memcpy(vram, data, (element_array_buffer ? sizeof(GLuint) : sizeof(GLfloat)) * length);
if (!glUnmapBuffer(buffer_type))
JGL::Logger::Fatal("We couldn't unmap the buffer?");
size_t element_size = element_array_buffer ? sizeof(GLuint) : sizeof(GLfloat);
glBufferSubData(buffer_type, 0, length * element_size, data);
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 (offset + length > Length()) {
unsigned long oob_delta = (offset + length) - Length();
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.
// 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());
}
@@ -134,7 +109,6 @@ void JGL::VRamList::UpdateData(void* data, const long& offset, const long& lengt
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());
}
@@ -149,46 +123,41 @@ void JGL::VRamList::UpdateData(void* data, const long& offset, const long& lengt
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?");
glBufferSubData(buffer_type, offset * element_size, length * element_size, data);
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);
std::vector<GLfloat> data(byte_count / 4);
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));
memcpy(data.data(), vram, (byte_count / 4) * 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?");
@@ -197,13 +166,13 @@ std::vector<GLuint> JGL::VRamList::GetDataUI() const {
glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &current_buffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, list_handle);
std::vector<GLuint> data(num_elements);
std::vector<GLuint> data(byte_count / 4);
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));
memcpy(data.data(), vram, (byte_count / 4) * sizeof(GLuint));
glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, current_buffer);
@@ -211,26 +180,35 @@ std::vector<GLuint> JGL::VRamList::GetDataUI() const {
return data;
}
JGL::VRamList::VRamList(const GLfloat* data, const long& length) {
JGL::VRamList::VRamList(const GLfloat* data, const long& length, VRamUsageHint hint) {
usage_hint = hint;
load(data, (long) sizeof(GLfloat) * length);
}
JGL::VRamList::VRamList(const GLuint* data, const long& length) {
JGL::VRamList::VRamList(const GLuint* data, const long& length, VRamUsageHint hint) {
usage_hint = hint;
load(data, (long) sizeof(GLuint) * length);
}
JGL::VRamList::VRamList(const Vector2* data, const long& length) {
JGL::VRamList::VRamList(const Vector2* data, const long& length, VRamUsageHint hint) {
usage_hint = hint;
load(reinterpret_cast<const GLfloat*>(data), (long) sizeof(Vector2) * length);
}
JGL::VRamList::VRamList(const Vector3* data, const long& length) {
JGL::VRamList::VRamList(const Vector3* data, const long& length, VRamUsageHint hint) {
usage_hint = hint;
load(reinterpret_cast<const GLfloat*>(data), (long) sizeof(Vector3) * length);
}
JGL::VRamList::VRamList(const Vector4* data, const long& length) {
JGL::VRamList::VRamList(const Vector4* data, const long& length, VRamUsageHint hint) {
usage_hint = hint;
load(reinterpret_cast<const GLfloat*>(data), (long) sizeof(Vector4) * length);
}
JGL::VRamList::VRamList(const Color4* data, const long& count, VRamUsageHint hint) {
usage_hint = hint;
load(reinterpret_cast<const GLfloat *>(data), (long) sizeof(GLuint) * count);
}
void JGL::VRamList::SetData(const GLfloat* data, const long& length) {
SetData((void*) data, length);
}
@@ -255,6 +233,10 @@ void JGL::VRamList::SetData(const Vector2i* data, const long& length) {
SetData((void*) data, length * 2);
}
void JGL::VRamList::SetData(const Color4* data, const long& count) {
SetData((void*) data, count);
}
void JGL::VRamList::UpdateData(const GLfloat* data, const long& offset, const long& length) {
UpdateData((void*) data, offset, length);
}
@@ -279,19 +261,49 @@ void JGL::VRamList::UpdateData(const Vector2i* data, const long& offset, const l
UpdateData((void*) data, offset, length * 2);
}
void JGL::VRamList::UpdateData(const Color4* data, const long &offset, const long &count) {
UpdateData((void*) data, offset, count);
}
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());
return;
}
auto data_array = rhs.GetDataF();
this->load(data_array.data(), data_array.size());
}
JGL::VRamList::VRamList(const size_t& byte_count, bool element_array_buffer, VRamUsageHint hint) {
this->element_array_buffer = element_array_buffer;
this->byte_count = byte_count;
GLint current_buffer = 0;
GLenum buffer = element_array_buffer ? GL_ELEMENT_ARRAY_BUFFER : GL_ARRAY_BUFFER;
GLenum buffer_binding = element_array_buffer ? GL_ELEMENT_ARRAY_BUFFER_BINDING : GL_ARRAY_BUFFER_BINDING;
glGetIntegerv(buffer_binding, &current_buffer);
glGenBuffers(1, &list_handle);
glBindBuffer(buffer, list_handle);
glBufferData(buffer, byte_count, nullptr, hint);
glBindBuffer(buffer, current_buffer);
}
void JGL::VRamList::UpdateData(const uint8_t* data, const long& offset, const long& count) {
GLint current_buffer = 0;
GLenum buffer_type = element_array_buffer ? GL_ELEMENT_ARRAY_BUFFER : GL_ARRAY_BUFFER;
GLenum buffer_binding = element_array_buffer ? GL_ELEMENT_ARRAY_BUFFER_BINDING : GL_ARRAY_BUFFER_BINDING;
glGetIntegerv(buffer_binding, &current_buffer);
glBindBuffer(buffer_type, list_handle);
glBufferSubData(buffer_type, offset, count, data);
glBindBuffer(buffer_type, current_buffer);
}

View File

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

File diff suppressed because it is too large Load Diff

View File

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