Compare commits

..

339 Commits

Author SHA1 Message Date
James Seibel 062ed5bcc8 remove dev from version number 2026-04-18 10:35:17 -05:00
James Seibel 539d152caa workaround for java 8 and forgix
Java 8 doesn't support "_" in the class name after "$". In other words inner classes can't have underscores in their names, which forgix adds for changed classes.

Moving the DhConfigScreen/ClassicConfigGUI into separate classes mitigates the problem until forgix has a long-term fix.
2026-04-18 09:44:37 -05:00
James Seibel a1af4335e0 fix buffer upload breaking UI on old MC versions 2026-04-17 07:49:04 -05:00
Ran-Mewo e68b112020 Forgix workaround 2026-04-17 00:14:28 +10:00
James Seibel fab6d187ca fix neoforge version range for mc 26 2026-04-15 21:51:17 -05:00
James Seibel 0daa00cec2 fix 26.1.2 version constant number 2026-04-15 21:51:02 -05:00
James Seibel f3036850ce Fix compiling for MC 1.19.4 and lower 2026-04-15 21:20:46 -05:00
James Seibel 51c8b47bba terrain data cash override close without exception 2026-04-15 07:47:02 -05:00
James Seibel 89efd53d61 fix unit test compiling 2026-04-14 20:38:19 -05:00
James Seibel 7667f51cf3 Add DhApiBlockColorOverrideEvent
Could be helpful to !1240
2026-04-14 20:36:34 -05:00
James Seibel 61eaa5a734 Add DhApiBlockStateWrapperCreatedEvent
Could be helpful to !1240
2026-04-14 19:44:24 -05:00
James Seibel 1d589d1a62 disable chat GL logging if Iris is detected 2026-04-14 19:03:43 -05:00
James Seibel 03b1eeb77e Add support for a couple render API events 2026-04-14 19:03:22 -05:00
James Seibel 8446df72f7 Merge branch 'change/channel_name_compat' 2026-04-14 17:08:45 -05:00
James Seibel c07397e9c0 update MC 26.1.0 -> 26.1.2 2026-04-14 17:05:16 -05:00
James Seibel 29a92aeb93 fix version constant 2026-04-14 07:48:58 -05:00
James Seibel 8467782b80 update unimined 1.4.17-k -> 1.4.18-k 2026-04-14 07:14:36 -05:00
James Seibel c8dbb21ea4 fix 6GB dev memory not working 2026-04-12 15:18:23 -05:00
James Seibel 63e1c12564 use down stream IBOs instead of re-creating at upload 2026-04-12 15:18:11 -05:00
James Seibel 52f58150da Fix MC crashing while triggering crashMinecraft() 2026-04-12 13:54:15 -05:00
James Seibel d1d642a7bb fix native dialog for MC 26 2026-04-12 13:53:52 -05:00
James Seibel 8e45358aad update buildall scripts to work with new forgix 2026-04-11 21:51:06 -05:00
James Seibel a959c7220b only run world gen for rendering levels 2026-04-11 21:41:07 -05:00
James Seibel e06425c5eb fix neo mixin chunk render preprocessors 2026-04-11 21:35:06 -05:00
James Seibel 66ce258fe1 fix biome/block wrapper preprocessors 2026-04-11 21:34:50 -05:00
James Seibel 181881a661 fix MC 1.17 compiling 2026-04-11 18:29:35 -05:00
James Seibel af0d8d1d2f add default MC memory 6gb command arg 2026-04-11 17:09:58 -05:00
James Seibel 6c68e94b96 Fix some compiling errors on old MC versions 2026-04-11 17:07:47 -05:00
James Seibel 93313a5c50 default to ZGC in dev environment 2026-04-11 17:01:57 -05:00
James Seibel 0527baa708 fix some old MC version compiling 2026-04-11 17:01:21 -05:00
James Seibel ce1fbde937 update gitlab URLs 2026-04-11 16:58:55 -05:00
James Seibel 764abdac45 add MC 26.1.2 to supported versions 2026-04-11 16:58:35 -05:00
James Seibel b42d3d8f74 remove Iris incompatibility and disable validation when present 2026-04-11 15:23:32 -05:00
James Seibel cd67a773c5 fix white beacons colored incorrectly 2026-04-11 12:34:14 -05:00
James Seibel 6d7bade7ca fix neoforge rendering on MC 26 2026-04-11 12:09:15 -05:00
James Seibel dea8d4498a fix double-rendering on fabric 2026-04-11 11:14:41 -05:00
James Seibel 2969916f34 minor optifine optimization 2026-04-11 11:11:56 -05:00
James Seibel 8785224c51 profile wrapper try-finally for pushes 2026-04-11 11:04:48 -05:00
James Seibel 605f02a655 fix compiling old MC versions 2026-04-11 09:22:14 -05:00
James Seibel 8099d37c14 add a comment for Iris 26 incompat reason 2026-04-11 09:19:44 -05:00
James Seibel dd4dbefe9a mark iris as incompatible with MC 26 2026-04-11 09:18:39 -05:00
James Seibel 52a15fd349 fix fading with sodium 2026-04-11 08:44:34 -05:00
James Seibel 3b3be6aed4 fix old MC compiling 2026-04-10 07:50:15 -05:00
James Seibel aeb7d6d0f9 update fabric api/iris references to latest versions 2026-04-10 07:47:19 -05:00
James Seibel 5336dbafec fix Intellij not debugging common classes 2026-04-10 07:21:45 -05:00
James Seibel 6079cb4830 minor mixin cleanup 2026-04-10 07:20:38 -05:00
James Seibel 50ff174104 Merge branch 'distant-horizons-view-bobbing' 2026-04-10 07:08:06 -05:00
James Seibel b77ef89df6 crash incomplete renderer in neo 2026-04-10 07:07:15 -05:00
James Seibel a701dd29a9 add 26.1.1 to supported version list 2026-04-10 07:07:01 -05:00
Brian McCoskey ab36fdd545 fix(1237): Updated LOD renderer for 26.1 to fix view bobbing issue described in issue #1237 2026-04-09 20:18:28 -04:00
James Seibel f87afb34f4 Rename MC 1.26.1 -> 26.1.0 2026-04-08 22:00:11 -05:00
James Seibel 053917d3d7 Fix compiling old MC versions 2026-04-08 21:53:37 -05:00
James Seibel 063ba01970 Fix tint color retrieval 2026-04-08 21:43:35 -05:00
James Seibel 72a888f3ff use more correct block color getting (thanks greener_ca) 2026-04-08 17:28:10 -05:00
James Seibel 0bd36bff1d re-add config UI validation 2026-04-08 17:27:44 -05:00
James Seibel 2bf125b7ac fix config button missing background 2026-04-08 17:27:33 -05:00
James Seibel ba3cf8fd56 up neoforge MC 26 version 1 -> 15 2026-04-08 07:46:24 -05:00
James Seibel 951f2a4ee7 add broken client block tinting 2026-04-08 07:45:41 -05:00
James Seibel d55b1bb3c2 default renderer to Blaze3D for MC 26 2026-04-08 07:45:26 -05:00
James Seibel 275ecb78c3 temporarily disable profiling
done due to bug with push/pop imbalance
2026-04-08 07:44:24 -05:00
James Seibel 64ac0d6017 Merge branch 'TheSecondGreatBuildscriptRewrite' 2026-04-08 07:41:00 -05:00
James Seibel 3f16d67746 fix blaze3d rendering 2026-04-08 07:39:36 -05:00
James Seibel 3a34dc8626 Create RenderPipelineBuilderWrapper and update test renderer 2026-04-05 18:42:02 -05:00
James Seibel c1766fb439 lightmap update 2026-04-05 17:30:09 -05:00
James Seibel cfd47adfda update fabric/neo api methods 2026-04-05 17:23:48 -05:00
James Seibel 9b9e6b9179 fix world gen 2026-04-05 17:23:14 -05:00
James Seibel 49d1587a71 re-add debug screen 2026-04-05 17:22:12 -05:00
James Seibel b0f5e55744 extend Iris incompat blaze3D message 2026-04-05 17:21:31 -05:00
James Seibel d9191534d5 add mod accessors 2026-04-05 17:21:14 -05:00
James Seibel ff459621e6 remove unused getFov() 2026-04-05 17:20:18 -05:00
James Seibel 64fb45d74d Fix access wideners 2026-04-05 17:18:32 -05:00
James Seibel 1590abb489 up CI java version 21 -> 25
will probably break old MC versions, the CI script will probably need some tweaking.
2026-04-03 22:38:23 -05:00
James Seibel a3c9b0654a Add MC 26 to the auto build script 2026-04-03 21:15:43 -05:00
James Seibel a9388321d9 Fix MC 1.21.11 compiling? 2026-04-03 20:46:50 -05:00
James Seibel 877c824e58 continue porting to MC 26 (2) 2026-04-03 20:02:32 -05:00
James Seibel 80f30dfd74 Continue porting to MC 26 2026-04-03 07:51:53 -05:00
Ran-Mewo 9a087025fe update stuffs 2026-03-31 12:21:11 +11:00
James Seibel 29381bce7b update manifold 25.1.31 -> 26.1.6 2026-03-30 18:05:26 -05:00
Ran-Mewo 21dc0f13c9 JVM Args & Don't use mappings on 26.1+ 2026-03-31 00:22:13 +11:00
James Seibel 7794958804 Start MC 26 porting 2026-03-30 07:49:36 -05:00
Ran-Mewo c245ed6598 improve filtering logic 2026-03-30 17:45:31 +11:00
Ran-Mewo 6270b03005 update unimined to support 26.1 2026-03-30 17:17:45 +11:00
Ran-Mewo 2674b945bb fix neoforg 2026-03-30 17:06:51 +11:00
James Seibel 0647bdbab3 update manifold 25.1.31 -> 26.1.6 2026-03-29 21:17:44 -05:00
Ran-Mewo 528a12ac83 fix javadocs 2026-03-30 02:20:00 +11:00
Ran-Mewo 4ac9de05df wao! 2026-03-30 02:07:40 +11:00
Ran-Mewo a0f1b72089 fix merged jars for CI 2026-03-30 01:13:28 +11:00
Ran-Mewo 85c07b11c6 meows 2026-03-30 00:51:58 +11:00
Ran-Mewo 215e1d46d0 wao 2026-03-29 19:29:11 +11:00
James Seibel 5f228f0567 Merge branch 'chunk-save-mixin-proto' into 'main'
On Chunk save ignore ProtoChunks

See merge request distant-horizons-team/distant-horizons!90
2026-03-27 19:24:42 +00:00
James Seibel f597958e1e disable thread pausing when render tasks exist 2026-03-27 07:01:12 -05:00
James Seibel 95921358f8 Merge branch 'main' of gitlab.com:distant-horizons-team/distant-horizons 2026-03-27 07:00:58 -05:00
James Seibel a22d494797 Merge branch 'internal-server-inverted-logic' into 'main'
Fix inverted chunk ignoring logic for generation mode "Save Chunks"

See merge request distant-horizons-team/distant-horizons!89
2026-03-26 21:32:36 +00:00
Fabian Maurer 666e917b8f Ignore ProtoChunks 2026-03-26 21:34:36 +01:00
Fabian Maurer a26a97e7fb Fix inverted chunk update ignoring logic 2026-03-26 21:32:15 +01:00
James Seibel cdbb9de933 remove instanced rendering config option 2026-03-24 19:38:03 -05:00
James Seibel feccf12580 Fix Iris using the wrong far clip plane
https://github.com/IrisShaders/Iris/issues/2534
2026-03-24 07:16:45 -05:00
James Seibel d371d93c9d add tracy to neo/fabric launch args 2026-03-22 21:26:58 -05:00
James Seibel 5f3e8d76b2 only set IS_RUNNING_IN_IDE for dev builds 2026-03-21 17:17:41 -05:00
James Seibel a7bd72e35b Minor speed improvement for GLBuffer upload 2026-03-21 17:09:26 -05:00
James Seibel 0a5326d2b1 fix phantom log when there is sufficient memory 2026-03-21 16:57:43 -05:00
James Seibel dad2014c46 fix cross level generic GL rendering 2026-03-21 16:35:50 -05:00
James Seibel 70dd0bda72 fix blaze3D memory leak 2026-03-21 16:13:30 -05:00
James Seibel 8213901229 remove unhelpful buffer delete mac test 2026-03-21 16:03:29 -05:00
James Seibel 668ba491e8 stage VBO/IBO upload and allow global IBO 2026-03-21 16:03:18 -05:00
James Seibel d85589c41a minor comment changes 2026-03-21 15:22:04 -05:00
James Seibel 7f5316108d Fix GL buffers not being deleted 2026-03-21 08:17:06 -05:00
James Seibel 9c1abbac2b ignore deprecation warning in TexturedButton 2026-03-21 07:32:56 -05:00
coolGi 1bb957a866 Add mailmap 2026-03-20 22:19:28 +13:00
James Seibel 6ccba17baf Fix render thread startup crash on Neoforge with GL 2026-03-19 07:29:46 -05:00
James Seibel e4c5d8adab Fix neoforge texture validation issue 2026-03-19 07:00:49 -05:00
James Seibel 823f204424 Fix texture using buffer flags 2026-03-19 07:00:35 -05:00
James Seibel 505e9a77fd Fix compiling for MC 1.20.4 and older 2026-03-18 07:49:09 -05:00
James Seibel 4c580fe4ff Add render task profiling 2026-03-18 07:45:50 -05:00
James Seibel efc1865f87 Add the ability to cull lilly pads, seaGrass, etc. 2026-03-15 20:53:29 -05:00
James Seibel ae08ad56c4 Fix MC complaining about GL shader file names 2026-03-15 16:52:17 -05:00
James Seibel 633544e0b0 remove unneeded chunk update warnings 2026-03-15 16:46:51 -05:00
James Seibel 2432028aa0 javadoc cleanup 2026-03-15 16:30:48 -05:00
James Seibel eb6aa13815 Disable generic rendering on Mac 2026-03-15 16:29:48 -05:00
James Seibel 55155103ec revert to AUTO rendering A if an invalid API is selected 2026-03-15 16:24:03 -05:00
James Seibel 667dd85aef Fix LOD rendering on Mac 2026-03-15 15:57:57 -05:00
James Seibel 9bd946a41c fix blaze wireframe renderer not appearing outside LODs 2026-03-14 15:55:57 -05:00
James Seibel 241baef7af Improve warning logs for chunkUpdateManager 2026-03-14 15:55:47 -05:00
James Seibel b5890a4783 clean up todo comments 2026-03-14 15:33:35 -05:00
James Seibel e3b67ef500 fix generic objects not uploading on first add
also clean up some comments
2026-03-14 15:27:16 -05:00
James Seibel 5905fe3df2 fix some generic objects not rendering 2026-03-14 14:33:55 -05:00
James Seibel 9d35a70437 fix shift-click not working on config min option 2026-03-14 14:33:39 -05:00
James Seibel f121860563 Fixes beacons not always showing/hiding correctly 2026-03-14 14:33:13 -05:00
James Seibel 9715db3ac6 fix generic rendering not uploading in some cases 2026-03-14 14:31:25 -05:00
James Seibel 03a29fbacb fix compiler warnings 2026-03-14 10:19:03 -05:00
James Seibel e91888934b Try fix concurrency issue with render closing 2026-03-14 10:18:50 -05:00
James Seibel 030f814398 Fix 1.21.11 compiling 2026-03-14 09:32:35 -05:00
James Seibel 90ef8fd64d Fix GL buffer upload corrupting the GL state 2026-03-14 09:20:36 -05:00
James Seibel 0ffefaa8c1 Fix a few Legacy GL issues 2026-03-13 07:42:41 -05:00
James Seibel 99703d65df Fix fade renderer using the wrong frame buffer 2026-03-13 07:42:09 -05:00
s809 bd2f5a7836 Stop pregen on server shutdown 2026-03-13 00:48:05 +05:00
s809 a44556f86a Replace fix with debug wireframe stub 2026-03-12 22:38:38 +05:00
s809 4597b7f647 Fix server not loading levels 2026-03-12 19:20:47 +05:00
James Seibel 0de80cfaa7 Fix AUTO rendering API crashing 2026-03-12 06:49:02 -05:00
James Seibel 034aaddca3 Allow translucent generic objects 2026-03-10 19:26:28 -05:00
James Seibel a1d493f25d fix refactor rename 2026-03-10 19:18:03 -05:00
James Seibel 4b5a4dda79 Up API version 5.1.0 -> 6.0.0 2026-03-10 19:08:01 -05:00
James Seibel ce528f3fd5 up DH version 2.4.6 -> 3.0.0 2026-03-10 19:07:50 -05:00
James Seibel e4c769e95e merge blaze renderer changes 2026-03-10 19:07:07 -05:00
James Seibel f13095875f fix debug wireframes not rendering 2026-03-10 19:06:09 -05:00
James Seibel 3d1af8f944 Disable blaze api for old MC versions 2026-03-10 18:46:49 -05:00
James Seibel d73867efd6 fix old forge compiling 2026-03-10 18:44:30 -05:00
James Seibel 045733deb5 add renderDebugLabels to neoforge args 2026-03-10 18:44:19 -05:00
James Seibel b552bf2566 Fix block state wrapper for old MC versions 2026-03-10 18:44:02 -05:00
James Seibel 33d37b3937 move GL shaders into the correct folder 2026-03-10 17:56:11 -05:00
James Seibel 15a044d059 Fix fog with Blaze3D 2026-03-10 17:50:41 -05:00
James Seibel 9e5be3ad21 prefix all blaze/openGl objects 2026-03-10 17:42:14 -05:00
James Seibel ba977a29f0 Move lightmap wrapper methods into common 2026-03-10 17:33:58 -05:00
James Seibel c6c94ef906 fix buffer render event for Blaze3D 2026-03-10 17:26:23 -05:00
James Seibel 7fed4b1ddb add missing comment 2026-03-10 17:24:01 -05:00
James Seibel dfee36f416 Make render interfaces consistent 2026-03-10 17:20:22 -05:00
James Seibel cf6945b44c finish a few comments 2026-03-10 17:03:39 -05:00
James Seibel a01a9ac356 Improve vertex attrib mod compatability 2026-03-10 16:59:19 -05:00
James Seibel 89619696cb fix iris rendering 2026-03-10 16:45:58 -05:00
James Seibel 88f945bf22 separate out some rendering logic 2026-03-10 14:52:09 -05:00
James Seibel 3555133d7f Add the option to use either Blaze3D or OpenGL rendering APIs 2026-03-10 11:47:40 -05:00
James Seibel 294013eff0 Add rendering API definition 2026-03-09 20:14:54 -05:00
James Seibel 6748ec25ff Rename and reorganize render pass interfaces 2026-03-09 18:59:29 -05:00
James Seibel 485c3dedb3 remove deprication warnings 2026-03-09 17:40:06 -05:00
James Seibel 4ceecdfb09 update debug wireframe renderer 2026-03-09 17:35:52 -05:00
James Seibel 23d3e44002 change where vertex size is found 2026-03-09 16:34:34 -05:00
James Seibel 3a5d6e7370 Add RenderThreadTaskHandler 2026-03-09 16:31:32 -05:00
James Seibel 71237caa81 Start moving OpenGL rendering to common 1 2026-03-09 16:28:57 -05:00
James Seibel 7e19f9e6eb MC -> Blaze renaming 2026-03-09 14:07:03 -05:00
James Seibel f7b5b9a2f5 merge postprocessing vertex format 2026-03-09 13:58:48 -05:00
James Seibel 77016de6e6 Only upload unique LOD uniforms once 2026-03-09 13:52:05 -05:00
James Seibel e530d0ffda wrapper movement 2026-03-09 12:28:58 -05:00
James Seibel b04a308090 MC -> blaze renaming and getName() cleanup 2026-03-09 12:20:13 -05:00
James Seibel 385d6919fb texture method renaming 2026-03-09 12:09:10 -05:00
James Seibel 458cc9b505 renaming 2026-03-09 12:05:50 -05:00
James Seibel afdccfe087 refactoring and cleanup 2026-03-09 12:03:27 -05:00
James Seibel 4d7674348b Move uniformbufferwrapper 2026-03-09 11:53:37 -05:00
James Seibel a2c96ba7ff move vertex format 2026-03-09 11:51:38 -05:00
James Seibel 5c5de3c744 javadocs 2026-03-09 11:48:07 -05:00
James Seibel 18acb5d101 move blaze wrapper objects 2026-03-09 11:31:42 -05:00
James Seibel c86c32b39e Rename texture wrappers 2026-03-09 11:28:52 -05:00
James Seibel 3c680b5daf rename vbo containers 2026-03-09 11:24:09 -05:00
James Seibel 84d1298e18 move blaze shader files 2026-03-09 09:57:43 -05:00
James Seibel 354b6567d4 don't render closed VBOs 2026-03-09 09:19:08 -05:00
James Seibel 3498faed59 1.21.11 update neoforge 0 -> 38 2026-03-09 09:18:59 -05:00
James Seibel f8eb1e8a97 Move blaze render namespace 2026-03-09 08:27:24 -05:00
James Seibel 19057218d6 Fix ssao application 2026-03-08 21:16:55 -05:00
James Seibel f745e9c51a Merge post-processing VBO logic 2026-03-08 19:32:52 -05:00
James Seibel da922a8c69 wrap GPU textures 2026-03-08 19:18:17 -05:00
James Seibel 3f2d8ea6ae merge apply renderers 2 2026-03-07 14:32:08 -06:00
James Seibel 550f36e9fa merge apply renderers 2026-03-07 14:31:22 -06:00
James Seibel ba0835bf4a Cleanup test renderer 2026-03-05 20:19:44 -06:00
James Seibel 766a41ce56 rearrange new renderers 2026-03-05 20:11:10 -06:00
James Seibel 3b5728692d fix leak in LOD renderer and start separating uniforms 2026-03-05 17:32:43 -06:00
James Seibel 05d573c847 debug rendering 2026-03-04 18:07:57 -06:00
James Seibel 9360e45a39 Add far fade 2026-03-04 07:39:05 -06:00
James Seibel 71aaff32a2 add fog 2026-03-03 07:48:04 -06:00
James Seibel 2fbeb43894 add SSAO 2026-03-02 07:45:30 -06:00
James Seibel ae6fa83c50 generic object rendering 2026-03-01 19:33:00 -06:00
James Seibel f4171ffca1 rename vertex element 2026-03-01 16:30:01 -06:00
James Seibel dedcc875d2 re-add lighting 2026-02-28 12:13:38 -06:00
James Seibel 224a5449ed re-Add vanilla fade 2026-02-28 11:35:09 -06:00
James Seibel b0d325fd14 minor render cleanup 2026-02-28 08:13:59 -06:00
James Seibel 36ad3f4e43 rough initial LOD rendering 2026-02-26 16:56:26 -06:00
James Seibel b5eff6aa80 remove unused shader files 2026-02-24 22:23:47 -06:00
James Seibel f1e93ad547 add mixinSharedConstants 2026-02-24 22:23:39 -06:00
James Seibel e59c22fb69 Fix fade shader not binding MC depth texture 2026-02-24 22:03:54 -06:00
James Seibel 183882f9ce start of vanilla fade logic 2026-02-24 09:57:13 -06:00
James Seibel 5b825f9dbb move test shader folder 2026-02-24 07:02:16 -06:00
James Seibel 933d5c957c Fix buffer deleted too quickly and color ranges 2026-02-23 17:09:02 -06:00
James Seibel 1bfa93eea3 proof-of-concept test renderer 2026-02-23 12:32:14 -06:00
James Seibel 2866aefb90 Fix chunks applying to the wrong dimension 2026-02-18 22:05:30 -06:00
James Seibel c5bfdbc430 Fix dark LODs with false tintWithAvoidedBlocks 2026-02-17 07:47:38 -06:00
James Seibel 0ae83929b7 Really bad hack to fix Mac GL issues 2026-02-17 07:17:11 -06:00
James Seibel 1a600e7c53 always disable instanced rendering on mac 2026-02-16 07:16:38 -06:00
James Seibel dc92a341d4 comment on a partially broken unit test 2026-02-14 22:34:24 -06:00
James Seibel 31a534f945 Fix beacon update locks 2026-02-14 22:01:36 -06:00
James Seibel 2c3345a596 Increase startup timeout for MAC 2026-02-14 08:32:48 -06:00
James Seibel d14349383b Force enable fog if MC is rendering fog
Done to fix underwater/blindness rendering
2026-02-14 08:24:44 -06:00
James Seibel db378d64b0 Add logs when changing vanilla settings 2026-02-13 07:49:38 -06:00
James Seibel 7963303a48 Finish all GL commands on Mac after buffer deletion
Hopefully this will fix GL buffer SIGSEV and/or memory corruption.
2026-02-13 07:39:59 -06:00
James Seibel 42d2cc6915 auto disable fancy graphics if enabled 2026-02-13 07:36:00 -06:00
James Seibel 45128fff6b Maybe fix Mac crashing with sodium on world start? 2026-02-12 07:27:16 -06:00
James Seibel 854885db64 Hopefully fix a rare concurrency issue in buffer Builder 2026-02-11 07:45:48 -06:00
James Seibel 963e62c595 Fix a harmless error message 2026-02-11 07:15:54 -06:00
James Seibel d2e41792a7 Merge branch 'main' of gitlab.com:distant-horizons-team/distant-horizons 2026-02-11 07:10:06 -06:00
James Seibel fedd4b5cce add error handling to chunkWrapper getblock() 2026-02-11 07:10:01 -06:00
James Seibel 804412698e re-add chat warning if G1GC is used 2026-02-11 07:09:39 -06:00
James Seibel 09fa469a7d Merge branch 'main' into 'main'
Grass Block Material Compatibility

See merge request distant-horizons-team/distant-horizons!87
2026-02-11 04:10:55 +00:00
James Seibel 163ee63f9e Reduce memory allocation slightly during LOD loading 2026-02-10 07:40:16 -06:00
Ada Aster e143fb91a3 Grass Block Material Compatibility 2026-02-09 20:48:34 -05:00
James Seibel 5897fc816c Fix loosing some thread pool tasks 2026-02-09 07:44:12 -06:00
James Seibel da1dc04e98 re-add pack.mcmeta to maybe fix forge lang files 2026-02-09 07:03:52 -06:00
James Seibel 7a05c8cdde Hide non-rendering levels in the F3 screen 2026-02-08 20:42:37 -06:00
James Seibel b219370469 remove unnecessary deprecated attribute 2026-02-08 20:13:48 -06:00
James Seibel 2f7ab04984 Remove unused ID mappings after data update 2026-02-08 20:01:32 -06:00
James Seibel caf7f64f11 fix vanilla fade order 2026-02-07 17:00:35 -06:00
James Seibel 7834213a60 Fix float configs 2026-02-07 16:56:19 -06:00
James Seibel 000c6053ed Add colors to the F3 screen 2026-02-07 14:14:16 -06:00
James Seibel 879b117959 Remove swing UI classes 2026-02-07 13:20:22 -06:00
James Seibel 272012ed22 cleanup TODOs in build script 2026-02-07 11:18:35 -06:00
James Seibel 3d9b988410 remove unused "fabric-like" gradle project 2026-02-07 11:17:26 -06:00
James Seibel 8b850f14d3 Move LWJGL variables into a MC version property 2026-02-07 10:20:23 -06:00
James Seibel 76f69b238a mark neoforge as both a server and client mod 2026-02-07 09:46:38 -06:00
James Seibel 0b6e1135ce Replace DhLodPos with DhSectionPos 2026-02-07 08:40:27 -06:00
James Seibel 17b0d2e763 remove completed TODO from CI script 2026-02-05 22:06:50 -06:00
James Seibel a1a92ffd14 Remove most SSAO configs 2026-02-05 21:58:37 -06:00
James Seibel 52cf6ac9df remove config UI text limit 2026-02-05 21:37:56 -06:00
James Seibel 02a309cd34 remove todos and hopefully fix some compiling? 2026-02-05 18:11:44 -06:00
James Seibel 0ec9e60396 fix some compiling? 2026-02-05 18:09:56 -06:00
James Seibel 597b659026 fix magic numbers for lightmap binding 2026-02-05 18:09:39 -06:00
James Seibel 67c6061c7a remove unneeded mixin files 2026-02-05 18:08:29 -06:00
James Seibel 0843a6f355 fix older MC version compiling 2026-02-05 18:01:33 -06:00
James Seibel d8d8d6f9a0 Fix render loading queuing incorrectly 2026-02-05 07:41:00 -06:00
James Seibel c42ce57022 Handle cave block culling more generically 2026-02-04 07:48:00 -06:00
James Seibel db34b455aa Fix !1088 (API config.getApiValue() not returning null) 2026-02-04 07:20:02 -06:00
James Seibel 59c82f0499 change generic test world gen to max sky light 2026-02-03 20:35:30 -06:00
James Seibel f78e771c8b Fix Debug renderer on newer MC versions 2026-02-03 19:59:39 -06:00
James Seibel 1be141348c Fix biomes in TestChunkWorldGenerator 2026-02-03 19:01:38 -06:00
James Seibel d9b5195bee Remove fabric server event validation
Neoforge doesn't have the same validation so I don't think it's necessary, but we'll see
2026-02-03 07:49:42 -06:00
James Seibel 4d0472749d remove unused TODOs 2026-02-03 07:45:37 -06:00
James Seibel b8ebc54dee remove commented out mixins 2026-02-03 07:43:59 -06:00
James Seibel 413ee3cdf4 remove world gen debug log 2026-02-03 07:41:35 -06:00
James Seibel dee58dafc9 remove TODO comments 2026-02-03 07:41:29 -06:00
James Seibel 7b326d63e8 feature world gen logging changes 2026-02-03 07:41:20 -06:00
James Seibel 74f80ca2f5 ClassicConfig TODO remove 2026-02-03 07:25:14 -06:00
James Seibel 6246f66300 Minor UI class renaming 2026-02-03 07:20:16 -06:00
James Seibel 037231c0ac Fix loading screen stuck when no update 2026-02-03 07:19:58 -06:00
James Seibel b416746ba2 increase the API version 5.1.0 -> 6.0.0
Also require a data cache for API Repo methods
2026-02-03 07:06:29 -06:00
James Seibel 5f7181f6f1 Put common auto-update GUI code in a single file 2026-02-02 07:46:10 -06:00
James Seibel 6b23e0de7e remove todo 2026-02-02 07:18:15 -06:00
James Seibel fe6c4f7507 Remove MC Texture LodBias config 2026-02-02 07:18:10 -06:00
James Seibel e83d490b0e move partial tick getting into RenderWrapper 2026-02-02 07:09:19 -06:00
James Seibel 09bc303583 up manifold 2025.1.28 -> 31 2026-02-02 06:59:36 -06:00
James Seibel 3fb09fa811 Make generic object updating async 2026-01-31 17:33:07 -06:00
James Seibel 4984d00d36 Merge branch 'main' of gitlab.com:distant-horizons-team/distant-horizons 2026-01-31 10:28:05 -06:00
James Seibel 87222f3b39 Handle MC running at 0 FPS 2026-01-31 10:27:49 -06:00
James Seibel c58a8d8e69 Clean up more TODOs 2026-01-29 07:48:35 -06:00
James Seibel f1deb7e592 clean up some API and world gen TODOs 2026-01-29 07:14:40 -06:00
James Seibel 00abfb735a Remove several TODOs 2026-01-28 07:47:35 -06:00
James Seibel d2e831885d Fix TODO in NeoForgeMain 2026-01-28 07:26:11 -06:00
James Seibel d7358ed7a3 reduce GC pressure and improve loading CPU usage 2026-01-27 21:19:56 -06:00
James Seibel bffffbb333 Fix compiling on older MC versions 2026-01-27 21:18:57 -06:00
James Seibel 2e7185da5d Change LOD loading to start at lowest detail 2026-01-24 13:39:41 -06:00
s809 32775ac3e5 Merge branch 'feature/split-generation-toggles' 2026-01-18 22:40:38 +05:00
s809 d360402bc3 Add correct descriptions 2026-01-18 22:38:06 +05:00
James Seibel cb59d76f09 pause world gen when moving quickly 2026-01-17 17:03:47 -06:00
James Seibel 7512a41ef7 add changes from DhRenderState 2026-01-17 16:15:58 -06:00
James Seibel 7d083bdad2 Add dynamic overdraw distance based on camera speed 2026-01-17 16:13:22 -06:00
James Seibel 9972363846 Change render wrapper get Texture error returns 2026-01-17 09:57:25 -06:00
James Seibel 4f171234c4 Remove unneeded resource pack meta files 2026-01-17 08:20:43 -06:00
s809 6c56d09b18 Split off server generation into a separate toggle 2026-01-17 01:43:18 +05:00
s809 2716059840 Make sure payload chunk is readable 2026-01-14 22:17:50 +05:00
James Seibel a60887e9a7 Dynamically get beacon base blocks in MC 1.18.2+ 2026-01-10 17:33:51 -06:00
James Seibel 1b6c5a451a Improve concurrent iterating in QuadTree 2026-01-10 17:03:48 -06:00
James Seibel 4e91911e58 Add extra error logging around getCloudColor() 2026-01-10 16:21:17 -06:00
James Seibel bfae194919 remove unused broken import 2026-01-10 16:02:05 -06:00
James Seibel e562b2fdca Fix farmland color (fixes #690) 2026-01-10 12:47:34 -06:00
James Seibel 47fa6d7d8b minor ui reformat 2026-01-10 11:57:01 -06:00
James Seibel 053d1333ca Fix opening DH in forge mod menu (Fixes #678)
Done to fix a forge limitation where logos can't contain a file pathhttps://github.com/MinecraftForge/MinecraftForge/issues/7348
2026-01-10 11:56:39 -06:00
James Seibel 961c4190ad Fix Fabric debug keys 2026-01-10 10:31:56 -06:00
James Seibel 2f2ac0859c let shift click go back in UI options 2026-01-10 10:28:24 -06:00
James Seibel b59781b064 Fix white water/tinting with BCLib 2026-01-10 09:22:57 -06:00
s809 39cc5bb8aa Fix compilation 2026-01-09 21:01:15 +05:00
s809 abdbc73865 Reapply "Run plugin messages on a DH thread" 2026-01-09 20:29:40 +05:00
James Seibel 596f9f834a expand distant beacon beams for visiblity 2026-01-07 07:50:34 -06:00
James Seibel 8f2aaf4ef4 Fix #1152 (beacon beam going through tinted glass) 2026-01-07 07:50:01 -06:00
James Seibel 2320740a4a Change EMinecraftColor -> MinecraftTextFormat 2026-01-06 07:11:04 -06:00
James Seibel 4f0dc07995 Minor refactoring after slab color fix merge 2026-01-05 07:48:34 -06:00
James Seibel 336a9a94c8 Merge Slab Color Fix 2026-01-05 07:45:24 -06:00
James Seibel 1158496d9d increase fabric version in 1.20.1 to latest 2026-01-05 07:38:43 -06:00
s809 84479ed48c Wrong message target 2026-01-04 20:04:34 +05:00
s809 0c71ca96c6 Add a chat message for incompatible messages 2026-01-04 19:34:58 +05:00
s809 43dff26063 Replace the failure state with future exceptions 2025-12-27 00:51:35 +05:00
James Seibel 3cba883ba2 up version number 2.4.5 -> 2.4.6-dev 2025-12-24 22:41:36 -06:00
James Seibel 2d2e7524ae up version number 2.4.4 -> 2.4.5 2025-12-24 22:06:59 -06:00
James Seibel e8ff7abaea Replace MC color code strings with an enum 2025-12-24 22:05:59 -06:00
James Seibel 008ad52bbc remove dev from version number 2025-12-23 22:57:10 -06:00
James Seibel d0b44a1ffc Fix toggling world gen not recreating queue 2025-12-23 22:57:05 -06:00
James Seibel 4ffe430686 up DH api version 5.0.0 -> 5.1.0 2025-12-23 20:01:19 -06:00
James Seibel 19ca97c6c1 add experimental option to ignore rendering dimensions by name 2025-12-23 12:22:05 -06:00
James Seibel 3334394bfd Fix earth curvature shader compiling 2025-12-23 08:47:50 -06:00
James Seibel 180e7cd814 change forge fog config to match neo/fabric 2025-12-22 20:32:41 -06:00
James Seibel 84cf4505f2 merge world gen refactor 2025-12-20 10:54:07 -06:00
James Seibel 1d74eea3ef reduce stuttering at the cost of lighting quality 2025-12-20 10:53:14 -06:00
James Seibel 6ee2e6be25 up manifold version 25.1.27 -> 25.1.28 2025-12-19 16:55:32 -06:00
James Seibel b5c47d67cb up version number 2.4.3 -> 2.4.4-dev 2025-12-18 10:04:57 -06:00
James Seibel ead59d0817 remove dev from version number 2025-12-18 09:35:57 -06:00
James Seibel 1c9229c8f1 Fix 1.21.11 stuttering when flying into new chunks in singleplayer 2025-12-18 09:35:54 -06:00
James Seibel 968a14c6a5 remove chunkWrapper.isStillValid() 2025-12-18 09:35:21 -06:00
ada_aster 5608db9c56 Slab Block Color Fix 2025-12-17 22:12:15 -05:00
ada_aster 81b6a25805 Slab Block Color Fix 2025-12-17 22:08:45 -05:00
ada_aster 276d90b3e6 Slab Block Color Fix 2025-12-17 21:48:31 -05:00
James Seibel 851c7439d5 Fix GLProxy error in multiplayer 2025-12-17 09:02:15 -06:00
s809 c902357a8f Update core 2025-12-17 00:17:27 +05:00
s809 63170078f5 Fix returning wrong dimension name 2025-12-17 00:17:21 +05:00
James Seibel d0dd1f125b ignore chunk update events during all world gen pos 2025-12-15 15:07:01 -06:00
James Seibel 32950d793e slight light engine optimization 2025-12-15 14:37:22 -06:00
James Seibel 54e9bad907 up version number 2.4.2 -> 2.4.3-dev 2025-12-15 10:17:34 -06:00
215 changed files with 18070 additions and 3683 deletions
+3 -4
View File
@@ -1,9 +1,8 @@
# use Eclipse's JDK
# The ci should always use a unix(-like) OS to work
image: eclipse-temurin:21
image: eclipse-temurin:25
# all stages need to be defined here
# TODO: Make stages depend on what is in versionProperties
stages:
- build
- api
@@ -36,6 +35,7 @@ build:
parallel:
matrix:
- MC_VER: [
"26.1.2",
"1.21.11", "1.21.10", "1.21.9", "1.21.8", "1.21.6", "1.21.5", "1.21.4", "1.21.3", "1.21.1",
"1.20.6", "1.20.4", "1.20.2", "1.20.1",
"1.19.4", "1.19.2",
@@ -47,8 +47,7 @@ build:
# this both runs the unit tests and assembles the code
- ./gradlew clean -PmcVer="${MC_VER}" -PinfoGitCommit="${CI_COMMIT_SHA}" -PinfoGitBranch="${CI_COMMIT_BRANCH}" -PinfoBuildSource="GitLab CI (${CI_PIPELINE_ID})" --gradle-user-home cache/;
- ./gradlew build -PmcVer="${MC_VER}" -PinfoGitCommit="${CI_COMMIT_SHA}" -PinfoGitBranch="${CI_COMMIT_BRANCH}" -PinfoBuildSource="GitLab CI (${CI_PIPELINE_ID})" --gradle-user-home cache/;
- ./gradlew mergeJars -PmcVer="${MC_VER}" -PinfoGitCommit="${CI_COMMIT_SHA}" -PinfoGitBranch="${CI_COMMIT_BRANCH}" -PinfoBuildSource="GitLab CI (${CI_PIPELINE_ID})" --gradle-user-home cache/;
- cp ./fabric/build/libs/* ./forge/build/libs/* ./neoforge/build/libs/* ./build/merged/* . || true
- cp ./fabric/build/libs/* ./forge/build/libs/* ./neoforge/build/libs/* ./build/forgix/* . || true
artifacts:
name: "NightlyBuild_${MC_VER}-${CI_COMMIT_SHORT_SHA}-${CI_COMMIT_TIMESTAMP}"
paths:
+16
View File
@@ -0,0 +1,16 @@
# See mailmap docs: https://git-scm.com/docs/gitmailmap
# Test with git shortlog --summary --email
# Keep sorted for easier editing and smaller diffs
Ada Aster <an.ada.poirier@gmail.com>
CodeF53 <fseusb@gmail.com> <37855219+CodeF53@users.noreply.github.com>
coolGi <me@coolgi.dev> <sasanaps@hotmail.com>
James Seibel <jeseibel@gondtc.com> <jseibel@vertsys.com>
Morippi <leoleo97@libero.it> <leoloe97@libero.it>
Morippi <leoleo97@libero.it> <Morippi>
Ran <43445785+Ran-Mewo@users.noreply.github.com> <10044908-_Ran@users.noreply.gitlab.com>
Ran <43445785+Ran-Mewo@users.noreply.github.com> <43445785+Ran-Mewo@users.noreply.github.com>
Ran <43445785+Ran-Mewo@users.noreply.github.com> <43445785+RanCraftPlayz@users.noreply.github.com>
TomTheFurry <tomlee92502@yahoo.com>
TomTheFurry <tomlee92502@yahoo.com> <46843632+TomTheFurry@users.noreply.github.com>
Yeshi <yeshi@newengine.org>
+1 -1
View File
@@ -1,4 +1,4 @@
FROM eclipse-temurin:17-jdk
FROM eclipse-temurin:25-jdk
WORKDIR /home/build/
COPY ./gradlew .
+4 -726
View File
@@ -1,730 +1,8 @@
import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer
import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext
import org.apache.tools.zip.ZipEntry
import javax.annotation.Nonnull
import org.apache.tools.zip.ZipOutputStream
import java.util.function.Function
import java.util.function.Predicate
plugins {
id "java"
// Plugin to put dependencies inside our final jar
id "com.github.johnrengelman.shadow" version '8.1.1' apply false
// Plugin to create merged jars
id "io.github.pacifistmc.forgix" version "1.3.4"
// Manifold preprocessor
id "systems.manifold.manifold-gradle-plugin" version "0.0.2-alpha"
// Architectury is used here only as a replacement for forge's own loom
id "dev.architectury.loom" version "1.13-SNAPSHOT" apply false
id 'root'
id 'io.github.pacifistmc.forgix' version '2.+'
}
/**
* Creates the list of preprocessors to use.
*
* @param mcVers array of all MC versions
* @param mcIndex array index of the currently active MC version
*/
def writeBuildGradlePredefine(List<String> mcVers, int mcIndex)
{
// Build the list of preprocessors to use
StringBuilder sb = new StringBuilder();
sb.append("# DON'T TOUCH THIS FILE, This is handled by the build script\n");
for (int i = 0; i < mcVers.size(); i++)
{
String verStr = mcVers[i].replace(".", "_");
sb.append("MC_" + verStr + "=" + i.toString() + "\n");
if (mcIndex == i)
{
sb.append("MC_VER=" + i.toString() + "\n");
}
}
// Check if this is a development build
if (mod_version.toLowerCase().contains("dev"))
{
// WARNING: only use this for logging, we don't want to have confusion
// when a method doesn't work correctly in the release build.
sb.append("DEV_BUILD=\n");
}
new File(projectDir, "build.properties").text = sb.toString()
}
// Transfers the values set in settings.gradle to the rest of the project
project.gradle.ext.getProperties().each { prop ->
rootProject.ext.set(prop.key, prop.value)
//println "Added prop [key:" + prop.key + ", value:" + prop.value + "]"
}
// Sets up manifold stuff
writeBuildGradlePredefine(rootProject.mcVers, rootProject.mcIndex)
// Sets up the version string (the name we use for our jar)
rootProject.versionStr = rootProject.mod_version + "-" + rootProject.minecraft_version // + "-" + new Date().format("yyyy_MM_dd_HH_mm")
class NativeTransformer implements Transformer {
private Predicate<String> fileMatcher
private Function<String, String> filePathMapper
private final HashMap<String, String> replacements = new HashMap()
private final HashMap<String, byte[]> rewrittenFiles = new HashMap()
private var nativeRelocator
public File rootDir
void matchFiles(Predicate<String> matcher) {
fileMatcher = matcher
}
void mapPaths(Function<String, String> mapper) {
filePathMapper = mapper
}
void relocateNative(String target, String replacement) {
if (replacement.length() > target.length()) {
throw new GradleException("Length of value \"${replacement}\" exceeds the length of \"${target}\": ${replacement.length()} > ${target.length()}")
}
replacements.put(target, replacement)
}
@Override
boolean canTransformResource(@Nonnull FileTreeElement element) {
return fileMatcher.test(element.name)
}
@Override
void transform(@Nonnull TransformerContext context) {
byte[] content = context.is.readAllBytes()
if (nativeRelocator == null) {
nativeRelocator = new NativeRelocator(rootDir.toPath().resolve("relocate_natives"))
}
try {
String path = filePathMapper != null
? filePathMapper.apply(context.path)
: context.path
content = nativeRelocator.processBinary(path, content, replacements)
rewrittenFiles.put(path, content)
}
catch (Throwable e) {
throw new GradleException("Failed to relocate", e)
}
}
@Override
boolean hasTransformedResource() { return !rewrittenFiles.isEmpty() }
@Override
void modifyOutputStream(@Nonnull ZipOutputStream os, boolean preserveFileTimestamps) {
for (Map.Entry<String, byte[]> rewrittenFile : rewrittenFiles.entrySet()) {
os.putNextEntry(new ZipEntry(rewrittenFile.key))
os.write(rewrittenFile.value)
}
}
}
subprojects { p ->
// Does the same as "p == project(":common") || p == project(":fabric") || p == project(":quilt") || p == project(":forge") || p == project("WhateverWeAddLaterOn")"
// Useful later on so we dont have duplicated code
def isMinecraftSubProject = p != project(":core") && p != project(":api")
// Apply plugins
apply plugin: "java"
apply plugin: "com.github.johnrengelman.shadow"
if (isMinecraftSubProject)
apply plugin: "systems.manifold.manifold-gradle-plugin"
// Apply forge's loom
if ((findProject(":forge") && p == project(":forge")) ||
(findProject(":neoforge") && p == project(":neoforge"))
)
{
apply plugin: "dev.architectury.loom"
}
// Set the manifold version (may not be required tough)
manifold {
manifoldVersion = rootProject.manifold_version
}
// set up custom configurations (configurations are a way to handle dependencies)
configurations {
// extends the shadowJar configuration
shadowMe
// have implemented dependencies automatically embedded in the final jar
implementation.extendsFrom(shadowMe)
// Configuration fpr core & api
coreProjects
shadowMe.extendsFrom(coreProjects)
// FIXME this additional configuration is necessary because forge
// needs forgeRuntimeLibrary, although adding it to shadowMe
// causes runtime issues where the libraries aren't properly added
forgeShadowMe
// this should match shadowMe pretty closely
implementation.extendsFrom(forgeShadowMe)
shadowMe.extendsFrom(forgeShadowMe)
forgeRuntimeLibrary.extendsFrom(forgeShadowMe)
if (isMinecraftSubProject && p != project(":common")) {
// Shadow common
common
shadowCommon // Don't use shadow from the shadow plugin because we don't want IDEA to index this.
compileClasspath.extendsFrom common
runtimeClasspath.extendsFrom common
if (findProject(":forge"))
developmentForge.extendsFrom common
if (findProject(":neoforge"))
developmentNeoForge.extendsFrom common
compileClasspath.extendsFrom coreProjects
runtimeClasspath.extendsFrom coreProjects
if (findProject(":forge"))
developmentForge.extendsFrom coreProjects
if (findProject(":neoforge"))
developmentNeoForge.extendsFrom coreProjects
// TODO remove unused fabricLike
if (findProject(":fabricLike") && p != project(":fabricLike")) {
// Shadow fabricLike
fabricLike
shadowFabricLike
compileClasspath.extendsFrom fabricLike
runtimeClasspath.extendsFrom fabricLike
}
}
}
dependencies {
//=====================//
// shared dependencies //
//=====================//
// Manifold
if (isMinecraftSubProject) {
annotationProcessor("systems.manifold:manifold-preprocessor:${rootProject.manifold_version}")
}
// Log4j
if (p == project(":core"))
{
// the standalone core jar needs logging shaded otherwise it won't run
forgeShadowMe("org.apache.logging.log4j:log4j-api:${rootProject.log4j_version}")
forgeShadowMe("org.apache.logging.log4j:log4j-core:${rootProject.log4j_version}")
}
else
{
// When running in MC, MC already includes logging
implementation("org.apache.logging.log4j:log4j-api:${rootProject.log4j_version}")
implementation("org.apache.logging.log4j:log4j-core:${rootProject.log4j_version}")
}
// JOML
if (project.hasProperty("embed_joml") && embed_joml == "true")
forgeShadowMe("org.joml:joml:${rootProject.joml_version}")
else
implementation("org.joml:joml:${rootProject.joml_version}")
// JUnit tests
implementation("org.junit.jupiter:junit-jupiter:5.8.2")
implementation("org.junit.jupiter:junit-jupiter-engine:5.8.2")
implementation("junit:junit:4.13")
// FastUtil
// Note: MC 1.16 uses 8.2.1, and versions after use 8.5.12
// We cannot relocate this library since we call some MC classes that reference it
implementation("it.unimi.dsi:fastutil:${rootProject.fastutil_version}")
forgeShadowMe("com.github.luben:zstd-jni:${rootProject.zstd_version}")
// Compression
forgeShadowMe("org.lz4:lz4-java:${rootProject.lz4_version}") // LZ4
forgeShadowMe("org.tukaani:xz:${rootProject.xz_version}") // LZMA
// Sqlite Database
forgeShadowMe("org.xerial:sqlite-jdbc:${rootProject.sqlite_jdbc_version}")
// NightConfig (includes Toml & Json)
forgeShadowMe("com.electronwill.night-config:toml:${rootProject.nightconfig_version}")
forgeShadowMe("com.electronwill.night-config:json:${rootProject.nightconfig_version}")
// SVG (not needed atm)
// forgeShadowMe("com.formdev:svgSalamander:${rootProject.svgSalamander_version}")
// Netty
implementation("io.netty:netty-buffer:${rootProject.netty_version}")
// Remember, for lwjgl dependencies that arent included in Minecraft, you need to also need to add it to the ShadowJar thing
forgeShadowMe("org.lwjgl:lwjgl-jawt:${rootProject.lwjgl_version}") {
exclude group: "org.lwjgl", module: "lwjgl" // This module is imported by Minecraft so exclude it
}
//==========================//
// conditional dependencies //
//==========================//
// Add core
if (isMinecraftSubProject) {
coreProjects(project(":core")) {
// Remove Junit test libraries
exclude group: "org.junit.jupiter", module: "junit-jupiter"
exclude group: "org.junit.jupiter", module: "junit-jupiter-engine"
exclude group: "junit", module: "junit"
// Removed dependencies
transitive false
}
}
// Add the api
if (p != project(":api")) {
coreProjects(project(":api")) {
// Remove Junit test libraries
exclude group: "org.junit.jupiter", module: "junit-jupiter"
exclude group: "org.junit.jupiter", module: "junit-jupiter-engine"
exclude group: "junit", module: "junit"
// Removed dependencies
transitive false
}
}
// Add common
if (isMinecraftSubProject && p != project(":common")) {
// Common
common(project(":common")) { transitive false }
shadowCommon(project(":common")) { transitive false }
// FabricLike
if (findProject(":fabricLike") && p != project(":fabricLike")) {
fabricLike(project(path: ":fabricLike")) { transitive false }
shadowFabricLike(project(path: ":fabricLike")) { transitive false }
}
}
}
shadowJar {
configurations = [project.configurations.shadowMe]
if (isMinecraftSubProject && p != project(":common")) {
configurations.push(project.configurations.shadowCommon) // Shadow the common subproject
relocate "com.seibel.distanthorizons.common", "loaderCommon.${p.name}.com.seibel.distanthorizons.common" // Move the loader files to a different location
if (findProject(":fabricLike") && p != project(":fabricLike")) {
configurations.push(project.configurations.shadowFabricLike) // Shadow the fabricLike subproject
relocate "com.seibel.distanthorizons.fabriclike", "loaderCommon.${p.name}.com.seibel.distanthorizons.fabriclike" // Move the loader files to a different location
}
}
def librariesLocation = "DistantHorizons.libraries"
// LWJGL
// Only ever shadow the dependencies we use otherwise some stuff would break when running on an external client
relocate "org.lwjgl.system.jawt", "${librariesLocation}.lwjgl.system.jawt"
// Compression (LZ4)
relocate "net.jpountz", "${librariesLocation}.jpountz"
// Logging
relocate "org.slf4j", "${librariesLocation}.slf4j"
// Sqlite Database
// librariesLocation isn't used because it's too long for replacing paths in native libraries
// Allowing strings larger than the original string would require shifting the entire binary's contents
relocate "org.sqlite", "dh_sqlite", {
exclude "org/sqlite/native/**"
}
relocate "jdbc:sqlite", "jdbc:dh_sqlite"
transform(NativeTransformer) {
rootDir = project.rootDir
matchFiles { it.startsWith("org/sqlite") }
mapPaths { it.replace("org/sqlite", "dh_sqlite") }
relocateNative "org/sqlite", "dh_sqlite"
relocateNative "org_sqlite", "dh_1sqlite"
}
// ZStd
// librariesLocation isn't used because it's too long for replacing paths in native libraries
// Allowing strings larger than the original string would require shifting the entire binary's contents
relocate "com.github.luben", "dhcomgithubluben"
relocate "libzstd-jni", "libzstd-jni_dh"
relocate "zstd-jni", "zstd-jni_dh"
transform(NativeTransformer) {
rootDir = project.rootDir
matchFiles { it.contains("libzstd-jni") && !it.contains("aix/ppc64") }
mapPaths { it.replace("libzstd-jni", "libzstd-jni_dh") }
relocateNative "com/github/luben", "dhcomgithubluben"
relocateNative "com_github_luben", "dhcomgithubluben"
}
// JOML
if (project.hasProperty("embed_joml") && embed_joml == "true")
relocate "org.joml", "${librariesLocation}.joml"
// NightConfig (includes Toml & Json)
relocate "com.electronwill.nightconfig", "${librariesLocation}.electronwill.nightconfig"
// SVG (not needed atm)
// relocate "com.kitfox.svg", "${librariesLocation}.kitfox.svg"
// Netty
// Don't relocate, it causes problems with using MC's FriendlyByteBufs
// relocate "io.netty", "${librariesLocation}.netty"
mergeServiceFiles()
}
// Using jar.finalizedBy(shadowJar) causes issues so we do this scuffed bypass
jar.dependsOn(shadowJar)
// Put stuff from gradle.properties into the mod info
processResources {
def resourceTargets = [ // Location of where to inject the properties
// Holds info like git commit
// TODO: For some reason this script doesnt work with the core project
"build_info.json",
// Properties for each of the loaders
"fabric.mod.json",
"quilt.mod.json",
"META-INF/mods.toml",
"META-INF/neoforge.mods.toml",
// The mixins for each of the loaders
"DistantHorizons."+ p.name +".fabricLike.mixins.json"
]
def intoTargets = ["$buildDir/resources/main/"] // Location of the built resources folder
// Fix forge version numbering system as it is weird
// For whatever reason forge uses [1.18, 1.18.1, 1.18.2) instead of the standard ["1.18", "1.18.1", "1.18.2"]
def compatible_forgemc_versions = "${compatible_minecraft_versions}".replaceAll("\"", "").replaceAll("]", ",)")
// println compatible_forgemc_versions
// Quilt's custom contributors system
// This has to be like
// "Person": "Developer", "Another person": "Developer"
def quilt_contributors = []
def mod_author_list = mod_authors.replaceAll("\"", "").replace("[", "").replace("]", "").split(",")
for (dev in mod_author_list) {
quilt_contributors.push("\"${dev.strip()}\": \"Developer\"")
}
quilt_contributors.reverse()
//println quilt_contributors.join(", ")
// TODO: Find something we can use so we can basically re-map only when the jar is shadowed and relocated
// println p.tasks.findByName('shadowJar')
// These "hasProperty"'s are so that they can be passed through the cli (ie in the CI)
try {
if (infoGitCommit == "null")
infoGitCommit = 'git rev-parse --verify HEAD'.execute().text.trim()
if (infoGitBranch == "null")
infoGitBranch = 'git symbolic-ref --short HEAD'.execute().text.trim()
} catch (Exception e) {
infoGitCommit = infoGitBranch = "Git not found"
println "Git or Git project not found"
}
// The left side is what gets replaced in the mod info and the right side is where to get it from in the gradle.properties
def replaceProperties = [
version : mod_version,
mod_name : mod_readable_name,
group : maven_group,
authors : mod_authors,
description : mod_description,
homepage : mod_homepage,
source : mod_source,
issues : mod_issues,
discord : mod_discord,
minecraft_version : minecraft_version,
compatible_minecraft_versions: compatible_minecraft_versions,
compatible_forgemc_versions : compatible_forgemc_versions,
java_version : java_version,
quilt_contributors : "{"+quilt_contributors.join(", ")+"}",
info_git_commit : infoGitBranch,
info_git_branch : infoGitCommit,
info_build_source : infoBuildSource,
fabric_incompatibility_list : fabric_incompatibility_list,
fabric_recommend_list : fabric_recommend_list,
neoforge_version_range : neoforge_version_range,
]
// replace any properties in the sub-projects with the values defined here
inputs.properties replaceProperties
replaceProperties.put "project", project
filesMatching(resourceTargets) {
expand replaceProperties
}
intoTargets.each { target ->
if (file(target).exists()) {
copy {
from(sourceSets.main.resources) {
include resourceTargets
expand replaceProperties
}
into target
}
}
}
// ==================== Delete un-needed files ====================
exclude "DistantHorizons.fabricLike.mixins.json" // This isnt required atm, but we will be using it later
// exclude "*.distanthorizons.accesswidener"
//// include "${accessWidenerVersion}.distanthorizons.accesswidener"
// Jank solution to remove all unused accesswideners
// The line above would work..., except that (neo)forge (well, mainly architectury) requires the original accesswidener file, meaning we require this jank solution to keep it
exclude { file ->
if (file.name.contains(".distanthorizons.accesswidener") && file.name != "${accessWidenerVersion}.distanthorizons.accesswidener") {
return true
}
return false
}
}
// Adds the standalone jar's entrypoint
jar {
from "LICENSE.txt"
manifest {
attributes(
'Implementation-Title': rootProject.mod_name,
'Implementation-Version': rootProject.mod_version,
'Multi-Release': true, // needed for logging in the standalone core jar
'Main-Class': 'com.seibel.distanthorizons.core.jar.JarMain', // When changing the main of the jar change this line
)
}
}
// this can be un-commented if we ever wanted to make DH modular (AKA use a module-info.java file) again
/*
// Tells gradle where to look for other modules
// Why isn't the classpath added to the modules path by default?
if (p == project(":core")) {
compileJava {
inputs.property('moduleName', 'dhApi')
doFirst {
options.compilerArgs = [
'--module-path', classpath.asPath
]
classpath = files()
}
}
}
*/
}
allprojects { p ->
// Does the same as "p == project(":common") || p == project(":fabric") || p == project(":quilt") || p == project(":forge") || p == project("WhateverWeAddLaterOn")"
// Useful later on so we dont have duplicated code
def isMinecraftSubProject = p != project(":core") && p != project(":api")
apply plugin: "java"
apply plugin: "maven-publish"
// Sets the name of the jar, the version will contain the name of the project if it isn't the root project
archivesBaseName = rootProject.mod_name
version = (project == rootProject ? "" : project.name + "-") + rootProject.versionStr
group = rootProject.maven_group
// this is the text that appears at the top of the overview (home) page
// and is used when bookmarking a page
javadoc.title = rootProject.mod_name + "-" + project.name
// Some annotations arent "technically" part of the official java standard,
// so we define it ourself here
javadoc {
configure( options ) {
tags(
'todo:X"',
'apiNote:a:API Note:',
'implSpec:a:Implementation Requirements:',
'implNote:a:Implementation Note:'
)
}
}
repositories {
// Mojang overrides (added to fix downloading the wrong LWJGL libs on M1 Mac's and potentially other arm64 based machines)
maven { url "https://libraries.minecraft.net/" }
// The central repo
mavenCentral()
// Used for Google's Collect library
maven { url "https://repo.enonic.com/public/" }
// For parchment mappings
// versions can be found here: https://ldtteam.jfrog.io/ui/native/parchmentmc-public/org/parchmentmc/data/
maven { url "https://maven.parchmentmc.org" }
// For Architectury API
maven { url "https://maven.architectury.dev" }
// For Git repositories
maven { url "https://jitpack.io" }
// For Manifold Preprocessor
maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
// Required for importing Modrinth mods
maven {
name = "Modrinth"
url = "https://api.modrinth.com/maven"
content {
includeGroup "maven.modrinth"
}
}
// Required for importing CursedForge mods
maven {
url "https://www.cursemaven.com"
content {
includeGroup "curse.maven"
}
}
// VanillaGradle and Mixins in common
maven { url "https://repo.spongepowered.org/maven/" }
// Canvas mod
maven { url "https://maven.vram.io/" }
// ModMenu mod
maven { url "https://maven.terraformersmc.com/" }
// neoforge
maven { url "https://maven.neoforged.net/releases/" }
// These 3 are for importing mods that arnt on CursedForge, Modrinth, GitHub, GitLab or anywhere opensource
flatDir {
dirs "${rootDir}/mods/fabric"
content {
includeGroup "fabric-mod"
}
}
flatDir {
dirs "${rootDir}/mods/quilt"
content {
includeGroup "quilt-mod"
}
}
flatDir {
dirs "${rootDir}/mods/forge"
content {
includeGroup "forge-mod"
}
}
// TODO: If neoforged is ever needed, should we use that, or call it a forge mod?
}
// Adds some dependencies that are in vanilla but not in core
if (p == project(":core")) {
OperatingSystem os = org.gradle.nativeplatform.platform.internal.DefaultNativePlatform.currentOperatingSystem;
// Set the OS lwjgl is using to the current os
project.ext.lwjglNatives = "natives-" + os.toFamilyName()
dependencies { // All of these dependencies are in Vanilla Minecraft, but we need to depend on it as we arent importing Minecraft in the core
// Imports most of lwjgl's libraries (well, only the ones that we need)
implementation platform("org.lwjgl:lwjgl-bom:${rootProject.lwjgl_version}") // TODO: Use Minecraft's version for lwjgl_version (which changes in nearly every version) instead of a hard defined version for all versions
// REMEMBER: Dont shadow stuff here, these are just the libs that are included in Minecraft so that the core can use
implementation "org.lwjgl:lwjgl"
implementation "org.lwjgl:lwjgl-assimp"
implementation "org.lwjgl:lwjgl-glfw"
implementation "org.lwjgl:lwjgl-openal"
implementation "org.lwjgl:lwjgl-opengl"
implementation "org.lwjgl:lwjgl-stb"
implementation "org.lwjgl:lwjgl-tinyfd"
runtimeOnly "org.lwjgl:lwjgl::$lwjglNatives"
runtimeOnly "org.lwjgl:lwjgl-assimp::$lwjglNatives"
runtimeOnly "org.lwjgl:lwjgl-glfw::$lwjglNatives"
runtimeOnly "org.lwjgl:lwjgl-openal::$lwjglNatives"
runtimeOnly "org.lwjgl:lwjgl-opengl::$lwjglNatives"
runtimeOnly "org.lwjgl:lwjgl-stb::$lwjglNatives"
runtimeOnly "org.lwjgl:lwjgl-tinyfd::$lwjglNatives"
implementation "org.joml:joml:${rootProject.joml_version}"
// Some other dependencies
implementation("org.jetbrains:annotations:16.0.2")
implementation("com.google.code.findbugs:jsr305:3.0.2")
implementation("com.google.common:google-collect:0.5")
implementation("com.google.guava:guava:31.1-jre")
}
}
task copyCommonLoaderResources(type: Copy) {
from project(":common").file("src/main/resources/${accessWidenerVersion}.distanthorizons.accesswidener")
into(file(p.file("build/resources/main")))
rename "${accessWidenerVersion}.distanthorizons.accesswidener", "distanthorizons.accesswidener"
// Move the fabricLike mixin to its different places for each subproject
if (findProject(":fabricLike")) {
from project(":fabricLike").file("src/main/resources/DistantHorizons.fabricLike.mixins.json")
into(file(p.file("build/resources/main")))
rename "DistantHorizons.fabricLike.mixins.json", "DistantHorizons." + p.name + ".fabricLike.mixins.json"
}
}
task copyCoreResources(type: Copy) {
from fileTree(project(":core").file("src/main/resources"))
into p.file("build/resources/main")
}
tasks.withType(JavaCompile) {
if (isMinecraftSubProject) {
options.release = rootProject.java_version as Integer
} else {
options.release = 8; // Core & Api should use Java 8 no matter what
//options.release = rootProject.java_version as Integer // But if you want to test some stuff, then this can be enabled
}
options.encoding = "UTF-8"
}
java {
withSourcesJar()
}
forgix {
autoRun = true
}
+2 -1
View File
@@ -3,6 +3,7 @@
echo "==================== Note: All build jars will be in the folder called 'buildAllJars' ===================="
mkdir -p buildAllJars
rm -rf buildAllJars/*
rm -rf build/forgix/*
# Loop trough everything in the version properties folder
for d in versionProperties/*; do
@@ -24,5 +25,5 @@ for d in versionProperties/*; do
if [ $? != 0 ]; then continue; fi
echo "==================== Moving jar ===================="
mv build/merged/*.jar buildAllJars/
mv build/forgix/*.jar buildAllJars/
done
+2 -1
View File
@@ -6,6 +6,7 @@
echo ==================== Note: All build jars will be in the folder called 'buildAllJars' ====================
mkdir buildAllJars
del buildAllJars/*
del build/forgix/*
@rem Loop trough everything in the version properties folder
for %%f in (versionProperties\*) do (
@@ -23,7 +24,7 @@ for %%f in (versionProperties\*) do (
call .\gradlew.bat mergeJars -PmcVer="!version!"
echo ==================== Moving jar ====================
move build\merged\*.jar buildAllJars\
move build\forgix\*.jar buildAllJars\
)
endlocal
+24
View File
@@ -0,0 +1,24 @@
plugins {
id 'groovy-gradle-plugin'
}
repositories {
gradlePluginPortal()
mavenCentral()
maven { url = 'https://maven.wagyourtail.xyz/releases' } // Jvmdowngrader & unimined libs
maven { url = 'https://maven.outlands.top/releases' } // Hosts the kappa fork of unimined
maven { url = 'https://maven.wagyourtail.xyz/snapshots' } // The manifold gradle plugin we use
maven { url = 'https://maven.architectury.dev/' } // Minecraft mod libs
maven { url = 'https://maven.fabricmc.net/' } // Fabric
maven { url = 'https://maven.neoforged.net/releases/' } // NeoForge
maven { url = 'https://maven.minecraftforge.net/' } // Forge
maven { url = 'https://repo.spongepowered.org/repository/maven-public/' } // Hosts minecraft libs
maven { url = 'https://oss.sonatype.org/content/repositories/snapshots/' } // Hosts a few dependencies we use
}
dependencies {
implementation 'com.gradleup.shadow:shadow-gradle-plugin:9.0.0'
implementation 'xyz.wagyourtail.unimined:xyz.wagyourtail.unimined.gradle.plugin:1.4.18-kappa'
implementation 'xyz.wagyourtail:manifold-gradle:1.0.0-SNAPSHOT'
implementation 'xyz.wagyourtail.jvmdowngrader:xyz.wagyourtail.jvmdowngrader.gradle.plugin:1.3.4'
}
+485
View File
@@ -0,0 +1,485 @@
import com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransformer
import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext
import org.apache.tools.zip.ZipEntry
import org.apache.tools.zip.ZipOutputStream
import javax.annotation.Nonnull
import java.util.function.Function
import java.util.function.Predicate
// Convention plugin for all MC-facing subprojects (common + loaders).
// Common uses this directly; loaders use it via unimined-fabric/forge/neoforge.
// IMPORTANT: unimined MUST be applied before shadow/jvmdowngrader
// so its afterEvaluate runs first and can modify configs.
plugins {
id 'java'
id 'maven-publish'
id 'xyz.wagyourtail.unimined'
id 'com.gradleup.shadow'
id 'xyz.wagyourtail.manifold'
id 'xyz.wagyourtail.jvmdowngrader'
}
def isNotCommonProject = project.name != "common"
// ==================== Version Properties ====================
project.gradle.ext.getProperties().each { prop ->
rootProject.ext.set(prop.key, prop.value)
}
manifold {
version = rootProject.manifold_version
}
// ==================== Repositories ====================
repositories {
maven { url "https://libraries.minecraft.net/" }
mavenCentral()
maven { url "https://repo.enonic.com/public/" }
maven { url "https://maven.parchmentmc.org" }
maven { url "https://maven.architectury.dev" }
maven { url "https://jitpack.io" }
maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
maven {
name = "Modrinth"
url = "https://api.modrinth.com/maven"
content { includeGroup "maven.modrinth" }
}
maven {
url "https://www.cursemaven.com"
content { includeGroup "curse.maven" }
}
maven { url "https://repo.spongepowered.org/maven/" }
maven { url "https://maven.terraformersmc.com/" }
maven { url "https://maven.neoforged.net/releases/" }
flatDir {
dirs "${rootDir}/mods/fabric"
content { includeGroup "fabric-mod" }
}
flatDir {
dirs "${rootDir}/mods/quilt"
content { includeGroup "quilt-mod" }
}
flatDir {
dirs "${rootDir}/mods/forge"
content { includeGroup "forge-mod" }
}
}
// ==================== Java Config ====================
tasks.withType(JavaCompile).configureEach {
options.release = rootProject.java_version as Integer
options.encoding = "UTF-8"
}
java {
sourceCompatibility = JavaVersion.toVersion(gradle.ext.java_version as Integer)
targetCompatibility = JavaVersion.toVersion(gradle.ext.java_version as Integer)
withSourcesJar()
}
// ==================== Loader-Only Config ====================
if (isNotCommonProject) {
base { archivesName = rootProject.mod_name }
rootProject.ext.versionStr = rootProject.mod_version + "-" + rootProject.minecraft_version
version = project.name + "-" + rootProject.versionStr
group = rootProject.maven_group
javadoc.title = rootProject.mod_name + "-" + project.name
tasks.withType(GenerateModuleMetadata).configureEach {
enabled = false
}
tasks.withType(Test).configureEach {
enabled = false
}
compileTestJava.enabled = false
tasks.withType(Sign).configureEach {
enabled = false
}
jar {
from "LICENSE.txt"
manifest {
attributes(
'Implementation-Title': rootProject.mod_name,
'Implementation-Version': rootProject.mod_version,
'Multi-Release': true,
'Main-Class': 'com.seibel.distanthorizons.core.jar.JarMain',
)
}
}
}
// ==================== Unimined Minecraft Config ====================
unimined.minecraft(sourceSets.main, true) {
version gradle.ext.minecraft_version
if (gradle.ext.minecraft_version.startsWith("1.")) { // 26.1+ doesn't use obfuscation
mappings {
mojmap()
devNamespace "mojmap"
}
}
}
if (isNotCommonProject) {
// Mixin remapping and common project wiring
unimined.minecraft(sourceSets.main, true) {
mods.modImplementation {
mixinRemap {
reset()
enableBaseMixin()
enableMixinExtra()
}
// Some Fabric API modules ship AW in 'named' namespace instead of 'intermediary'
catchAWNamespaceAssertion()
}
}
dependencies {
implementation(project(":common"))
}
processResources {
from project(":common").sourceSets.main.resources
}
tasks.withType(JavaCompile).configureEach {
source(project(":common").sourceSets.main.allSource)
}
} else {
// Common: fabric for compilation + access widener, no jar remapping or runs
unimined.minecraft {
fabric {
loader gradle.ext.fabric_loader_version
accessWidener project.file("src/main/resources/${gradle.ext.accessWidenerVersion}.distanthorizons.accesswidener")
}
defaultRemapJar = false
runs.off = true
}
}
// ==================== Configurations ====================
evaluationDependsOn(":core")
configurations {
shadowMe
coreProjects
shadowMe.extendsFrom(coreProjects)
implementation.extendsFrom(shadowMe)
common
implementation.extendsFrom(common)
}
// ==================== Dependencies ====================
// Copy core's compileOnly deps so MC-provided deps are visible without redeclaring them.
project(":core").configurations.compileOnly.allDependencies.each { dep ->
if (!(dep instanceof ProjectDependency))
dependencies.add("compileOnly", dep)
}
dependencies {
// Manifold preprocessor & strings
annotationProcessor(manifold.module("preprocessor"))
// NightConfig: implementation in core (bundled) but Unimined strips it from compile classpath
compileOnly("com.electronwill.night-config:toml:${rootProject.nightconfig_version}")
// Core & API projects — bundled into shadow jar
coreProjects(project(":core"))
coreProjects(project(":api"))
// JOML: shadow for old MC versions that don't bundle it (core has it compileOnly already)
if (project.hasProperty("embed_joml") && embed_joml == "true")
shadowMe("org.joml:joml:${rootProject.joml_version}")
// Common project dependency
if (isNotCommonProject)
common(project(":common")) { transitive false }
}
// ==================== NativeTransformer ====================
class NativeTransformer implements ResourceTransformer {
private Predicate<String> fileMatcher
private Function<String, String> filePathMapper
private final HashMap<String, String> replacements = new HashMap()
private final HashMap<String, byte[]> rewrittenFiles = new HashMap()
private nativeRelocator
public File rootDir
void matchFiles(Predicate<String> matcher) {
fileMatcher = matcher
}
void mapPaths(Function<String, String> mapper) {
filePathMapper = mapper
}
void relocateNative(String target, String replacement) {
if (replacement.length() > target.length()) {
throw new GradleException("Length of value \"${replacement}\" exceeds the length of \"${target}\": ${replacement.length()} > ${target.length()}")
}
replacements.put(target, replacement)
}
@Override
boolean canTransformResource(@Nonnull FileTreeElement element) {
return fileMatcher != null && fileMatcher.test(element.relativePath.pathString)
}
@Override
void transform(@Nonnull TransformerContext context) {
byte[] content = context.inputStream.readAllBytes()
if (nativeRelocator == null) {
nativeRelocator = new NativeRelocator(rootDir.toPath().resolve("relocate_natives"))
}
try {
String path = filePathMapper != null
? filePathMapper.apply(context.path)
: context.path
content = nativeRelocator.processBinary(path, content, replacements)
rewrittenFiles.put(path, content)
}
catch (Throwable e) {
throw new GradleException("Failed to relocate", e)
}
}
@Override
boolean hasTransformedResource() { return !rewrittenFiles.isEmpty() }
@Override
void modifyOutputStream(@Nonnull ZipOutputStream os, boolean preserveFileTimestamps) {
for (Map.Entry<String, byte[]> rewrittenFile : rewrittenFiles.entrySet()) {
os.putNextEntry(new ZipEntry(rewrittenFile.key))
os.write(rewrittenFile.value)
}
}
}
// ==================== Shadow JAR (loaders only) ====================
if (isNotCommonProject) {
shadowJar {
configurations = [project.configurations.shadowMe]
def librariesLocation = "DistantHorizons.libraries"
// LZ4
relocate "net.jpountz", "${librariesLocation}.jpountz"
// SLF4J
relocate "org.slf4j", "${librariesLocation}.slf4j"
// SQLite
relocate "org.sqlite", "dh_sqlite", { exclude "org/sqlite/native/**" }
relocate "jdbc:sqlite", "jdbc:dh_sqlite"
transform(NativeTransformer) {
rootDir = project.rootDir
matchFiles { it.startsWith("org/sqlite") }
mapPaths { it.replace("org/sqlite", "dh_sqlite") }
relocateNative "org/sqlite", "dh_sqlite"
relocateNative "org_sqlite", "dh_1sqlite"
}
// ZStd
relocate "com.github.luben", "dhcomgithubluben"
relocate "libzstd-jni", "libzstd-jni_dh"
relocate "zstd-jni", "zstd-jni_dh"
transform(NativeTransformer) {
rootDir = project.rootDir
matchFiles { it.contains("libzstd-jni") && !it.contains("aix/ppc64") }
mapPaths { it.replace("libzstd-jni", "libzstd-jni_dh") }
relocateNative "com/github/luben", "dhcomgithubluben"
relocateNative "com_github_luben", "dhcomgithubluben"
}
// JOML (conditional)
if (project.hasProperty("embed_joml") && embed_joml == "true")
relocate "org.joml", "${librariesLocation}.joml"
// NightConfig
relocate "com.electronwill.nightconfig", "${librariesLocation}.electronwill.nightconfig"
mergeServiceFiles()
}
afterEvaluate {
tasks.named("remapJar").configure {
dependsOn(shadowJar)
inputFile.set(shadowJar.archiveFile)
}
// Make run tasks use the shadow jar so relocated deps work in dev.
// Filter out jars bundled in the shadow jar, but keep jars that the loader also
// needs (e.g. NightConfig — DH relocates it, but NeoForge needs the original).
def shadowedPaths = configurations.shadowMe.resolve().collect { it.path }.toSet()
def loaderPaths = configurations.minecraftLibraries.resolve().collect { it.path }.toSet()
tasks.withType(JavaExec).configureEach { runTask ->
dependsOn(shadowJar)
classpath = files(shadowJar.archiveFile) + classpath.filter { file ->
!file.path.contains(project.buildDir.path) &&
!file.path.contains("core${File.separator}build") &&
!file.path.contains("api${File.separator}build") &&
!file.path.contains("common${File.separator}build") &&
!(shadowedPaths.contains(file.path) && !loaderPaths.contains(file.path))
}
// Shared run directory so all loaders use the same worlds
def isClient = runTask.name.toLowerCase().contains("client")
runTask.workingDir = rootProject.file("run/${isClient ? 'client' : 'server'}")
// Minecraft automatically has G1GC args present,
// remove them so we can use ZGC instead
def filteredArgs = runTask.jvmArgs.findAll { arg ->
!arg.startsWith("-XX:+UseG1GC") &&
!arg.startsWith("-XX:G1") &&
!arg.startsWith("-XX:MaxGCPauseMillis")
}
runTask.jvmArgs = filteredArgs
// JVM args
runTask.jvmArgs(
"-Dio.netty.leakDetection.level=advanced",
// TODO only use for modern java versions
"-XX:+UseZGC",
// TODO don't use for even more modern-er java versions
//"-XX:+ZGenerational",
rootProject.minecraftMemoryJavaArg,
)
if (isClient) {
runTask.jvmArgs(
"-Dminecraft.api.auth.host=https://nope.invalid",
"-Dminecraft.api.account.host=https://nope.invalid",
"-Dminecraft.api.session.host=https://nope.invalid",
"-Dminecraft.api.services.host=https://nope.invalid",
)
runTask.args(
// use a consistent username for easier debugging in a given world (vs randomly teleporting to a new user each time the game boots)
"--username", "Dev",
// "--renderDebugLabels" is a Mojang command to show render names in RenderDoc
"--renderDebugLabels",
// "--tracy" is a Mojang command to allow individual frames to be debugged using Tracy https://github.com/wolfpld/tracy/releases/tag/v0.13.1
"--tracy")
}
}
}
}
// ==================== Process Resources (loaders only) ====================
if (isNotCommonProject) {
processResources {
def resourceTargets = [
"build_info.json",
"fabric.mod.json",
"quilt.mod.json",
"META-INF/mods.toml",
"META-INF/neoforge.mods.toml",
]
def compatible_forgemc_versions = "${rootProject.compatible_minecraft_versions}".replaceAll("\"", "").replaceAll("]", ",)")
// Quilt contributors
def quilt_contributors = []
def mod_author_list = rootProject.mod_authors.replaceAll("\"", "").replace("[", "").replace("]", "").split(",")
for (dev in mod_author_list) {
quilt_contributors.push("\"${dev.strip()}\": \"Developer\"")
}
quilt_contributors.reverse()
try {
if (rootProject.infoGitCommit == "null")
rootProject.ext.infoGitCommit = 'git rev-parse --verify HEAD'.execute().text.trim()
if (rootProject.infoGitBranch == "null")
rootProject.ext.infoGitBranch = 'git symbolic-ref --short HEAD'.execute().text.trim()
} catch (Exception e) {
rootProject.ext.infoGitCommit = "Git not found"
rootProject.ext.infoGitBranch = "Git not found"
}
def replaceProperties = [
version : rootProject.mod_version,
mod_name : rootProject.mod_readable_name,
group : rootProject.maven_group,
authors : rootProject.mod_authors,
description : rootProject.mod_description,
homepage : rootProject.mod_homepage,
source : rootProject.mod_source,
issues : rootProject.mod_issues,
discord : rootProject.mod_discord,
minecraft_version : rootProject.minecraft_version,
accessWidenerVersion : rootProject.accessWidenerVersion,
compatible_minecraft_versions: rootProject.compatible_minecraft_versions,
compatible_forgemc_versions : compatible_forgemc_versions,
java_version : rootProject.java_version,
quilt_contributors : "{" + quilt_contributors.join(", ") + "}",
info_git_commit : rootProject.infoGitBranch,
info_git_branch : rootProject.infoGitCommit,
info_build_source : rootProject.infoBuildSource,
fabric_incompatibility_list : rootProject.fabric_incompatibility_list,
fabric_recommend_list : rootProject.fabric_recommend_list,
neoforge_version_range : rootProject.neoforge_version_range,
]
inputs.properties replaceProperties
replaceProperties.put "project", project
filesMatching(resourceTargets) {
expand replaceProperties
}
// Remove unused access wideners
exclude { file ->
if (file.name.contains(".distanthorizons.accesswidener") && file.name != "${rootProject.accessWidenerVersion}.distanthorizons.accesswidener") {
return true
}
return false
}
}
// ==================== Resource Copy Tasks ====================
// task copyCommonLoaderResources(type: Copy) {
// from project(":common").file("src/main/resources/${rootProject.accessWidenerVersion}.distanthorizons.accesswidener")
// into(file(project.file("build/resources/main")))
// rename "${rootProject.accessWidenerVersion}.distanthorizons.accesswidener", "distanthorizons.accesswidener"
// }
task copyCoreResources(type: Copy) {
from fileTree(project(":core").file("src/main/resources"))
into project.file("build/resources/main")
}
// ==================== JVMDowngrader ====================
jvmdg.downgradeTo = JavaVersion.toVersion(rootProject.java_version)
downgradeJar.archiveClassifier.set(null)
shadeDowngradedApi.archiveClassifier.set(null)
}
+56
View File
@@ -0,0 +1,56 @@
plugins {
id 'java'
}
// Transfer version properties from settings.gradle to project
project.gradle.ext.getProperties().each { prop ->
rootProject.ext.set(prop.key, prop.value)
}
// Version string for archives
rootProject.ext.versionStr = rootProject.mod_version + "-" + rootProject.minecraft_version
rootProject.allprojects {
version = (it == rootProject ? "" : it.name + "-") + rootProject.versionStr
group = rootProject.maven_group
// Custom javadoc tags for all subprojects
plugins.withType(JavaPlugin) {
javadoc {
options.tags(
'todo:X"',
'apiNote:a:API Note:',
'implSpec:a:Implementation Requirements:',
'implNote:a:Implementation Note:'
)
}
}
}
// Create build.properties with preprocessor definitions
def writePreprocessorDefinitions() {
StringBuilder sb = new StringBuilder()
sb.append("# DON'T TOUCH THIS FILE, This is handled by the build script\n")
gradle.ext.mcVers.eachWithIndex { ver, idx ->
sb.append("MC_${ver.replace('.', '_')}=${idx}\n")
if (gradle.ext.mcIndex == idx)
sb.append("MC_VER=${idx}\n")
}
if (rootProject.mod_version.toLowerCase().contains("dev")) {
sb.append("DEV_BUILD=\n")
}
new File(rootDir, "build.properties").text = sb.toString()
}
writePreprocessorDefinitions()
// Wire JVMDowngrader to process remapped jars
gradle.projectsEvaluated {
rootProject.subprojects.each {
if (it.tasks.findByName('remapJar') == null) return
it.tasks.downgradeJar.inputFile = it.tasks.remapJar.archiveFile
it.tasks.jar.finalizedBy(it.tasks.remapJar)
it.tasks.remapJar.finalizedBy(it.tasks.shadeDowngradedApi)
}
}
@@ -0,0 +1,13 @@
plugins {
id 'dh-loader'
}
unimined.minecraft {
fabric {
loader gradle.ext.fabric_loader_version
accessWidener project(":common").file("src/main/resources/${gradle.ext.accessWidenerVersion}.distanthorizons.accesswidener")
}
}
runClient.javaLauncher = null
runServer.javaLauncher = null
@@ -0,0 +1,17 @@
plugins {
id 'dh-loader'
}
def awFile = project(":common").file("src/main/resources/${gradle.ext.accessWidenerVersion}.distanthorizons.accesswidener")
unimined.minecraft {
forge {
loader gradle.ext.forge_version
useToolchains = false
mixinConfig("DistantHorizons.forge.mixins.json")
accessTransformer aw2at(awFile)
}
}
runClient.javaLauncher = null
runServer.javaLauncher = null
@@ -0,0 +1,16 @@
plugins {
id 'dh-loader'
}
def awFile = project(":common").file("src/main/resources/${gradle.ext.accessWidenerVersion}.distanthorizons.accesswidener")
unimined.minecraft {
neoForged {
loader gradle.ext.neoforge_version
useToolchains = false
accessTransformer aw2at(awFile)
}
}
runClient.javaLauncher = null
runServer.javaLauncher = null
+1 -39
View File
@@ -1,41 +1,3 @@
// temporary fix for broken spongepowered version
buildscript {
configurations.configureEach {
resolutionStrategy {
force 'org.spongepowered:vanillagradle:0.2.1-20240507.024226-82'
// newer versions can be found by going to the link:
// https://repo.spongepowered.org/#browse/browse:maven-public:org%2Fspongepowered%2Fvanillagradle%2F0.2.1-SNAPSHOT
}
}
}
plugins {
id "org.spongepowered.gradle.vanilla" version "0.2.1-SNAPSHOT"
id 'dh-loader'
}
minecraft {
accessWideners(project(":common").file("src/main/resources/${accessWidenerVersion}.distanthorizons.accesswidener"))
version(rootProject.minecraft_version)
}
dependencies {
// So mixins can be written in common
compileOnly group:'org.spongepowered', name:'mixin', version:'0.8.5'
}
publishing {
publications {
mavenCommon(MavenPublication) {
artifactId = rootProject.mod_readable_name
from components.java
}
}
// See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing.
repositories {
// Add repositories to publish to here.
}
}
@@ -1,6 +1,7 @@
package com.seibel.distanthorizons.common;
import com.mojang.brigadier.CommandDispatcher;
import com.seibel.distanthorizons.api.enums.config.EDhApiRenderApi;
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiAfterDhInitEvent;
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiBeforeDhInitEvent;
import com.seibel.distanthorizons.common.commands.CommandInitializer;
@@ -14,9 +15,17 @@ import com.seibel.distanthorizons.core.config.ConfigHandler;
import com.seibel.distanthorizons.core.config.eventHandlers.presets.ThreadPresetConfigEventHandler;
import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.enums.MinecraftTextFormat;
import com.seibel.distanthorizons.core.jar.ModJarInfo;
import com.seibel.distanthorizons.core.jar.updater.SelfUpdater;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.render.renderer.AbstractDebugWireframeRenderer;
import com.seibel.distanthorizons.core.render.renderer.StubDebugWireframeRenderer;
import com.seibel.distanthorizons.common.wrappers.gui.NativeDialogUtil;
import com.seibel.distanthorizons.core.util.ThreadUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.IVersionConstants;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IIrisAccessor;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IModAccessor;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IModChecker;
import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector;
@@ -26,7 +35,7 @@ import net.minecraft.server.MinecraftServer;
import net.minecraft.server.dedicated.DedicatedServer;
import com.seibel.distanthorizons.core.logging.DhLogger;
import java.lang.invoke.MethodHandles;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
import java.util.function.Supplier;
@@ -45,6 +54,7 @@ public abstract class AbstractModInitializer
//==================//
// abstract methods //
//==================//
//region
protected abstract void createInitialSharedBindings();
protected abstract void createInitialClientBindings();
@@ -58,11 +68,14 @@ public abstract class AbstractModInitializer
protected abstract void subscribeServerStartingEvent(Consumer<MinecraftServer> eventHandler);
protected abstract void runDelayedSetup();
//endregion
//===================//
// initialize events //
//===================//
//region
public void onInitializeClient()
{
@@ -93,6 +106,7 @@ public abstract class AbstractModInitializer
#endif
this.subscribeClientStartedEvent(this::postInit);
this.subscribeClientStartedEvent(this::postClientInit);
}
public void onInitializeServer()
@@ -124,6 +138,7 @@ public abstract class AbstractModInitializer
this.initConfig();
this.postInit();
this.postServerInit();
this.commandInitializer.onServerReady();
this.checkForUpdates();
@@ -132,11 +147,14 @@ public abstract class AbstractModInitializer
});
}
//endregion
//===========================//
// inner initializer methods //
//===========================//
//region
private void startup()
{
@@ -209,11 +227,44 @@ public abstract class AbstractModInitializer
ApiEventInjector.INSTANCE.fireAllEvents(DhApiAfterDhInitEvent.class, null);
}
private void postClientInit()
{
CompletableFuture<Void> future = new CompletableFuture<>();
// This method may be called from either the render thread,
// or some other random setup thread depending on the mod loader.
// In order to avoid confusion/inconsistent problems, we're always going
// to run setup on our own thread.
Thread dhSetupThread = new Thread(() ->
{
try
{
DependencySetup.setRenderingApiBindings();
}
catch (Exception e)
{
future.completeExceptionally(e);
}
finally
{
future.complete(null);
}
});
dhSetupThread.setName(ThreadUtil.THREAD_NAME_PREFIX + "PostClientInit Thread");
dhSetupThread.start();
future.join();
}
private void postServerInit() { SingletonInjector.INSTANCE.bind(AbstractDebugWireframeRenderer.class, new StubDebugWireframeRenderer()); }
//endregion
//==================================//
// mod partial compatibility checks //
//==================================//
//region
/**
* Some mods will work with a few tweaks
@@ -231,6 +282,7 @@ public abstract class AbstractModInitializer
// Alex's caves
//region
if (modChecker.isModLoaded("alexscaves"))
{
// There've been a few reports about this mod breaking DH at a few different points in time
@@ -240,16 +292,17 @@ public abstract class AbstractModInitializer
if (showChatWarnings)
{
String message =
// orange text
"\u00A76" + "Distant Horizons: Alex's Cave detected." + "\u00A7r\n" +
MinecraftTextFormat.ORANGE + "Distant Horizons: Alex's Cave detected." + MinecraftTextFormat.CLEAR_FORMATTING +
"You may have to change Alex's config for DH to render. ";
ClientApi.INSTANCE.showChatMessageNextFrame(message);
}
LOGGER.warn(startingString + "[Alex's Caves] may require some config changes in order to render Distant Horizons correctly.");
}
//endregion
// William Wythers' Overhauled Overworld (WWOO)
//region
if (modChecker.isModLoaded("wwoo"))
{
// WWOO has a bug with it's world gen that can't be fixed by DH or WWOO
@@ -263,16 +316,18 @@ public abstract class AbstractModInitializer
if (showChatWarnings)
{
String message =
// orange text
"\u00A76" + "Distant Horizons: WWOO detected." + "\u00A7r\n" +
MinecraftTextFormat.ORANGE + "Distant Horizons: WWOO detected." + MinecraftTextFormat.CLEAR_FORMATTING + "\n" +
wwooWarning;
ClientApi.INSTANCE.showChatMessageNextFrame(message);
}
LOGGER.warn(startingString + "[WWOO] "+ wwooWarning);
}
//endregion
// Chunky //
//region
// Chunky
boolean chunkyPresent = false;
try
{
@@ -294,8 +349,7 @@ public abstract class AbstractModInitializer
if (showChatWarnings)
{
String message =
// orange text
"\u00A76" + "Distant Horizons: Chunky detected." + "\u00A7r\n" +
MinecraftTextFormat.ORANGE + "Distant Horizons: Chunky detected." + MinecraftTextFormat.CLEAR_FORMATTING + "\n" +
chunkyWarning;
ClientApi.INSTANCE.showChatMessageNextFrame(message);
}
@@ -303,17 +357,56 @@ public abstract class AbstractModInitializer
LOGGER.warn(startingString + "[Chunky] "+ chunkyWarning);
}
//endregion
// iris //
//region
IIrisAccessor iris = ModAccessorInjector.INSTANCE.get(IIrisAccessor.class);
if (iris != null)
{
// get the currently selected rendering API
EDhApiRenderApi renderApi = Config.Client.Advanced.Graphics.Experimental.renderingApi.get();
if (renderApi == EDhApiRenderApi.AUTO)
{
IVersionConstants versionConstants = SingletonInjector.INSTANCE.get(IVersionConstants.class);
renderApi = versionConstants.getDefaultRenderingApi();
}
// Iris only supports native OpenGL
if (renderApi != EDhApiRenderApi.OPEN_GL)
{
String irisUnsupportedMessage = "Iris doesn't support DH when using the ["+EDhApiRenderApi.BLAZE_3D+"] rendering API, this will need to be fixed on Iris end. As a temporary fix please change the rendering API to ["+EDhApiRenderApi.OPEN_GL+"] in the DH config file.";
LOGGER.fatal(irisUnsupportedMessage);
NativeDialogUtil.showDialog(ModInfo.READABLE_NAME, irisUnsupportedMessage, "ok", "error");
IMinecraftClientWrapper mc = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
String errorMessage = "loading Distant Horizons. "+irisUnsupportedMessage;
String exceptionError = "Distant Horizons conditional mod config Exception";
mc.crashMinecraft(errorMessage, new Exception(exceptionError));
}
}
//endregion
}
//endregion
//================//
// helper classes //
//================//
//region
public interface IEventProxy
{
void registerEvents();
}
//endregion
}
@@ -4,14 +4,17 @@ import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
import com.seibel.distanthorizons.core.api.internal.SharedApi;
import com.seibel.distanthorizons.core.generation.PregenManager;
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos2D;
import com.seibel.distanthorizons.core.world.DhServerWorld;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.arguments.DimensionArgument;
import net.minecraft.commands.arguments.coordinates.ColumnPosArgument;
import net.minecraft.server.level.ColumnPos;
import net.minecraft.server.level.ServerLevel;
import java.util.Objects;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CompletableFuture;
@@ -22,7 +25,11 @@ import static net.minecraft.commands.Commands.literal;
public class PregenCommand extends AbstractCommand
{
private final PregenManager pregenManager = new PregenManager();
private PregenManager getPregenManager()
{
DhServerWorld world = (DhServerWorld) Objects.requireNonNull(SharedApi.getAbstractDhWorld());
return world.getPregenManager();
}
@Override
public LiteralArgumentBuilder<CommandSourceStack> buildCommand()
@@ -48,7 +55,7 @@ public class PregenCommand extends AbstractCommand
private int pregenStatus(CommandContext<CommandSourceStack> c)
{
String statusString = this.pregenManager.getStatusString();
String statusString = this.getPregenManager().getStatusString();
//noinspection ReplaceNullCheck
if (statusString != null)
{
@@ -68,7 +75,7 @@ public class PregenCommand extends AbstractCommand
ColumnPos origin = ColumnPosArgument.getColumnPos(c, "origin");
int chunkRadius = getInteger(c, "chunkRadius");
CompletableFuture<Void> future = this.pregenManager.startPregen(
CompletableFuture<Void> future = this.getPregenManager().startPregen(
ServerLevelWrapper.getWrapper(level),
new DhBlockPos2D(#if MC_VER >= MC_1_19_2 origin.x(), origin.z() #else origin.x, origin.z #endif),
chunkRadius
@@ -94,7 +101,7 @@ public class PregenCommand extends AbstractCommand
private int pregenStop(CommandContext<CommandSourceStack> c)
{
CompletableFuture<Void> runningPregen = this.pregenManager.getRunningPregen();
CompletableFuture<Void> runningPregen = this.getPregenManager().getRunningPregen();
if (runningPregen == null)
{
return this.sendFailureResponse(c, "Pregen is not running");
@@ -0,0 +1,88 @@
package com.seibel.distanthorizons.common.commonMixins;
import com.seibel.distanthorizons.api.enums.config.EDhApiUpdateBranch;
import com.seibel.distanthorizons.common.wrappers.gui.updater.UpdateModScreen;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.jar.installer.GitlabGetter;
import com.seibel.distanthorizons.core.jar.installer.ModrinthGetter;
import com.seibel.distanthorizons.core.jar.updater.SelfUpdater;
import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.wrapperInterfaces.IVersionConstants;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screens.TitleScreen;
import java.util.ArrayList;
public class DhUpdateScreenBase
{
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
private static final Minecraft MC = Minecraft.getInstance();
public static void tryShowUpdateScreenAndRunAutoUpdateStartup(Runnable runnable)
{
// always needs to be called, otherwise auto update setup won't be completed
boolean newUpdateAvailable = SelfUpdater.onStart();
if (!newUpdateAvailable)
{
return;
}
if (!Config.Client.Advanced.AutoUpdater.enableAutoUpdater.get())
{
LOGGER.info("Auto update disabled, ignoring new version...");
return;
}
runnable = () ->
{
String versionId;
EDhApiUpdateBranch updateBranch = EDhApiUpdateBranch.convertAutoToStableOrNightly(Config.Client.Advanced.AutoUpdater.updateBranch.get());
if (updateBranch == EDhApiUpdateBranch.STABLE)
{
versionId = ModrinthGetter.getLatestIDForVersion(SingletonInjector.INSTANCE.get(IVersionConstants.class).getMinecraftVersion());
}
else
{
ArrayList<com.electronwill.nightconfig.core.Config> pipelines = GitlabGetter.INSTANCE.projectPipelines;
if (pipelines != null
&& pipelines.size() > 0)
{
versionId = pipelines.get(0).get("sha");
}
else
{
versionId = null;
}
}
if (versionId == null)
{
LOGGER.info("Unable to find new DH update for the ["+updateBranch+"] branch. Assuming DH is up to date...");
return;
}
try
{
MC.setScreen(new UpdateModScreen(
new TitleScreen(false),
versionId
));
}
catch (Exception e)
{
// info instead of error since this can be ignored and probably just means
// there isn't a new DH version available
LOGGER.info("Unable to show DH update screen, reason: ["+e.getMessage()+"].");
}
};
runnable.run();
}
}
@@ -4,8 +4,11 @@ import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
import com.seibel.distanthorizons.core.api.internal.ServerApi;
import com.seibel.distanthorizons.core.api.internal.SharedApi;
import com.seibel.distanthorizons.core.pos.DhChunkPos;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ProtoChunk;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
public class MixinChunkMapCommon
@@ -13,8 +16,20 @@ public class MixinChunkMapCommon
public static void onChunkSave(ServerLevel level, ChunkAccess chunk, CallbackInfoReturnable<Boolean> ci)
{
IServerLevelWrapper levelWrapper = ServerLevelWrapper.getWrapper(level);
int chunkPosX;
int chunkPosZ;
#if MC_VER <= MC_1_21_11
chunkPosX = chunk.getPos().x;
chunkPosZ = chunk.getPos().z;
#else
chunkPosX = chunk.getPos().x();
chunkPosZ = chunk.getPos().z();
#endif
// is this position already being updated?
if (SharedApi.isChunkAtChunkPosAlreadyUpdating(chunk.getPos().x, chunk.getPos().z))
if (SharedApi.isChunkAtChunkPosAlreadyUpdating(levelWrapper, chunkPosX, chunkPosZ))
{
return;
}
@@ -29,23 +44,19 @@ public class MixinChunkMapCommon
return;
}
// TODO are the following validations necessary since we are checking above if
// the callback return value should state if the chunk was actually saved or not?
// Do we trust it to always be correct?
// corrupt/incomplete chunk validation //
// MC has a tendency to try saving incomplete or corrupted chunks (which show up as empty or black chunks)
// this logic should prevent that from happening
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
#if MC_VER <= MC_1_17_1
if (chunk.isUnsaved() || chunk.getUpgradeData() != null || !chunk.isLightCorrect())
{
return;
}
#else
if (chunk.isUnsaved() || chunk.isUpgrading() || !chunk.isLightCorrect())
if (chunk.isUnsaved() || chunk.isUpgrading() || !chunk.isLightCorrect() || chunk instanceof ProtoChunk)
{
return;
}
@@ -56,7 +67,7 @@ public class MixinChunkMapCommon
// biome validation //
// some chunks may be missing their biomes, which cause issues when attempting to save them
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
#if MC_VER <= MC_1_17_1
if (chunk.getBiomes() == null)
{
return;
@@ -77,8 +88,8 @@ public class MixinChunkMapCommon
// submit the update event
ServerApi.INSTANCE.serverChunkSaveEvent(
new ChunkWrapper(chunk, ServerLevelWrapper.getWrapper(level)),
ServerLevelWrapper.getWrapper(level)
new ChunkWrapper(chunk, levelWrapper),
levelWrapper
);
}
@@ -0,0 +1,331 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.render.blaze;
#if MC_VER <= MC_1_21_10
public class BlazeDebugWireframeRenderer {}
#else
import com.mojang.blaze3d.buffers.GpuBuffer;
import com.mojang.blaze3d.buffers.GpuBufferSlice;
import com.mojang.blaze3d.buffers.Std140Builder;
import com.mojang.blaze3d.buffers.Std140SizeCalculator;
import com.mojang.blaze3d.pipeline.RenderPipeline;
import com.mojang.blaze3d.platform.PolygonMode;
import com.mojang.blaze3d.shaders.UniformType;
import com.mojang.blaze3d.systems.CommandEncoder;
import com.mojang.blaze3d.systems.GpuDevice;
import com.mojang.blaze3d.systems.RenderPass;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.VertexFormat;
import com.seibel.distanthorizons.common.render.blaze.util.BlazeUniformUtil;
import com.seibel.distanthorizons.common.render.blaze.util.BlazeDhVertexFormatUtil;
import com.seibel.distanthorizons.common.render.blaze.wrappers.RenderPipelineBuilderWrapper;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.render.renderer.AbstractDebugWireframeRenderer;
import com.seibel.distanthorizons.core.util.math.Mat4f;
import com.seibel.distanthorizons.core.util.math.Vec3d;
import com.seibel.distanthorizons.core.util.math.Vec3f;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import net.minecraft.resources.Identifier;
import org.lwjgl.system.MemoryUtil;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.OptionalDouble;
import java.util.OptionalInt;
/**
* Handles rendering the wireframe particles
* that are used for seeing what the system's doing.
*/
public class BlazeDebugWireframeRenderer extends AbstractDebugWireframeRenderer
{
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
public static BlazeDebugWireframeRenderer INSTANCE = new BlazeDebugWireframeRenderer();
/** A box from 0,0,0 to 1,1,1 */
private static final float[] BOX_VERTICES = {
//region
// Pos x y z
0, 0, 0,
1, 0, 0,
1, 1, 0,
0, 1, 0,
0, 0, 1,
1, 0, 1,
1, 1, 1,
0, 1, 1,
//endregion
};
private static final int[] BOX_OUTLINE_INDICES = {
//region
0, 1,
1, 2,
2, 3,
3, 0,
4, 5,
5, 6,
6, 7,
7, 4,
0, 4,
1, 5,
2, 6,
3, 7,
//endregion
};
// rendering setup
private boolean init = false;
private RenderPipeline pipeline;
private GpuBuffer boxVertexBuffer;
private GpuBuffer boxIndexBuffer;
private GpuBuffer uniformBuffer;
//=============//
// constructor //
//=============//
//region
public BlazeDebugWireframeRenderer() { }
public void init()
{
if (this.init)
{
return;
}
this.init = true;
this.createPipelines();
this.createBuffers();
}
private void createPipelines()
{
RenderPipelineBuilderWrapper pipelineBuilder = new RenderPipelineBuilderWrapper();
{
pipelineBuilder.withFaceCulling(false);
pipelineBuilder.withDepthWrite(true);
pipelineBuilder.withDepthTest(RenderPipelineBuilderWrapper.EDhDepthTest.LESS);
pipelineBuilder.withColorWrite(true);
pipelineBuilder.withoutBlend();
pipelineBuilder.withPolygonMode(RenderPipelineBuilderWrapper.EDhPolygonMode.WIREFRAME);
pipelineBuilder.withName("debug_wireframe_renderer");
pipelineBuilder.withVertexShader("debug/blaze/vert");
pipelineBuilder.withFragmentShader("debug/blaze/frag");
pipelineBuilder.withUniformBuffer("uniformBlock");
VertexFormat vertexFormat = VertexFormat.builder()
.add("vPosition", BlazeDhVertexFormatUtil.FLOAT_XYZ_POS)
.build();
pipelineBuilder.withVertexFormat(vertexFormat);
pipelineBuilder.withVertexMode(RenderPipelineBuilderWrapper.EDhVertexMode.LINES);
}
this.pipeline = pipelineBuilder.build();
}
private void createBuffers()
{
GpuDevice GPU_DEVICE = RenderSystem.getDevice();
CommandEncoder COMMAND_ENCODER = GPU_DEVICE.createCommandEncoder();
// box vertices
ByteBuffer boxVerticesBuffer = MemoryUtil.memAlloc(BOX_VERTICES.length * Float.BYTES);
boxVerticesBuffer.asFloatBuffer().put(BOX_VERTICES);
boxVerticesBuffer.rewind();
MemoryUtil.memFree(boxVerticesBuffer);
// upload vertex data
{
int usage = GpuBuffer.USAGE_COPY_DST
| GpuBuffer.USAGE_VERTEX;
int size = BOX_VERTICES.length * Float.BYTES;
this.boxVertexBuffer = GPU_DEVICE.createBuffer(() -> "distantHorizons:McDebugWireframeBox", usage, size);
{
int length = BOX_VERTICES.length * Float.BYTES;
GpuBufferSlice bufferSlice = new GpuBufferSlice(this.boxVertexBuffer, /*offset*/ 0, length);
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(BOX_VERTICES.length * Float.BYTES);
byteBuffer.order(ByteOrder.nativeOrder());
byteBuffer.asFloatBuffer().put(BOX_VERTICES);
byteBuffer.rewind();
COMMAND_ENCODER.writeToBuffer(bufferSlice, byteBuffer);
}
}
// box vertex indexes
{
ByteBuffer buffer = ByteBuffer.allocateDirect(BOX_OUTLINE_INDICES.length * Integer.BYTES);
buffer.order(ByteOrder.nativeOrder());
buffer.asIntBuffer().put(BOX_OUTLINE_INDICES);
buffer.rewind();
int usage = GpuBuffer.USAGE_COPY_DST
| GpuBuffer.USAGE_VERTEX
| GpuBuffer.USAGE_INDEX
| GpuBuffer.USAGE_UNIFORM;
this.boxIndexBuffer = GPU_DEVICE.createBuffer(() -> "DH Debug Index Buffer", usage, buffer.capacity());
int offset = 0;
GpuBufferSlice bufferSlice = new GpuBufferSlice(this.boxIndexBuffer, offset, buffer.capacity());
COMMAND_ENCODER.writeToBuffer(bufferSlice, buffer);
}
}
//endregion
//===========//
// rendering //
//===========//
//region
@Override
public void renderBox(Box box)
{
this.init();
//if (BlazeDhMetaRenderer.INSTANCE.dhColorTextureWrapper.isEmpty()
// || BlazeDhMetaRenderer.INSTANCE.dhDepthTextureWrapper.isEmpty())
//{
// return;
//}
// shouldn't happen, but just in case
if (box == null)
{
return;
}
// delayed getters since this class may be initialized before
// the GPU device has been set
GpuDevice gpuDevice = RenderSystem.getDevice();
CommandEncoder commandEncoder = gpuDevice.createCommandEncoder();
// uniforms
{
int uniformBufferSize = new Std140SizeCalculator()
.putMat4f() // uTransform
.putVec4() // uColor
.get();
// create data //
Vec3d camPos = MC_RENDER.getCameraExactPosition();
Vec3f camPosFloatThisFrame = new Vec3f((float) camPos.x, (float) camPos.y, (float) camPos.z);
Mat4f boxTransform = Mat4f.createTranslateMatrix(
box.minPos.x - camPosFloatThisFrame.x,
box.minPos.y - camPosFloatThisFrame.y,
box.minPos.z - camPosFloatThisFrame.z);
boxTransform.multiply(Mat4f.createScaleMatrix(
box.maxPos.x - box.minPos.x,
box.maxPos.y - box.minPos.y,
box.maxPos.z - box.minPos.z));
Mat4f transformMatrix = this.dhMvmProjMatrixThisFrame.copy();
transformMatrix.multiply(boxTransform);
// upload data //
ByteBuffer buffer = ByteBuffer.allocateDirect(uniformBufferSize);
buffer.order(ByteOrder.nativeOrder());
buffer = Std140Builder.intoBuffer(buffer)
.putMat4f(transformMatrix.createJomlMatrix()) // uTransform
.putVec4(
box.color.getRed() / 255.0f,
box.color.getGreen() / 255.0f,
box.color.getBlue() / 255.0f,
box.color.getAlpha() / 255.0f) // uColor
.get()
;
this.uniformBuffer = BlazeUniformUtil.createBuffer("uniformBlock", uniformBufferSize, this.uniformBuffer);
GpuBufferSlice bufferSlice = new GpuBufferSlice(this.uniformBuffer, 0, uniformBufferSize);
commandEncoder.writeToBuffer(bufferSlice, buffer);
}
// render //
//try (RenderPass renderPass = commandEncoder.createRenderPass(
// this::getRenderPassName,
// BlazeDhMetaRenderer.INSTANCE.dhColorTextureWrapper.textureView,
// /*optionalClearColorAsInt*/ OptionalInt.empty(),
// BlazeDhMetaRenderer.INSTANCE.dhDepthTextureWrapper.textureView,
// /*optionalDepthValueAsDouble*/ OptionalDouble.empty()))
//{
// // Bind instance data //
// renderPass.setUniform("uniformBlock", this.uniformBuffer);
//
// renderPass.setPipeline(this.pipeline);
// renderPass.setIndexBuffer(this.boxIndexBuffer, VertexFormat.IndexType.INT);
//
// renderPass.setVertexBuffer(0, this.boxVertexBuffer);
//
// renderPass.drawIndexed(
// /*indexStart*/ 0,
// /*firstIndex*/0,
// /*indexCount*/BOX_OUTLINE_INDICES.length,
// /*instanceCount*/1);
//}
}
private String getRenderPassName() { return "distantHorizons:McDebugRenderer"; }
//endregion
}
#endif
@@ -0,0 +1,619 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.render.blaze;
#if MC_VER <= MC_1_21_10
public class BlazeDhGenericObjectRenderer {}
#else
import com.mojang.blaze3d.buffers.GpuBuffer;
import com.mojang.blaze3d.buffers.GpuBufferSlice;
import com.mojang.blaze3d.buffers.Std140Builder;
import com.mojang.blaze3d.buffers.Std140SizeCalculator;
import com.mojang.blaze3d.pipeline.BlendFunction;
import com.mojang.blaze3d.pipeline.RenderPipeline;
import com.mojang.blaze3d.platform.PolygonMode;
import com.mojang.blaze3d.shaders.UniformType;
import com.mojang.blaze3d.systems.CommandEncoder;
import com.mojang.blaze3d.systems.GpuDevice;
import com.mojang.blaze3d.systems.RenderPass;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.VertexFormat;
import com.seibel.distanthorizons.api.enums.rendering.EDhApiBlockMaterial;
import com.seibel.distanthorizons.api.interfaces.render.IDhApiCustomRenderRegister;
import com.seibel.distanthorizons.api.interfaces.render.IDhApiRenderableBoxGroup;
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiBeforeGenericObjectRenderEvent;
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiBeforeGenericRenderCleanupEvent;
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiBeforeGenericRenderSetupEvent;
import com.seibel.distanthorizons.api.objects.math.DhApiVec3d;
import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBox;
import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBoxGroupShading;
import com.seibel.distanthorizons.common.render.blaze.objects.BlazeGenericObjectVertexContainer;
import com.seibel.distanthorizons.common.render.blaze.util.BlazeDhVertexFormatUtil;
import com.seibel.distanthorizons.common.render.blaze.wrappers.RenderPipelineBuilderWrapper;
import com.seibel.distanthorizons.common.render.blaze.wrappers.texture.BlazeTextureViewWrapper;
import com.seibel.distanthorizons.common.render.blaze.util.BlazeUniformUtil;
import com.seibel.distanthorizons.common.wrappers.misc.LightMapWrapper;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.logging.f3.F3Screen;
import com.seibel.distanthorizons.core.render.RenderParams;
import com.seibel.distanthorizons.core.render.renderer.GenericRenderObjectFactory;
import com.seibel.distanthorizons.core.wrapperInterfaces.render.objects.IDhGenericObjectVertexBufferContainer;
import com.seibel.distanthorizons.core.render.renderer.RenderableBoxGroup;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.util.math.Mat4f;
import com.seibel.distanthorizons.core.util.math.Vec3d;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhGenericRenderer;
import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector;
import com.seibel.distanthorizons.coreapi.ModInfo;
import net.minecraft.resources.Identifier;
import java.awt.*;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Collection;
import java.util.OptionalDouble;
import java.util.OptionalInt;
import java.util.concurrent.ConcurrentHashMap;
/**
* Handles rendering generic groups of {@link DhApiRenderableBox}.
*
* @see IDhApiCustomRenderRegister
* @see DhApiRenderableBox
*/
public class BlazeDhGenericObjectRenderer implements IDhGenericRenderer
{
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
private static final GpuDevice GPU_DEVICE = RenderSystem.getDevice();
private static final CommandEncoder COMMAND_ENCODER = GPU_DEVICE.createCommandEncoder();
private static final DhApiRenderableBoxGroupShading DEFAULT_SHADING = DhApiRenderableBoxGroupShading.getUnshaded();
/**
* Can be used to troubleshoot the renderer.
* If enabled several debug objects will render around (0,150,0).
*/
public static final boolean RENDER_DEBUG_OBJECTS = false;
private final ConcurrentHashMap<Long, RenderableBoxGroup> boxGroupById = new ConcurrentHashMap<>();
// rendering setup
private boolean init = false;
private RenderPipeline pipeline;
private GpuBuffer vertUniformBuffer;
//=============//
// constructor //
//=============//
//region
public BlazeDhGenericObjectRenderer() { }
public void init()
{
if (this.init)
{
return;
}
this.init = true;
this.createPipelines();
if (RENDER_DEBUG_OBJECTS)
{
this.addGenericDebugObjects();
}
}
private void createPipelines()
{
RenderPipelineBuilderWrapper pipelineBuilder = new RenderPipelineBuilderWrapper();
{
pipelineBuilder.withFaceCulling(true);
pipelineBuilder.withDepthWrite(true);
pipelineBuilder.withDepthTest(RenderPipelineBuilderWrapper.EDhDepthTest.LESS);
pipelineBuilder.withBlend(BlendFunction.TRANSLUCENT); // TRANSLUCENT = new BlendFunction(SourceFactor.SRC_ALPHA, DestFactor.ONE_MINUS_SRC_ALPHA, SourceFactor.ONE, DestFactor.ONE_MINUS_SRC_ALPHA);
pipelineBuilder.withColorWrite(true);
pipelineBuilder.withPolygonMode(RenderPipelineBuilderWrapper.EDhPolygonMode.FILL);
pipelineBuilder.withName("generic_objects");
pipelineBuilder.withVertexShader("generic/blaze/vert");
pipelineBuilder.withFragmentShader("generic/blaze/frag");
pipelineBuilder.withSampler("uLightMap");
pipelineBuilder.withUniformBuffer("vertUniformBlock");
VertexFormat vertexFormat = VertexFormat.builder()
.add("vPosition", BlazeDhVertexFormatUtil.FLOAT_XYZ_POS)
.add("aColor", BlazeDhVertexFormatUtil.RGBA_UBYTE_COLOR)
.add("aMaterial", BlazeDhVertexFormatUtil.IRIS_MATERIAL)
.add("paddingOne", BlazeDhVertexFormatUtil.BYTE_PAD)
.add("paddingTwo", BlazeDhVertexFormatUtil.BYTE_PAD)
.add("paddingThree", BlazeDhVertexFormatUtil.BYTE_PAD)
.build();
pipelineBuilder.withVertexFormat(vertexFormat);
pipelineBuilder.withVertexMode(RenderPipelineBuilderWrapper.EDhVertexMode.TRIANGLES);
}
this.pipeline = pipelineBuilder.build();
}
private void addGenericDebugObjects()
{
GenericRenderObjectFactory factory = GenericRenderObjectFactory.INSTANCE;
// single giant box
IDhApiRenderableBoxGroup singleGiantBoxGroup = factory.createForSingleBox(
ModInfo.NAME + ":CyanChunkBox",
new DhApiRenderableBox(
new DhApiVec3d(0,0,0), new DhApiVec3d(16,190,16),
new Color(Color.CYAN.getRed(), Color.CYAN.getGreen(), Color.CYAN.getBlue(), 125),
EDhApiBlockMaterial.WATER)
);
singleGiantBoxGroup.setSkyLight(LodUtil.MAX_MC_LIGHT);
singleGiantBoxGroup.setBlockLight(LodUtil.MAX_MC_LIGHT);
this.add(singleGiantBoxGroup);
// single slender box
IDhApiRenderableBoxGroup singleTallBoxGroup = factory.createForSingleBox(
ModInfo.NAME + ":GreenBeacon",
new DhApiRenderableBox(
new DhApiVec3d(16,0,31), new DhApiVec3d(17,2000,32),
new Color(Color.GREEN.getRed(), Color.GREEN.getGreen(), Color.GREEN.getBlue(), 125),
EDhApiBlockMaterial.ILLUMINATED)
);
singleTallBoxGroup.setSkyLight(LodUtil.MAX_MC_LIGHT);
singleTallBoxGroup.setBlockLight(LodUtil.MAX_MC_LIGHT);
this.add(singleTallBoxGroup);
// absolute box group
ArrayList<DhApiRenderableBox> absBoxList = new ArrayList<>();
for (int i = 0; i < 18; i++)
{
absBoxList.add(new DhApiRenderableBox(
new DhApiVec3d(i,150+i,24), new DhApiVec3d(1+i,151+i,25),
new Color(Color.ORANGE.getRed(), Color.ORANGE.getGreen(), Color.ORANGE.getBlue()),
EDhApiBlockMaterial.LAVA
)
);
}
IDhApiRenderableBoxGroup absolutePosBoxGroup = factory.createAbsolutePositionedGroup(ModInfo.NAME + ":OrangeStairs", absBoxList);
this.add(absolutePosBoxGroup);
// relative box group
ArrayList<DhApiRenderableBox> relBoxList = new ArrayList<>();
for (int i = 0; i < 8; i+=2)
{
relBoxList.add(new DhApiRenderableBox(
new DhApiVec3d(0,i,0), new DhApiVec3d(1,1+i,1),
new Color(Color.MAGENTA.getRed(), Color.MAGENTA.getGreen(), Color.MAGENTA.getBlue()),
EDhApiBlockMaterial.METAL
)
);
}
IDhApiRenderableBoxGroup relativePosBoxGroup = factory.createRelativePositionedGroup(
ModInfo.NAME + ":MovingMagentaGroup",
new DhApiVec3d(24, 140, 24),
relBoxList);
relativePosBoxGroup.setPreRenderFunc((event) ->
{
DhApiVec3d pos = relativePosBoxGroup.getOriginBlockPos();
pos.x += event.partialTicks / 2;
pos.x %= 32;
relativePosBoxGroup.setOriginBlockPos(pos);
});
this.add(relativePosBoxGroup);
// massive relative box group
ArrayList<DhApiRenderableBox> massRelBoxList = new ArrayList<>();
for (int x = 0; x < 50*2; x+=2)
{
for (int z = 0; z < 50*2; z+=2)
{
massRelBoxList.add(new DhApiRenderableBox(
new DhApiVec3d(-x, 0, -z), new DhApiVec3d(1-x, 1, 1-z),
new Color(Color.RED.getRed(), Color.RED.getGreen(), Color.RED.getBlue()),
EDhApiBlockMaterial.TERRACOTTA
)
);
}
}
IDhApiRenderableBoxGroup massRelativePosBoxGroup = factory.createRelativePositionedGroup(
ModInfo.NAME + ":MassRedGroup",
new DhApiVec3d(-25, 140, 0),
massRelBoxList);
massRelativePosBoxGroup.setPreRenderFunc((event) ->
{
DhApiVec3d blockPos = massRelativePosBoxGroup.getOriginBlockPos();
blockPos.y += event.partialTicks / 4;
if (blockPos.y > 150f)
{
blockPos.y = 140f;
Color newColor = (massRelativePosBoxGroup.get(0).color == Color.RED) ? Color.RED.darker() : Color.RED;
massRelativePosBoxGroup.forEach((box) -> { box.color = newColor; });
massRelativePosBoxGroup.triggerBoxChange();
}
massRelativePosBoxGroup.setOriginBlockPos(blockPos);
});
this.add(massRelativePosBoxGroup);
}
//endregion
//==============//
// registration //
//==============//
//region
@Override
public void add(IDhApiRenderableBoxGroup iBoxGroup) throws IllegalArgumentException
{
if (!(iBoxGroup instanceof RenderableBoxGroup))
{
throw new IllegalArgumentException("Box group must be of type ["+ RenderableBoxGroup.class.getSimpleName()+"], type received: ["+(iBoxGroup != null ? iBoxGroup.getClass() : "NULL")+"].");
}
RenderableBoxGroup boxGroup = (RenderableBoxGroup) iBoxGroup;
if (boxGroup.size() != 0)
{
// trigger a box change to make sure the initial data is uploaded
boxGroup.triggerBoxChange();
}
long id = boxGroup.getId();
if (this.boxGroupById.containsKey(id))
{
throw new IllegalArgumentException("A box group with the ID [" + id + "] is already present.");
}
this.boxGroupById.put(id, boxGroup);
}
@Override
public IDhApiRenderableBoxGroup remove(long id) { return this.boxGroupById.remove(id); }
public void clear() { this.boxGroupById.clear(); }
//endregion
//===========//
// rendering //
//===========//
//region
/**
* @param renderingWithSsao
* if true that means this render call is happening before the SSAO pass
* and any objects rendered in this pass will have SSAO applied to them.
*/
@Override
public void render(RenderParams renderEventParam, IProfilerWrapper profiler, boolean renderingWithSsao)
{
try (IProfilerWrapper.IProfileBlock generic_profile = profiler.push("setup"))
{
//==============//
// render setup //
//==============//
//#region
this.init();
ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeGenericRenderSetupEvent.class, renderEventParam);
Vec3d camPos = MC_RENDER.getCameraExactPosition();
//#endregion
if (BlazeDhMetaRenderer.INSTANCE.dhColorTextureWrapper.isEmpty()
|| BlazeDhMetaRenderer.INSTANCE.dhDepthTextureWrapper.isEmpty())
{
return;
}
//===========//
// rendering //
//===========//
//#region
Collection<RenderableBoxGroup> boxList = this.boxGroupById.values();
for (RenderableBoxGroup boxGroup : boxList)
{
// validation //
// shouldn't happen, but just in case
if (boxGroup == null)
{
continue;
}
// skip boxes that shouldn't render this pass
if (boxGroup.ssaoEnabled != renderingWithSsao)
{
continue;
}
profiler.popPush("render prep");
boxGroup.preRender(renderEventParam); // called even if the group is inactive, so the group can be activate if desired
// ignore inactive groups
if (!boxGroup.active)
{
continue;
}
// allow API users to cancel this object's rendering
boolean cancelRendering = ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeGenericObjectRenderEvent.class, new DhApiBeforeGenericObjectRenderEvent.EventParam(renderEventParam, boxGroup));
if (cancelRendering)
{
continue;
}
// update instanced data if needed
{
boxGroup.tryUpdateInstancedDataAsync();
// skip groups that haven't been uploaded yet
if (boxGroup.vertexBufferContainer.getState() != IDhGenericObjectVertexBufferContainer.EState.RENDER)
{
continue;
}
}
DhApiRenderableBoxGroupShading shading = boxGroup.shading;
if (shading == null)
{
shading = DEFAULT_SHADING;
}
// uniforms
{
int uniformBufferSize = new Std140SizeCalculator()
.putIVec3() // uOffsetChunk
.putVec3() // uOffsetSubChunk
.putIVec3() // uCameraPosChunk
.putVec3() // uCameraPosSubChunk
.putVec3() // aTranslateChunk
.putVec3() // aTranslateSubChunk
.putMat4f() // uProjectionMvm
.putInt() // uSkyLight
.putInt() // uBlockLight
.putFloat() // uNorthShading
.putFloat() // uSouthShading
.putFloat() // uEastShading
.putFloat() // uWestShading
.putFloat() // uTopShading
.putFloat() // uBottomShading
.get();
// create data //
Mat4f projectionMvmMatrix = new Mat4f(renderEventParam.dhProjectionMatrix);
projectionMvmMatrix.multiply(renderEventParam.dhModelViewMatrix);
// upload data //
ByteBuffer buffer = ByteBuffer.allocateDirect(uniformBufferSize);
buffer.order(ByteOrder.nativeOrder());
buffer = Std140Builder.intoBuffer(buffer)
.putIVec3(
LodUtil.getChunkPosFromDouble(boxGroup.getOriginBlockPos().x),
LodUtil.getChunkPosFromDouble(boxGroup.getOriginBlockPos().y),
LodUtil.getChunkPosFromDouble(boxGroup.getOriginBlockPos().z)
) // uOffsetChunk
.putVec3(
LodUtil.getSubChunkPosFromDouble(boxGroup.getOriginBlockPos().x),
LodUtil.getSubChunkPosFromDouble(boxGroup.getOriginBlockPos().y),
LodUtil.getSubChunkPosFromDouble(boxGroup.getOriginBlockPos().z)
) // uOffsetSubChunk
.putIVec3(
LodUtil.getChunkPosFromDouble(camPos.x),
LodUtil.getChunkPosFromDouble(camPos.y),
LodUtil.getChunkPosFromDouble(camPos.z)
) // uCameraPosChunk
.putVec3(
LodUtil.getSubChunkPosFromDouble(camPos.x),
LodUtil.getSubChunkPosFromDouble(camPos.y),
LodUtil.getSubChunkPosFromDouble(camPos.z)
) // uCameraPosSubChunk
.putMat4f(projectionMvmMatrix.createJomlMatrix()) // uProjectionMvm
.putInt(boxGroup.getSkyLight()) // uSkyLight
.putInt(boxGroup.getBlockLight()) // uBlockLight
.putFloat(shading.north)
.putFloat(shading.south)
.putFloat(shading.east)
.putFloat(shading.west)
.putFloat(shading.top)
.putFloat(shading.bottom)
.get()
;
this.vertUniformBuffer = BlazeUniformUtil.createBuffer("vertUniformBlock", uniformBufferSize, this.vertUniformBuffer);
GpuBufferSlice bufferSlice = new GpuBufferSlice(this.vertUniformBuffer, 0, uniformBufferSize);
COMMAND_ENCODER.writeToBuffer(bufferSlice, buffer);
}
// render //
profiler.popPush("rendering");
try (IProfilerWrapper.IProfileBlock namespace_profile = profiler.push(boxGroup.getResourceLocationNamespace());
IProfilerWrapper.IProfileBlock location_profile = profiler.push(boxGroup.getResourceLocationPath()))
{
this.renderBoxGroupInstanced(renderEventParam, boxGroup, profiler);
}
boxGroup.postRender(renderEventParam);
}
//#endregion
//==========//
// clean up //
//==========//
//region
profiler.popPush("cleanup");
ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeGenericRenderCleanupEvent.class, renderEventParam);
//endregion
}
}
private String getRenderPassName() { return "distantHorizons:McGenericObjectRenderer"; }
//endregion
//=====================//
// instanced rendering //
//=====================//
//region
private void renderBoxGroupInstanced(
RenderParams renderEventParam,
RenderableBoxGroup boxGroup,
IProfilerWrapper profiler)
{
try (RenderPass renderPass = COMMAND_ENCODER.createRenderPass(
this::getRenderPassName,
BlazeDhMetaRenderer.INSTANCE.dhColorTextureWrapper.textureView,
/*optionalClearColorAsInt*/ OptionalInt.empty(),
BlazeDhMetaRenderer.INSTANCE.dhDepthTextureWrapper.textureView,
/*optionalDepthValueAsDouble*/ OptionalDouble.empty()))
{
// update instance data //
BlazeGenericObjectVertexContainer container = (BlazeGenericObjectVertexContainer) boxGroup.vertexBufferContainer;
LightMapWrapper lightMapWrapper = (LightMapWrapper) renderEventParam.lightmap;
BlazeTextureViewWrapper lightmapTextureViewWrapper = lightMapWrapper.getTextureViewWrapper();
renderPass.bindTexture("uLightMap", lightmapTextureViewWrapper.textureView, lightmapTextureViewWrapper.textureSampler);
// Bind instance data //
renderPass.setUniform("vertUniformBlock", this.vertUniformBuffer);
// set pipeline
renderPass.setPipeline(this.pipeline);
renderPass.setIndexBuffer(container.indexGpuBuffer, VertexFormat.IndexType.INT);
renderPass.setVertexBuffer(0, container.vboGpuBuffer);
// Draw instanced
if (container.uploadedBoxCount > 0)
{
renderPass.drawIndexed(
/*indexStart*/ 0,
/*firstIndex*/0,
/*indexCount*/container.uploadedBoxCount * 36, // 36 = 6 faces * 6 verticies per face
/*instanceCount*/1);
}
}
}
//endregion
//=========//
// F3 menu //
//=========//
//region
public String getVboRenderDebugMenuString()
{
// get counts
int totalGroupCount = this.boxGroupById.size();
int totalBoxCount = 0;
int activeGroupCount = 0;
int activeBoxCount = 0;
for (long key : this.boxGroupById.keySet())
{
RenderableBoxGroup renderGroup = this.boxGroupById.get(key);
if (renderGroup.active)
{
activeGroupCount++;
activeBoxCount += renderGroup.size();
}
totalBoxCount += renderGroup.size();
}
return "Generic Obj #: " + F3Screen.NUMBER_FORMAT.format(activeGroupCount) + "/" + F3Screen.NUMBER_FORMAT.format(totalGroupCount) + ", " +
"Cube #: " + F3Screen.NUMBER_FORMAT.format(activeBoxCount) + "/" + F3Screen.NUMBER_FORMAT.format(totalBoxCount);
}
//endregion
}
#endif
@@ -0,0 +1,115 @@
package com.seibel.distanthorizons.common.render.blaze;
#if MC_VER <= MC_1_21_10
public class BlazeDhMetaRenderer {}
#else
import com.mojang.blaze3d.textures.GpuTexture;
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiAfterColorDepthTextureCreatedEvent;
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiTextureCreatedParam;
import com.seibel.distanthorizons.common.render.blaze.apply.BlazeDhApplyRenderer;
import com.seibel.distanthorizons.common.render.blaze.wrappers.texture.BlazeTextureWrapper;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.render.RenderParams;
import com.seibel.distanthorizons.coreapi.util.ColorUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhMetaRenderer;
import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector;
import net.minecraft.client.Minecraft;
import java.awt.*;
public class BlazeDhMetaRenderer implements IDhMetaRenderer
{
public static final BlazeDhMetaRenderer INSTANCE = new BlazeDhMetaRenderer();
private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
private BlazeDhApplyRenderer applyRenderer;
public final BlazeTextureWrapper dhDepthTextureWrapper = BlazeTextureWrapper.createDepth("DhDepthTexture");
public final BlazeTextureWrapper dhColorTextureWrapper = BlazeTextureWrapper.createColor("DhColorTexture");
//=============//
// constructor //
//=============//
//region
private BlazeDhMetaRenderer()
{
this.applyRenderer = new BlazeDhApplyRenderer(
"dh_apply_to_mc",
null,
"apply/blaze/vert", "apply/blaze/frag"
);
}
//endregion
//=================//
// pre/post render //
//=================//
//region
@Override
public void runRenderPassSetup(RenderParams renderParams)
{
int oldWidth = this.dhDepthTextureWrapper.getWidth();
int oldHeight = this.dhDepthTextureWrapper.getHeight();
boolean texturesChanged = false;
texturesChanged = this.dhDepthTextureWrapper.tryCreateOrResize() | texturesChanged;
texturesChanged = this.dhColorTextureWrapper.tryCreateOrResize() | texturesChanged;
if (texturesChanged)
{
int newTextureWidth = MC_RENDER.getTargetFramebufferViewportWidth();
int newTextureHeight = MC_RENDER.getTargetFramebufferViewportHeight();
DhApiTextureCreatedParam textureCreatedParam = new DhApiTextureCreatedParam(
oldWidth, oldHeight,
newTextureWidth, newTextureHeight
);
ApiEventInjector.INSTANCE.fireAllEvents(DhApiAfterColorDepthTextureCreatedEvent.class, textureCreatedParam);
}
}
@Override
public void runRenderPassCleanup(RenderParams renderParams) {}
@Override
public void applyToMcTexture(RenderParams renderParams)
{
GpuTexture mcColorTexture = Minecraft.getInstance().getMainRenderTarget().getColorTexture();
this.applyRenderer.render(this.dhColorTextureWrapper.texture, this.dhDepthTextureWrapper.texture, mcColorTexture);
}
//endregion
//================//
// clear textures //
//================//
//region
@Override
public void clearDhDepthAndColorTextures(RenderParams renderParams)
{
this.dhDepthTextureWrapper.clearDepth(1.0f);
Color color = MC_RENDER.getSkyColor();
this.dhColorTextureWrapper.clearColor(ColorUtil.toColorInt(color));
}
//endregion
}
#endif
@@ -0,0 +1,71 @@
package com.seibel.distanthorizons.common.render.blaze;
#if MC_VER <= MC_1_21_10
public class BlazeDhRenderApiDefinition {}
#else
import com.seibel.distanthorizons.common.render.blaze.objects.BlazeGenericObjectVertexContainer;
import com.seibel.distanthorizons.common.render.blaze.postProcessing.BlazeDhFarFadeRenderer;
import com.seibel.distanthorizons.common.render.blaze.postProcessing.BlazeDhFogRenderer;
import com.seibel.distanthorizons.common.render.blaze.postProcessing.BlazeDhSsaoRenderer;
import com.seibel.distanthorizons.common.render.blaze.postProcessing.BlazeVanillaFadeRenderer;
import com.seibel.distanthorizons.common.render.blaze.test.BlazeDhTestTriangleRenderer;
import com.seibel.distanthorizons.common.render.blaze.wrappers.buffer.BlazeVertexBufferWrapper;
import com.seibel.distanthorizons.common.render.blaze.wrappers.uniform.BlazeLodUniformBufferWrapper;
import com.seibel.distanthorizons.core.render.renderer.AbstractDebugWireframeRenderer;
import com.seibel.distanthorizons.core.wrapperInterfaces.render.AbstractDhRenderApiDefinition;
import com.seibel.distanthorizons.core.wrapperInterfaces.render.objects.IDhGenericObjectVertexBufferContainer;
import com.seibel.distanthorizons.core.wrapperInterfaces.render.objects.ILodContainerUniformBufferWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.render.objects.IVertexBufferWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.*;
public class BlazeDhRenderApiDefinition extends AbstractDhRenderApiDefinition
{
//=========//
// getters //
//=========//
//region
public String getApiName() { return "Blaze3D"; }
//endregion
//============//
// singletons //
//============//
//region
@Override public IDhMetaRenderer getMetaRenderer() { return BlazeDhMetaRenderer.INSTANCE; }
@Override public IDhTerrainRenderer getTerrainRenderer() { return BlazeDhTerrainRenderer.INSTANCE; }
@Override public IDhSsaoRenderer getSsaoRenderer() { return BlazeDhSsaoRenderer.INSTANCE; }
@Override public IDhFogRenderer getFogRenderer() { return BlazeDhFogRenderer.INSTANCE; }
@Override public IDhFarFadeRenderer getFarFadeRenderer() { return BlazeDhFarFadeRenderer.INSTANCE; }
@Override public AbstractDebugWireframeRenderer getDebugWireframeRenderer() { return BlazeDebugWireframeRenderer.INSTANCE; }
@Override public IDhVanillaFadeRenderer getVanillaFadeRenderer() { return BlazeVanillaFadeRenderer.INSTANCE; }
@Override public IDhTestTriangleRenderer getTestTriangleRenderer() { return BlazeDhTestTriangleRenderer.INSTANCE; }
//endregion
//===========//
// factories //
//===========//
//region
@Override public IDhGenericRenderer createGenericRenderer() { return new BlazeDhGenericObjectRenderer(); }
@Override public IVertexBufferWrapper createVboWrapper(String name) { return new BlazeVertexBufferWrapper(name); }
@Override public ILodContainerUniformBufferWrapper createLodContainerUniformWrapper() { return new BlazeLodUniformBufferWrapper(); }
@Override public IDhGenericObjectVertexBufferContainer createGenericVboContainer() { return new BlazeGenericObjectVertexContainer(); }
//endregion
}
#endif
@@ -0,0 +1,363 @@
package com.seibel.distanthorizons.common.render.blaze;
#if MC_VER <= MC_1_21_10
public class BlazeDhTerrainRenderer {}
#else
import com.mojang.blaze3d.buffers.GpuBuffer;
import com.mojang.blaze3d.buffers.GpuBufferSlice;
import com.mojang.blaze3d.buffers.Std140Builder;
import com.mojang.blaze3d.buffers.Std140SizeCalculator;
import com.mojang.blaze3d.pipeline.BlendFunction;
import com.mojang.blaze3d.pipeline.RenderPipeline;
import com.mojang.blaze3d.platform.PolygonMode;
import com.mojang.blaze3d.shaders.UniformType;
import com.mojang.blaze3d.systems.CommandEncoder;
import com.mojang.blaze3d.systems.GpuDevice;
import com.mojang.blaze3d.systems.RenderPass;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.VertexFormat;
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiBeforeBufferRenderEvent;
import com.seibel.distanthorizons.common.render.blaze.util.BlazeDhVertexFormatUtil;
import com.seibel.distanthorizons.common.render.blaze.util.BlazeUniformUtil;
import com.seibel.distanthorizons.common.render.blaze.wrappers.RenderPipelineBuilderWrapper;
import com.seibel.distanthorizons.common.render.blaze.wrappers.texture.BlazeTextureViewWrapper;
import com.seibel.distanthorizons.common.render.blaze.wrappers.uniform.BlazeLodUniformBufferWrapper;
import com.seibel.distanthorizons.common.render.blaze.wrappers.buffer.BlazeVertexBufferWrapper;
import com.seibel.distanthorizons.common.wrappers.misc.LightMapWrapper;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding.LodBufferContainer;
import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pos.DhSectionPos;
import com.seibel.distanthorizons.core.render.RenderParams;
import com.seibel.distanthorizons.core.util.RenderUtil;
import com.seibel.distanthorizons.core.util.math.Mat4f;
import com.seibel.distanthorizons.core.util.math.Vec3d;
import com.seibel.distanthorizons.core.util.math.Vec3f;
import com.seibel.distanthorizons.core.util.objects.SortedArraySet;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhTerrainRenderer;
import com.seibel.distanthorizons.core.wrapperInterfaces.render.objects.IVertexBufferWrapper;
import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector;
import net.minecraft.resources.Identifier;
import org.lwjgl.system.MemoryUtil;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.OptionalDouble;
import java.util.OptionalInt;
/** Renders rendering DH's LOD terrain. */
public class BlazeDhTerrainRenderer implements IDhTerrainRenderer
{
public static final DhLogger LOGGER = new DhLoggerBuilder().build();
private static final GpuDevice GPU_DEVICE = RenderSystem.getDevice();
private static final CommandEncoder COMMAND_ENCODER = GPU_DEVICE.createCommandEncoder();
public static final BlazeDhTerrainRenderer INSTANCE = new BlazeDhTerrainRenderer();
private RenderPipeline opaquePipeline;
private RenderPipeline transparentPipeline;
private boolean init = false;
private GpuBuffer fragUniformBuffer;
private GpuBuffer vertSharedUniformBuffer;
//=============//
// constructor //
//=============//
//region
private BlazeDhTerrainRenderer() { }
private void tryInit()
{
if (this.init)
{
return;
}
RenderPipelineBuilderWrapper pipelineBuilder = new RenderPipelineBuilderWrapper();
{
pipelineBuilder.withFaceCulling(true);
pipelineBuilder.withDepthWrite(true);
pipelineBuilder.withDepthTest(RenderPipelineBuilderWrapper.EDhDepthTest.LESS);
pipelineBuilder.withColorWrite(true);
pipelineBuilder.withPolygonMode(RenderPipelineBuilderWrapper.EDhPolygonMode.FILL);
pipelineBuilder.withName("terrain");
pipelineBuilder.withSampler("uLightMap");
pipelineBuilder.withVertexShader("lod/blaze/vert");
pipelineBuilder.withFragmentShader("lod/blaze/frag");
pipelineBuilder.withUniformBuffer("vertUniqueUniformBlock");
pipelineBuilder.withUniformBuffer("vertSharedUniformBlock");
pipelineBuilder.withUniformBuffer("fragUniformBlock");
VertexFormat vertexFormat = VertexFormat.builder()
.add("vPosition", BlazeDhVertexFormatUtil.SHORT_XYZ_POS)
.add("meta", BlazeDhVertexFormatUtil.META)
.add("vColor", BlazeDhVertexFormatUtil.RGBA_UBYTE_COLOR)
.add("irisMaterial", BlazeDhVertexFormatUtil.IRIS_MATERIAL)
.add("irisNormal", BlazeDhVertexFormatUtil.IRIS_NORMAL)
.add("paddingTwo", BlazeDhVertexFormatUtil.BYTE_PAD)
.add("paddingThree", BlazeDhVertexFormatUtil.BYTE_PAD) // padding is to make sure the format is a multiple of 4
.build();
pipelineBuilder.withVertexFormat(vertexFormat);
pipelineBuilder.withVertexMode(RenderPipelineBuilderWrapper.EDhVertexMode.TRIANGLES);
}
// opaque
{
pipelineBuilder.withoutBlend();
this.opaquePipeline = pipelineBuilder.build();
}
// transparent
{
// TRANSLUCENT = new BlendFunction(SourceFactor.SRC_ALPHA, DestFactor.ONE_MINUS_SRC_ALPHA, SourceFactor.ONE, DestFactor.ONE_MINUS_SRC_ALPHA);
pipelineBuilder.withBlend(BlendFunction.TRANSLUCENT);
this.transparentPipeline = pipelineBuilder.build();
}
this.init = true;
}
//endregion
//========//
// render //
//========//
//region
@Override
public void render(
RenderParams renderEventParam,
boolean opaquePass,
SortedArraySet<LodBufferContainer> bufferContainers,
IProfilerWrapper profiler)
{
this.tryInit();
try(IProfilerWrapper.IProfileBlock terrain_profile = profiler.push("terrain render"))
{
profiler.popPush("vert unique uniforms");
{
// create data //
for (int lodIndex = 0; lodIndex < bufferContainers.size(); lodIndex++)
{
LodBufferContainer bufferContainer = bufferContainers.get(lodIndex);
bufferContainer.uniformContainer.tryUpload();
}
}
profiler.popPush("vert share uniforms");
{
Mat4f combinedMatrix = new Mat4f(renderEventParam.dhProjectionMatrix);
combinedMatrix.multiply(renderEventParam.dhModelViewMatrix);
float earthCurveRatio = Config.Client.Advanced.Graphics.Experimental.earthCurveRatio.get();
if (earthCurveRatio < -1.0f || earthCurveRatio > 1.0f)
{
earthCurveRatio = /*6371KM*/ 6371000.0f / earthCurveRatio;
}
else
{
// disable curvature if the config value is between -1 and 1
earthCurveRatio = 0.0f;
}
// upload data //
int uniformBufferSize = new Std140SizeCalculator()
.putInt() // uIsWhiteWorld
.putFloat() // uWorldYOffset
.putFloat() // uMircoOffset
.putFloat() // uEarthRadius
.putVec3() // uCameraPos
.putMat4f() // uCombinedMatrix
.get();
ByteBuffer buffer = MemoryUtil.memAlloc(uniformBufferSize);
buffer.order(ByteOrder.nativeOrder());
Std140Builder.intoBuffer(buffer)
.putInt(0) // uIsWhiteWorld
.putFloat((float) renderEventParam.worldYOffset) // uWorldYOffset
.putFloat(0.01f) // uMircoOffset // 0.01 block offset
.putFloat(earthCurveRatio) // uEarthRadius
.putVec3(
(float) renderEventParam.exactCameraPosition.x,
(float) renderEventParam.exactCameraPosition.y,
(float) renderEventParam.exactCameraPosition.z) // uCameraPos
.putMat4f(combinedMatrix.createJomlMatrix()) // uCombinedMatrix
.get();
this.vertSharedUniformBuffer = BlazeUniformUtil.createBuffer("vertSharedUniformBlock", uniformBufferSize, this.vertSharedUniformBuffer);
GpuBufferSlice bufferSlice = new GpuBufferSlice(this.vertSharedUniformBuffer, 0, uniformBufferSize);
COMMAND_ENCODER.writeToBuffer(bufferSlice, buffer);
MemoryUtil.memFree(buffer);
}
profiler.popPush("set frag uniforms");
{
int uniformBufferSize = new Std140SizeCalculator()
.putFloat() // uClipDistance
.putFloat() // uNoiseIntensity
.putInt() // uNoiseSteps
.putInt() // uNoiseDropoff
.putInt() // uDitherDhRendering
.putInt() // uNoiseEnabled
.get();
// create data //
float dhNearClipDistance = RenderUtil.getNearClipPlaneInBlocks();
if (!Config.Client.Advanced.Debugging.lodOnlyMode.get())
{
// this added value prevents the near clip plane and discard circle from touching, which looks bad
dhNearClipDistance += 16f;
}
// upload data //
ByteBuffer buffer = MemoryUtil.memAlloc(uniformBufferSize);
buffer.order(ByteOrder.nativeOrder());
buffer = Std140Builder.intoBuffer(buffer)
.putFloat(dhNearClipDistance) // uClipDistance
.putFloat(Config.Client.Advanced.Graphics.NoiseTexture.noiseIntensity.get()) // uNoiseIntensity
.putInt(Config.Client.Advanced.Graphics.NoiseTexture.noiseSteps.get()) // uNoiseSteps
.putInt(Config.Client.Advanced.Graphics.NoiseTexture.noiseDropoff.get()) // uNoiseDropoff
.putInt(Config.Client.Advanced.Graphics.Quality.ditherDhFade.get() ? 1 : 0) // uDitherDhRendering
.putInt(Config.Client.Advanced.Graphics.NoiseTexture.enableNoiseTexture.get() ? 1 : 0) // uNoiseEnabled
.get()
;
this.fragUniformBuffer = BlazeUniformUtil.createBuffer("fragUniformBlock", uniformBufferSize, this.fragUniformBuffer);
GpuBufferSlice bufferSlice = new GpuBufferSlice(this.fragUniformBuffer, 0, uniformBufferSize);
COMMAND_ENCODER.writeToBuffer(bufferSlice, buffer);
MemoryUtil.memFree(buffer);
}
// render pass setup
{
profiler.popPush("rendering");
// create a render pass
try (RenderPass renderPass = COMMAND_ENCODER.createRenderPass(
this::getRenderPassName,
BlazeDhMetaRenderer.INSTANCE.dhColorTextureWrapper.textureView,
/*optionalClearColorAsInt*/ OptionalInt.empty(),
BlazeDhMetaRenderer.INSTANCE.dhDepthTextureWrapper.textureView,
/*optionalDepthValueAsDouble*/ OptionalDouble.empty())
)
{
LightMapWrapper lightMapWrapper = (LightMapWrapper) renderEventParam.lightmap;
BlazeTextureViewWrapper lightmapTextureViewWrapper = lightMapWrapper.getTextureViewWrapper();
renderPass.bindTexture("uLightMap", lightmapTextureViewWrapper.textureView, lightmapTextureViewWrapper.textureSampler);
// set pipeline
renderPass.setPipeline(opaquePass ? this.opaquePipeline : this.transparentPipeline);
// shared uniforms
renderPass.setUniform("fragUniformBlock", this.fragUniformBuffer);
renderPass.setUniform("vertSharedUniformBlock", this.vertSharedUniformBuffer);
for (int lodIndex = 0; lodIndex < bufferContainers.size(); lodIndex++)
{
LodBufferContainer bufferContainer = bufferContainers.get(lodIndex);
BlazeLodUniformBufferWrapper uniformWrapper = (BlazeLodUniformBufferWrapper) bufferContainer.uniformContainer;
boolean columnBuilderDebugEnabled = Config.Client.Advanced.Debugging.ColumnBuilderDebugging.columnBuilderDebugEnable.get();
if (columnBuilderDebugEnabled)
{
if (DhSectionPos.getDetailLevel(bufferContainer.pos) == Config.Client.Advanced.Debugging.ColumnBuilderDebugging.columnBuilderDebugDetailLevel.get()
&& DhSectionPos.getX(bufferContainer.pos) == Config.Client.Advanced.Debugging.ColumnBuilderDebugging.columnBuilderDebugXPos.get()
&& DhSectionPos.getZ(bufferContainer.pos) == Config.Client.Advanced.Debugging.ColumnBuilderDebugging.columnBuilderDebugZPos.get())
{
int breakpoint = 0;
}
else
{
continue;
}
}
renderPass.setUniform("vertUniqueUniformBlock", uniformWrapper.gpuBuffer);
// render each buffer
IVertexBufferWrapper[] bufferWrapperList = opaquePass ? bufferContainer.vboOpaqueWrappers : bufferContainer.vboTransparentWrappers;
for (int i = 0; i < bufferWrapperList.length; i++)
{
BlazeVertexBufferWrapper bufferWrapper = (BlazeVertexBufferWrapper) bufferWrapperList[i];
if (!bufferWrapper.uploaded
|| bufferWrapper.vertexCount == 0)
{
continue;
}
// fire render event
{
Vec3d camPos = renderEventParam.exactCameraPosition;
Vec3f modelPos = new Vec3f(
(float) (bufferContainer.minCornerBlockPos.getX() - camPos.x),
(float) (bufferContainer.minCornerBlockPos.getY() - camPos.y),
(float) (bufferContainer.minCornerBlockPos.getZ() - camPos.z));
ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeBufferRenderEvent.class, new DhApiBeforeBufferRenderEvent.EventParam(renderEventParam, modelPos));
}
renderPass.setIndexBuffer(bufferWrapper.getIndexGpuBuffer(), VertexFormat.IndexType.INT);
renderPass.setVertexBuffer(0, bufferWrapper.vertexGpuBuffer); // vertex buffer can only be "0" lol
if (!bufferWrapper.vertexGpuBuffer.isClosed())
{
renderPass.drawIndexed(
/*indexStart*/ 0,
/*firstIndex*/0,
/*indexCount*/bufferWrapper.indexCount,
/*instanceCount*/1);
}
}
}
}
}
}
}
private String getIndexBufferName() { return "distantHorizons:LodIndexBuffer"; }
private String getRenderPassName() { return "distantHorizons:McLodRenderer"; }
//endregion
}
#endif
@@ -0,0 +1,270 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.render.blaze.apply;
#if MC_VER <= MC_1_21_10
public class BlazeDhApplyRenderer {}
#else
import com.mojang.blaze3d.buffers.GpuBuffer;
import com.mojang.blaze3d.pipeline.BlendFunction;
import com.mojang.blaze3d.pipeline.RenderPipeline;
import com.mojang.blaze3d.systems.CommandEncoder;
import com.mojang.blaze3d.systems.GpuDevice;
import com.mojang.blaze3d.systems.RenderPass;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.textures.*;
import com.mojang.blaze3d.vertex.VertexFormat;
import com.seibel.distanthorizons.common.render.blaze.util.BlazeDhVertexFormatUtil;
import com.seibel.distanthorizons.common.render.blaze.wrappers.RenderPipelineBuilderWrapper;
import com.seibel.distanthorizons.common.render.blaze.wrappers.texture.BlazeTextureViewWrapper;
import com.seibel.distanthorizons.common.render.blaze.util.BlazePostProcessUtil;
import com.seibel.distanthorizons.common.render.blaze.wrappers.texture.BlazeTextureWrapper;
import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.coreapi.ModInfo;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.OptionalDouble;
import java.util.OptionalInt;
/**
* Copies the given color texture
* where the depth (or another attribute) is valid.
* Often used to apply post processing effects or
* the DH texture to MC's color texture. <br><br>
*
* @see BlazeDhCopyRenderer
*/
public class BlazeDhApplyRenderer
{
public static final DhLogger LOGGER = new DhLoggerBuilder().build();
private static final GpuDevice GPU_DEVICE = RenderSystem.getDevice();
private static final CommandEncoder COMMAND_ENCODER = GPU_DEVICE.createCommandEncoder();
private RenderPipeline pipeline;
protected GpuBuffer vboGpuBuffer;
protected final String name;
protected final String identifierName;
public String getIdentifierName() { return this.identifierName; }
@Nullable
private final BlendFunction blendFunction;
private final String vertexShaderPath;
private final String fragmentShaderPath;
private final BlazeTextureViewWrapper sourceColorTextureViewWrapper = new BlazeTextureViewWrapper();
private final BlazeTextureViewWrapper sourceDepthTextureViewWrapper = new BlazeTextureViewWrapper();
private final BlazeTextureViewWrapper destinationColorTextureViewWrapper = new BlazeTextureViewWrapper();
/** We don't want to actually write any depth data, but blaze3D complains if we don't bind a depth texture. */
private final BlazeTextureWrapper dummyDepthTextureWrapper = BlazeTextureWrapper.createDepth("apply_dummy_depth");
/**
* Can be set for special application shaders that need
* extra information. <br><br>
*
* will be an empty array if unneeded
*/
private final String[] uniformNames;
/** will be an empty array if unneeded */
private final GpuBuffer[] uniformBuffers;
//=============//
// constructor //
//=============//
//region
public BlazeDhApplyRenderer(
String name,
@Nullable BlendFunction blendFunction,
String vertexShaderPath, String fragmentShaderPath
)
{
this(
name,
blendFunction,
vertexShaderPath, fragmentShaderPath,
new String[0] // no extra uniforms
);
}
public BlazeDhApplyRenderer(
String name,
@Nullable BlendFunction blendFunction,
String vertexShaderPath, String fragmentShaderPath,
String[] uniformNames
)
{
this.name = name;
this.identifierName = "distanthorizons:"+this.name;
this.blendFunction = blendFunction;
this.vertexShaderPath = vertexShaderPath;
this.fragmentShaderPath = fragmentShaderPath;
this.uniformNames = uniformNames;
this.uniformBuffers = new GpuBuffer[this.uniformNames.length];
}
private void tryInit(
GpuTexture sourceColorTexture,
GpuTexture sourceDepthTexture,
GpuTexture destinationColorTexture)
{
// one-time setup
if (this.pipeline == null)
{
this.createPipeline();
this.vboGpuBuffer = BlazePostProcessUtil.createAndUploadScreenVertexData(this.name);
}
this.sourceColorTextureViewWrapper.tryWrap(sourceColorTexture);
this.sourceDepthTextureViewWrapper.tryWrap(sourceDepthTexture);
this.destinationColorTextureViewWrapper.tryWrap(destinationColorTexture);
}
private void createPipeline()
{
RenderPipelineBuilderWrapper pipelineBuilder = new RenderPipelineBuilderWrapper();
{
pipelineBuilder.withFaceCulling(false);
pipelineBuilder.withDepthWrite(false);
pipelineBuilder.withDepthTest(RenderPipelineBuilderWrapper.EDhDepthTest.NONE);
pipelineBuilder.withColorWrite(true);
if (this.blendFunction != null)
{
pipelineBuilder.withBlend(this.blendFunction);
}
else
{
pipelineBuilder.withoutBlend();
}
pipelineBuilder.withPolygonMode(RenderPipelineBuilderWrapper.EDhPolygonMode.FILL);
pipelineBuilder.withName(this.name);
pipelineBuilder.withVertexShader(this.vertexShaderPath);
pipelineBuilder.withFragmentShader(this.fragmentShaderPath);
for (int i = 0; i < this.uniformNames.length; i++)
{
String uniformName = this.uniformNames[i];
pipelineBuilder.withUniformBuffer(uniformName);
}
pipelineBuilder.withSampler("uSourceColorTexture");
pipelineBuilder.withSampler("uSourceDepthTexture");
VertexFormat vertexFormat = VertexFormat.builder()
.add("vPosition", BlazeDhVertexFormatUtil.SCREEN_POS)
.build();
pipelineBuilder.withVertexFormat(vertexFormat);
pipelineBuilder.withVertexMode(RenderPipelineBuilderWrapper.EDhVertexMode.TRIANGLE_FAN);
}
this.pipeline = pipelineBuilder.build();
}
//endregion
//========//
// render //
//========//
//region
public void setUniform(String uniformName, GpuBuffer uniformBuffer)
{
// the uniform array should be short enough (less than 10 items)
// where a sequential search should be plenty fast
for (int i = 0; i < this.uniformNames.length; i++)
{
String nameAtIndex = this.uniformNames[i];
if (nameAtIndex.equals(uniformName))
{
this.uniformBuffers[i] = uniformBuffer;
break;
}
}
}
public void render(
GpuTexture sourceColorTexture,
GpuTexture sourceDepthTexture,
GpuTexture destinationColorTexture)
{
this.tryInit(sourceColorTexture, sourceDepthTexture, destinationColorTexture);
this.dummyDepthTextureWrapper.tryCreateOrResize();
try (RenderPass renderPass = COMMAND_ENCODER.createRenderPass(
this::getIdentifierName,
this.destinationColorTextureViewWrapper.textureView,
/*optionalClearColorAsInt*/ OptionalInt.empty(),
this.dummyDepthTextureWrapper.textureView,
/*optionalDepthValueAsDouble*/ OptionalDouble.empty()))
{
renderPass.bindTexture("uSourceColorTexture", this.sourceColorTextureViewWrapper.textureView, this.sourceColorTextureViewWrapper.textureSampler);
renderPass.bindTexture("uSourceDepthTexture", this.sourceDepthTextureViewWrapper.textureView, this.sourceDepthTextureViewWrapper.textureSampler);
for (int i = 0; i < this.uniformNames.length; i++)
{
String uniformName = this.uniformNames[i];
GpuBuffer uniformBuffer = this.uniformBuffers[i];
if (uniformBuffer == null)
{
throw new IllegalStateException("Missing uniform ["+uniformName+"], please set the uniform before rendering.");
}
renderPass.setUniform(uniformName, uniformBuffer);
}
renderPass.setVertexBuffer(0, this.vboGpuBuffer);
renderPass.setPipeline(this.pipeline);
renderPass.draw(/*indexStart*/ 0, /*indexCount*/ 4);
}
// clear the uniforms after rendering
// so we can check if they're missing during next frame's rendering
if (ModInfo.IS_DEV_BUILD)
{
Arrays.fill(this.uniformBuffers, null);
}
}
//endregion
}
#endif
@@ -0,0 +1,168 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.render.blaze.apply;
#if MC_VER <= MC_1_21_10
public class BlazeDhCopyRenderer {}
#else
import com.mojang.blaze3d.buffers.GpuBuffer;
import com.mojang.blaze3d.pipeline.RenderPipeline;
import com.mojang.blaze3d.systems.CommandEncoder;
import com.mojang.blaze3d.systems.GpuDevice;
import com.mojang.blaze3d.systems.RenderPass;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.textures.*;
import com.seibel.distanthorizons.common.render.blaze.wrappers.RenderPipelineBuilderWrapper;
import com.seibel.distanthorizons.common.render.blaze.wrappers.texture.BlazeTextureViewWrapper;
import com.seibel.distanthorizons.common.render.blaze.wrappers.texture.BlazeTextureWrapper;
import com.seibel.distanthorizons.common.render.blaze.util.BlazePostProcessUtil;
import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import java.util.OptionalDouble;
import java.util.OptionalInt;
/**
* Blindly copies one texture into another.
*
* @see BlazeDhApplyRenderer
*/
public class BlazeDhCopyRenderer
{
public static final DhLogger LOGGER = new DhLoggerBuilder().build();
private static final GpuDevice GPU_DEVICE = RenderSystem.getDevice();
private static final CommandEncoder COMMAND_ENCODER = GPU_DEVICE.createCommandEncoder();
public static final BlazeDhCopyRenderer INSTANCE = new BlazeDhCopyRenderer();
private RenderPipeline pipeline;
private boolean init = false;
private GpuBuffer vboGpuBuffer;
private BlazeTextureWrapper dummyDepthTextureWrapper;
//=============//
// constructor //
//=============//
//region
private BlazeDhCopyRenderer() { }
private void tryInit()
{
if (this.init)
{
return;
}
this.init = true;
this.dummyDepthTextureWrapper = BlazeTextureWrapper.createDepth("dh_copy_depth_texture");
RenderPipelineBuilderWrapper pipelineBuilder = new RenderPipelineBuilderWrapper();
{
pipelineBuilder.withFaceCulling(false);
pipelineBuilder.withDepthWrite(false);
pipelineBuilder.withDepthTest(RenderPipelineBuilderWrapper.EDhDepthTest.NONE);
pipelineBuilder.withColorWrite(true);
pipelineBuilder.withoutBlend();
pipelineBuilder.withPolygonMode(RenderPipelineBuilderWrapper.EDhPolygonMode.FILL);
pipelineBuilder.withName("copy");
pipelineBuilder.withVertexShader("copy/blaze/vert");
pipelineBuilder.withFragmentShader("copy/blaze/frag");
pipelineBuilder.withSampler("uCopyTexture");
pipelineBuilder.withVertexFormat(BlazePostProcessUtil.createVertexFormat());
pipelineBuilder.withVertexMode(RenderPipelineBuilderWrapper.EDhVertexMode.TRIANGLE_FAN);
}
this.pipeline = pipelineBuilder.build();
this.vboGpuBuffer = BlazePostProcessUtil.createAndUploadScreenVertexData("McCopyRenderer");
}
//endregion
//========//
// render //
//========//
//region
public void render(
BlazeTextureWrapper sourceColorTextureWrapper,
BlazeTextureViewWrapper destinationColorTextureWrapper)
{
this.render(
sourceColorTextureWrapper.textureView, sourceColorTextureWrapper.textureSampler,
destinationColorTextureWrapper.textureView);
}
public void render(
BlazeTextureWrapper sourceColorTextureWrapper,
BlazeTextureWrapper destinationColorTextureWrapper)
{
this.render(
sourceColorTextureWrapper.textureView, sourceColorTextureWrapper.textureSampler,
destinationColorTextureWrapper.textureView);
}
private void render(
GpuTextureView sourceTextureView,
GpuSampler sourceTextureSampler,
GpuTextureView destinationTextureView)
{
this.tryInit();
this.dummyDepthTextureWrapper.tryCreateOrResize();
try (RenderPass renderPass = COMMAND_ENCODER.createRenderPass(
this::getRenderPassName,
destinationTextureView,
/*optionalClearColorAsInt*/ OptionalInt.empty(),
this.dummyDepthTextureWrapper.textureView,
/*optionalDepthValueAsDouble*/ OptionalDouble.empty()))
{
renderPass.bindTexture("uCopyTexture", sourceTextureView, sourceTextureSampler);
renderPass.setVertexBuffer(0, this.vboGpuBuffer); // vertex buffer can only be "0" lol
renderPass.setPipeline(this.pipeline);
renderPass.draw(/*indexStart*/ 0, /*indexCount*/ 4);
}
}
private String getRenderPassName() { return "distantHorizons:McCopyRenderer"; }
//endregion
}
#endif
@@ -0,0 +1,299 @@
package com.seibel.distanthorizons.common.render.blaze.objects;
#if MC_VER <= MC_1_21_10
public class BlazeGenericObjectVertexContainer {}
#else
import com.mojang.blaze3d.buffers.GpuBuffer;
import com.mojang.blaze3d.buffers.GpuBufferSlice;
import com.mojang.blaze3d.systems.CommandEncoder;
import com.mojang.blaze3d.systems.GpuDevice;
import com.mojang.blaze3d.systems.RenderSystem;
import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBox;
import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.common.render.openGl.glObject.enums.GLEnums;
import com.seibel.distanthorizons.core.render.RenderThreadTaskHandler;
import com.seibel.distanthorizons.core.wrapperInterfaces.render.objects.IDhGenericObjectVertexBufferContainer;
import com.seibel.distanthorizons.core.render.renderer.RenderableBoxGroup;
import com.seibel.distanthorizons.coreapi.util.ColorUtil;
import org.lwjgl.opengl.GL32;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.List;
/**
* For use by {@link RenderableBoxGroup}
*
* @see RenderableBoxGroup
*/
public class BlazeGenericObjectVertexContainer implements IDhGenericObjectVertexBufferContainer
{
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
private static final GpuDevice GPU_DEVICE = RenderSystem.getDevice();
private static final CommandEncoder COMMAND_ENCODER = GPU_DEVICE.createCommandEncoder();
private static final int[] BOX_INDICES = {
//region
// min X, vertical face
2, 1, 0,
0, 3, 2,
// max X, vertical face
6, 5, 4,
4, 7, 6,
// min Z, vertical face
10, 9, 8,
8, 11, 10,
// max Z, vertical face
14, 13, 12,
12, 15, 14,
// min Y, horizontal face
18, 17, 16,
16, 19, 18,
// max Y, horizontal face
20, 21, 22,
22, 23, 20,
//endregion
};
public GpuBuffer vboGpuBuffer;
public GpuBuffer indexGpuBuffer;
private ByteBuffer vertexBuffer = ByteBuffer.allocateDirect(0);
private ByteBuffer indexBuffer = ByteBuffer.allocateDirect(0);
public int uploadedBoxCount = 0;
private EState state = EState.NEW;
@Override
public IDhGenericObjectVertexBufferContainer.EState getState() { return this.state; }
@Override
public void setState(IDhGenericObjectVertexBufferContainer.EState state) { this.state = state; }
//===========================//
// render building/uploading //
//===========================//
//region
@Override
public void updateVertexData(List<DhApiRenderableBox> uploadBoxList)
{
int boxCount = uploadBoxList.size();
// recreate the data arrays if their size is different
if (this.uploadedBoxCount != boxCount)
{
this.uploadedBoxCount = boxCount;
int vertexBufferSize = this.vertexBufferSize();
this.vertexBuffer = ByteBuffer.allocateDirect(vertexBufferSize);
this.vertexBuffer.order(ByteOrder.nativeOrder());
int indexBufferSize = this.indexBufferSize();
this.indexBuffer = ByteBuffer.allocateDirect(indexBufferSize);
this.indexBuffer.order(ByteOrder.nativeOrder());
}
this.vertexBuffer.position(0);
this.indexBuffer.position(0);
for (int boxIndex = 0; boxIndex < boxCount; boxIndex++)
{
// index
int indexOffset = (boxIndex * 24 /*24 is the number of vertices in a box*/);
for (int i = 0; i < BOX_INDICES.length; i++)
{
this.indexBuffer.putInt(BOX_INDICES[i] + indexOffset);
}
// vertex
DhApiRenderableBox box = uploadBoxList.get(boxIndex);
final double[] boxVertices =
{
//region
// Pos x y z
// min X, vertical face
box.minPos.x, box.minPos.y, box.minPos.z,
box.maxPos.x, box.minPos.y, box.minPos.z,
box.maxPos.x, box.maxPos.y, box.minPos.z,
box.minPos.x, box.maxPos.y, box.minPos.z,
// max X, vertical face
box.minPos.x, box.maxPos.y, box.maxPos.z,
box.maxPos.x, box.maxPos.y, box.maxPos.z,
box.maxPos.x, box.minPos.y, box.maxPos.z,
box.minPos.x, box.minPos.y, box.maxPos.z,
// min Z, vertical face
box.minPos.x, box.minPos.y, box.maxPos.z,
box.minPos.x, box.minPos.y, box.minPos.z,
box.minPos.x, box.maxPos.y, box.minPos.z,
box.minPos.x, box.maxPos.y, box.maxPos.z,
// max Z, vertical face
box.maxPos.x, box.minPos.y, box.maxPos.z,
box.maxPos.x, box.maxPos.y, box.maxPos.z,
box.maxPos.x, box.maxPos.y, box.minPos.z,
box.maxPos.x, box.minPos.y, box.minPos.z,
// min Y, horizontal face
box.minPos.x, box.minPos.y, box.maxPos.z,
box.maxPos.x, box.minPos.y, box.maxPos.z,
box.maxPos.x, box.minPos.y, box.minPos.z,
box.minPos.x, box.minPos.y, box.minPos.z,
// max Y, horizontal face
box.minPos.x, box.maxPos.y, box.maxPos.z,
box.maxPos.x, box.maxPos.y, box.maxPos.z,
box.maxPos.x, box.maxPos.y, box.minPos.z,
box.minPos.x, box.maxPos.y, box.minPos.z,
//endregion
};
for (int vertexIndex = 0; vertexIndex < boxVertices.length; vertexIndex+=3)
{
this.vertexBuffer.putFloat((float)boxVertices[vertexIndex]); // x
this.vertexBuffer.putFloat((float)boxVertices[vertexIndex+1]); // y
this.vertexBuffer.putFloat((float)boxVertices[vertexIndex+2]); // z
int color = ColorUtil.toColorInt(box.color);
byte r = (byte) ColorUtil.getRed(color);
byte g = (byte) ColorUtil.getGreen(color);
byte b = (byte) ColorUtil.getBlue(color);
byte a = (byte) ColorUtil.getAlpha(color);
this.vertexBuffer.put(r);
this.vertexBuffer.put(g);
this.vertexBuffer.put(b);
this.vertexBuffer.put(a);
this.vertexBuffer.put(box.material);
// padding so the vertex format's byte count is a multiple of 4
this.vertexBuffer.put((byte)0);
this.vertexBuffer.put((byte)0);
this.vertexBuffer.put((byte)0);
}
}
this.vertexBuffer.flip();
this.indexBuffer.flip();
}
private int vertexBufferSize()
{
// minimum of 1 box to prevent trying to create a buffer of size 0
int boxCount = Math.max(this.uploadedBoxCount, 1);
int faceCount = boxCount * 6; // 6 faces on a cube
int vertexCount = faceCount * 6; // 6 vertices per cube
int byteSize = vertexCount * 3 * Float.BYTES; // x,y,z
byteSize += vertexCount * 4; // r,g,b,a
byteSize += 1; // material
return byteSize;
}
private int indexBufferSize()
{
// minimum of 1 box to prevent trying to create a buffer of size 0
int boxCount = Math.max(this.uploadedBoxCount, 1);
int quadCount = boxCount * 6 * 6; // 6 faces with 6 vertices each
int byteSize = quadCount * GLEnums.getTypeSize(GL32.GL_UNSIGNED_INT);
return byteSize;
}
@Override
public void uploadDataToGpu()
{
// vertex
{
int totalVertexByteSize = this.vertexBufferSize();
if (this.vboGpuBuffer == null
// recreating if the size changes is always necessary (even if we only need a smaller amount)
// due to a bug on Mac where it will attempt to render anything allocated in the buffer
|| this.vboGpuBuffer.size() != totalVertexByteSize)
{
if (this.vboGpuBuffer != null)
{
this.vboGpuBuffer.close();
}
int usage = GpuBuffer.USAGE_COPY_DST
| GpuBuffer.USAGE_VERTEX;
this.vboGpuBuffer = GPU_DEVICE.createBuffer(this::getVertexBufferName, usage, totalVertexByteSize);
}
GpuBufferSlice bufferSlice = new GpuBufferSlice(this.vboGpuBuffer, /*offset*/0, totalVertexByteSize);
COMMAND_ENCODER.writeToBuffer(bufferSlice, this.vertexBuffer);
}
// index
{
int totalVertexByteSize = this.indexBufferSize();
if (this.indexGpuBuffer == null
// recreating if the size changes is always necessary (even if we only need a smaller amount)
// due to a bug on Mac where it will attempt to render anything allocated in the buffer
|| this.indexGpuBuffer.size() != totalVertexByteSize)
{
if (this.indexGpuBuffer != null)
{
this.indexGpuBuffer.close();
}
int usage = GpuBuffer.USAGE_COPY_DST
| GpuBuffer.USAGE_INDEX;
this.indexGpuBuffer = GPU_DEVICE.createBuffer(this::getIndexBufferName, usage, totalVertexByteSize);
}
int offset = 0;
GpuBufferSlice bufferSlice = new GpuBufferSlice(this.indexGpuBuffer, offset, totalVertexByteSize);
COMMAND_ENCODER.writeToBuffer(bufferSlice, this.indexBuffer);
}
}
private String getVertexBufferName() { return "distantHorizons:GenericVertexBuffer"; }
private String getIndexBufferName() { return "distantHorizons:GenericIndexBuffer"; }
//endregion
//================//
// base overrides //
//================//
//region
@Override
public void close()
{
RenderThreadTaskHandler.INSTANCE.queueRunningOnRenderThread("BlazeGenericObjectVertexContainer close", () ->
{
if (this.vboGpuBuffer != null)
{
this.vboGpuBuffer.close();
}
if (this.indexGpuBuffer != null)
{
this.indexGpuBuffer.close();
}
});
}
//endregion
}
#endif
@@ -0,0 +1,237 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.render.blaze.postProcessing;
#if MC_VER <= MC_1_21_10
public class BlazeDhFarFadeRenderer {}
#else
import com.mojang.blaze3d.buffers.GpuBuffer;
import com.mojang.blaze3d.buffers.GpuBufferSlice;
import com.mojang.blaze3d.buffers.Std140Builder;
import com.mojang.blaze3d.buffers.Std140SizeCalculator;
import com.mojang.blaze3d.pipeline.RenderPipeline;
import com.mojang.blaze3d.systems.CommandEncoder;
import com.mojang.blaze3d.systems.GpuDevice;
import com.mojang.blaze3d.systems.RenderPass;
import com.mojang.blaze3d.systems.RenderSystem;
import com.seibel.distanthorizons.common.render.blaze.BlazeDhMetaRenderer;
import com.seibel.distanthorizons.common.render.blaze.apply.BlazeDhCopyRenderer;
import com.seibel.distanthorizons.common.render.blaze.util.BlazePostProcessUtil;
import com.seibel.distanthorizons.common.render.blaze.util.BlazeUniformUtil;
import com.seibel.distanthorizons.common.render.blaze.wrappers.RenderPipelineBuilderWrapper;
import com.seibel.distanthorizons.common.render.blaze.wrappers.texture.BlazeTextureViewWrapper;
import com.seibel.distanthorizons.common.render.blaze.wrappers.texture.BlazeTextureWrapper;
import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.render.RenderParams;
import com.seibel.distanthorizons.core.util.RenderUtil;
import com.seibel.distanthorizons.core.util.math.Mat4f;
import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhFarFadeRenderer;
import net.minecraft.client.Minecraft;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.OptionalDouble;
import java.util.OptionalInt;
/**
* Fades out DH's far clip plane
*/
public class BlazeDhFarFadeRenderer implements IDhFarFadeRenderer
{
public static final DhLogger LOGGER = new DhLoggerBuilder().build();
private static final GpuDevice GPU_DEVICE = RenderSystem.getDevice();
private static final CommandEncoder COMMAND_ENCODER = GPU_DEVICE.createCommandEncoder();
public static final BlazeDhFarFadeRenderer INSTANCE = new BlazeDhFarFadeRenderer();
private RenderPipeline pipeline;
private boolean init = false;
private GpuBuffer fragUniformBuffer;
private GpuBuffer vboGpuBuffer;
private final BlazeTextureWrapper dhFadeColorTextureWrapper = BlazeTextureWrapper.createColor("dh_far_fade_color_texture");
/** We don't want to actually write any depth data, but blaze3D complains if we don't bind a depth texture. */
private final BlazeTextureWrapper dhFadeDepthTextureWrapper = BlazeTextureWrapper.createDepth("dh_far_fade_depth_texture");
private final BlazeTextureViewWrapper mcColorTextureViewWrapper = new BlazeTextureViewWrapper();
//=============//
// constructor //
//=============//
//region
private BlazeDhFarFadeRenderer() { }
private void tryInit()
{
if (this.init)
{
return;
}
this.init = true;
RenderPipelineBuilderWrapper pipelineBuilder = new RenderPipelineBuilderWrapper();
{
pipelineBuilder.withFaceCulling(false);
pipelineBuilder.withDepthWrite(false);
pipelineBuilder.withDepthTest(RenderPipelineBuilderWrapper.EDhDepthTest.NONE);
pipelineBuilder.withColorWrite(true);
pipelineBuilder.withoutBlend();
pipelineBuilder.withPolygonMode(RenderPipelineBuilderWrapper.EDhPolygonMode.FILL);
pipelineBuilder.withName("far_fade");
pipelineBuilder.withVertexShader("fade/blaze/vert");
pipelineBuilder.withFragmentShader("fade/blaze/dh_fade");
pipelineBuilder.withSampler("uMcColorTexture");
pipelineBuilder.withSampler("uDhDepthTexture");
pipelineBuilder.withSampler("uDhColorTexture");
pipelineBuilder.withUniformBuffer("fragUniformBlock");
pipelineBuilder.withVertexFormat(BlazePostProcessUtil.createVertexFormat());
pipelineBuilder.withVertexMode(RenderPipelineBuilderWrapper.EDhVertexMode.TRIANGLE_FAN);
}
this.pipeline = pipelineBuilder.build();
this.vboGpuBuffer = BlazePostProcessUtil.createAndUploadScreenVertexData("McFadeRenderer");
}
//endregion
//========//
// render //
//========//
//region
@Override
public void render(RenderParams renderParams)
{
this.tryInit();
if (BlazeDhMetaRenderer.INSTANCE.dhDepthTextureWrapper.isEmpty()
|| BlazeDhMetaRenderer.INSTANCE.dhColorTextureWrapper.isEmpty())
{
return;
}
// textures
this.dhFadeColorTextureWrapper.tryCreateOrResize();
this.mcColorTextureViewWrapper.tryWrap(Minecraft.getInstance().getMainRenderTarget().getColorTexture());
this.dhFadeDepthTextureWrapper.tryCreateOrResize();
{
int uniformBufferSize = new Std140SizeCalculator()
.putFloat() // uStartFadeBlockDistance
.putFloat() // uEndFadeBlockDistance
.putMat4f() // uDhInvMvmProj
.get();
// create data //
float dhFarClipDistance = RenderUtil.getFarClipPlaneDistanceInBlocks();
float fadeStartDistance = dhFarClipDistance * 0.5f;
float fadeEndDistance = dhFarClipDistance * 0.9f;
Mat4f dhProjectionMatrix = RenderUtil.createLodProjectionMatrix(renderParams.mcProjectionMatrix);
Mat4f dhModelViewMatrix = RenderUtil.createLodModelViewMatrix(renderParams.mcModelViewMatrix);
Mat4f inverseDhMvmProjMatrix = new Mat4f(dhProjectionMatrix);
inverseDhMvmProjMatrix.multiply(dhModelViewMatrix);
inverseDhMvmProjMatrix.invert();
// upload data //
ByteBuffer buffer = ByteBuffer.allocateDirect(uniformBufferSize);
buffer.order(ByteOrder.nativeOrder());
buffer = Std140Builder.intoBuffer(buffer)
.putFloat(fadeStartDistance) // uStartFadeBlockDistance
.putFloat(fadeEndDistance) // uEndFadeBlockDistance
.putMat4f(inverseDhMvmProjMatrix.createJomlMatrix()) // uDhInvMvmProj
.get()
;
this.fragUniformBuffer = BlazeUniformUtil.createBuffer("fragUniformBlock", uniformBufferSize, this.fragUniformBuffer);
GpuBufferSlice bufferSlice = new GpuBufferSlice(this.fragUniformBuffer, 0, uniformBufferSize);
COMMAND_ENCODER.writeToBuffer(bufferSlice, buffer);
}
this.renderFadeToTexture();
BlazeDhCopyRenderer.INSTANCE.render(this.dhFadeColorTextureWrapper, BlazeDhMetaRenderer.INSTANCE.dhColorTextureWrapper);
}
private void renderFadeToTexture()
{
try (RenderPass renderPass = COMMAND_ENCODER.createRenderPass(
this::getRenderPassName,
this.dhFadeColorTextureWrapper.textureView,
/*optionalClearColorAsInt*/ OptionalInt.empty(),
this.dhFadeDepthTextureWrapper.textureView,
/*optionalDepthValueAsDouble*/ OptionalDouble.empty()))
{
// MC texture
renderPass.bindTexture("uMcColorTexture", this.mcColorTextureViewWrapper.textureView, this.mcColorTextureViewWrapper.textureSampler);
// DH textures
renderPass.bindTexture("uDhDepthTexture", BlazeDhMetaRenderer.INSTANCE.dhDepthTextureWrapper.textureView, BlazeDhMetaRenderer.INSTANCE.dhDepthTextureWrapper.textureSampler);
renderPass.bindTexture("uDhColorTexture", BlazeDhMetaRenderer.INSTANCE.dhColorTextureWrapper.textureView, BlazeDhMetaRenderer.INSTANCE.dhColorTextureWrapper.textureSampler);
renderPass.setUniform("fragUniformBlock", this.fragUniformBuffer);
renderPass.setVertexBuffer(0, this.vboGpuBuffer); // vertex buffer can only be "0" lol
renderPass.setPipeline(this.pipeline);
renderPass.draw(/*indexStart*/ 0, /*indexCount*/ 4);
}
}
private String getRenderPassName() { return "distantHorizons:McFadeRenderer"; }
//endregion
}
#endif
@@ -0,0 +1,362 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.render.blaze.postProcessing;
#if MC_VER <= MC_1_21_10
public class BlazeDhFogRenderer {}
#else
import com.mojang.blaze3d.buffers.GpuBuffer;
import com.mojang.blaze3d.buffers.GpuBufferSlice;
import com.mojang.blaze3d.buffers.Std140Builder;
import com.mojang.blaze3d.buffers.Std140SizeCalculator;
import com.mojang.blaze3d.pipeline.BlendFunction;
import com.mojang.blaze3d.pipeline.RenderPipeline;
import com.mojang.blaze3d.platform.DestFactor;
import com.mojang.blaze3d.platform.SourceFactor;
import com.mojang.blaze3d.systems.CommandEncoder;
import com.mojang.blaze3d.systems.GpuDevice;
import com.mojang.blaze3d.systems.RenderPass;
import com.mojang.blaze3d.systems.RenderSystem;
import com.seibel.distanthorizons.api.enums.rendering.EDhApiFogColorMode;
import com.seibel.distanthorizons.api.enums.rendering.EDhApiHeightFogDirection;
import com.seibel.distanthorizons.api.enums.rendering.EDhApiHeightFogMixMode;
import com.seibel.distanthorizons.common.render.blaze.BlazeDhMetaRenderer;
import com.seibel.distanthorizons.common.render.blaze.apply.BlazeDhApplyRenderer;
import com.seibel.distanthorizons.common.render.blaze.wrappers.RenderPipelineBuilderWrapper;
import com.seibel.distanthorizons.common.render.blaze.wrappers.texture.BlazeTextureWrapper;
import com.seibel.distanthorizons.common.render.blaze.util.BlazePostProcessUtil;
import com.seibel.distanthorizons.common.render.blaze.util.BlazeUniformUtil;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.render.RenderParams;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.util.math.Mat4f;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhFogRenderer;
import java.awt.*;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.OptionalDouble;
import java.util.OptionalInt;
/**
* Renders fog onto the LODs.
*/
public class BlazeDhFogRenderer implements IDhFogRenderer
{
public static final DhLogger LOGGER = new DhLoggerBuilder().build();
private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
private static final GpuDevice GPU_DEVICE = RenderSystem.getDevice();
private static final CommandEncoder COMMAND_ENCODER = GPU_DEVICE.createCommandEncoder();
public static final BlazeDhFogRenderer INSTANCE = new BlazeDhFogRenderer();
private BlazeDhApplyRenderer applyRenderer;
private RenderPipeline pipeline;
private boolean init = false;
private GpuBuffer fragUniformBuffer;
private GpuBuffer vboGpuBuffer;
private final BlazeTextureWrapper fogColorTextureWrapper = BlazeTextureWrapper.createColor("dh_fog_color_texture");
/** We don't want to actually write any depth data, but blaze3D complains if we don't bind a depth texture. */
private final BlazeTextureWrapper fogDepthTextureWrapper = BlazeTextureWrapper.createDepth("dh_fog_depth_texture");
//=============//
// constructor //
//=============//
//region
private BlazeDhFogRenderer() { }
private void tryInit()
{
if (this.init)
{
return;
}
this.init = true;
this.applyRenderer = new BlazeDhApplyRenderer(
"fog_apply_to_dh",
new BlendFunction(SourceFactor.SRC_ALPHA, DestFactor.ONE_MINUS_SRC_ALPHA, SourceFactor.ONE, DestFactor.ONE_MINUS_SRC_ALPHA),
"apply/blaze/vert", "apply/blaze/frag"
);
RenderPipelineBuilderWrapper pipelineBuilder = new RenderPipelineBuilderWrapper();
{
pipelineBuilder.withFaceCulling(false);
pipelineBuilder.withDepthWrite(false);
pipelineBuilder.withDepthTest(RenderPipelineBuilderWrapper.EDhDepthTest.NONE);
pipelineBuilder.withColorWrite(true);
pipelineBuilder.withoutBlend();
pipelineBuilder.withPolygonMode(RenderPipelineBuilderWrapper.EDhPolygonMode.FILL);
pipelineBuilder.withName("fog_render");
pipelineBuilder.withVertexShader("fog/blaze/vert");
pipelineBuilder.withFragmentShader("fog/blaze/frag");
pipelineBuilder.withSampler("uDhDepthTexture");
pipelineBuilder.withUniformBuffer("fragUniformBlock");
pipelineBuilder.withVertexFormat(BlazePostProcessUtil.createVertexFormat());
pipelineBuilder.withVertexMode(RenderPipelineBuilderWrapper.EDhVertexMode.TRIANGLE_FAN);
}
this.pipeline = pipelineBuilder.build();
this.vboGpuBuffer = BlazePostProcessUtil.createAndUploadScreenVertexData("McFogRenderer");
}
//endregion
//========//
// render //
//========//
//region
@Override
public void render(RenderParams renderParams)
{
this.tryInit();
if (BlazeDhMetaRenderer.INSTANCE.dhDepthTextureWrapper.isEmpty()
|| BlazeDhMetaRenderer.INSTANCE.dhColorTextureWrapper.isEmpty())
{
return;
}
this.fogColorTextureWrapper.tryCreateOrResize();
this.fogDepthTextureWrapper.tryCreateOrResize();
{
int uniformBufferSize = new Std140SizeCalculator()
// fog uniforms
.putVec4() // uFogColor
.putFloat() //uFogScale
.putFloat() //uFogVerticalScale
// only used for debugging
.putInt() //uFogDebugMode // 1 = render everything with fog color // 7 = use debug rendering
.putInt() //uFogFalloffType
// fog config
.putFloat() // uFarFogStart
.putFloat() // uFarFogLength
.putFloat() // uFarFogMin
.putFloat() // uFarFogRange
.putFloat() // uFarFogDensity
// height fog config
.putFloat() // uHeightFogStart
.putFloat() // uHeightFogLength
.putFloat() // uHeightFogMin
.putFloat() // uHeightFogRange
.putFloat() // uHeightFogDensity
// ??
.putInt() // uHeightFogEnabled
.putInt() // uHeightFogFalloffType
.putInt() // uHeightBasedOnCamera
.putFloat() // uHeightFogBaseHeight
.putInt() // uHeightFogAppliesUp
.putInt() // uHeightFogAppliesDown
.putInt() // uUseSphericalFog
.putInt() // uHeightFogMixingMode
.putFloat() // uCameraBlockYPos
.putMat4f() // uInvMvmProj
.get();
// create data //
int lodDrawDistance = Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistanceRadius.get() * LodUtil.CHUNK_WIDTH;
Mat4f inverseMvmProjMatrix = new Mat4f(renderParams.dhMvmProjMatrix);
inverseMvmProjMatrix.invert();
if (renderParams.dhMvmProjMatrix == null)
{
return;
}
Color fogColor = this.getFogColor(renderParams.partialTicks);
// fog config
float farFogStart = Config.Client.Advanced.Graphics.Fog.farFogStart.get();
float farFogEnd = Config.Client.Advanced.Graphics.Fog.farFogEnd.get();
float farFogMin = Config.Client.Advanced.Graphics.Fog.farFogMin.get();
float farFogMax = Config.Client.Advanced.Graphics.Fog.farFogMax.get();
float farFogDensity = Config.Client.Advanced.Graphics.Fog.farFogDensity.get();
// override fog if underwater
if (MC_RENDER.isFogStateSpecial())
{
// hide everything behind fog
farFogStart = 0.0f;
farFogEnd = 0.0f;
}
// height config
EDhApiHeightFogMixMode heightFogMixingMode = Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogMixMode.get();
boolean heightFogEnabled = heightFogMixingMode != EDhApiHeightFogMixMode.SPHERICAL && heightFogMixingMode != EDhApiHeightFogMixMode.CYLINDRICAL;
boolean useSphericalFog = heightFogMixingMode == EDhApiHeightFogMixMode.SPHERICAL;
EDhApiHeightFogDirection heightFogCameraDirection = Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogDirection.get();
float heightFogStart = Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogStart.get();
float heightFogEnd = Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogEnd.get();
float heightFogMin = Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogMin.get();
float heightFogMax = Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogMax.get();
float heightFogDensity = Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogDensity.get();
// upload data //
ByteBuffer buffer = ByteBuffer.allocateDirect(uniformBufferSize);
buffer.order(ByteOrder.nativeOrder());
buffer = Std140Builder.intoBuffer(buffer)
// fog uniforms
.putVec4(
fogColor.getRed() / 255.0f,
fogColor.getGreen() / 255.0f,
fogColor.getBlue() / 255.0f,
fogColor.getAlpha() / 255.0f) // uFogColor
.putFloat(1.f / lodDrawDistance) //uFogScale
.putFloat(1.f / MC.getWrappedClientLevel().getMaxHeight()) //uFogVerticalScale
// only used for debugging
.putInt(0) //uFogDebugMode // 1 = render everything with fog color // 7 = use debug rendering
.putInt(Config.Client.Advanced.Graphics.Fog.farFogFalloff.get().value) //uFogFalloffType
// fog config
.putFloat(farFogStart) // uFarFogStart
.putFloat(farFogEnd - farFogStart) // uFarFogLength
.putFloat(farFogMin) // uFarFogMin
.putFloat(farFogMax - farFogMin) // uFarFogRange
.putFloat(farFogDensity) // uFarFogDensity
// height fog config
.putFloat(heightFogStart) // uHeightFogStart
.putFloat(heightFogEnd - heightFogStart) // uHeightFogLength
.putFloat(heightFogMin) // uHeightFogMin
.putFloat(heightFogMax - heightFogMin) // uHeightFogRange
.putFloat(heightFogDensity) // uHeightFogDensity
// ??
.putInt(heightFogEnabled ? 1 : 0) // uHeightFogEnabled
.putInt(Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogFalloff.get().value) // uHeightFogFalloffType
.putInt(heightFogCameraDirection.basedOnCamera ? 1 : 0) // uHeightBasedOnCamera
.putFloat(Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogBaseHeight.get()) // uHeightFogBaseHeight
.putInt(heightFogCameraDirection.fogAppliesUp ? 1 : 0) // uHeightFogAppliesUp
.putInt(heightFogCameraDirection.fogAppliesDown ? 1 : 0) // uHeightFogAppliesDown
.putInt(useSphericalFog ? 1 : 0) // uUseSphericalFog
.putInt(heightFogMixingMode.value) // uHeightFogMixingMode
.putFloat((float)MC_RENDER.getCameraExactPosition().y) // uCameraBlockYPos
.putMat4f(inverseMvmProjMatrix.createJomlMatrix()) // uInvMvmProj
.get()
;
this.fragUniformBuffer = BlazeUniformUtil.createBuffer("fragUniformBlock", uniformBufferSize, this.fragUniformBuffer);
GpuBufferSlice bufferSlice = new GpuBufferSlice(this.fragUniformBuffer, 0, uniformBufferSize);
COMMAND_ENCODER.writeToBuffer(bufferSlice, buffer);
}
this.renderFogToTexture();
this.applyRenderer.render(this.fogColorTextureWrapper.texture, BlazeDhMetaRenderer.INSTANCE.dhDepthTextureWrapper.texture, BlazeDhMetaRenderer.INSTANCE.dhColorTextureWrapper.texture);
}
private Color getFogColor(float partialTicks)
{
Color fogColor;
if (Config.Client.Advanced.Graphics.Fog.colorMode.get() == EDhApiFogColorMode.USE_SKY_COLOR)
{
fogColor = MC_RENDER.getSkyColor();
}
else
{
fogColor = MC_RENDER.getFogColor(partialTicks);
}
return fogColor;
}
private void renderFogToTexture()
{
try (RenderPass renderPass = COMMAND_ENCODER.createRenderPass(
this::getRenderPassName,
this.fogColorTextureWrapper.textureView,
/*optionalClearColorAsInt*/ OptionalInt.empty(),
this.fogDepthTextureWrapper.textureView,
/*optionalDepthValueAsDouble*/ OptionalDouble.empty()))
{
renderPass.bindTexture("uDhDepthTexture", BlazeDhMetaRenderer.INSTANCE.dhDepthTextureWrapper.textureView, BlazeDhMetaRenderer.INSTANCE.dhDepthTextureWrapper.textureSampler);
renderPass.setUniform("fragUniformBlock", this.fragUniformBuffer);
renderPass.setVertexBuffer(0, this.vboGpuBuffer); // vertex buffer can only be "0" lol
renderPass.setPipeline(this.pipeline);
renderPass.draw(/*indexStart*/ 0, /*indexCount*/ 4);
}
}
private String getRenderPassName() { return "distantHorizons:McFogRenderer"; }
//endregion
}
#endif
@@ -0,0 +1,283 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.render.blaze.postProcessing;
#if MC_VER <= MC_1_21_10
public class BlazeDhSsaoRenderer {}
#else
import com.mojang.blaze3d.buffers.GpuBuffer;
import com.mojang.blaze3d.buffers.GpuBufferSlice;
import com.mojang.blaze3d.buffers.Std140Builder;
import com.mojang.blaze3d.buffers.Std140SizeCalculator;
import com.mojang.blaze3d.pipeline.BlendFunction;
import com.mojang.blaze3d.pipeline.RenderPipeline;
import com.mojang.blaze3d.platform.DestFactor;
import com.mojang.blaze3d.platform.SourceFactor;
import com.mojang.blaze3d.systems.CommandEncoder;
import com.mojang.blaze3d.systems.GpuDevice;
import com.mojang.blaze3d.systems.RenderPass;
import com.mojang.blaze3d.systems.RenderSystem;
import com.seibel.distanthorizons.common.render.blaze.BlazeDhMetaRenderer;
import com.seibel.distanthorizons.common.render.blaze.apply.BlazeDhApplyRenderer;
import com.seibel.distanthorizons.common.render.blaze.wrappers.RenderPipelineBuilderWrapper;
import com.seibel.distanthorizons.common.render.blaze.wrappers.texture.BlazeTextureWrapper;
import com.seibel.distanthorizons.common.render.blaze.util.BlazePostProcessUtil;
import com.seibel.distanthorizons.common.render.blaze.util.BlazeUniformUtil;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.render.RenderParams;
import com.seibel.distanthorizons.core.util.RenderUtil;
import com.seibel.distanthorizons.core.util.math.Mat4f;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhSsaoRenderer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.OptionalDouble;
import java.util.OptionalInt;
/** Renders SSAO to the DH LODs. */
public class BlazeDhSsaoRenderer implements IDhSsaoRenderer
{
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
private static final GpuDevice GPU_DEVICE = RenderSystem.getDevice();
private static final CommandEncoder COMMAND_ENCODER = GPU_DEVICE.createCommandEncoder();
public static final BlazeDhSsaoRenderer INSTANCE = new BlazeDhSsaoRenderer();
private BlazeDhApplyRenderer applyRenderer;
private RenderPipeline pipeline;
private boolean init = false;
private GpuBuffer fragUniformBuffer;
private GpuBuffer applyFragUniformBuffer;
private GpuBuffer vboGpuBuffer;
private final BlazeTextureWrapper ssaoColorTextureWrapper = BlazeTextureWrapper.createColor("dh_ssao_color_texture");
/** We don't want to actually write any depth data, but blaze3D complains if we don't bind a depth texture. */
private final BlazeTextureWrapper ssaoDepthTextureWrapper = BlazeTextureWrapper.createDepth("dh_ssao_depth_texture");
//=============//
// constructor //
//=============//
//region
private BlazeDhSsaoRenderer() { }
private void tryInit()
{
if (this.init)
{
return;
}
this.init = true;
this.applyRenderer = new BlazeDhApplyRenderer(
"ssao_apply_to_dh",
new BlendFunction(SourceFactor.ZERO, DestFactor.SRC_ALPHA, SourceFactor.ZERO, DestFactor.ONE),
"apply/blaze/vert", "ssao/blaze/apply",
/*uniforms*/ new String[] { "applyFragUniformBlock" }
);
RenderPipelineBuilderWrapper pipelineBuilder = new RenderPipelineBuilderWrapper();
{
pipelineBuilder.withFaceCulling(false);
pipelineBuilder.withDepthWrite(false);
pipelineBuilder.withDepthTest(RenderPipelineBuilderWrapper.EDhDepthTest.NONE);
pipelineBuilder.withColorWrite(true);
pipelineBuilder.withoutBlend();
pipelineBuilder.withPolygonMode(RenderPipelineBuilderWrapper.EDhPolygonMode.FILL);
pipelineBuilder.withName("ssao_render");
pipelineBuilder.withVertexShader("ssao/blaze/vert");
pipelineBuilder.withFragmentShader("ssao/blaze/frag");
pipelineBuilder.withSampler("uDhDepthTexture");
pipelineBuilder.withUniformBuffer("fragUniformBlock");
pipelineBuilder.withVertexFormat(BlazePostProcessUtil.createVertexFormat());
pipelineBuilder.withVertexMode(RenderPipelineBuilderWrapper.EDhVertexMode.TRIANGLE_FAN);
}
this.pipeline = pipelineBuilder.build();
this.vboGpuBuffer = BlazePostProcessUtil.createAndUploadScreenVertexData("McSsao");
}
//endregion
//========//
// render //
//========//
//region
@Override
public void render(RenderParams renderParams)
{
this.tryInit();
if (BlazeDhMetaRenderer.INSTANCE.dhDepthTextureWrapper.isEmpty()
|| BlazeDhMetaRenderer.INSTANCE.dhColorTextureWrapper.isEmpty())
{
return;
}
// textures
this.ssaoColorTextureWrapper.tryCreateOrResize();
this.ssaoDepthTextureWrapper.tryCreateOrResize();
// frag uniforms
{
int uniformBufferSize = new Std140SizeCalculator()
.putInt() // uSampleCount\
.putFloat() // uRadius
.putFloat() // uStrength
.putFloat() // uMinLight
.putFloat() // uBias
.putFloat() // uFadeDistanceInBlocks
.putMat4f() // uInvProj
.putMat4f() // uProj
.get();
// create data //
Mat4f projMatrix = new Mat4f(renderParams.dhProjectionMatrix);
Mat4f invertedProjMatrix = new Mat4f(renderParams.dhProjectionMatrix);
invertedProjMatrix.invert();
// upload data //
ByteBuffer buffer = ByteBuffer.allocateDirect(uniformBufferSize);
buffer.order(ByteOrder.nativeOrder());
buffer = Std140Builder.intoBuffer(buffer)
.putInt(6) // uSampleCount
.putFloat(4.0f) // uRadius
.putFloat(0.2f) // uStrength
.putFloat(0.25f) // uMinLight
.putFloat(0.02f) // uBias
.putFloat(1_600.0f) // uFadeDistanceInBlocks
.putMat4f(invertedProjMatrix.createJomlMatrix())
.putMat4f(projMatrix.createJomlMatrix())
.get()
;
this.fragUniformBuffer = BlazeUniformUtil.createBuffer("fragUniformBlock", uniformBufferSize, this.fragUniformBuffer);
GpuBufferSlice bufferSlice = new GpuBufferSlice(this.fragUniformBuffer, 0, uniformBufferSize);
COMMAND_ENCODER.writeToBuffer(bufferSlice, buffer);
}
// apply frag uniforms
{
int uniformBufferSize = new Std140SizeCalculator()
.putVec2() // uViewSize
.putInt() // uBlurRadius
.putFloat() // uNearClipPlane
.putFloat() // uFarClipPlane
.get();
// create data //
float viewWidth = (float)MC_RENDER.getTargetFramebufferViewportWidth();
float viewHeight = (float)MC_RENDER.getTargetFramebufferViewportHeight();
float nearClipPlane = RenderUtil.getNearClipPlaneInBlocks();
float farClipPlane = RenderUtil.getFarClipPlaneDistanceInBlocks();
// upload data //
ByteBuffer buffer = ByteBuffer.allocateDirect(uniformBufferSize);
buffer.order(ByteOrder.nativeOrder());
buffer = Std140Builder.intoBuffer(buffer)
.putVec2(viewWidth, viewHeight) // uViewSize
.putInt(2) // uBlurRadius
.putFloat(nearClipPlane) // uNearClipPlane
.putFloat(farClipPlane) // uFarClipPlane
.get()
;
this.applyFragUniformBuffer = BlazeUniformUtil.createBuffer("applyFragUniformBlock", uniformBufferSize, this.applyFragUniformBuffer);
GpuBufferSlice bufferSlice = new GpuBufferSlice(this.applyFragUniformBuffer, 0, uniformBufferSize);
COMMAND_ENCODER.writeToBuffer(bufferSlice, buffer);
}
this.renderSsaoToTexture();
this.applyRenderer.setUniform("applyFragUniformBlock", this.applyFragUniformBuffer);
this.applyRenderer.render(this.ssaoColorTextureWrapper.texture, BlazeDhMetaRenderer.INSTANCE.dhDepthTextureWrapper.texture, BlazeDhMetaRenderer.INSTANCE.dhColorTextureWrapper.texture);
}
private void renderSsaoToTexture()
{
try (RenderPass renderPass = COMMAND_ENCODER.createRenderPass(
this::getRenderPassName,
this.ssaoColorTextureWrapper.textureView,
/*optionalClearColorAsInt*/ OptionalInt.empty(),
this.ssaoDepthTextureWrapper.textureView,
/*optionalDepthValueAsDouble*/ OptionalDouble.empty()))
{
renderPass.bindTexture("uDhDepthTexture", BlazeDhMetaRenderer.INSTANCE.dhDepthTextureWrapper.textureView, BlazeDhMetaRenderer.INSTANCE.dhDepthTextureWrapper.textureSampler);
renderPass.setUniform("fragUniformBlock", this.fragUniformBuffer);
renderPass.setVertexBuffer(0, this.vboGpuBuffer); // vertex buffer can only be "0" lol
renderPass.setPipeline(this.pipeline);
renderPass.draw(/*indexStart*/ 0, /*indexCount*/ 4);
}
}
private String getRenderPassName() { return "distantHorizons:McSsaoRenderer"; }
//endregion
}
#endif
@@ -0,0 +1,267 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.render.blaze.postProcessing;
#if MC_VER <= MC_1_21_10
public class BlazeVanillaFadeRenderer {}
#else
import com.mojang.blaze3d.buffers.GpuBuffer;
import com.mojang.blaze3d.buffers.GpuBufferSlice;
import com.mojang.blaze3d.buffers.Std140Builder;
import com.mojang.blaze3d.buffers.Std140SizeCalculator;
import com.mojang.blaze3d.pipeline.RenderPipeline;
import com.mojang.blaze3d.platform.PolygonMode;
import com.mojang.blaze3d.shaders.UniformType;
import com.mojang.blaze3d.systems.CommandEncoder;
import com.mojang.blaze3d.systems.GpuDevice;
import com.mojang.blaze3d.systems.RenderPass;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.VertexFormat;
import com.seibel.distanthorizons.common.render.blaze.BlazeDhMetaRenderer;
import com.seibel.distanthorizons.common.render.blaze.apply.BlazeDhCopyRenderer;
import com.seibel.distanthorizons.common.render.blaze.util.BlazePostProcessUtil;
import com.seibel.distanthorizons.common.render.blaze.util.BlazeUniformUtil;
import com.seibel.distanthorizons.common.render.blaze.wrappers.RenderPipelineBuilderWrapper;
import com.seibel.distanthorizons.common.render.blaze.wrappers.texture.BlazeTextureViewWrapper;
import com.seibel.distanthorizons.common.render.blaze.wrappers.texture.BlazeTextureWrapper;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.render.RenderParams;
import com.seibel.distanthorizons.core.util.RenderUtil;
import com.seibel.distanthorizons.core.util.math.Mat4f;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhVanillaFadeRenderer;
import net.minecraft.client.Minecraft;
import net.minecraft.resources.Identifier;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.OptionalDouble;
import java.util.OptionalInt;
/**
* Fades the vanilla chunks
* into DH's LODs.
*/
public class BlazeVanillaFadeRenderer implements IDhVanillaFadeRenderer
{
public static final DhLogger LOGGER = new DhLoggerBuilder().build();
private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
private static final GpuDevice GPU_DEVICE = RenderSystem.getDevice();
private static final CommandEncoder COMMAND_ENCODER = GPU_DEVICE.createCommandEncoder();
public static final BlazeVanillaFadeRenderer INSTANCE = new BlazeVanillaFadeRenderer();
private RenderPipeline pipeline;
private boolean init = false;
private GpuBuffer fragUniformBuffer;
private GpuBuffer vboGpuBuffer;
public final BlazeTextureWrapper fadeColorTextureWrapper = BlazeTextureWrapper.createColor("DhVanillaFadeColorTexture");
/** We don't want to actually write any depth data, but blaze3D complains if we don't bind a depth texture. */
private final BlazeTextureWrapper fadeDepthTextureWrapper = BlazeTextureWrapper.createDepth("DhVanillaFadeDepthTexture");
public final BlazeTextureViewWrapper mcDepthTextureWrapper = new BlazeTextureViewWrapper();
public final BlazeTextureViewWrapper mcColorTextureWrapper = new BlazeTextureViewWrapper();
//=============//
// constructor //
//=============//
//region
private BlazeVanillaFadeRenderer() { }
private void tryInit()
{
if (this.init)
{
return;
}
this.init = true;
RenderPipelineBuilderWrapper pipelineBuilder = new RenderPipelineBuilderWrapper();
{
pipelineBuilder.withFaceCulling(false);
pipelineBuilder.withDepthWrite(false);
pipelineBuilder.withDepthTest(RenderPipelineBuilderWrapper.EDhDepthTest.NONE);
pipelineBuilder.withColorWrite(true);
pipelineBuilder.withoutBlend();
pipelineBuilder.withPolygonMode(RenderPipelineBuilderWrapper.EDhPolygonMode.FILL);
pipelineBuilder.withName("vanilla_fade");
pipelineBuilder.withVertexShader("fade/blaze/vert");
pipelineBuilder.withFragmentShader("fade/blaze/vanilla_fade");
pipelineBuilder.withSampler("uMcDepthTexture");
pipelineBuilder.withSampler("uCombinedMcDhColorTexture");
pipelineBuilder.withSampler("uDhDepthTexture");
pipelineBuilder.withSampler("uDhColorTexture");
pipelineBuilder.withUniformBuffer("fragUniformBlock");
pipelineBuilder.withVertexFormat(BlazePostProcessUtil.createVertexFormat());
pipelineBuilder.withVertexMode(RenderPipelineBuilderWrapper.EDhVertexMode.TRIANGLE_FAN);
}
this.pipeline = pipelineBuilder.build();
this.vboGpuBuffer = BlazePostProcessUtil.createAndUploadScreenVertexData("McFadeRenderer");
}
//endregion
//========//
// render //
//========//
//region
@Override
public void render(RenderParams renderParams)
{
this.tryInit();
if (BlazeDhMetaRenderer.INSTANCE.dhDepthTextureWrapper.isEmpty()
|| BlazeDhMetaRenderer.INSTANCE.dhColorTextureWrapper.isEmpty())
{
return;
}
// textures
this.fadeColorTextureWrapper.tryCreateOrResize();
this.fadeDepthTextureWrapper.tryCreateOrResize();
this.mcDepthTextureWrapper.tryWrap(Minecraft.getInstance().getMainRenderTarget().getDepthTexture());
this.mcColorTextureWrapper.tryWrap(Minecraft.getInstance().getMainRenderTarget().getColorTexture());
{
int uniformBufferSize = new Std140SizeCalculator()
.putInt() // uOnlyRenderLods
.putFloat() // uStartFadeBlockDistance
.putFloat() // uEndFadeBlockDistance
.putFloat() // uMaxLevelHeight
.putMat4f() // uDhInvMvmProj
.putMat4f() // uMcInvMvmProj
.get();
// create data //
float dhNearClipDistance = RenderUtil.getNearClipPlaneInBlocks();
// this added value prevents the near clip plane and discard circle from touching, which looks bad
dhNearClipDistance += 16f;
// measured in blocks
// these multipliers in James' tests should provide a fairly smooth transition
// without having underdraw issues
float fadeStartDistance = dhNearClipDistance * 1.5f;
float fadeEndDistance = dhNearClipDistance * 1.9f;
Mat4f inverseMcModelViewProjectionMatrix = new Mat4f(renderParams.mcProjectionMatrix);
inverseMcModelViewProjectionMatrix.multiply(renderParams.mcModelViewMatrix);
inverseMcModelViewProjectionMatrix.invert();
Mat4f inverseMcMvmProjMatrix = inverseMcModelViewProjectionMatrix;
Mat4f dhProjectionMatrix = RenderUtil.createLodProjectionMatrix(renderParams.mcProjectionMatrix);
Mat4f dhModelViewMatrix = RenderUtil.createLodModelViewMatrix(renderParams.mcModelViewMatrix);
Mat4f inverseDhModelViewProjectionMatrix = new Mat4f(dhProjectionMatrix);
inverseDhModelViewProjectionMatrix.multiply(dhModelViewMatrix);
inverseDhModelViewProjectionMatrix.invert();
Mat4f inverseDhMvmProjMatrix = inverseDhModelViewProjectionMatrix;
// upload data //
ByteBuffer buffer = ByteBuffer.allocateDirect(uniformBufferSize);
buffer.order(ByteOrder.nativeOrder());
buffer = Std140Builder.intoBuffer(buffer)
.putInt(Config.Client.Advanced.Debugging.lodOnlyMode.get() ? 1 : 0) // uOnlyRenderLods
.putFloat(fadeStartDistance) // uStartFadeBlockDistance
.putFloat(fadeEndDistance) // uEndFadeBlockDistance
.putFloat(renderParams.clientLevelWrapper.getMaxHeight()) // uMaxLevelHeight
.putMat4f(inverseDhMvmProjMatrix.createJomlMatrix()) // uDhInvMvmProj
.putMat4f(inverseMcMvmProjMatrix.createJomlMatrix()) // uMcInvMvmProj
.get()
;
this.fragUniformBuffer = BlazeUniformUtil.createBuffer("fragUniformBlock", uniformBufferSize, this.fragUniformBuffer);
GpuBufferSlice bufferSlice = new GpuBufferSlice(this.fragUniformBuffer, 0, uniformBufferSize);
COMMAND_ENCODER.writeToBuffer(bufferSlice, buffer);
}
this.renderFadeToTexture();
BlazeDhCopyRenderer.INSTANCE.render(this.fadeColorTextureWrapper, this.mcColorTextureWrapper);
}
private void renderFadeToTexture()
{
try (RenderPass renderPass = COMMAND_ENCODER.createRenderPass(
this::getRenderPassName,
this.fadeColorTextureWrapper.textureView,
/*optionalClearColorAsInt*/ OptionalInt.empty(),
this.fadeDepthTextureWrapper.textureView,
/*optionalDepthValueAsDouble*/ OptionalDouble.empty()))
{
renderPass.bindTexture("uMcDepthTexture", this.mcDepthTextureWrapper.textureView, this.mcDepthTextureWrapper.textureSampler);
renderPass.bindTexture("uCombinedMcDhColorTexture", this.mcColorTextureWrapper.textureView, this.mcColorTextureWrapper.textureSampler);
renderPass.bindTexture("uDhDepthTexture", BlazeDhMetaRenderer.INSTANCE.dhDepthTextureWrapper.textureView, BlazeDhMetaRenderer.INSTANCE.dhDepthTextureWrapper.textureSampler);
renderPass.bindTexture("uDhColorTexture", BlazeDhMetaRenderer.INSTANCE.dhColorTextureWrapper.textureView, BlazeDhMetaRenderer.INSTANCE.dhColorTextureWrapper.textureSampler);
renderPass.setUniform("fragUniformBlock", this.fragUniformBuffer);
renderPass.setVertexBuffer(0, this.vboGpuBuffer); // vertex buffer can only be "0" lol
renderPass.setPipeline(this.pipeline);
renderPass.draw(/*indexStart*/ 0, /*indexCount*/ 4);
}
}
private String getRenderPassName() { return "distantHorizons:McFadeRenderer"; }
//endregion
}
#endif
@@ -0,0 +1,189 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.render.blaze.test;
#if MC_VER <= MC_1_21_10
public class BlazeDhTestTriangleRenderer {}
#else
import com.mojang.blaze3d.buffers.GpuBuffer;
import com.mojang.blaze3d.buffers.GpuBufferSlice;
import com.mojang.blaze3d.pipeline.RenderPipeline;
import com.mojang.blaze3d.platform.PolygonMode;
import com.mojang.blaze3d.systems.CommandEncoder;
import com.mojang.blaze3d.systems.GpuDevice;
import com.mojang.blaze3d.systems.RenderPass;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.textures.*;
import com.mojang.blaze3d.vertex.VertexFormat;
import com.seibel.distanthorizons.common.render.blaze.util.BlazeDhVertexFormatUtil;
import com.seibel.distanthorizons.common.render.blaze.wrappers.RenderPipelineBuilderWrapper;
import com.seibel.distanthorizons.common.render.blaze.wrappers.texture.BlazeTextureViewWrapper;
import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.render.RenderParams;
import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhTestTriangleRenderer;
import net.minecraft.client.Minecraft;
import net.minecraft.resources.Identifier;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.OptionalDouble;
import java.util.OptionalInt;
#if MC_VER <= MC_1_21_11
#else
import com.mojang.blaze3d.pipeline.DepthStencilState;
import com.mojang.blaze3d.platform.CompareOp;
#endif
/**
* Renders the OpenGL/Vulkan triangle
* to the center of the screen to confirm DH's
* apply shader is running correctly
*/
public class BlazeDhTestTriangleRenderer implements IDhTestTriangleRenderer
{
public static final DhLogger LOGGER = new DhLoggerBuilder().build();
private static final GpuDevice GPU_DEVICE = RenderSystem.getDevice();
private static final CommandEncoder COMMAND_ENCODER = GPU_DEVICE.createCommandEncoder();
public static final BlazeDhTestTriangleRenderer INSTANCE = new BlazeDhTestTriangleRenderer();
private RenderPipeline pipeline;
private boolean init = false;
public final BlazeTextureViewWrapper mcColorTextureViewWrapper = new BlazeTextureViewWrapper();
public final BlazeTextureViewWrapper mcDepthTextureViewWrapper = new BlazeTextureViewWrapper();
private GpuBuffer vboGpuBuffer;
//=============//
// constructor //
//=============//
//region
private BlazeDhTestTriangleRenderer() { }
private void tryInit()
{
if (this.init)
{
return;
}
this.init = true;
RenderPipelineBuilderWrapper pipelineBuilder = new RenderPipelineBuilderWrapper();
{
pipelineBuilder.withFaceCulling(false);
pipelineBuilder.withDepthWrite(false);
pipelineBuilder.withDepthTest(RenderPipelineBuilderWrapper.EDhDepthTest.NONE);
pipelineBuilder.withColorWrite(true);
pipelineBuilder.withoutBlend();
pipelineBuilder.withName("triangle_test");
pipelineBuilder.withVertexShader("test/blaze/vert");
pipelineBuilder.withFragmentShader("test/blaze/frag");
VertexFormat vertexFormat = VertexFormat.builder()
.add("vPosition", BlazeDhVertexFormatUtil.SCREEN_POS)
.add("vColor", BlazeDhVertexFormatUtil.RGBA_FLOAT_COLOR)
.build();
pipelineBuilder.withVertexFormat(vertexFormat);
pipelineBuilder.withVertexMode(RenderPipelineBuilderWrapper.EDhVertexMode.TRIANGLES);
}
this.pipeline = pipelineBuilder.build();
this.uploadVertexData();
}
private void uploadVertexData()
{
// vertices for the OpenGL/Vulkan Triangle
float[] vertices = new float[]
{
// PosX,Y, ColorR,G,B,A
-0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f,
0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f,
0.0f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f,
};
int usage = GpuBuffer.USAGE_COPY_DST
| GpuBuffer.USAGE_VERTEX;
int size = vertices.length * Float.BYTES;
this.vboGpuBuffer = GPU_DEVICE.createBuffer(this::getRenderPassName, usage, size);
{
int offset = 0;
int length = vertices.length * Float.BYTES;
GpuBufferSlice bufferSlice = new GpuBufferSlice(this.vboGpuBuffer, offset, length);
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(vertices.length * Float.BYTES);
// Fill buffer with vertices.
byteBuffer.order(ByteOrder.nativeOrder());
byteBuffer.asFloatBuffer().put(vertices);
byteBuffer.rewind();
COMMAND_ENCODER.writeToBuffer(bufferSlice, byteBuffer);
}
}
//endregion
//========//
// render //
//========//
//region
@Override
public void render(RenderParams renderParams)
{
this.tryInit();
this.mcColorTextureViewWrapper.tryWrap(Minecraft.getInstance().getMainRenderTarget().getColorTexture());
this.mcDepthTextureViewWrapper.tryWrap(Minecraft.getInstance().getMainRenderTarget().getDepthTexture());
try (RenderPass renderPass = COMMAND_ENCODER.createRenderPass(
this::getRenderPassName,
this.mcColorTextureViewWrapper.textureView,
/*optionalClearColorAsInt*/ OptionalInt.empty(),
this.mcDepthTextureViewWrapper.textureView,
/*optionalDepthValueAsDouble*/ OptionalDouble.empty()))
{
renderPass.setVertexBuffer(0, this.vboGpuBuffer);
renderPass.setPipeline(this.pipeline);
renderPass.draw(/*indexStart*/ 0, /*indexCount*/ 3);
}
}
private String getRenderPassName() { return "distantHorizons:DhTestRenderer"; }
//endregion
}
#endif
@@ -0,0 +1,122 @@
package com.seibel.distanthorizons.common.render.blaze.util;
#if MC_VER <= MC_1_21_10
public class BlazeDhVertexFormatUtil {}
#else
import com.mojang.blaze3d.vertex.VertexFormatElement;
import com.seibel.distanthorizons.api.enums.config.EDhApiRenderApi;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding.LodQuadBuilder;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.wrapperInterfaces.IVersionConstants;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import org.jetbrains.annotations.NotNull;
/**
* @see LodQuadBuilder
*/
@SuppressWarnings("DataFlowIssue") // ignore null setter warnings in the static constructor (those will only be null if the render API is GL and in that case we should never use these objects)
public class BlazeDhVertexFormatUtil
{
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
@NotNull public static final VertexFormatElement SCREEN_POS;
@NotNull public static final VertexFormatElement RGBA_FLOAT_COLOR;
@NotNull public static final VertexFormatElement SHORT_XYZ_POS;
@NotNull public static final VertexFormatElement BYTE_PAD;
/** contains light and micro-offset */
@NotNull public static final VertexFormatElement META;
@NotNull public static final VertexFormatElement RGBA_UBYTE_COLOR;
@NotNull public static final VertexFormatElement IRIS_MATERIAL;
@NotNull public static final VertexFormatElement IRIS_NORMAL;
@NotNull public static final VertexFormatElement FLOAT_XYZ_POS;
static
{
EDhApiRenderApi renderingApi = Config.Client.Advanced.Graphics.Experimental.renderingApi.get();
if (renderingApi == EDhApiRenderApi.AUTO)
{
IVersionConstants versionConstants = SingletonInjector.INSTANCE.get(IVersionConstants.class);
renderingApi = versionConstants.getDefaultRenderingApi();
}
boolean register = (renderingApi == EDhApiRenderApi.BLAZE_3D);
if (register)
{
LOGGER.debug("Attempting to register ["+VertexFormatElement.class.getSimpleName()+"]...");
try
{
#if MC_VER <= MC_1_21_11
SCREEN_POS = VertexFormatElement.register(/*id*/22, /*index*/0, VertexFormatElement.Type.FLOAT, VertexFormatElement.Usage.POSITION, /*count*/ 2);
RGBA_FLOAT_COLOR = VertexFormatElement.register(/*id*/23, /*index*/0, VertexFormatElement.Type.FLOAT, VertexFormatElement.Usage.COLOR, /*count*/ 4);
SHORT_XYZ_POS = VertexFormatElement.register(/*id*/24, /*index*/0, VertexFormatElement.Type.USHORT, VertexFormatElement.Usage.POSITION, /*count*/ 3);
BYTE_PAD = VertexFormatElement.register(/*id*/25, /*index*/0, VertexFormatElement.Type.BYTE, VertexFormatElement.Usage.GENERIC, /*count*/ 1);
META = VertexFormatElement.register(/*id*/26, /*index*/0, VertexFormatElement.Type.USHORT, VertexFormatElement.Usage.GENERIC, /*count*/ 1);
RGBA_UBYTE_COLOR = VertexFormatElement.register(/*id*/27, /*index*/0, VertexFormatElement.Type.UBYTE, VertexFormatElement.Usage.COLOR, /*count*/ 4);
IRIS_MATERIAL = VertexFormatElement.register(/*id*/28, /*index*/0, VertexFormatElement.Type.BYTE, VertexFormatElement.Usage.GENERIC, /*count*/ 1);
IRIS_NORMAL = VertexFormatElement.register(/*id*/29, /*index*/0, VertexFormatElement.Type.BYTE, VertexFormatElement.Usage.GENERIC, /*count*/ 1);
FLOAT_XYZ_POS = VertexFormatElement.register(/*id*/30, /*index*/0, VertexFormatElement.Type.FLOAT, VertexFormatElement.Usage.POSITION, /*count*/ 3);
#else
SCREEN_POS = VertexFormatElement.register(/*id*/22, /*index*/0, VertexFormatElement.Type.FLOAT, false, /*count*/ 2);
RGBA_FLOAT_COLOR = VertexFormatElement.register(/*id*/23, /*index*/0, VertexFormatElement.Type.FLOAT, false, /*count*/ 4);
SHORT_XYZ_POS = VertexFormatElement.register(/*id*/24, /*index*/0, VertexFormatElement.Type.USHORT, false, /*count*/ 3);
BYTE_PAD = VertexFormatElement.register(/*id*/25, /*index*/0, VertexFormatElement.Type.BYTE, false, /*count*/ 1);
META = VertexFormatElement.register(/*id*/26, /*index*/0, VertexFormatElement.Type.USHORT, false, /*count*/ 1);
RGBA_UBYTE_COLOR = VertexFormatElement.register(/*id*/27, /*index*/0, VertexFormatElement.Type.UBYTE, true, /*count*/ 4);
IRIS_MATERIAL = VertexFormatElement.register(/*id*/28, /*index*/0, VertexFormatElement.Type.BYTE, false, /*count*/ 1);
IRIS_NORMAL = VertexFormatElement.register(/*id*/29, /*index*/0, VertexFormatElement.Type.BYTE, false, /*count*/ 1);
FLOAT_XYZ_POS = VertexFormatElement.register(/*id*/30, /*index*/0, VertexFormatElement.Type.FLOAT, false, /*count*/ 3);
#endif
}
catch (Exception e)
{
String message = "Unable to register one or more ["+VertexFormatElement.class.getSimpleName()+"] this is likely caused by another mod registering their own custom ["+VertexFormatElement.class.getSimpleName()+"]'s. This should be fixed in the next major Minecraft version.";
IMinecraftClientWrapper mc = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
mc.crashMinecraft(message, new Exception(message, e));
// here to make the compiler happy, the process should shut down before this
throw new RuntimeException(e);
}
LOGGER.debug("Successfully registered ["+VertexFormatElement.class.getSimpleName()+"].");
}
else
{
// set to null so we can fail fast with a null pointer if we ever attempt to incorrectly use these
SCREEN_POS = null;
RGBA_FLOAT_COLOR = null;
SHORT_XYZ_POS = null;
BYTE_PAD = null;
META = null;
RGBA_UBYTE_COLOR = null;
IRIS_MATERIAL = null;
IRIS_NORMAL = null;
FLOAT_XYZ_POS = null;
}
}
}
#endif
@@ -0,0 +1,81 @@
package com.seibel.distanthorizons.common.render.blaze.util;
#if MC_VER <= MC_1_21_10
public class BlazePostProcessUtil {}
#else
import com.mojang.blaze3d.buffers.GpuBuffer;
import com.mojang.blaze3d.buffers.GpuBufferSlice;
import com.mojang.blaze3d.systems.CommandEncoder;
import com.mojang.blaze3d.systems.GpuDevice;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.VertexFormat;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.function.Supplier;
/** Contains code that's used by all post-processing effects. */
public class BlazePostProcessUtil
{
private static final GpuDevice GPU_DEVICE = RenderSystem.getDevice();
private static final CommandEncoder COMMAND_ENCODER = GPU_DEVICE.createCommandEncoder();
// vertices for a full-screen quad
private static final float[] VERTICES = new float[]
{
// PosX,Y,
-1f, -1f,
1f, -1f,
1f, 1f,
-1f, 1f,
};
//=========//
// methods //
//=========//
//region
public static GpuBuffer createAndUploadScreenVertexData(String name)
{
Supplier<String> labelSupplier = () -> "distantHorizons:"+name;
int usage = GpuBuffer.USAGE_COPY_DST
| GpuBuffer.USAGE_VERTEX;
int size = VERTICES.length * Float.BYTES;
GpuBuffer vboGpuBuffer = GPU_DEVICE.createBuffer(labelSupplier, usage, size);
{
int length = VERTICES.length * Float.BYTES;
GpuBufferSlice bufferSlice = new GpuBufferSlice(vboGpuBuffer, /*offset*/ 0, length);
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(VERTICES.length * Float.BYTES);
// Fill buffer with vertices.
byteBuffer.order(ByteOrder.nativeOrder());
byteBuffer.asFloatBuffer().put(VERTICES);
byteBuffer.rewind();
COMMAND_ENCODER.writeToBuffer(bufferSlice, byteBuffer);
}
return vboGpuBuffer;
}
public static VertexFormat createVertexFormat()
{
VertexFormat vertexFormat = VertexFormat.builder()
.add("vPosition", BlazeDhVertexFormatUtil.SCREEN_POS)
.build();
return vertexFormat;
}
//endregion
}
#endif
@@ -0,0 +1,46 @@
package com.seibel.distanthorizons.common.render.blaze.util;
#if MC_VER <= MC_1_21_10
public class BlazeUniformUtil {}
#else
import com.mojang.blaze3d.buffers.GpuBuffer;
import com.mojang.blaze3d.systems.CommandEncoder;
import com.mojang.blaze3d.systems.GpuDevice;
import com.mojang.blaze3d.systems.RenderSystem;
import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
public class BlazeUniformUtil
{
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
private static final GpuDevice GPU_DEVICE = RenderSystem.getDevice();
private static final CommandEncoder COMMAND_ENCODER = GPU_DEVICE.createCommandEncoder();
public static GpuBuffer createBuffer(String uniformName, int size, GpuBuffer vboGpuBuffer)
{
// create VBO if needed
if (vboGpuBuffer == null
|| vboGpuBuffer.size() < size)
{
if (vboGpuBuffer != null)
{
vboGpuBuffer.close();
}
int usage = GpuBuffer.USAGE_COPY_DST
| GpuBuffer.USAGE_VERTEX
| GpuBuffer.USAGE_UNIFORM;
vboGpuBuffer = GPU_DEVICE.createBuffer(() -> uniformName, usage, size);
}
return vboGpuBuffer;
}
}
#endif
@@ -0,0 +1,368 @@
package com.seibel.distanthorizons.common.render.blaze.wrappers;
#if MC_VER <= MC_1_21_10
public class RenderPipelineBuilderWrapper {}
#else
import com.mojang.blaze3d.pipeline.BlendFunction;
import com.mojang.blaze3d.pipeline.RenderPipeline;
import com.mojang.blaze3d.platform.PolygonMode;
import com.mojang.blaze3d.shaders.UniformType;
import com.mojang.blaze3d.vertex.VertexFormat;
import net.minecraft.resources.Identifier;
#if MC_VER <= MC_1_21_11
import com.mojang.blaze3d.platform.DepthTestFunction;
#else
import com.mojang.blaze3d.pipeline.ColorTargetState;
import com.mojang.blaze3d.pipeline.DepthStencilState;
import com.mojang.blaze3d.platform.CompareOp;
#endif
import java.io.IOException;
import java.io.InputStream;
import java.util.Optional;
public class RenderPipelineBuilderWrapper
{
public static final String NAME_PREFIX = "distanthorizons:";
private static final String SHADER_RESOURCE_FOLDER = "assets/distanthorizons/shaders/";
private static final ClassLoader CLASS_LOADER = RenderPipelineBuilderWrapper.class.getClassLoader();
private final RenderPipeline.Builder blazePipelineBuilder;
// variables for specific builder options should be put next to their builder methods for simpler organization
//=============//
// constructor //
//=============//
//region
public RenderPipelineBuilderWrapper()
{
this.blazePipelineBuilder = RenderPipeline.builder();
}
//endregion
//==========//
// building //
//==========//
//region
private boolean writeDepth = false;
public RenderPipelineBuilderWrapper withDepthWrite(boolean write)
{
this.writeDepth = write;
return this;
}
private boolean writeColor = false;
public RenderPipelineBuilderWrapper withColorWrite(boolean write)
{
this.writeColor = write;
return this;
}
private BlendFunction blendFunction = null;
public RenderPipelineBuilderWrapper withBlend(BlendFunction blendFunction)
{
this.blendFunction = blendFunction;
return this;
}
public RenderPipelineBuilderWrapper withoutBlend()
{
this.blendFunction = null;
return this;
}
private EDhDepthTest depthTest;
public RenderPipelineBuilderWrapper withDepthTest(EDhDepthTest depthTest)
{
this.depthTest = depthTest;
return this;
}
public RenderPipelineBuilderWrapper withFaceCulling(boolean culling)
{
this.blazePipelineBuilder.withCull(culling);
return this;
}
public RenderPipelineBuilderWrapper withPolygonMode(EDhPolygonMode dhMode)
{
PolygonMode blazeMode;
switch (dhMode)
{
case FILL:
blazeMode = PolygonMode.FILL;
break;
case WIREFRAME:
blazeMode = PolygonMode.WIREFRAME;
break;
default:
throw new UnsupportedOperationException("No polygonMode defined for type ["+dhMode+"].");
}
this.blazePipelineBuilder.withPolygonMode(blazeMode);
return this;
}
public RenderPipelineBuilderWrapper withName(String name) throws IllegalArgumentException
{
// Identifiers must be of a specific format
if (!isValidIdentifier(name))
{
throw new IllegalArgumentException("Non [a-z0-9/._-] character in name: ["+name+"].");
}
this.blazePipelineBuilder.withLocation(Identifier.parse(NAME_PREFIX + name));
return this;
}
public RenderPipelineBuilderWrapper withSampler(String name) throws IllegalArgumentException
{
this.blazePipelineBuilder.withSampler(name);
return this;
}
public RenderPipelineBuilderWrapper withUniformBuffer(String name) throws IllegalArgumentException
{
this.blazePipelineBuilder.withUniform(name, UniformType.UNIFORM_BUFFER);
return this;
}
private VertexFormat vertexFormat = null;
public RenderPipelineBuilderWrapper withVertexFormat(VertexFormat vertexFormat)
{
this.vertexFormat = vertexFormat;
return this;
}
private EDhVertexMode vertexMode = null;
public RenderPipelineBuilderWrapper withVertexMode(EDhVertexMode vertexMode)
{
this.vertexMode = vertexMode;
return this;
}
public RenderPipelineBuilderWrapper withVertexShader(String scriptResourcePath) { return this.withShader(EDhShaderType.VERTEX, scriptResourcePath); }
public RenderPipelineBuilderWrapper withFragmentShader(String scriptResourcePath) { return this.withShader(EDhShaderType.FRAGMENT, scriptResourcePath); }
private RenderPipelineBuilderWrapper withShader(EDhShaderType shaderType, String scriptResourcePath)
{
String fullShaderResourcePath = SHADER_RESOURCE_FOLDER + scriptResourcePath + shaderType.fileExtension;
// confirm the shader file exists
try (InputStream scriptListInputStream = CLASS_LOADER.getResourceAsStream(fullShaderResourcePath))
{
if (scriptListInputStream == null)
{
throw new NullPointerException("Failed to find the SQL Script list file [" + fullShaderResourcePath + "], no auto update scripts can be run.");
}
}
catch (IOException e)
{
// shouldn't happen, but just in case
throw new RuntimeException("Unexpected issue closing resource stream for shader type: ["+shaderType+"] at: ["+fullShaderResourcePath+"], error: ["+e.getMessage()+"].", e);
}
if (shaderType == EDhShaderType.VERTEX)
{
this.blazePipelineBuilder.withVertexShader(Identifier.parse(NAME_PREFIX + scriptResourcePath));
}
else
{
this.blazePipelineBuilder.withFragmentShader(Identifier.parse(NAME_PREFIX + scriptResourcePath));
}
return this;
}
//endregion
//=====//
// end //
//=====//
//region
public RenderPipeline build() throws UnsupportedOperationException
{
// depth/color
{
#if MC_VER <= MC_1_21_11
this.blazePipelineBuilder.withDepthWrite(this.writeDepth);
this.blazePipelineBuilder.withColorWrite(this.writeColor);
if (this.blendFunction != null)
{
this.blazePipelineBuilder.withBlend(this.blendFunction);
}
else
{
this.blazePipelineBuilder.withoutBlend();
}
DepthTestFunction depthTestFunction;
switch (this.depthTest)
{
case NONE:
depthTestFunction = DepthTestFunction.NO_DEPTH_TEST;
break;
case LESS:
depthTestFunction = DepthTestFunction.LESS_DEPTH_TEST;
break;
default:
throw new UnsupportedOperationException("No depth test defined for type ["+this.depthTest+"].");
}
this.blazePipelineBuilder.withDepthTestFunction(depthTestFunction);
#else
CompareOp compareOp;
switch (this.depthTest)
{
case NONE:
compareOp = CompareOp.ALWAYS_PASS;
break;
case LESS:
compareOp = CompareOp.LESS_THAN;
break;
default:
throw new UnsupportedOperationException("No depth test defined for type ["+this.depthTest+"].");
}
this.blazePipelineBuilder.withDepthStencilState(new DepthStencilState(compareOp, this.writeDepth));
this.blazePipelineBuilder.withColorTargetState(
new ColorTargetState(
Optional.ofNullable(this.blendFunction),
this.writeColor ? ColorTargetState.WRITE_ALL : ColorTargetState.WRITE_NONE
)
);
#endif
}
// vertex format
{
VertexFormat.Mode blazeVertexMode;
switch (this.vertexMode)
{
case TRIANGLES:
blazeVertexMode = VertexFormat.Mode.TRIANGLES;
break;
case TRIANGLE_FAN:
blazeVertexMode = VertexFormat.Mode.TRIANGLE_FAN;
break;
case LINES:
blazeVertexMode = VertexFormat.Mode.DEBUG_LINES;
break;
default:
throw new UnsupportedOperationException("No vertex mode defined for type ["+this.vertexMode+"].");
}
this.blazePipelineBuilder.withVertexFormat(vertexFormat, blazeVertexMode);
}
return this.blazePipelineBuilder.build();
}
//endregion
//================//
// helper methods //
//================//
//region
private static boolean isValidIdentifier(String identifier)
{
for (int i = 0; i < identifier.length(); i++)
{
char ch = identifier.charAt(i);
if (!isValidNamespaceChar(ch))
{
return false;
}
}
return true;
}
private static boolean isValidNamespaceChar(final char ch)
{
return ch == '_'
|| ch == '-'
// only lower case characters
|| (ch >= 'a' && ch <= 'z')
|| (ch >= '0' && ch <= '9')
|| ch == '.';
}
//endregion
//================//
// helper classes //
//================//
//region
public enum EDhPolygonMode
{
FILL,
WIREFRAME;
}
public enum EDhVertexMode
{
TRIANGLES,
TRIANGLE_FAN,
LINES;
}
public enum EDhDepthTest
{
NONE,
LESS;
}
private enum EDhShaderType
{
FRAGMENT(".fsh"),
VERTEX(".vsh");
public final String fileExtension;
EDhShaderType(String fileExtension)
{
this.fileExtension = fileExtension;
}
}
//endregion
}
#endif
@@ -0,0 +1,214 @@
package com.seibel.distanthorizons.common.render.blaze.wrappers.buffer;
#if MC_VER <= MC_1_21_10
public class BlazeVertexBufferWrapper {}
#else
import com.mojang.blaze3d.buffers.GpuBuffer;
import com.mojang.blaze3d.buffers.GpuBufferSlice;
import com.mojang.blaze3d.systems.CommandEncoder;
import com.mojang.blaze3d.systems.GpuDevice;
import com.mojang.blaze3d.systems.RenderSystem;
import com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding.IndexBufferBuilder;
import com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding.LodQuadBuilder;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.render.RenderThreadTaskHandler;
import com.seibel.distanthorizons.core.wrapperInterfaces.render.AbstractDhRenderApiDefinition;
import com.seibel.distanthorizons.core.wrapperInterfaces.render.objects.IVertexBufferWrapper;
import org.lwjgl.system.MemoryUtil;
import java.nio.ByteBuffer;
import java.util.concurrent.atomic.AtomicInteger;
public class BlazeVertexBufferWrapper implements IVertexBufferWrapper
{
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
private static final AbstractDhRenderApiDefinition RENDER_DEF = SingletonInjector.INSTANCE.get(AbstractDhRenderApiDefinition.class);
private static final GpuDevice GPU_DEVICE = RenderSystem.getDevice();
private static final CommandEncoder COMMAND_ENCODER = GPU_DEVICE.createCommandEncoder();
private static final AtomicInteger BUFFER_COUNT_REF = new AtomicInteger(0);
public final String name;
public String getName() { return this.name; }
public GpuBuffer vertexGpuBuffer = null;
public int vertexCount = -1;
public int indexCount = -1;
public boolean uploaded = false;
private GpuBuffer indexGpuBuffer = null;
private static GpuBuffer GLOBAL_INDEX_GPU_BUFFER = null;
public GpuBuffer getIndexGpuBuffer()
{
if (RENDER_DEF.useSingleIbo())
{
return GLOBAL_INDEX_GPU_BUFFER;
}
else
{
return this.indexGpuBuffer;
}
}
//=============//
// constructor //
//=============//
//region
static
{
if (RENDER_DEF.useSingleIbo())
{
RenderThreadTaskHandler.INSTANCE.queueRunningOnRenderThread("Global IBO Creation", () ->
{
int maxSize = LodQuadBuilder.getMaxBufferByteSize();
int maxVertexCount = maxSize / LodQuadBuilder.BYTES_PER_VERTEX;
int maxQuadCount = (maxVertexCount / 4);
ByteBuffer indexBuffer = IndexBufferBuilder.createBuffer(maxQuadCount);
int usage = GpuBuffer.USAGE_COPY_DST
| GpuBuffer.USAGE_INDEX;
GLOBAL_INDEX_GPU_BUFFER = GPU_DEVICE.createBuffer(BlazeVertexBufferWrapper::getIndexBufferName, usage, indexBuffer.capacity());
GpuBufferSlice bufferSlice = new GpuBufferSlice(GLOBAL_INDEX_GPU_BUFFER, /*offset*/ 0, indexBuffer.capacity());
COMMAND_ENCODER.writeToBuffer(bufferSlice, indexBuffer);
MemoryUtil.memFree(indexBuffer);
});
}
}
public BlazeVertexBufferWrapper(String name) { this.name = name; }
//endregion
//========//
// upload //
//========//
//region
@Override
public void uploadVertexBuffer(ByteBuffer vertexBuffer, int vertexCount)
{
int oldVertexCount = this.vertexCount;
this.vertexCount = vertexCount;
// 4 vertices per face, but 6 indices (IE 2 triangles) per face, aka need to multiply by 1.5
this.indexCount = (int)(vertexCount * 1.5);
this.uploaded = true;
if (this.vertexGpuBuffer == null
// recreating if the size changes is always necessary (even if we only need a smaller amount)
// due to a bug on Mac where it will attempt to render anything allocated in the buffer
|| oldVertexCount != vertexCount)
{
if (this.vertexGpuBuffer == null)
{
BUFFER_COUNT_REF.incrementAndGet();
//LOGGER.info("Create, count: ["+BUFFER_COUNT_REF.get()+"]");
}
if (this.vertexGpuBuffer != null)
{
this.vertexGpuBuffer.close();
}
int usage = GpuBuffer.USAGE_COPY_DST
| GpuBuffer.USAGE_VERTEX;
int byteSize = (vertexBuffer.limit() - vertexBuffer.position());
this.vertexGpuBuffer = GPU_DEVICE.createBuffer(this::getName, usage, byteSize);
GpuBufferSlice bufferSlice = new GpuBufferSlice(this.vertexGpuBuffer, /*offset*/0, byteSize);
COMMAND_ENCODER.writeToBuffer(bufferSlice, vertexBuffer);
}
}
@Override
public void uploadIndexBuffer(ByteBuffer indexBuffer, int vertexCount)
{
int oldIndexCount = this.indexCount;
// 4 vertices per face, but 6 indices (IE 2 triangles) per face, aka need to multiply by 1.5
this.indexCount = (int)(vertexCount * 1.5);
if (RENDER_DEF.useSingleIbo())
{
// ignore index uploading when running a single IBO
return;
}
// recreating if the size changes is always necessary (even if we only need a smaller amount)
// due to a bug on Mac where it will attempt to render anything allocated in the buffer
if (this.indexGpuBuffer == null
|| oldIndexCount != this.indexCount)
{
if (this.indexGpuBuffer == null)
{
BUFFER_COUNT_REF.incrementAndGet();
}
if (this.indexGpuBuffer != null)
{
this.indexGpuBuffer.close();
}
int usage = GpuBuffer.USAGE_COPY_DST
| GpuBuffer.USAGE_INDEX;
this.indexGpuBuffer = GPU_DEVICE.createBuffer(BlazeVertexBufferWrapper::getIndexBufferName, usage, indexBuffer.capacity());
GpuBufferSlice bufferSlice = new GpuBufferSlice(this.indexGpuBuffer, /*offset*/ 0, indexBuffer.capacity());
COMMAND_ENCODER.writeToBuffer(bufferSlice, indexBuffer);
}
}
private static String getIndexBufferName() { return "distantHorizons:LodIndexBuffer"; }
//endregion
//================//
// base overrides //
//================//
//region
@Override
public void close()
{
if (this.vertexGpuBuffer != null)
{
BUFFER_COUNT_REF.decrementAndGet();
this.vertexGpuBuffer.close();
}
if (this.indexGpuBuffer != null)
{
BUFFER_COUNT_REF.decrementAndGet();
this.indexGpuBuffer.close();
}
//LOGGER.info("Close, count: ["+BUFFER_COUNT_REF.get()+"]");
}
//endregion
}
#endif
@@ -0,0 +1,72 @@
package com.seibel.distanthorizons.common.render.blaze.wrappers.texture;
#if MC_VER <= MC_1_21_10
public class BlazeTextureViewWrapper {}
#else
import com.mojang.blaze3d.systems.CommandEncoder;
import com.mojang.blaze3d.systems.GpuDevice;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.textures.*;
import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import java.util.OptionalDouble;
public class BlazeTextureViewWrapper
{
public static final DhLogger LOGGER = new DhLoggerBuilder().build();
private static final GpuDevice GPU_DEVICE = RenderSystem.getDevice();
private static final CommandEncoder COMMAND_ENCODER = GPU_DEVICE.createCommandEncoder();
public GpuTextureView textureView = null;
public GpuSampler textureSampler = null;
//=======//
// setup //
//=======//
//region
/** does nothing if the texture is already wrapped */
public void tryWrap(GpuTexture texture)
{
this.tryRecreateTextureView(texture);
this.tryCreateSampler();
}
private void tryRecreateTextureView(GpuTexture texture)
{
if (this.textureView == null
|| this.textureView.texture() != texture)
{
if (this.textureView != null)
{
this.textureView.close();
}
this.textureView = GPU_DEVICE.createTextureView(texture);
}
}
private void tryCreateSampler()
{
if (this.textureSampler == null)
{
this.textureSampler = GPU_DEVICE.createSampler(
AddressMode.CLAMP_TO_EDGE, AddressMode.CLAMP_TO_EDGE, // U,V
FilterMode.LINEAR, FilterMode.LINEAR, // minFilter, magFilter
1, // maxAnisotropy
OptionalDouble.empty() // maxLod
);
}
}
//endregion
}
#endif
@@ -0,0 +1,177 @@
package com.seibel.distanthorizons.common.render.blaze.wrappers.texture;
#if MC_VER <= MC_1_21_10
public class BlazeTextureWrapper {}
#else
import com.mojang.blaze3d.buffers.GpuBuffer;
import com.mojang.blaze3d.systems.CommandEncoder;
import com.mojang.blaze3d.systems.GpuDevice;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.textures.*;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.coreapi.util.ColorUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import java.util.OptionalDouble;
public class BlazeTextureWrapper
{
public static final DhLogger LOGGER = new DhLoggerBuilder().build();
private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
private static final GpuDevice GPU_DEVICE = RenderSystem.getDevice();
private static final CommandEncoder COMMAND_ENCODER = GPU_DEVICE.createCommandEncoder();
public final String name;
public final TextureFormat textureFormat;
public GpuTexture texture = null;
public GpuTextureView textureView = null;
public GpuSampler textureSampler = null;
private int width = -1;
private int height = -1;
//==============//
// constructors //
//==============//
//region
public static BlazeTextureWrapper createDepth(String name) { return new BlazeTextureWrapper(name, TextureFormat.DEPTH32); }
public static BlazeTextureWrapper createColor(String name) { return new BlazeTextureWrapper(name, TextureFormat.RGBA8); }
private BlazeTextureWrapper(String name, TextureFormat textureFormat)
{
this.name = name;
this.textureFormat = textureFormat;
}
//endregion
//=========//
// getters //
//=========//
//region
public boolean isEmpty() { return this.texture == null; }
/** @return -1 if the texture is null */
public int getWidth() { return this.width; }
/** @return -1 if the texture is null */
public int getHeight() { return this.height; }
//endregion
//=======//
// setup //
//=======//
//region
/**
* does nothing if the texture is already created and the correct size
* @return true if the texture was (re)created
*/
public boolean tryCreateOrResize()
{
boolean textureChanged = this.tryCreateTexture();
this.tryCreateSampler();
return textureChanged;
}
private boolean tryCreateTexture()
{
int viewWidth = MC_RENDER.getTargetFramebufferViewportWidth();
int viewHeight = MC_RENDER.getTargetFramebufferViewportHeight();
if (this.texture != null
&& this.width == viewWidth
&& this.height == viewHeight)
{
// no changes needed
return false;
}
if (this.texture != null)
{
this.texture.close();
this.textureView.close();
}
this.width = viewWidth;
this.height = viewHeight;
int usage = GpuTexture.USAGE_COPY_DST
| GpuTexture.USAGE_TEXTURE_BINDING
| GpuTexture.USAGE_COPY_SRC
| GpuTexture.USAGE_RENDER_ATTACHMENT;
this.texture = GPU_DEVICE.createTexture(this.name,
usage,
this.textureFormat,
viewWidth, viewHeight,
/*depthOrLayers*/ 1, /*mipLevels*/ 1
);
this.textureView = GPU_DEVICE.createTextureView(this.texture);
return true;
}
private void tryCreateSampler()
{
if (this.textureSampler == null)
{
this.textureSampler = GPU_DEVICE.createSampler(
AddressMode.CLAMP_TO_EDGE, AddressMode.CLAMP_TO_EDGE, // U,V
FilterMode.LINEAR, FilterMode.LINEAR, // minFilter, magFilter
1, // maxAnisotropy
OptionalDouble.empty() // maxLod
);
}
}
//endregion
//==========//
// clearing //
//==========//
//region
/**
* Will throw an exception if not a color texture.
* @see ColorUtil#argbToInt
*/
public void clearColor(int clearArgbColor)
{
if (this.texture != null)
{
COMMAND_ENCODER.clearColorTexture(this.texture, clearArgbColor);
}
}
/** Will throw an exception if not a depth texture. */
public void clearDepth(float depth)
{
if (this.texture != null)
{
COMMAND_ENCODER.clearDepthTexture(this.texture, depth);
}
}
//endregion
}
#endif
@@ -0,0 +1,78 @@
package com.seibel.distanthorizons.common.render.blaze.wrappers.uniform;
#if MC_VER <= MC_1_21_10
public class BlazeLodUniformBufferWrapper {}
#else
import com.mojang.blaze3d.buffers.Std140Builder;
import com.mojang.blaze3d.buffers.Std140SizeCalculator;
import com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding.LodBufferContainer;
import com.seibel.distanthorizons.core.util.math.Vec3f;
import com.seibel.distanthorizons.core.wrapperInterfaces.render.objects.ILodContainerUniformBufferWrapper;
import java.nio.ByteBuffer;
public class BlazeLodUniformBufferWrapper extends BlazeUniformBufferWrapper implements ILodContainerUniformBufferWrapper
{
private boolean uploaded = false;
//=============//
// constructor //
//=============//
//region
public BlazeLodUniformBufferWrapper() { super(BlazeLodUniformBufferWrapper.class.getName()); }
//endregion
//========//
// upload //
//========//
//region
@Override
public void createUniformData(LodBufferContainer bufferContainer)
{
Vec3f modelOffset = new Vec3f(
(float) (bufferContainer.minCornerBlockPos.getX()),
(float) (bufferContainer.minCornerBlockPos.getY()),
(float) (bufferContainer.minCornerBlockPos.getZ()));
// upload data //
int uniformBufferSize = new Std140SizeCalculator()
.putVec3() // uModelOffset
.get();
ByteBuffer buffer = this.getOrCreateBuffer(uniformBufferSize);
Std140Builder.intoBuffer(buffer)
.putVec3(modelOffset.x, modelOffset.y, modelOffset.z) // uModelOffset
.get();
}
@Override
public void tryUpload()
{
if (this.uploaded)
{
return;
}
this.upload();
this.uploaded = true;
}
//endregion
}
#endif
@@ -0,0 +1,130 @@
package com.seibel.distanthorizons.common.render.blaze.wrappers.uniform;
#if MC_VER <= MC_1_21_10
public class BlazeUniformBufferWrapper {}
#else
import com.mojang.blaze3d.buffers.GpuBuffer;
import com.mojang.blaze3d.buffers.GpuBufferSlice;
import com.mojang.blaze3d.systems.CommandEncoder;
import com.mojang.blaze3d.systems.GpuDevice;
import com.mojang.blaze3d.systems.RenderSystem;
import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.wrapperInterfaces.render.objects.IUniformBufferWrapper;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
public class BlazeUniformBufferWrapper implements IUniformBufferWrapper
{
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
private static final GpuDevice GPU_DEVICE = RenderSystem.getDevice();
private static final CommandEncoder COMMAND_ENCODER = GPU_DEVICE.createCommandEncoder();
private final String name;
private int cpuBufferSize = 0;
private int gpuBufferSize = 0;
private ByteBuffer cpuBuffer = null;
public GpuBuffer gpuBuffer = null;
//=============//
// constructor //
//=============//
//region
public BlazeUniformBufferWrapper(String name) { this.name = name; }
//endregion
//========//
// render //
//========//
//region
protected ByteBuffer getOrCreateBuffer(int size)
{
if (this.cpuBuffer == null
|| this.cpuBufferSize != size)
{
this.cpuBuffer = ByteBuffer.allocateDirect(size);
this.cpuBuffer.order(ByteOrder.nativeOrder());
this.cpuBufferSize = size;
}
return this.cpuBuffer;
}
@Override
public void upload() throws IllegalStateException
{
if (this.cpuBuffer == null)
{
throw new IllegalStateException("Upload called before buffer was created");
}
if (this.gpuBuffer == null
|| this.gpuBufferSize != this.cpuBufferSize)
{
if (this.gpuBuffer != null)
{
this.gpuBuffer.close();
}
int usage = GpuBuffer.USAGE_COPY_DST
| GpuBuffer.USAGE_VERTEX
| GpuBuffer.USAGE_UNIFORM;
this.gpuBuffer = GPU_DEVICE.createBuffer(this::getBufferName, usage, this.cpuBufferSize);
this.gpuBufferSize = this.cpuBufferSize;
}
int byteSize = (this.cpuBuffer.limit() - this.cpuBuffer.position());
GpuBufferSlice bufferSlice = new GpuBufferSlice(this.gpuBuffer, /*offset*/0, byteSize);
if (!bufferSlice.buffer().isClosed())
{
COMMAND_ENCODER.writeToBuffer(bufferSlice, this.cpuBuffer);
}
else
{
LOGGER.warn("Uploading to buffer ["+this.name+"] failed due to already being closed");
}
}
private String getBufferName() { return this.name; }
//endregion
//================//
// base overrides //
//================//
//region
@Override
public void close()
{
if (this.gpuBuffer != null)
{
this.gpuBuffer.close();
}
}
//endregion
}
#endif
@@ -0,0 +1,197 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.render.openGl;
import com.seibel.distanthorizons.api.enums.config.EDhApiGpuUploadMethod;
import com.seibel.distanthorizons.common.render.openGl.glObject.buffer.GLIndexBuffer;
import com.seibel.distanthorizons.common.render.openGl.glObject.buffer.GLVertexBuffer;
import com.seibel.distanthorizons.common.render.openGl.glObject.shader.GlShaderProgram;
import com.seibel.distanthorizons.common.render.openGl.glObject.vertexAttribute.GlAbstractVertexAttribute;
import com.seibel.distanthorizons.common.render.openGl.glObject.vertexAttribute.GlVertexPointer;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper;
import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.render.RenderParams;
import com.seibel.distanthorizons.core.render.renderer.AbstractDebugWireframeRenderer;
import com.seibel.distanthorizons.core.util.math.Mat4f;
import org.lwjgl.opengl.GL32;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
/**
* Handles rendering the wireframe particles
* that are used for seeing what the system's doing.
*/
public class GlDhDebugWireframeRenderer extends AbstractDebugWireframeRenderer
{
public static GlDhDebugWireframeRenderer INSTANCE = new GlDhDebugWireframeRenderer();
public static final DhLogger LOGGER = new DhLoggerBuilder().build();
private static final MinecraftGLWrapper GLMC = MinecraftGLWrapper.INSTANCE;
// rendering setup
private GlShaderProgram basicShader;
private GLVertexBuffer vertexBuffer;
private GLIndexBuffer indexBuffer;
private GlAbstractVertexAttribute va;
private boolean init = false;
/** A box from 0,0,0 to 1,1,1 */
private static final float[] BOX_VERTICES = {
//region
// Pos x y z
0, 0, 0,
1, 0, 0,
1, 1, 0,
0, 1, 0,
0, 0, 1,
1, 0, 1,
1, 1, 1,
0, 1, 1,
//endregion
};
private static final int[] BOX_OUTLINE_INDICES = {
//region
0, 1,
1, 2,
2, 3,
3, 0,
4, 5,
5, 6,
6, 7,
7, 4,
0, 4,
1, 5,
2, 6,
3, 7,
//endregion
};
//=============//
// constructor //
//=============//
//region
private GlDhDebugWireframeRenderer() { }
public void init()
{
if (this.init)
{
return;
}
this.init = true;
this.va = GlAbstractVertexAttribute.create();
this.va.bind();
// Pos
this.va.setVertexAttribute(0, 0, GlVertexPointer.addVec3Pointer(false));
this.va.completeAndCheck(Float.BYTES * 3);
this.basicShader = new GlShaderProgram(
"assets/distanthorizons/shaders/debug/gl/vert.vert",
"assets/distanthorizons/shaders/debug/gl/frag.frag",
"vPosition"
);
this.createBuffer();
}
private void createBuffer()
{
// box vertices
ByteBuffer boxVerticesBuffer = ByteBuffer.allocateDirect(BOX_VERTICES.length * Float.BYTES);
boxVerticesBuffer.order(ByteOrder.nativeOrder());
boxVerticesBuffer.asFloatBuffer().put(BOX_VERTICES);
boxVerticesBuffer.rewind();
this.vertexBuffer = new GLVertexBuffer(false);
this.vertexBuffer.bind();
this.vertexBuffer.uploadBuffer(boxVerticesBuffer, 8, EDhApiGpuUploadMethod.DATA, BOX_VERTICES.length * Float.BYTES);
// outline vertex indexes
ByteBuffer boxOutlineBuffer = ByteBuffer.allocateDirect(BOX_OUTLINE_INDICES.length * Integer.BYTES);
boxOutlineBuffer.order(ByteOrder.nativeOrder());
boxOutlineBuffer.asIntBuffer().put(BOX_OUTLINE_INDICES);
boxOutlineBuffer.rewind();
this.indexBuffer = new GLIndexBuffer(false);
this.indexBuffer.uploadBuffer(boxOutlineBuffer, EDhApiGpuUploadMethod.DATA, BOX_OUTLINE_INDICES.length * Integer.BYTES, GL32.GL_STATIC_DRAW);
}
//endregion
//===========//
// rendering //
//===========//
//region
@Override
public void render(RenderParams renderParams)
{
this.init();
GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, GL32.GL_LINE);
GLMC.enableDepthTest();
this.basicShader.bind();
this.va.bind();
this.va.bindBufferToAllBindingPoints(this.vertexBuffer.getId());
this.indexBuffer.bind();
super.render(renderParams);
// revert to prevent issues with the following passes
GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, GL32.GL_FILL);
}
@Override
public void renderBox(Box box)
{
Mat4f boxTransform = Mat4f.createTranslateMatrix(box.minPos.x - this.camPosFloatThisFrame.x, box.minPos.y - this.camPosFloatThisFrame.y, box.minPos.z - this.camPosFloatThisFrame.z);
boxTransform.multiply(Mat4f.createScaleMatrix(box.maxPos.x - box.minPos.x, box.maxPos.y - box.minPos.y, box.maxPos.z - box.minPos.z));
Mat4f transformMatrix = this.dhMvmProjMatrixThisFrame.copy();
transformMatrix.multiply(boxTransform);
this.basicShader.setUniform(this.basicShader.getUniformLocation("uTransform"), transformMatrix);
this.basicShader.setUniform(this.basicShader.getUniformLocation("uColor"), box.color);
GL32.glDrawElements(GL32.GL_LINES, BOX_OUTLINE_INDICES.length, GL32.GL_UNSIGNED_INT, 0);
}
//endregion
}
@@ -0,0 +1,467 @@
package com.seibel.distanthorizons.common.render.openGl;
import com.seibel.distanthorizons.api.enums.rendering.EDhApiRenderPass;
import com.seibel.distanthorizons.api.interfaces.override.rendering.IDhApiFramebuffer;
import com.seibel.distanthorizons.api.interfaces.override.rendering.IDhApiShaderProgram;
import com.seibel.distanthorizons.api.methods.events.abstractEvents.*;
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam;
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiTextureCreatedParam;
import com.seibel.distanthorizons.common.render.openGl.glObject.GLProxy;
import com.seibel.distanthorizons.common.render.openGl.glObject.GlDhFramebuffer;
import com.seibel.distanthorizons.common.render.openGl.glObject.texture.*;
import com.seibel.distanthorizons.common.render.openGl.postProcessing.apply.GlDhApplyShader;
import com.seibel.distanthorizons.common.render.openGl.terrain.GlDhTerrainShaderProgram;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper;
import com.seibel.distanthorizons.common.wrappers.misc.LightMapWrapper;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.render.DhApiRenderProxy;
import com.seibel.distanthorizons.core.render.RenderParams;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.ILightMapWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.AbstractOptifineAccessor;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IOptifineAccessor;
import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhMetaRenderer;
import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector;
import com.seibel.distanthorizons.coreapi.DependencyInjection.OverrideInjector;
import org.jetbrains.annotations.Nullable;
import org.lwjgl.opengl.GL32;
public class GlDhMetaRenderer implements IDhMetaRenderer
{
public static final DhLogger LOGGER = new DhLoggerBuilder()
.fileLevelConfig(Config.Common.Logging.logRendererEventToFile)
.build();
public static final DhLogger RATE_LIMITED_LOGGER = new DhLoggerBuilder()
.fileLevelConfig(Config.Common.Logging.logRendererEventToFile)
.maxCountPerSecond(4)
.build();
public static final GlDhMetaRenderer INSTANCE = new GlDhMetaRenderer();
private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
private static final MinecraftGLWrapper GLMC = MinecraftGLWrapper.INSTANCE;
private static final IOptifineAccessor OPTIFINE_ACCESSOR = ModAccessorInjector.INSTANCE.get(IOptifineAccessor.class);
private int activeFramebufferId = -1;
private int activeColorTextureId = -1;
private int activeDepthTextureId = -1;
private int textureWidth;
private int textureHeight;
// framebuffer and texture ID's for this renderer
private IDhApiFramebuffer framebuffer;
/** will be null if MC's framebuffer is being used since MC already has a color texture */
@Nullable
private GlDhColorTexture nullableColorTexture;
private GlDhDepthTexture depthTexture;
/**
* If true the {@link GlDhMetaRenderer#framebuffer} is the same as MC's.
* This should only be true in the case of Optifine so LODs won't be overwritten when shaders are enabled.
*/
private boolean usingMcFramebuffer = false;
private boolean renderObjectsCreated = false;
/** used in case there's an API override */
public IDhApiShaderProgram shaderProgramForThisFrame;
//============//
// pre render //
//============//
//region
@Override
public void runRenderPassSetup(RenderParams renderParams)
{
boolean firstPass =
(renderParams.renderPass == EDhApiRenderPass.OPAQUE
|| renderParams.renderPass == EDhApiRenderPass.OPAQUE_AND_TRANSPARENT);
if (!this.renderObjectsCreated)
{
boolean setupSuccess = this.createRenderObjects();
if (!setupSuccess)
{
// shouldn't normally happen, but just in case
return;
}
this.renderObjectsCreated = true;
}
this.shaderProgramForThisFrame = GlDhTerrainRenderer.INSTANCE.getTerrainShaderProgram();
IDhApiShaderProgram lodShaderProgramOverride = OverrideInjector.INSTANCE.get(IDhApiShaderProgram.class);
if (lodShaderProgramOverride != null && this.shaderProgramForThisFrame.overrideThisFrame())
{
this.shaderProgramForThisFrame = lodShaderProgramOverride;
}
this.setGLState(renderParams, firstPass);
this.bindLightmap(renderParams.lightmap);
}
private void setGLState(
DhApiRenderParam renderEventParam,
boolean firstPass)
{
//===================//
// framebuffer setup //
//===================//
// get the active framebuffer
IDhApiFramebuffer framebuffer = this.framebuffer;
IDhApiFramebuffer framebufferOverride = OverrideInjector.INSTANCE.get(IDhApiFramebuffer.class);
if (framebufferOverride != null && framebufferOverride.overrideThisFrame())
{
framebuffer = framebufferOverride;
}
this.setActiveFramebufferId(framebuffer.getId());
framebuffer.bind();
//==========//
// bindings //
//==========//
// by default draw everything as triangles
GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, GL32.GL_FILL);
GLMC.enableFaceCulling();
GLMC.glBlendFunc(GL32.GL_SRC_ALPHA, GL32.GL_ONE_MINUS_SRC_ALPHA);
GLMC.glBlendFuncSeparate(GL32.GL_SRC_ALPHA, GL32.GL_ONE_MINUS_SRC_ALPHA, GL32.GL_ONE, GL32.GL_ZERO);
GL32.glDisable(GL32.GL_SCISSOR_TEST);
// Enable depth test and depth mask
GLMC.enableDepthTest();
GLMC.glDepthFunc(GL32.GL_LESS);
GLMC.enableDepthMask();
// This is required for MC versions 1.21.5+
// due to MC updating the lightmap by changing the viewport size
GL32.glViewport(0, 0, this.textureWidth, this.textureHeight);
this.shaderProgramForThisFrame.bind();
//==========//
// uniforms //
//==========//
IDhApiShaderProgram shaderProgramOverride = OverrideInjector.INSTANCE.get(IDhApiShaderProgram.class);
if (shaderProgramOverride != null)
{
shaderProgramOverride.fillUniformData(renderEventParam);
}
this.shaderProgramForThisFrame.fillUniformData(renderEventParam);
//===============//
// texture setup //
//===============//
// resize the textures if needed
if (MC_RENDER.getTargetFramebufferViewportWidth() != this.textureWidth
|| MC_RENDER.getTargetFramebufferViewportHeight() != this.textureHeight)
{
// just resizing the textures doesn't work when Optifine is present,
// so recreate the textures with the new size instead
this.createAndBindTextures();
}
// set the active textures
int depthTextureId = this.depthTexture.getTextureId();
this.setActiveDepthTextureId(depthTextureId);
if (this.nullableColorTexture != null)
{
int colorTextureId = this.nullableColorTexture.getTextureId();
this.setActiveColorTextureId(colorTextureId);
}
else
{
// get MC's color texture
int colorTextureId = GL32.glGetFramebufferAttachmentParameteri(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT0, GL32.GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME);
this.setActiveColorTextureId(colorTextureId);
}
// needs to be fired after all the textures have been created/bound
boolean clearTextures = !ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeTextureClearEvent.class, renderEventParam);
if (clearTextures)
{
GL32.glClearDepth(1.0);
float[] clearColorValues = new float[4];
GL32.glGetFloatv(GL32.GL_COLOR_CLEAR_VALUE, clearColorValues);
GL32.glClearColor(clearColorValues[0], clearColorValues[1], clearColorValues[2], 1.0f);
if (this.usingMcFramebuffer && framebufferOverride == null)
{
// Due to using MC/Optifine's framebuffer we need to re-bind the depth texture,
// otherwise we'll be writing to MC/Optifine's depth texture which causes rendering issues
framebuffer.addDepthAttachment(this.depthTexture.getTextureId(), EGlDhDepthBufferFormat.DEPTH32F.isCombinedStencil());
// don't clear the color texture, that removes the sky
GL32.glClear(GL32.GL_DEPTH_BUFFER_BIT);
}
else if (firstPass)
{
GL32.glClear(GL32.GL_COLOR_BUFFER_BIT | GL32.GL_DEPTH_BUFFER_BIT);
}
}
}
private boolean createRenderObjects()
{
if (this.renderObjectsCreated)
{
LOGGER.warn("Renderer setup called but it has already completed setup!");
return false;
}
// GLProxy should have already been created by this point, but just in case create it now
GLProxy.getInstance();
LOGGER.info("Setting up renderer");
// create or get the frame buffer
if (OPTIFINE_ACCESSOR != null)
{
// use MC/Optifine's default Framebuffer so shaders won't remove the LODs
int currentFramebufferId = MC_RENDER.getTargetFramebuffer();
this.framebuffer = new GlDhFramebuffer(currentFramebufferId);
this.usingMcFramebuffer = true;
}
else
{
// normal use case
this.framebuffer = new GlDhFramebuffer();
this.usingMcFramebuffer = false;
}
// create and bind the necessary textures
this.createAndBindTextures();
if(this.framebuffer.getStatus() != GL32.GL_FRAMEBUFFER_COMPLETE)
{
// This generally means something wasn't bound, IE missing either the color or depth texture
LOGGER.warn("Framebuffer ["+this.framebuffer.getId()+"] isn't complete.");
return false;
}
LOGGER.info("Renderer setup complete");
return true;
}
@SuppressWarnings( "deprecation" ) // done to ignore DhApiColorDepthTextureCreatedEvent
private void createAndBindTextures()
{
int oldWidth = this.textureWidth;
int oldHeight = this.textureHeight;
this.textureWidth = MC_RENDER.getTargetFramebufferViewportWidth();
this.textureHeight = MC_RENDER.getTargetFramebufferViewportHeight();
DhApiTextureCreatedParam textureCreatedParam = new DhApiTextureCreatedParam(
oldWidth, oldHeight,
this.textureWidth, this.textureHeight
);
// DhApiColorDepthTextureCreatedEvent needs to be kept around since old versions of Iris need it
ApiEventInjector.INSTANCE.fireAllEvents(DhApiColorDepthTextureCreatedEvent.class, new DhApiColorDepthTextureCreatedEvent.EventParam(textureCreatedParam));
ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeColorDepthTextureCreatedEvent.class, textureCreatedParam);
// also update the framebuffer override if present
IDhApiFramebuffer framebufferOverride = OverrideInjector.INSTANCE.get(IDhApiFramebuffer.class);
this.depthTexture = new GlDhDepthTexture(this.textureWidth, this.textureHeight, EGlDhDepthBufferFormat.DEPTH32F);
this.framebuffer.addDepthAttachment(this.depthTexture.getTextureId(), EGlDhDepthBufferFormat.DEPTH32F.isCombinedStencil());
if (framebufferOverride != null)
{
framebufferOverride.addDepthAttachment(this.depthTexture.getTextureId(), EGlDhDepthBufferFormat.DEPTH32F.isCombinedStencil());
}
// if we are using MC's frame buffer, a color texture is already present and shouldn't need to be bound
if (!this.usingMcFramebuffer)
{
this.nullableColorTexture = GlDhColorTexture.builder()
.setDimensions(this.textureWidth, this.textureHeight)
.setInternalFormat(EGlDhInternalTextureFormat.RGBA8)
.setPixelType(EGlDhPixelType.UNSIGNED_BYTE)
.setPixelFormat(EGlDhPixelFormat.RGBA)
.build();
this.framebuffer.addColorAttachment(0, this.nullableColorTexture.getTextureId());
if (framebufferOverride != null)
{
framebufferOverride.addColorAttachment(0, this.nullableColorTexture.getTextureId());
}
}
else
{
this.nullableColorTexture = null;
}
ApiEventInjector.INSTANCE.fireAllEvents(DhApiAfterColorDepthTextureCreatedEvent.class, textureCreatedParam);
}
//endregion
//=============//
// post render //
//=============//
//region
@Override
public void runRenderPassCleanup(RenderParams renderParams)
{
boolean runningDeferredPass = (renderParams.renderPass == EDhApiRenderPass.TRANSPARENT);
if (!runningDeferredPass)
{
//===================//
// optifine clean up //
//===================//
if (this.usingMcFramebuffer)
{
// If MC's framebuffer is being used the depth needs to be cleared to prevent rendering on top of MC.
// This should only happen when Optifine shaders are being used.
GL32.glClear(GL32.GL_DEPTH_BUFFER_BIT);
}
}
this.unbindLightmap();
this.shaderProgramForThisFrame.unbind();
}
@Override
public void applyToMcTexture(RenderParams renderParams) { GlDhApplyShader.INSTANCE.render(renderParams); }
//endregion
//================//
// clear textures //
//================//
//region
@Override
public void clearDhDepthAndColorTextures(RenderParams renderParams)
{
IDhApiFramebuffer framebufferOverride = OverrideInjector.INSTANCE.get(IDhApiFramebuffer.class);
boolean firstPass =
(renderParams.renderPass == EDhApiRenderPass.OPAQUE
|| renderParams.renderPass == EDhApiRenderPass.OPAQUE_AND_TRANSPARENT);
GL32.glClearDepth(1.0);
float[] clearColorValues = new float[4];
GL32.glGetFloatv(GL32.GL_COLOR_CLEAR_VALUE, clearColorValues);
GL32.glClearColor(clearColorValues[0], clearColorValues[1], clearColorValues[2], 1.0f);
if (this.usingMcFramebuffer
&& framebufferOverride == null)
{
//// Due to using MC/Optifine's framebuffer we need to re-bind the depth texture,
//// otherwise we'll be writing to MC/Optifine's depth texture which causes rendering issues
//this.framebuffer.addDepthAttachment(this.depthTexture.getTextureId(), EDhDepthBufferFormat.DEPTH32F.isCombinedStencil());
// don't clear the color texture, that removes the sky
GL32.glClear(GL32.GL_DEPTH_BUFFER_BIT);
}
else if (firstPass)
{
GL32.glClear(GL32.GL_COLOR_BUFFER_BIT | GL32.GL_DEPTH_BUFFER_BIT);
}
}
//endregion
//===============//
// API functions //
//===============//
//region
public void setActiveFramebufferId(int id) { this.activeFramebufferId = id; }
/** @return -1 if no frame buffer has been bound yet */
public int getActiveFramebufferId() { return this.activeFramebufferId; }
public void setActiveColorTextureId(int id)
{
this.activeColorTextureId = id;
DhApiRenderProxy.activeOpenGlDhColorTextureId = id;
}
/** @return -1 if no texture has been bound yet */
public int getActiveColorTextureId() { return this.activeColorTextureId; }
public void setActiveDepthTextureId(int id)
{
this.activeDepthTextureId = id;
DhApiRenderProxy.activeOpenGlDhDepthTextureId = id;
}
/** @return -1 if no texture has been bound yet */
public int getActiveDepthTextureId() { return this.activeDepthTextureId; }
//endregion
//================//
// helper methods //
//================//
//region
public void bindLightmap(ILightMapWrapper lightMapWrapper)
{
LightMapWrapper lightMap = (LightMapWrapper)lightMapWrapper;
GLMC.glActiveTexture(GL32.GL_TEXTURE0 + LightMapWrapper.GL_BOUND_INDEX);
GLMC.glBindTexture(lightMap.getOpenGlId());
}
public void unbindLightmap()
{
// strange that we don't call "glActiveTexture" here but since it's working James isn't going to change it right now (2026-03-10)
GLMC.glBindTexture(0);
}
//endregion
}
@@ -0,0 +1,67 @@
package com.seibel.distanthorizons.common.render.openGl;
import com.seibel.distanthorizons.common.render.openGl.generic.GlGenericObjectRenderer;
import com.seibel.distanthorizons.common.render.openGl.generic.GlGenericObjectVertexContainer;
import com.seibel.distanthorizons.common.render.openGl.glObject.GlDummyUniformData;
import com.seibel.distanthorizons.common.render.openGl.glObject.buffer.GLVertexBuffer;
import com.seibel.distanthorizons.common.render.openGl.postProcessing.fade.GlDhFarFadeRenderer;
import com.seibel.distanthorizons.common.render.openGl.postProcessing.fade.GlVanillaFadeRenderer;
import com.seibel.distanthorizons.common.render.openGl.postProcessing.fog.GlDhFogRenderer;
import com.seibel.distanthorizons.common.render.openGl.postProcessing.ssao.GlDhSSAORenderer;
import com.seibel.distanthorizons.common.render.openGl.terrain.GlDhTerrainShaderProgram;
import com.seibel.distanthorizons.common.render.openGl.test.GlTestTriangleRenderer;
import com.seibel.distanthorizons.core.render.renderer.AbstractDebugWireframeRenderer;
import com.seibel.distanthorizons.core.wrapperInterfaces.render.AbstractDhRenderApiDefinition;
import com.seibel.distanthorizons.core.wrapperInterfaces.render.objects.IDhGenericObjectVertexBufferContainer;
import com.seibel.distanthorizons.core.wrapperInterfaces.render.objects.ILodContainerUniformBufferWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.render.objects.IVertexBufferWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.*;
public class GlDhRenderApiDefinition extends AbstractDhRenderApiDefinition
{
//=========//
// getters //
//=========//
//region
public String getApiName() { return "OpenGL"; }
//endregion
//============//
// singletons //
//============//
//region
@Override public IDhMetaRenderer getMetaRenderer() { return GlDhMetaRenderer.INSTANCE; }
@Override public IDhTerrainRenderer getTerrainRenderer() { return GlDhTerrainRenderer.INSTANCE; }
@Override public IDhSsaoRenderer getSsaoRenderer() { return GlDhSSAORenderer.INSTANCE; }
@Override public IDhFogRenderer getFogRenderer() { return GlDhFogRenderer.INSTANCE; }
@Override public IDhFarFadeRenderer getFarFadeRenderer() { return GlDhFarFadeRenderer.INSTANCE; }
@Override public AbstractDebugWireframeRenderer getDebugWireframeRenderer() { return GlDhDebugWireframeRenderer.INSTANCE; }
@Override public IDhVanillaFadeRenderer getVanillaFadeRenderer() { return GlVanillaFadeRenderer.INSTANCE; }
@Override public IDhTestTriangleRenderer getTestTriangleRenderer() { return GlTestTriangleRenderer.INSTANCE; }
//endregion
//===========//
// factories //
//===========//
//region
@Override public IDhGenericRenderer createGenericRenderer() { return new GlGenericObjectRenderer(); }
@Override public IVertexBufferWrapper createVboWrapper(String name) { return new GLVertexBuffer(); }
@Override public ILodContainerUniformBufferWrapper createLodContainerUniformWrapper() { return new GlDummyUniformData(); }
@Override public IDhGenericObjectVertexBufferContainer createGenericVboContainer() { return new GlGenericObjectVertexContainer(); }
//endregion
}
@@ -0,0 +1,67 @@
package com.seibel.distanthorizons.common.render.openGl;
import com.seibel.distanthorizons.common.render.openGl.terrain.GlDhTerrainShaderProgram;
import com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding.LodBufferContainer;
import com.seibel.distanthorizons.core.render.RenderParams;
import com.seibel.distanthorizons.core.util.objects.SortedArraySet;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhTerrainRenderer;
public class GlDhTerrainRenderer implements IDhTerrainRenderer
{
public static final GlDhTerrainRenderer INSTANCE = new GlDhTerrainRenderer();
private GlDhTerrainShaderProgram terrainShaderProgram = null;
//=============//
// constructor //
//=============//
//region
private GlDhTerrainRenderer() {}
//endregion
//=========//
// getters //
//=========//
//region
/** must be called on the render thread the first time so GL can run it's setup */
public GlDhTerrainShaderProgram getTerrainShaderProgram()
{
if (this.terrainShaderProgram == null)
{
this.terrainShaderProgram = new GlDhTerrainShaderProgram();
}
return this.terrainShaderProgram;
}
//endregion
//========//
// render //
//========//
//region
@Override
public void render(RenderParams renderEventParam, boolean opaquePass, SortedArraySet<LodBufferContainer> bufferContainers, IProfilerWrapper profiler)
{
this.getTerrainShaderProgram();
this.terrainShaderProgram.tryInit();
this.terrainShaderProgram.render(renderEventParam, opaquePass, bufferContainers, profiler);
}
//endregion
}
@@ -0,0 +1,751 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.render.openGl.generic;
import com.seibel.distanthorizons.api.enums.config.EDhApiGpuUploadMethod;
import com.seibel.distanthorizons.api.enums.rendering.EDhApiBlockMaterial;
import com.seibel.distanthorizons.api.interfaces.override.rendering.IDhApiGenericObjectShaderProgram;
import com.seibel.distanthorizons.api.interfaces.render.IDhApiRenderableBoxGroup;
import com.seibel.distanthorizons.api.interfaces.render.IDhApiCustomRenderRegister;
import com.seibel.distanthorizons.api.methods.events.abstractEvents.*;
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam;
import com.seibel.distanthorizons.api.objects.math.DhApiVec3d;
import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBox;
import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBoxGroupShading;
import com.seibel.distanthorizons.common.render.openGl.glObject.GLProxy;
import com.seibel.distanthorizons.common.render.openGl.glObject.buffer.GLIndexBuffer;
import com.seibel.distanthorizons.common.render.openGl.glObject.buffer.GLVertexBuffer;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.jar.EPlatform;
import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.logging.f3.F3Screen;
import com.seibel.distanthorizons.core.render.RenderParams;
import com.seibel.distanthorizons.core.render.renderer.GenericRenderObjectFactory;
import com.seibel.distanthorizons.core.render.renderer.RenderableBoxGroup;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper;
import com.seibel.distanthorizons.core.util.math.Vec3d;
import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhGenericRenderer;
import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector;
import com.seibel.distanthorizons.coreapi.DependencyInjection.OverrideInjector;
import com.seibel.distanthorizons.coreapi.ModInfo;
import org.lwjgl.opengl.ARBInstancedArrays;
import org.lwjgl.opengl.GL32;
import org.lwjgl.opengl.GL33;
import org.lwjgl.system.MemoryUtil;
import java.awt.*;
import java.nio.ByteBuffer;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
* Handles rendering generic groups of {@link DhApiRenderableBox}.
*
* @see IDhApiCustomRenderRegister
* @see DhApiRenderableBox
*/
public class GlGenericObjectRenderer implements IDhGenericRenderer
{
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
private static final MinecraftGLWrapper GLMC = MinecraftGLWrapper.INSTANCE;
private static final DhApiRenderableBoxGroupShading DEFAULT_SHADING = DhApiRenderableBoxGroupShading.getUnshaded();
/**
* Can be used to troubleshoot the renderer.
* If enabled several debug objects will render around (0,150,0).
*/
public static final boolean RENDER_DEBUG_OBJECTS = false;
// rendering setup
private boolean init = false;
private IDhApiGenericObjectShaderProgram instancedShaderProgram;
private IDhApiGenericObjectShaderProgram directShaderProgram;
private GLVertexBuffer boxVertexBuffer;
private GLIndexBuffer boxIndexBuffer;
private boolean instancedRenderingAvailable;
private boolean vertexAttribDivisorSupported;
private boolean instancedArraysSupported;
private final ConcurrentHashMap<Long, RenderableBoxGroup> boxGroupById = new ConcurrentHashMap<>();
/** A box from 0,0,0 to 1,1,1 */
private static final float[] BOX_VERTICES = {
//region
// Pos x y z
// min X, vertical face
0, 0, 0,
1, 0, 0,
1, 1, 0,
0, 1, 0,
// max X, vertical face
0, 1, 1,
1, 1, 1,
1, 0, 1,
0, 0, 1,
// min Z, vertical face
0, 0, 1,
0, 0, 0,
0, 1, 0,
0, 1, 1,
// max Z, vertical face
1, 0, 1,
1, 1, 1,
1, 1, 0,
1, 0, 0,
// min Y, horizontal face
0, 0, 1,
1, 0, 1,
1, 0, 0,
0, 0, 0,
// max Y, horizontal face
0, 1, 1,
1, 1, 1,
1, 1, 0,
0, 1, 0,
//endregion
};
private static final int[] BOX_INDICES = {
//region
// min X, vertical face
2, 1, 0,
0, 3, 2,
// max X, vertical face
6, 5, 4,
4, 7, 6,
// min Z, vertical face
10, 9, 8,
8, 11, 10,
// max Z, vertical face
14, 13, 12,
12, 15, 14,
// min Y, horizontal face
18, 17, 16,
16, 19, 18,
// max Y, horizontal face
20, 21, 22,
22, 23, 20,
//endregion
};
//=============//
// constructor //
//=============//
//region
public GlGenericObjectRenderer() { }
public void init()
{
if (this.init)
{
return;
}
this.init = true;
//===================================//
// is instanced rendering available? //
//===================================//
this.vertexAttribDivisorSupported = GLProxy.getInstance().vertexAttribDivisorSupported;
this.instancedArraysSupported = GLProxy.getInstance().instancedArraysSupported;
boolean isMac = (EPlatform.get() == EPlatform.MACOS);
if (isMac)
{
LOGGER.warn("Generic rendering not supported by Mac. Clouds, beacons, and some other effects will be disabled.");
Config.Client.Advanced.Graphics.GenericRendering.enableGenericRendering.setApiValue(false);
return;
}
this.instancedRenderingAvailable = (this.vertexAttribDivisorSupported || this.instancedArraysSupported) && !isMac;
if (!this.instancedRenderingAvailable)
{
LOGGER.warn("Instanced rendering not supported by this GPU, falling back to direct rendering. Generic object rendering will be slow and some effects may be disabled.");
}
//======================//
// startup the renderer //
//======================//
this.instancedShaderProgram = new GlGenericObjectShaderProgram(true);
this.directShaderProgram = new GlGenericObjectShaderProgram(false);
this.createBuffers();
if (RENDER_DEBUG_OBJECTS)
{
this.addGenericDebugObjects();
}
}
private void createBuffers()
{
// box vertices
ByteBuffer boxVerticesBuffer = MemoryUtil.memAlloc(BOX_VERTICES.length * Float.BYTES);
boxVerticesBuffer.asFloatBuffer().put(BOX_VERTICES);
boxVerticesBuffer.rewind();
this.boxVertexBuffer = new GLVertexBuffer(false);
this.boxVertexBuffer.bind();
this.boxVertexBuffer.uploadBuffer(boxVerticesBuffer, 8, EDhApiGpuUploadMethod.DATA, BOX_VERTICES.length * Float.BYTES);
MemoryUtil.memFree(boxVerticesBuffer);
// box vertex indexes
ByteBuffer solidIndexBuffer = MemoryUtil.memAlloc(BOX_INDICES.length * Integer.BYTES);
solidIndexBuffer.asIntBuffer().put(BOX_INDICES);
solidIndexBuffer.rewind();
this.boxIndexBuffer = new GLIndexBuffer(false);
this.boxIndexBuffer.uploadBuffer(solidIndexBuffer, EDhApiGpuUploadMethod.DATA, BOX_INDICES.length * Integer.BYTES, GL32.GL_STATIC_DRAW);
this.boxIndexBuffer.bind();
MemoryUtil.memFree(solidIndexBuffer);
}
private void addGenericDebugObjects()
{
GenericRenderObjectFactory factory = GenericRenderObjectFactory.INSTANCE;
// single giant box
IDhApiRenderableBoxGroup singleGiantBoxGroup = factory.createForSingleBox(
ModInfo.NAME + ":CyanChunkBox",
new DhApiRenderableBox(
new DhApiVec3d(0,0,0), new DhApiVec3d(16,190,16),
new Color(Color.CYAN.getRed(), Color.CYAN.getGreen(), Color.CYAN.getBlue(), 125),
EDhApiBlockMaterial.WATER)
);
singleGiantBoxGroup.setSkyLight(LodUtil.MAX_MC_LIGHT);
singleGiantBoxGroup.setBlockLight(LodUtil.MAX_MC_LIGHT);
this.add(singleGiantBoxGroup);
// single slender box
IDhApiRenderableBoxGroup singleTallBoxGroup = factory.createForSingleBox(
ModInfo.NAME + ":GreenBeacon",
new DhApiRenderableBox(
new DhApiVec3d(16,0,31), new DhApiVec3d(17,2000,32),
new Color(Color.GREEN.getRed(), Color.GREEN.getGreen(), Color.GREEN.getBlue(), 125),
EDhApiBlockMaterial.ILLUMINATED)
);
singleTallBoxGroup.setSkyLight(LodUtil.MAX_MC_LIGHT);
singleTallBoxGroup.setBlockLight(LodUtil.MAX_MC_LIGHT);
this.add(singleTallBoxGroup);
// absolute box group
ArrayList<DhApiRenderableBox> absBoxList = new ArrayList<>();
for (int i = 0; i < 18; i++)
{
absBoxList.add(new DhApiRenderableBox(
new DhApiVec3d(i,150+i,24), new DhApiVec3d(1+i,151+i,25),
new Color(Color.ORANGE.getRed(), Color.ORANGE.getGreen(), Color.ORANGE.getBlue()),
EDhApiBlockMaterial.LAVA
)
);
}
IDhApiRenderableBoxGroup absolutePosBoxGroup = factory.createAbsolutePositionedGroup(ModInfo.NAME + ":OrangeStairs", absBoxList);
this.add(absolutePosBoxGroup);
// relative box group
ArrayList<DhApiRenderableBox> relBoxList = new ArrayList<>();
for (int i = 0; i < 8; i+=2)
{
relBoxList.add(new DhApiRenderableBox(
new DhApiVec3d(0,i,0), new DhApiVec3d(1,1+i,1),
new Color(Color.MAGENTA.getRed(), Color.MAGENTA.getGreen(), Color.MAGENTA.getBlue()),
EDhApiBlockMaterial.METAL
)
);
}
IDhApiRenderableBoxGroup relativePosBoxGroup = factory.createRelativePositionedGroup(
ModInfo.NAME + ":MovingMagentaGroup",
new DhApiVec3d(24, 140, 24),
relBoxList);
relativePosBoxGroup.setPreRenderFunc((event) ->
{
DhApiVec3d pos = relativePosBoxGroup.getOriginBlockPos();
pos.x += event.partialTicks / 2;
pos.x %= 32;
relativePosBoxGroup.setOriginBlockPos(pos);
});
this.add(relativePosBoxGroup);
// massive relative box group
ArrayList<DhApiRenderableBox> massRelBoxList = new ArrayList<>();
for (int x = 0; x < 50*2; x+=2)
{
for (int z = 0; z < 50*2; z+=2)
{
massRelBoxList.add(new DhApiRenderableBox(
new DhApiVec3d(-x, 0, -z), new DhApiVec3d(1-x, 1, 1-z),
new Color(Color.RED.getRed(), Color.RED.getGreen(), Color.RED.getBlue()),
EDhApiBlockMaterial.TERRACOTTA
)
);
}
}
IDhApiRenderableBoxGroup massRelativePosBoxGroup = factory.createRelativePositionedGroup(
ModInfo.NAME + ":MassRedGroup",
new DhApiVec3d(-25, 140, 0),
massRelBoxList);
massRelativePosBoxGroup.setPreRenderFunc((event) ->
{
DhApiVec3d blockPos = massRelativePosBoxGroup.getOriginBlockPos();
blockPos.y += event.partialTicks / 4;
if (blockPos.y > 150f)
{
blockPos.y = 140f;
Color newColor = (massRelativePosBoxGroup.get(0).color == Color.RED) ? Color.RED.darker() : Color.RED;
massRelativePosBoxGroup.forEach((box) -> { box.color = newColor; });
massRelativePosBoxGroup.triggerBoxChange();
}
massRelativePosBoxGroup.setOriginBlockPos(blockPos);
});
this.add(massRelativePosBoxGroup);
}
//endregion
//==============//
// registration //
//==============//
//region
@Override
public void add(IDhApiRenderableBoxGroup iBoxGroup) throws IllegalArgumentException
{
if (!(iBoxGroup instanceof RenderableBoxGroup))
{
throw new IllegalArgumentException("Box group must be of type ["+ RenderableBoxGroup.class.getSimpleName()+"], type received: ["+(iBoxGroup != null ? iBoxGroup.getClass() : "NULL")+"].");
}
RenderableBoxGroup boxGroup = (RenderableBoxGroup) iBoxGroup;
if (boxGroup.size() != 0)
{
// trigger a box change to make sure the initial data is uploaded
boxGroup.triggerBoxChange();
}
long id = boxGroup.getId();
if (this.boxGroupById.containsKey(id))
{
throw new IllegalArgumentException("A box group with the ID [" + id + "] is already present.");
}
this.boxGroupById.put(id, boxGroup);
}
@Override
public IDhApiRenderableBoxGroup remove(long id) { return this.boxGroupById.remove(id); }
public void clear() { this.boxGroupById.clear(); }
//endregion
//===========//
// rendering //
//===========//
//region
/**
* @param renderingWithSsao
* if true that means this render call is happening before the SSAO pass
* and any objects rendered in this pass will have SSAO applied to them.
*/
@Override
public void render(RenderParams renderEventParam, IProfilerWrapper profiler, boolean renderingWithSsao)
{
// generic rendering (both instanced and direct) is extremely unstable on Mac, so don't render anything
if (EPlatform.get() == EPlatform.MACOS)
{
return;
}
// render setup //
try (IProfilerWrapper.IProfileBlock setup_profile = profiler.push("setup"))
{
this.init();
ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeGenericRenderSetupEvent.class, renderEventParam);
boolean renderWireframe = Config.Client.Advanced.Debugging.renderWireframe.get();
if (renderWireframe)
{
GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, GL32.GL_LINE);
GLMC.disableFaceCulling();
}
else
{
GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, GL32.GL_FILL);
GLMC.enableFaceCulling();
}
GLMC.enableBlend();
GL32.glBlendEquation(GL32.GL_FUNC_ADD);
GLMC.glBlendFuncSeparate(GL32.GL_SRC_ALPHA, GL32.GL_ONE_MINUS_SRC_ALPHA, GL32.GL_ONE, GL32.GL_ONE_MINUS_SRC_ALPHA);
IDhApiGenericObjectShaderProgram shaderProgram = this.instancedRenderingAvailable ? this.instancedShaderProgram : this.directShaderProgram;
IDhApiGenericObjectShaderProgram shaderProgramOverride = OverrideInjector.INSTANCE.get(IDhApiGenericObjectShaderProgram.class);
if (shaderProgramOverride != null && shaderProgram.overrideThisFrame())
{
shaderProgram = shaderProgramOverride;
}
shaderProgram.bind(renderEventParam);
shaderProgram.bindVertexBuffer(this.boxVertexBuffer.getId());
this.boxIndexBuffer.bind();
Vec3d camPos = MC_RENDER.getCameraExactPosition();
// rendering //
Collection<RenderableBoxGroup> boxList = this.boxGroupById.values();
for (RenderableBoxGroup boxGroup : boxList)
{
// validation //
// shouldn't happen, but just in case
if (boxGroup == null)
{
continue;
}
// skip boxes that shouldn't render this pass
if (boxGroup.ssaoEnabled != renderingWithSsao)
{
continue;
}
profiler.popPush("render prep");
boxGroup.preRender(renderEventParam); // called even if the group is inactive, so the group can be activate if desired
// ignore inactive groups
if (!boxGroup.active)
{
continue;
}
// allow API users to cancel this object's rendering
boolean cancelRendering = ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeGenericObjectRenderEvent.class, new DhApiBeforeGenericObjectRenderEvent.EventParam(renderEventParam, boxGroup));
if (cancelRendering)
{
continue;
}
// update instanced data if needed
if (this.instancedRenderingAvailable)
{
boxGroup.tryUpdateInstancedDataAsync();
// skip groups that haven't been uploaded yet
if (boxGroup.vertexBufferContainer.getState() != GlGenericObjectVertexContainer.EState.RENDER)
{
continue;
}
}
// render //
profiler.popPush("rendering");
try (IProfilerWrapper.IProfileBlock namespace_profile = profiler.push(boxGroup.getResourceLocationNamespace());
IProfilerWrapper.IProfileBlock location_profile = profiler.push(boxGroup.getResourceLocationPath()))
{
if (this.instancedRenderingAvailable)
{
this.renderBoxGroupInstanced(shaderProgram, renderEventParam, boxGroup, camPos, profiler);
}
else
{
this.renderBoxGroupDirect(shaderProgram, renderEventParam, boxGroup, camPos, profiler);
}
}
boxGroup.postRender(renderEventParam);
}
//==========//
// clean up //
//==========//
profiler.popPush("cleanup");
ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeGenericRenderCleanupEvent.class, renderEventParam);
if (renderWireframe)
{
// default back to GL_FILL since all other rendering uses it
GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, GL32.GL_FILL);
GLMC.enableFaceCulling();
}
shaderProgram.unbind();
}
}
//endregion
//=====================//
// instanced rendering //
//=====================//
//region
private void renderBoxGroupInstanced(
IDhApiGenericObjectShaderProgram shaderProgram, DhApiRenderParam renderEventParam,
RenderableBoxGroup boxGroup, Vec3d camPos,
IProfilerWrapper profiler)
{
try (IProfilerWrapper.IProfileBlock render_profile = profiler.push("vertex setup"))
{
// update instance data //
DhApiRenderableBoxGroupShading shading = boxGroup.shading;
if (shading == null)
{
shading = DEFAULT_SHADING;
}
shaderProgram.fillIndirectUniformData(
renderEventParam,
shading, boxGroup,
camPos);
// Bind instance data //
profiler.popPush("binding");
GlGenericObjectVertexContainer container = (GlGenericObjectVertexContainer) (boxGroup.vertexBufferContainer);
GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, container.color);
GL32.glEnableVertexAttribArray(1);
GL32.glVertexAttribPointer(1, 4, GL32.GL_FLOAT, false, 4 * Float.BYTES, 0);
this.vertexAttribDivisor(1, 1);
GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, container.scale);
GL32.glEnableVertexAttribArray(2);
this.vertexAttribDivisor(2, 1);
GL32.glVertexAttribPointer(2, 3, GL32.GL_FLOAT, false, 3 * Float.BYTES, 0);
GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, container.chunkPos);
GL32.glEnableVertexAttribArray(3);
this.vertexAttribDivisor(3, 1);
GL32.glVertexAttribIPointer(3, 3, GL32.GL_INT, 3 * Integer.BYTES, 0);
GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, container.subChunkPos);
GL32.glEnableVertexAttribArray(4);
this.vertexAttribDivisor(4, 1);
GL32.glVertexAttribPointer(4, 3, GL32.GL_FLOAT, false, 3 * Float.BYTES, 0);
GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, container.material);
GL32.glEnableVertexAttribArray(5);
this.vertexAttribDivisor(5, 1);
GL32.glVertexAttribIPointer(5, 1, GL32.GL_BYTE, Byte.BYTES, 0);
// Draw instanced
profiler.popPush("render");
if (container.uploadedBoxCount > 0)
{
GL32.glDrawElementsInstanced(GL32.GL_TRIANGLES, BOX_INDICES.length, GL32.GL_UNSIGNED_INT, 0, container.uploadedBoxCount);
}
// Clean up
profiler.popPush("cleanup");
GL32.glDisableVertexAttribArray(1);
GL32.glDisableVertexAttribArray(2);
GL32.glDisableVertexAttribArray(3);
GL32.glDisableVertexAttribArray(4);
GL32.glDisableVertexAttribArray(5);
}
}
/**
* Clean way to handle both {@link GL33#glVertexAttribDivisor} and {@link ARBInstancedArrays#glVertexAttribDivisorARB}
* based on which one is supported.
*/
private void vertexAttribDivisor(int index, int divisor)
{
if (this.vertexAttribDivisorSupported)
{
GL33.glVertexAttribDivisor(index, divisor);
}
else if(this.instancedArraysSupported)
{
ARBInstancedArrays.glVertexAttribDivisorARB(index, divisor);
}
else
{
throw new IllegalStateException("Instanced rendering isn't supported by this machine. Direct rendering should have been used instead.");
}
}
//endregion
//==================//
// direct rendering //
//==================//
//region
private void renderBoxGroupDirect(
IDhApiGenericObjectShaderProgram shaderProgram,
DhApiRenderParam renderEventParam,
RenderableBoxGroup boxGroup, Vec3d camPos,
IProfilerWrapper profiler)
{
profiler.popPush("shared uniforms");
DhApiRenderableBoxGroupShading shading = boxGroup.shading;
if (shading == null)
{
shading = DhApiRenderableBoxGroupShading.getUnshaded();
}
shaderProgram.fillSharedDirectUniformData(renderEventParam, shading, boxGroup, camPos);
for (int i = 0; i < boxGroup.size(); i++)
{
try
{
DhApiRenderableBox box = boxGroup.get(i);
if (box != null)
{
profiler.popPush("direct uniforms");
shaderProgram.fillDirectUniformData(renderEventParam, boxGroup, box, camPos);
profiler.popPush("render");
GL32.glDrawElements(GL32.GL_TRIANGLES, BOX_INDICES.length, GL32.GL_UNSIGNED_INT, 0);
}
}
catch (IndexOutOfBoundsException e)
{
// Concurrency issue, the list was modified while rendering
// this can probably be ignored.
// However, if it does become a problem we can add locks to the box group.
break;
}
}
}
//endregion
//=========//
// getters //
//=========//
//region
/** @throws IllegalStateException if {@link #init()} function hasn't been called yet */
public boolean getInstancedRenderingAvailable() throws IllegalStateException
{
if (!this.init)
{
throw new IllegalStateException("GL initialization hasn't been completed.");
}
return this.instancedRenderingAvailable;
}
//endregion
//=========//
// F3 menu //
//=========//
//region
public String getVboRenderDebugMenuString()
{
// get counts
int totalGroupCount = this.boxGroupById.size();
int totalBoxCount = 0;
int activeGroupCount = 0;
int activeBoxCount = 0;
for (long key : this.boxGroupById.keySet())
{
RenderableBoxGroup renderGroup = this.boxGroupById.get(key);
if (renderGroup.active)
{
activeGroupCount++;
activeBoxCount += renderGroup.size();
}
totalBoxCount += renderGroup.size();
}
return "Generic Obj #: " + F3Screen.NUMBER_FORMAT.format(activeGroupCount) + "/" + F3Screen.NUMBER_FORMAT.format(totalGroupCount) + ", " +
"Cube #: " + F3Screen.NUMBER_FORMAT.format(activeBoxCount) + "/" + F3Screen.NUMBER_FORMAT.format(totalBoxCount);
}
//endregion
}
@@ -0,0 +1,231 @@
package com.seibel.distanthorizons.common.render.openGl.generic;
import com.seibel.distanthorizons.api.interfaces.override.rendering.IDhApiGenericObjectShaderProgram;
import com.seibel.distanthorizons.api.interfaces.render.IDhApiRenderableBoxGroup;
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam;
import com.seibel.distanthorizons.api.objects.math.DhApiVec3d;
import com.seibel.distanthorizons.api.objects.math.DhApiVec3i;
import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBox;
import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBoxGroupShading;
import com.seibel.distanthorizons.common.render.openGl.glObject.shader.GlShaderProgram;
import com.seibel.distanthorizons.common.render.openGl.glObject.vertexAttribute.GlAbstractVertexAttribute;
import com.seibel.distanthorizons.common.render.openGl.glObject.vertexAttribute.GlVertexPointer;
import com.seibel.distanthorizons.common.wrappers.misc.LightMapWrapper;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.util.math.Mat4f;
import com.seibel.distanthorizons.core.util.math.Vec3f;
public class GlGenericObjectShaderProgram extends GlShaderProgram implements IDhApiGenericObjectShaderProgram
{
public static final String VERTEX_SHADER_INSTANCED_PATH = "assets/distanthorizons/shaders/generic/gl/instanced/vert.vert";
public static final String VERTEX_SHADER_DIRECT_PATH = "assets/distanthorizons/shaders/generic/gl/direct/vert.vert";
public static final String FRAGMENT_SHADER_INSTANCED_PATH = "assets/distanthorizons/shaders/generic/gl/instanced/frag.frag";
public static final String FRAGMENT_SHADER_DIRECT_PATH = "assets/distanthorizons/shaders/generic/gl/direct/frag.frag";
public final GlAbstractVertexAttribute va;
// shader uniforms
private final int directShaderTransformUniform;
private final int directShaderColorUniform;
private final int instancedShaderOffsetChunkUniform;
private final int instancedShaderOffsetSubChunkUniform;
private final int instancedShaderCameraChunkPosUniform;
private final int instancedShaderCameraSubChunkPosUniform;
private final int instancedShaderProjectionModelViewMatrixUniform;
private final int lightMapUniform;
private final int skyLightUniform;
private final int blockLightUniform;
private final int northShadingUniform;
private final int southShadingUniform;
private final int eastShadingUniform;
private final int westShadingUniform;
private final int topShadingUniform;
private final int bottomShadingUniform;
//=============//
// constructor //
//=============//
public GlGenericObjectShaderProgram(boolean useInstancedRendering)
{
super(
useInstancedRendering ? VERTEX_SHADER_INSTANCED_PATH : VERTEX_SHADER_DIRECT_PATH,
useInstancedRendering ? FRAGMENT_SHADER_INSTANCED_PATH : FRAGMENT_SHADER_DIRECT_PATH,
"vPosition"
);
this.va = GlAbstractVertexAttribute.create();
this.va.bind();
// Pos
this.va.setVertexAttribute(0, 0, GlVertexPointer.addVec3Pointer(false));
this.va.completeAndCheck(Float.BYTES * 3);
this.directShaderTransformUniform = this.tryGetUniformLocation("uTransform");
this.directShaderColorUniform = this.tryGetUniformLocation("uColor");
this.instancedShaderOffsetChunkUniform = this.tryGetUniformLocation("uOffsetChunk");
this.instancedShaderOffsetSubChunkUniform = this.tryGetUniformLocation("uOffsetSubChunk");
this.instancedShaderCameraChunkPosUniform = this.tryGetUniformLocation("uCameraPosChunk");
this.instancedShaderCameraSubChunkPosUniform = this.tryGetUniformLocation("uCameraPosSubChunk");
this.instancedShaderProjectionModelViewMatrixUniform = this.tryGetUniformLocation("uProjectionMvm");
this.lightMapUniform = this.getUniformLocation("uLightMap");
this.skyLightUniform = this.getUniformLocation("uSkyLight");
this.blockLightUniform = this.getUniformLocation("uBlockLight");
this.northShadingUniform = this.getUniformLocation("uNorthShading");
this.southShadingUniform = this.getUniformLocation("uSouthShading");
this.eastShadingUniform = this.getUniformLocation("uEastShading");
this.westShadingUniform = this.getUniformLocation("uWestShading");
this.topShadingUniform = this.getUniformLocation("uTopShading");
this.bottomShadingUniform = this.getUniformLocation("uBottomShading");
}
//=========//
// methods //
//=========//
@Override
public void bind(DhApiRenderParam renderEventParam)
{
super.bind();
this.va.bind();
}
@Override
public void unbind()
{
super.unbind();
this.va.unbind();
}
@Override
public void free()
{
this.va.free();
super.free();
}
@Override
public void bindVertexBuffer(int vbo) { this.va.bindBufferToAllBindingPoints(vbo); }
@Override
public void fillIndirectUniformData(
DhApiRenderParam renderParameters,
DhApiRenderableBoxGroupShading shading, IDhApiRenderableBoxGroup boxGroup,
DhApiVec3d camPos
)
{
Mat4f projectionMvmMatrix = new Mat4f(renderParameters.dhProjectionMatrix);
projectionMvmMatrix.multiply(renderParameters.dhModelViewMatrix);
super.bind();
this.setUniform(this.instancedShaderOffsetChunkUniform,
new DhApiVec3i(
LodUtil.getChunkPosFromDouble(boxGroup.getOriginBlockPos().x),
LodUtil.getChunkPosFromDouble(boxGroup.getOriginBlockPos().y),
LodUtil.getChunkPosFromDouble(boxGroup.getOriginBlockPos().z)
));
this.setUniform(this.instancedShaderOffsetSubChunkUniform,
new Vec3f(
LodUtil.getSubChunkPosFromDouble(boxGroup.getOriginBlockPos().x),
LodUtil.getSubChunkPosFromDouble(boxGroup.getOriginBlockPos().y),
LodUtil.getSubChunkPosFromDouble(boxGroup.getOriginBlockPos().z)
));
this.setUniform(this.instancedShaderCameraChunkPosUniform,
new DhApiVec3i(
LodUtil.getChunkPosFromDouble(camPos.x),
LodUtil.getChunkPosFromDouble(camPos.y),
LodUtil.getChunkPosFromDouble(camPos.z)
));
this.setUniform(this.instancedShaderCameraSubChunkPosUniform,
new Vec3f(
LodUtil.getSubChunkPosFromDouble(camPos.x),
LodUtil.getSubChunkPosFromDouble(camPos.y),
LodUtil.getSubChunkPosFromDouble(camPos.z)
));
this.setUniform(this.instancedShaderProjectionModelViewMatrixUniform, projectionMvmMatrix);
this.setUniform(this.lightMapUniform, LightMapWrapper.GL_BOUND_INDEX);
this.setUniform(this.skyLightUniform, boxGroup.getSkyLight());
this.setUniform(this.blockLightUniform, boxGroup.getBlockLight());
this.setUniform(this.northShadingUniform, shading.north);
this.setUniform(this.southShadingUniform, shading.south);
this.setUniform(this.eastShadingUniform, shading.east);
this.setUniform(this.westShadingUniform, shading.west);
this.setUniform(this.topShadingUniform, shading.top);
this.setUniform(this.bottomShadingUniform, shading.bottom);
}
@Override
public void fillSharedDirectUniformData(
DhApiRenderParam renderParameters,
DhApiRenderableBoxGroupShading shading, IDhApiRenderableBoxGroup boxGroup,
DhApiVec3d camPos)
{
this.setUniform(this.lightMapUniform, LightMapWrapper.GL_BOUND_INDEX);
this.setUniform(this.skyLightUniform, boxGroup.getSkyLight());
this.setUniform(this.blockLightUniform, boxGroup.getBlockLight());
this.setUniform(this.northShadingUniform, shading.north);
this.setUniform(this.southShadingUniform, shading.south);
this.setUniform(this.eastShadingUniform, shading.east);
this.setUniform(this.westShadingUniform, shading.west);
this.setUniform(this.topShadingUniform, shading.top);
this.setUniform(this.bottomShadingUniform, shading.bottom);
}
public void fillDirectUniformData(
DhApiRenderParam renderParameters,
IDhApiRenderableBoxGroup boxGroup, DhApiRenderableBox box,
DhApiVec3d camPos)
{
Mat4f projectionMvmMatrix = new Mat4f(renderParameters.dhProjectionMatrix);
projectionMvmMatrix.multiply(renderParameters.dhModelViewMatrix);
Mat4f boxTransform = Mat4f.createTranslateMatrix(
(float) (box.minPos.x + boxGroup.getOriginBlockPos().x - camPos.x),
(float) (box.minPos.y + boxGroup.getOriginBlockPos().y - camPos.y),
(float) (box.minPos.z + boxGroup.getOriginBlockPos().z - camPos.z));
boxTransform.multiply(Mat4f.createScaleMatrix(
(float) (box.maxPos.x - box.minPos.x),
(float) (box.maxPos.y - box.minPos.y),
(float) (box.maxPos.z - box.minPos.z)));
projectionMvmMatrix.multiply(boxTransform);
this.setUniform(this.directShaderTransformUniform, projectionMvmMatrix);
this.setUniform(this.directShaderColorUniform, box.color);
}
@Override
public int getId() { return this.id; }
/** The base DH render program should always render */
@Override
public boolean overrideThisFrame() { return true; }
}
@@ -0,0 +1,177 @@
package com.seibel.distanthorizons.common.render.openGl.generic;
import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBox;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper;
import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.render.renderer.RenderableBoxGroup;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.render.objects.IDhGenericObjectVertexBufferContainer;
import org.lwjgl.opengl.GL32;
import java.awt.*;
import java.util.List;
/**
* For use by {@link RenderableBoxGroup}
*
* @see RenderableBoxGroup
*/
public class GlGenericObjectVertexContainer implements IDhGenericObjectVertexBufferContainer
{
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
private static final MinecraftGLWrapper GLMC = MinecraftGLWrapper.INSTANCE;
public int chunkPos = 0;
public int subChunkPos = 0;
public int scale = 0;
public int color = 0;
public int material = 0;
public int[] chunkPosData = new int[0];
public float[] subChunkPosData = new float[0];
public float[] scalingData = new float[0];
public float[] colorData = new float[0];
public int[] materialData = new int[0];
public int uploadedBoxCount = 0;
private EState state = EState.NEW;
@Override
public EState getState() { return this.state; }
@Override
public void setState(EState state) { this.state = state; }
//===========================//
// render building/uploading //
//===========================//
//region
public void updateVertexData(List<DhApiRenderableBox> uploadBoxList)
{
int boxCount = uploadBoxList.size();
// recreate the data arrays if their size is different
if (this.uploadedBoxCount != boxCount)
{
this.uploadedBoxCount = boxCount;
this.chunkPosData = new int[boxCount * 3]; // 3 elements XYZ
this.subChunkPosData = new float[boxCount * 3]; // 3 elements XYZ
this.scalingData = new float[boxCount * 3]; // 3 elements XYZ
this.colorData = new float[boxCount * 4]; // 4 elements, RGBA
this.materialData = new int[boxCount];
}
// transformation / scaling //
for (int i = 0; i < boxCount; i++)
{
DhApiRenderableBox box = uploadBoxList.get(i);
int dataIndex = i * 3;
this.chunkPosData[dataIndex] = LodUtil.getChunkPosFromDouble(box.minPos.x);
this.chunkPosData[dataIndex + 1] = LodUtil.getChunkPosFromDouble(box.minPos.y);
this.chunkPosData[dataIndex + 2] = LodUtil.getChunkPosFromDouble(box.minPos.z);
this.subChunkPosData[dataIndex] = LodUtil.getSubChunkPosFromDouble(box.minPos.x);
this.subChunkPosData[dataIndex + 1] = LodUtil.getSubChunkPosFromDouble(box.minPos.y);
this.subChunkPosData[dataIndex + 2] = LodUtil.getSubChunkPosFromDouble(box.minPos.z);
this.scalingData[dataIndex] = (float) (box.maxPos.x - box.minPos.x);
this.scalingData[dataIndex + 1] = (float) (box.maxPos.y - box.minPos.y);
this.scalingData[dataIndex + 2] = (float) (box.maxPos.z - box.minPos.z);
}
// colors/materials //
for (int i = 0; i < boxCount; i++)
{
DhApiRenderableBox box = uploadBoxList.get(i);
Color color = box.color;
int colorIndex = i * 4;
this.colorData[colorIndex] = color.getRed() / 255.0f;
this.colorData[colorIndex + 1] = color.getGreen() / 255.0f;
this.colorData[colorIndex + 2] = color.getBlue() / 255.0f;
this.colorData[colorIndex + 3] = color.getAlpha() / 255.0f;
this.materialData[i] = box.material;
}
}
@Override
public void uploadDataToGpu()
{
this.tryCreateBuffers();
// Upload transformation matrices
GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, this.chunkPos);
GL32.glBufferData(GL32.GL_ARRAY_BUFFER, this.chunkPosData, GL32.GL_DYNAMIC_DRAW);
GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, this.subChunkPos);
GL32.glBufferData(GL32.GL_ARRAY_BUFFER, this.subChunkPosData, GL32.GL_DYNAMIC_DRAW);
GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, this.scale);
GL32.glBufferData(GL32.GL_ARRAY_BUFFER, this.scalingData, GL32.GL_DYNAMIC_DRAW);
// Upload colors
GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, this.color);
GL32.glBufferData(GL32.GL_ARRAY_BUFFER, this.colorData, GL32.GL_DYNAMIC_DRAW);
// Upload materials
GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, this.material);
GL32.glBufferData(GL32.GL_ARRAY_BUFFER, this.materialData, GL32.GL_DYNAMIC_DRAW);
}
/** needs to be done on the render thread */
private void tryCreateBuffers()
{
if (this.chunkPos == 0)
{
this.chunkPos = GLMC.glGenBuffers();
this.subChunkPos = GLMC.glGenBuffers();
this.scale = GLMC.glGenBuffers();
this.color = GLMC.glGenBuffers();
this.material = GLMC.glGenBuffers();
}
}
//endregion
//================//
// base overrides //
//================//
//region
@Override
public void close()
{
tryDeleteBuffer(this.chunkPos);
tryDeleteBuffer(this.subChunkPos);
tryDeleteBuffer(this.scale);
tryDeleteBuffer(this.color);
tryDeleteBuffer(this.material);
}
private static void tryDeleteBuffer(int bufferId)
{
// usually unnecessary, but just in case
if (bufferId != 0)
{
GLMC.glDeleteBuffers(bufferId);
}
}
//endregion
}
@@ -0,0 +1,369 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.render.openGl.glObject;
import com.seibel.distanthorizons.api.enums.config.EDhApiGLErrorHandlingMode;
import com.seibel.distanthorizons.api.enums.config.EDhApiGpuUploadMethod;
import com.seibel.distanthorizons.api.enums.config.EDhApiLoggerLevel;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.config.types.ConfigEntry;
import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.jar.EPlatform;
import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.util.objects.GLMessages.*;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IIrisAccessor;
import com.seibel.distanthorizons.coreapi.ModInfo;
import org.lwjgl.glfw.GLFW;
import org.lwjgl.opengl.GL;
import org.lwjgl.opengl.GL32;
import org.lwjgl.opengl.GLCapabilities;
import org.lwjgl.opengl.GLUtil;
import java.io.PrintStream;
import java.util.Collections;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* A singleton that holds references to different openGL contexts
* and GPU capabilities.
*/
public class GLProxy
{
private static final IIrisAccessor IRIS_ACCESSOR = ModAccessorInjector.INSTANCE.get(IIrisAccessor.class);
public static final DhLogger LOGGER;
static
{
DhLoggerBuilder loggerBuilder = new DhLoggerBuilder();
loggerBuilder.fileLevelConfig(Config.Common.Logging.logRendererGLEventToFile);
// don't send chat messages if Iris is present since
// Iris is known to cause (harmless) GL errors
// and this can confuse users
boolean irisPresent = (IRIS_ACCESSOR != null);
if (!irisPresent)
{
loggerBuilder.chatLevelConfig(Config.Common.Logging.logRendererGLEventToChat);
}
LOGGER = loggerBuilder.build();
if (irisPresent)
{
LOGGER.info("Iris detected, Distant Horizons OpenGL error logging won't be sent in the chat due to Iris throwing known (harmless) OpenGL errors. This is a bug with Iris, not Distant Horizons.");
}
}
public static final Set<String> LOGGED_GL_MESSAGES = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
private static GLProxy instance = null;
/** Minecraft's GL capabilities */
public final GLCapabilities glCapabilities;
public boolean namedObjectSupported = false; // ~OpenGL 4.5 (UNUSED CURRENTLY)
public boolean bufferStorageSupported = false; // ~OpenGL 4.4
public boolean vertexAttributeBufferBindingSupported = false; // ~OpenGL 4.3
public boolean instancedArraysSupported = false;
public boolean vertexAttribDivisorSupported = false; // OpenGL 3.3 or newer
private final EDhApiGpuUploadMethod preferredUploadMethod;
public final GLMessageBuilder vanillaDebugMessageBuilder =
new GLMessageBuilder(
(type) ->
{
if (type == EGLMessageType.POP_GROUP)
return false;
else if (type == EGLMessageType.PUSH_GROUP)
return false;
else if (type == EGLMessageType.MARKER)
return false;
else
return true;
},
(severity) ->
{
// notifications can generally be ignored (if they are logged at all)
if (severity == EGLMessageSeverity.NOTIFICATION)
return false;
else
return true;
},
null
);
//=============//
// constructor //
//=============//
//region
private GLProxy() throws IllegalStateException
{
// this must be created on minecraft's render context to work correctly
if (GLFW.glfwGetCurrentContext() == 0L)
{
throw new IllegalStateException(GLProxy.class.getSimpleName() + " was created outside the render thread!");
}
LOGGER.info("Creating " + GLProxy.class.getSimpleName() + "... If this is the last message you see there must have been an OpenGL error.");
LOGGER.info("Lod Render OpenGL version [" + GL32.glGetString(GL32.GL_VERSION) + "].");
//============================//
// get Minecraft's GL context //
//============================//
// get Minecraft's capabilities
this.glCapabilities = GL.getCapabilities();
// crash the game if the GPU doesn't support OpenGL 3.2
if (!this.glCapabilities.OpenGL32)
{
String supportedVersionInfo = this.getFailedVersionInfo(this.glCapabilities);
// See full requirement at above.
String errorMessage = ModInfo.READABLE_NAME + " was initializing " + GLProxy.class.getSimpleName()
+ " and discovered this GPU doesn't meet the OpenGL requirements. Sorry I couldn't tell you sooner :(\n" +
"Additional info:\n" + supportedVersionInfo;
IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
MC.crashMinecraft(errorMessage, new UnsupportedOperationException("Distant Horizon OpenGL requirements not met"));
}
LOGGER.info("minecraftGlCapabilities:\n" + this.versionInfoToString(this.glCapabilities));
if (Config.Client.Advanced.Debugging.OpenGl.overrideVanillaGLLogger.get())
{
GLUtil.setupDebugMessageCallback(new PrintStream(new GLMessageOutputStream(GLProxy::logMessage, this.vanillaDebugMessageBuilder), true));
}
//======================//
// get GPU capabilities //
//======================//
// UNUSED currently
// Check if we can use the named version of all calls, which is available in GL4.5 or after
this.namedObjectSupported = this.glCapabilities.glNamedBufferData != 0L; //Nullptr
// Check if we can use the Buffer Storage, which is available in GL4.4 or after
this.bufferStorageSupported = this.glCapabilities.glBufferStorage != 0L; // Nullptr
if (!this.bufferStorageSupported)
{
LOGGER.info("This GPU doesn't support Buffer Storage (OpenGL 4.4), falling back to using other methods.");
}
// Check if we can use the make-over version of Vertex Attribute, which is available in GL4.3 or after
this.vertexAttributeBufferBindingSupported = this.glCapabilities.glBindVertexBuffer != 0L; // Nullptr
// used by instanced rendering
this.vertexAttribDivisorSupported = this.glCapabilities.OpenGL33;
// denotes if ARBInstancedArrays.glVertexAttribDivisorARB() is available or not
// can be used as a backup if MC didn't create a GL 3.3+ context
this.instancedArraysSupported = this.glCapabilities.GL_ARB_instanced_arrays;
// get the best automatic upload method
String vendor = GL32.glGetString(GL32.GL_VENDOR).toUpperCase(); // example return: "NVIDIA CORPORATION"
if (EPlatform.get() != EPlatform.MACOS)
{
if (vendor.contains("NVIDIA") || vendor.contains("GEFORCE"))
{
// NVIDIA card
this.preferredUploadMethod = this.bufferStorageSupported ? EDhApiGpuUploadMethod.BUFFER_STORAGE : EDhApiGpuUploadMethod.SUB_DATA;
}
else
{
// AMD or Intel card
this.preferredUploadMethod = this.bufferStorageSupported ? EDhApiGpuUploadMethod.BUFFER_STORAGE : EDhApiGpuUploadMethod.DATA;
}
}
else
{
// Mac may have an issue with Buffer Storage, so default to the most basic
// form of uploading
this.preferredUploadMethod = EDhApiGpuUploadMethod.DATA;
}
LOGGER.info("GPU Vendor [" + vendor + "] with OS [" + EPlatform.get().getName() + "], Preferred upload method is [" + this.preferredUploadMethod + "].");
//==========//
// clean up //
//==========//
// GLProxy creation success
LOGGER.info(GLProxy.class.getSimpleName() + " creation successful. OpenGL smiles upon you this day.");
}
//endregion
//=========//
// getters //
//=========//
//region
public static boolean hasInstance() { return instance != null; }
/** @throws IllegalStateException if the Proxy hasn't been created yet and this is called outside the render thread */
public static GLProxy getInstance() throws IllegalStateException
{
if (instance == null)
{
instance = new GLProxy();
}
return instance;
}
public EDhApiGpuUploadMethod getGpuUploadMethod() { return this.preferredUploadMethod; }
public static boolean runningOnRenderThread()
{
long currentContext = GLFW.glfwGetCurrentContext();
return currentContext != 0L; // if the context isn't null, it's the MC context
}
//endregion
//=========//
// logging //
//=========//
//region
/** this method is called on the render thread at the point of the GL Error */
private static void logMessage(GLMessage glMessage)
{
EDhApiGLErrorHandlingMode errorHandlingMode = Config.Client.Advanced.Debugging.OpenGl.glErrorHandlingMode.get();
if (errorHandlingMode == EDhApiGLErrorHandlingMode.IGNORE)
{
return;
}
boolean onlyLogOnce = Config.Client.Advanced.Debugging.OpenGl.onlyLogGlErrorsOnce.get();
if (onlyLogOnce
&& !LOGGED_GL_MESSAGES.add(glMessage.message))
{
// this message has already been logged
return;
}
String errorMessage = "GL ERROR [" + glMessage.id + "] from [" + glMessage.source + "]: [" + glMessage.message + "].";
if (onlyLogOnce)
{
errorMessage += " This message will only be logged once.";
errorMessage += " Note: Distant Horizons will catch and log OpenGL errors from other mods, not just DH itself; if everything is rendering correctly these errors can probably be ignored.";
}
// create an exception so we get a stacktrace of where the message was triggered from
RuntimeException exception = new RuntimeException(errorMessage);
if (glMessage.type == EGLMessageType.ERROR || glMessage.type == EGLMessageType.UNDEFINED_BEHAVIOR)
{
// critical error
LOGGER.error(exception.getMessage(), exception);
if (errorHandlingMode == EDhApiGLErrorHandlingMode.LOG_THROW)
{
// will probably crash the game,
// good for quickly checking if there's a problem while preventing log spam
throw exception;
}
}
else
{
// non-critical log
EGLMessageSeverity severity = glMessage.severity;
if (severity == null)
{
// just in case the message was malformed
severity = EGLMessageSeverity.LOW;
}
switch (severity)
{
case HIGH:
LOGGER.error(exception.getMessage(), exception);
break;
case MEDIUM:
LOGGER.warn(exception.getMessage(), exception);
break;
case LOW:
LOGGER.info(exception.getMessage(), exception);
break;
case NOTIFICATION:
LOGGER.debug(exception.getMessage(), exception);
break;
}
}
}
//endregion
//================//
// helper methods //
//================//
//region
private String getFailedVersionInfo(GLCapabilities c)
{
return "Your OpenGL support:\n" +
"openGL version 3.2+: [" + c.OpenGL32 + "] <- REQUIRED\n" +
"Vertex Attribute Buffer Binding: [" + (c.glVertexAttribBinding != 0) + "] <- optional improvement\n" +
"Buffer Storage: [" + (c.glBufferStorage != 0) + "] <- optional improvement\n" +
"If you noticed that your computer supports higher OpenGL versions"
+ " but not the required version, try running the game in compatibility mode."
+ " (How you turn that on, I have no clue~)";
}
private String versionInfoToString(GLCapabilities c)
{
return "Your OpenGL support:\n" +
"openGL version 3.2+: [" + c.OpenGL32 + "] <- REQUIRED\n" +
"Vertex Attribute Buffer Binding: [" + (c.glVertexAttribBinding != 0) + "] <- optional improvement\n" +
"Buffer Storage: [" + (c.glBufferStorage != 0) + "] <- optional improvement\n";
}
//endregion
}
@@ -0,0 +1,259 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.render.openGl.glObject;
import com.seibel.distanthorizons.common.render.openGl.glObject.enums.GLEnums;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper;
import org.lwjgl.opengl.GL32;
public class GLState implements AutoCloseable
{
private static final MinecraftGLWrapper GLMC = MinecraftGLWrapper.INSTANCE;
public int program;
public int vao;
public int vbo;
public int ebo;
public int fbo;
public int texture2D;
/** IE: GL_TEXTURE0, GL_TEXTURE1, etc. */
public int activeTextureNumber;
public int texture0;
public int texture1;
public int texture2;
public int texture3;
public int frameBufferTexture0;
public int frameBufferTexture1;
public int frameBufferDepthTexture;
public boolean blend;
public boolean scissor;
public int blendEqRGB;
public int blendEqAlpha;
public int blendSrcColor;
public int blendSrcAlpha;
public int blendDstColor;
public int blendDstAlpha;
public boolean depth;
public boolean writeToDepthBuffer;
public int depthFunc;
public boolean stencil;
public int stencilFunc;
public int stencilRef;
public int stencilMask;
public int[] view;
public boolean cull;
public int cullMode;
public int polyMode;
public GLState() { this.saveState(); }
public void saveState()
{
this.program = GL32.glGetInteger(GL32.GL_CURRENT_PROGRAM);
this.vao = GL32.glGetInteger(GL32.GL_VERTEX_ARRAY_BINDING);
this.vbo = GL32.glGetInteger(GL32.GL_ARRAY_BUFFER_BINDING);
this.ebo = GL32.glGetInteger(GL32.GL_ELEMENT_ARRAY_BUFFER_BINDING);
this.fbo = GL32.glGetInteger(GL32.GL_FRAMEBUFFER_BINDING);
this.texture2D = GL32.glGetInteger(GL32.GL_TEXTURE_BINDING_2D);
this.activeTextureNumber = GL32.glGetInteger(GL32.GL_ACTIVE_TEXTURE);
GLMC.glActiveTexture(GL32.GL_TEXTURE0);
this.texture0 = GL32.glGetInteger(GL32.GL_TEXTURE_BINDING_2D);
GLMC.glActiveTexture(GL32.GL_TEXTURE1);
this.texture1 = GL32.glGetInteger(GL32.GL_TEXTURE_BINDING_2D);
GLMC.glActiveTexture(GL32.GL_TEXTURE2); // problem with Iris
this.texture2 = GL32.glGetInteger(GL32.GL_TEXTURE_BINDING_2D);
GLMC.glActiveTexture(GL32.GL_TEXTURE3);
this.texture3 = GL32.glGetInteger(GL32.GL_TEXTURE_BINDING_2D);
GLMC.glActiveTexture(this.activeTextureNumber);
if (this.fbo != 0)
{
this.frameBufferTexture0 = GL32.glGetFramebufferAttachmentParameteri(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT0, GL32.GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME);
this.frameBufferTexture1 = GL32.glGetFramebufferAttachmentParameteri(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT1, GL32.GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME);
this.frameBufferDepthTexture = GL32.glGetFramebufferAttachmentParameteri(GL32.GL_FRAMEBUFFER, GL32.GL_DEPTH_ATTACHMENT, GL32.GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME);
}
else
{
// attempting to get values from the default framebuffer can throw errors on Linux
this.frameBufferTexture0 = 0;
this.frameBufferTexture1 = 0;
this.frameBufferDepthTexture = 0;
}
this.blend = GL32.glIsEnabled(GL32.GL_BLEND);
this.scissor = GL32.glIsEnabled(GL32.GL_SCISSOR_TEST);
this.blendEqRGB = GL32.glGetInteger(GL32.GL_BLEND_EQUATION_RGB);
this.blendEqAlpha = GL32.glGetInteger(GL32.GL_BLEND_EQUATION_ALPHA);
this.blendSrcColor = GL32.glGetInteger(GL32.GL_BLEND_SRC_RGB);
this.blendSrcAlpha = GL32.glGetInteger(GL32.GL_BLEND_SRC_ALPHA);
this.blendDstColor = GL32.glGetInteger(GL32.GL_BLEND_DST_RGB);
this.blendDstAlpha = GL32.glGetInteger(GL32.GL_BLEND_DST_ALPHA);
this.depth = GL32.glIsEnabled(GL32.GL_DEPTH_TEST);
this.writeToDepthBuffer = GL32.glGetInteger(GL32.GL_DEPTH_WRITEMASK) == GL32.GL_TRUE;
this.depthFunc = GL32.glGetInteger(GL32.GL_DEPTH_FUNC);
this.stencil = GL32.glIsEnabled(GL32.GL_STENCIL_TEST);
this.stencilFunc = GL32.glGetInteger(GL32.GL_STENCIL_FUNC);
this.stencilRef = GL32.glGetInteger(GL32.GL_STENCIL_REF);
this.stencilMask = GL32.glGetInteger(GL32.GL_STENCIL_VALUE_MASK);
this.view = new int[4];
GL32.glGetIntegerv(GL32.GL_VIEWPORT, this.view);
this.cull = GL32.glIsEnabled(GL32.GL_CULL_FACE);
this.cullMode = GL32.glGetInteger(GL32.GL_CULL_FACE_MODE);
this.polyMode = GL32.glGetInteger(GL32.GL_POLYGON_MODE);
}
@Override
public void close()
{
// explicitly unbinding the frame buffer is necessary to prevent GL_CLEAR calls from hitting the wrong buffer
GLMC.glBindFramebuffer(GL32.GL_FRAMEBUFFER, 0);
boolean frameBufferSet = false;
if (this.fbo != 0 && GL32.glIsFramebuffer(this.fbo))
{
GLMC.glBindFramebuffer(GL32.GL_FRAMEBUFFER, this.fbo);
frameBufferSet = true;
}
if (this.blend)
{
GLMC.enableBlend();
}
else
{
GLMC.disableBlend();
}
if (this.scissor)
{
GLMC.enableScissorTest();
}
else
{
GLMC.disableScissorTest();
}
GLMC.glActiveTexture(GL32.GL_TEXTURE0);
GLMC.glBindTexture(GL32.glIsTexture(this.texture0) ? this.texture0 : 0);
GLMC.glActiveTexture(GL32.GL_TEXTURE1);
GLMC.glBindTexture(GL32.glIsTexture(this.texture1) ? this.texture1 : 0);
GLMC.glActiveTexture(GL32.GL_TEXTURE2);
GLMC.glBindTexture(GL32.glIsTexture(this.texture2) ? this.texture2 : 0);
GLMC.glActiveTexture(GL32.GL_TEXTURE3);
GLMC.glBindTexture(GL32.glIsTexture(this.texture3) ? this.texture3 : 0);
GLMC.glActiveTexture(this.activeTextureNumber);
GLMC.glBindTexture(GL32.glIsTexture(this.texture2D) ? this.texture2D : 0);
// attempting to set textures on the default frame buffer (ID 0) will throw errors
if (frameBufferSet)
{
GL32.glFramebufferTexture2D(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT0, GL32.GL_TEXTURE_2D, this.frameBufferTexture0, 0);
GL32.glFramebufferTexture2D(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT1, GL32.GL_TEXTURE_2D, this.frameBufferTexture1, 0);
GL32.glFramebufferTexture2D(GL32.GL_FRAMEBUFFER, GL32.GL_DEPTH_ATTACHMENT, GL32.GL_TEXTURE_2D, this.frameBufferDepthTexture, 0);
}
GL32.glBindVertexArray(GL32.glIsVertexArray(this.vao) ? this.vao : 0);
GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, GL32.glIsBuffer(this.vbo) ? this.vbo : 0);
GL32.glBindBuffer(GL32.GL_ELEMENT_ARRAY_BUFFER, GL32.glIsBuffer(this.ebo) ? this.ebo: 0);
GL32.glUseProgram(GL32.glIsProgram(this.program) ? this.program : 0);
if (this.writeToDepthBuffer)
{
GLMC.enableDepthMask();
}
else
{
GLMC.disableDepthMask();
}
GLMC.glBlendFunc(this.blendSrcColor, this.blendDstColor);
GL32.glBlendEquationSeparate(this.blendEqRGB, this.blendEqAlpha);
GLMC.glBlendFuncSeparate(this.blendSrcColor, this.blendDstColor, this.blendSrcAlpha, this.blendDstAlpha);
if (this.depth)
{
GLMC.enableDepthTest();
}
else
{
GLMC.disableDepthTest();
}
GLMC.glDepthFunc(this.depthFunc);
if (this.stencil)
{
GL32.glEnable(GL32.GL_STENCIL_TEST);
}
else
{
GL32.glDisable(GL32.GL_STENCIL_TEST);
}
GL32.glStencilFunc(this.stencilFunc, this.stencilRef, this.stencilMask);
GL32.glViewport(this.view[0], this.view[1], this.view[2], this.view[3]);
if (this.cull)
{
GLMC.enableFaceCulling();
}
else
{
GLMC.disableFaceCulling();
}
GL32.glCullFace(this.cullMode);
GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, this.polyMode);
}
@Override
public String toString()
{
return "GLState{" +
"program=" + this.program + ", vao=" + this.vao + ", vbo=" + this.vbo + ", ebo=" + this.ebo + ", fbo=" + this.fbo +
", text=" + GLEnums.getString(this.texture2D) + "@" + this.activeTextureNumber + ", text0=" + GLEnums.getString(this.texture0) +
", FB text0=" + this.frameBufferTexture0 +
", FB text1=" + this.frameBufferTexture1 +
", FB depth=" + this.frameBufferDepthTexture +
", blend=" + this.blend + ", scissor=" + this.scissor + ", blendMode=" + GLEnums.getString(this.blendSrcColor) + "," + GLEnums.getString(this.blendDstColor) +
", depth=" + this.depth +
", depthFunc=" + GLEnums.getString(this.depthFunc) + ", stencil=" + this.stencil +
", stencilFunc=" + GLEnums.getString(this.stencilFunc) + ", stencilRef=" + this.stencilRef + ", stencilMask=" + this.stencilMask +
", view={x:" + this.view[0] + ", y:" + this.view[1] +
", w:" + this.view[2] + ", h:" + this.view[3] + "}" + ", cull=" + this.cull +
", cullMode=" + GLEnums.getString(this.cullMode) + ", polyMode=" + GLEnums.getString(this.polyMode) +
'}';
}
}
@@ -0,0 +1,94 @@
package com.seibel.distanthorizons.common.render.openGl.glObject;
import com.seibel.distanthorizons.api.interfaces.override.rendering.IDhApiFramebuffer;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper;
import org.lwjgl.opengl.GL32;
public class GlDhFramebuffer implements IDhApiFramebuffer
{
private static final MinecraftGLWrapper GLMC = MinecraftGLWrapper.INSTANCE;
private int id;
//=============//
// constructor //
//=============//
//region
public GlDhFramebuffer() { this.id = GL32.glGenFramebuffers(); }
/** For internal use by Iris, do not remove. */
public GlDhFramebuffer(int id) { this.id = id; }
//endregion
//=========//
// methods //
//=========//
//region
@Override
public void addDepthAttachment(int textureId, boolean isCombinedStencil)
{
this.bind();
int depthAttachment = isCombinedStencil ? GL32.GL_DEPTH_STENCIL_ATTACHMENT : GL32.GL_DEPTH_ATTACHMENT;
GL32.glFramebufferTexture2D(GL32.GL_FRAMEBUFFER, depthAttachment, GL32.GL_TEXTURE_2D, textureId, 0);
}
@Override
public void addColorAttachment(int textureIndex, int textureId)
{
this.bind();
GL32.glFramebufferTexture2D(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT0 + textureIndex, GL32.GL_TEXTURE_2D, textureId, 0);
}
@Override
public void bind()
{
if (this.id == -1)
{
throw new IllegalStateException("Framebuffer does not exist!");
}
GLMC.glBindFramebuffer(GL32.GL_FRAMEBUFFER, this.id);
}
@Override
public void destroy()
{
GL32.glDeleteFramebuffers(this.id);
this.id = -1;
}
@Override
public int getStatus()
{
this.bind();
int status = GL32.glCheckFramebufferStatus(GL32.GL_FRAMEBUFFER);
return status;
}
@Override
public int getId() { return this.id; }
//endregion
//=============//
// API methods //
//=============//
//region
public boolean overrideThisFrame() { return true; }
//endregion
}
@@ -0,0 +1,17 @@
package com.seibel.distanthorizons.common.render.openGl.glObject;
import com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding.LodBufferContainer;
import com.seibel.distanthorizons.core.wrapperInterfaces.render.objects.ILodContainerUniformBufferWrapper;
/**
* With OpenGL all uniform data is uploaded during the rendering phase
* so nothing is needed here.
*/
public class GlDummyUniformData implements ILodContainerUniformBufferWrapper
{
@Override public void createUniformData(LodBufferContainer bufferContainer) { }
@Override public void tryUpload() { }
@Override public void upload() { }
@Override public void close() { }
}
@@ -0,0 +1,388 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.render.openGl.glObject.buffer;
import com.seibel.distanthorizons.api.enums.config.EDhApiGpuUploadMethod;
import com.seibel.distanthorizons.common.render.openGl.glObject.GLProxy;
import com.seibel.distanthorizons.common.render.openGl.glObject.GLState;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.render.RenderThreadTaskHandler;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.util.ThreadUtil;
import org.lwjgl.opengl.GL32;
import org.lwjgl.opengl.GL44;
import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.nio.ByteBuffer;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.atomic.AtomicInteger;
public class GLBuffer implements AutoCloseable
{
private static final DhLogger LOGGER = new DhLoggerBuilder()
.fileLevelConfig(Config.Common.Logging.logRendererGLEventToFile)
.chatLevelConfig(Config.Common.Logging.logRendererGLEventToChat)
.build();
private static final MinecraftGLWrapper GLMC = MinecraftGLWrapper.INSTANCE;
public static final double BUFFER_EXPANSION_MULTIPLIER = 1.3;
public static final double BUFFER_SHRINK_TRIGGER = BUFFER_EXPANSION_MULTIPLIER * BUFFER_EXPANSION_MULTIPLIER;
/** the number of active buffers, can be used for debugging */
public static AtomicInteger bufferCount = new AtomicInteger(0);
private static final int PHANTOM_REF_CHECK_TIME_IN_MS = 5 * 1000;
private static final ConcurrentHashMap<PhantomReference<? extends GLBuffer>, Integer> PHANTOM_TO_BUFFER_ID = new ConcurrentHashMap<>();
private static final ConcurrentHashMap<Integer, PhantomReference<? extends GLBuffer>> BUFFER_ID_TO_PHANTOM = new ConcurrentHashMap<>();
private static final ReferenceQueue<GLBuffer> PHANTOM_REFERENCE_QUEUE = new ReferenceQueue<>();
private static final ThreadPoolExecutor CLEANUP_THREAD = ThreadUtil.makeSingleDaemonThreadPool("GLBuffer Cleanup");
protected int id;
public final int getId() { return this.id; }
protected int size = 0;
public int getSize() { return this.size; }
protected boolean bufferStorage;
public final boolean isBufferStorage() { return this.bufferStorage; }
protected boolean isMapped = false;
//==============//
// constructors //
//==============//
//region
static { CLEANUP_THREAD.execute(() -> runPhantomReferenceCleanupLoop()); }
public GLBuffer(boolean isBufferStorage) { this.destroyOldAndCreate(isBufferStorage); }
//endregion
//=========//
// methods //
//=========//
//region
// Should be override by subclasses
public int getBufferBindingTarget() { return GL32.GL_COPY_READ_BUFFER; }
public void bind() { GL32.glBindBuffer(this.getBufferBindingTarget(), this.id); }
public void unbind() { GL32.glBindBuffer(this.getBufferBindingTarget(), 0); }
//endregion
//====================//
// create and destroy //
//====================//
//region
protected void destroyOldAndCreate(boolean asBufferStorage)
{
if (!GLProxy.runningOnRenderThread())
{
LodUtil.assertNotReach("Thread ["+Thread.currentThread()+"] tried to create a GLBuffer outside the MC render thread.");
}
// destroy the old buffer if one is present
if (this.id != 0)
{
destroyBufferIdNow(this.id);
}
this.id = GLMC.glGenBuffers();
this.bufferStorage = asBufferStorage;
bufferCount.getAndIncrement();
PhantomReference<GLBuffer> phantom = new PhantomReference<>(this, PHANTOM_REFERENCE_QUEUE);
PHANTOM_TO_BUFFER_ID.put(phantom, this.id);
BUFFER_ID_TO_PHANTOM.put(this.id, phantom);
}
protected void destroyAsync()
{
if (this.id == 0)
{
// the buffer has already been closed
return;
}
final int idToDelete = this.id; // saving the ID to a separate variable is necessary so it can be captured by the lambda
RenderThreadTaskHandler.INSTANCE.queueRunningOnRenderThread("GLBuffer destroyAsync", () -> { destroyBufferIdNow(idToDelete); });
this.id = 0;
this.size = 0;
}
private static void destroyBufferIdNow(int id)
{
// only delete valid buffers
if (id == 0)
{
LOGGER.warn("Attempted to destroy a buffer with ID 0, VRAM memory leaks may occur.");
return;
}
// remove and clear the phantom reference if present
if (BUFFER_ID_TO_PHANTOM.containsKey(id))
{
Reference<? extends GLBuffer> phantom = BUFFER_ID_TO_PHANTOM.get(id);
// if we are manually closing this buffer, we don't want the phantom reference to accidentally close it again
// this can cause a race condition were we accidentally delete an in-use buffer and cause NVIDIA
// to throw an EXCEPTION_ACCESS_VIOLATION when we attempt to render it
phantom.clear();
PHANTOM_TO_BUFFER_ID.remove(phantom);
BUFFER_ID_TO_PHANTOM.remove(id);
}
bufferCount.decrementAndGet();
// destroy the buffer if it exists,
// the buffer may not exist if the destroy method is called twice
if (GL32.glIsBuffer(id))
{
GLMC.glDeleteBuffers(id);
if (Config.Client.Advanced.Debugging.logBufferGarbageCollection.get())
{
LOGGER.info("destroyed buffer [" + id + "], remaining: [" + BUFFER_ID_TO_PHANTOM.size() + "]");
}
}
}
//endregion
//==================//
// buffer uploading //
//==================//
//region
/**
* Assumes the GL Context is already bound. <br>
* Will create the VBO if one exist.
*/
public void uploadBuffer(ByteBuffer bb, EDhApiGpuUploadMethod uploadMethod, int maxExpansionSize, int bufferHint)
{
LodUtil.assertTrue(!uploadMethod.useEarlyMapping, "UploadMethod signal that this should use Mapping instead of uploadBuffer!");
int bbSize = bb.limit() - bb.position();
if (bbSize > maxExpansionSize)
{
LodUtil.assertNotReach("maxExpansionSize is [" + maxExpansionSize + "] but buffer size is [" + bbSize + "]!");
}
// Don't upload an empty buffer
if (bbSize == 0)
{
return;
}
// re-binding the old buffers is necessary for old MC versions for the following reasons:
// for 16.5 and older the screen may be black when on the home menu
// and for 1.19.2 - 1.21.4 the inventory/UI will render without a background
int vao = GL32.glGetInteger(GL32.GL_VERTEX_ARRAY_BINDING);
int vbo = GL32.glGetInteger(GL32.GL_ARRAY_BUFFER_BINDING);
int ebo = GL32.glGetInteger(GL32.GL_ELEMENT_ARRAY_BUFFER_BINDING);
try
{
// make sure the buffer is ready for uploading
this.createOrChangeBufferTypeForUpload(uploadMethod);
switch (uploadMethod)
{
case AUTO:
LodUtil.assertNotReach("GpuUploadMethod AUTO must be resolved before call to uploadBuffer()!");
case BUFFER_STORAGE:
this.uploadBufferStorage(bb);
break;
case DATA:
this.uploadBufferData(bb, bufferHint);
break;
case SUB_DATA:
this.uploadSubData(bb, maxExpansionSize, bufferHint);
break;
default:
LodUtil.assertNotReach("Unknown GpuUploadMethod!");
}
}
finally
{
GL32.glBindVertexArray(GL32.glIsVertexArray(vao) ? vao : 0);
GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, GL32.glIsBuffer(vbo) ? vbo : 0);
GL32.glBindBuffer(GL32.GL_ELEMENT_ARRAY_BUFFER, GL32.glIsBuffer(ebo) ? ebo: 0);
}
}
/** Requires the buffer to be bound */
protected void uploadBufferStorage(ByteBuffer bb)
{
LodUtil.assertTrue(this.bufferStorage, "Buffer is not bufferStorage but its trying to use bufferStorage upload method!");
int bbSize = bb.limit() - bb.position();
this.destroyOldAndCreate(true);
this.bind();
GL44.glBufferStorage(this.getBufferBindingTarget(), bb, 0);
this.size = bbSize;
}
/** Requires the buffer to be bound */
protected void uploadBufferData(ByteBuffer bb, int bufferDataHint)
{
LodUtil.assertTrue(!this.bufferStorage, "Buffer is bufferStorage but its trying to use bufferData upload method!");
int bbSize = bb.limit() - bb.position();
GL32.glBufferData(this.getBufferBindingTarget(), bb, bufferDataHint);
this.size = bbSize;
}
/** Requires the buffer to be bound */
protected void uploadSubData(ByteBuffer bb, int maxExpansionSize, int bufferDataHint)
{
LodUtil.assertTrue(!this.bufferStorage, "Buffer is bufferStorage but its trying to use subData upload method!");
int bbSize = bb.limit() - bb.position();
if (this.size < bbSize || this.size > bbSize * BUFFER_SHRINK_TRIGGER)
{
int newSize = (int) (bbSize * BUFFER_EXPANSION_MULTIPLIER);
if (newSize > maxExpansionSize) newSize = maxExpansionSize;
GL32.glBufferData(this.getBufferBindingTarget(), newSize, bufferDataHint);
this.size = newSize;
}
GL32.glBufferSubData(this.getBufferBindingTarget(), 0, bb);
}
//endregion
//================//
// base overrides //
//================//
//region
@Override
public void close() { this.destroyAsync(); }
@Override
public String toString()
{
return (this.bufferStorage ? "" : "Static-") + this.getClass().getSimpleName() +
"[id:" + this.id + ",size:" + this.size + (this.isMapped ? ",MAPPED" : "") + "]";
}
//endregion
//================//
// helper methods //
//================//
//region
/**
* Makes sure the buffer exists and is of the correct format
* before uploading.
*/
private void createOrChangeBufferTypeForUpload(EDhApiGpuUploadMethod uploadMethod)
{
// create/change the buffer type if necessary
if (uploadMethod.useBufferStorage != this.bufferStorage)
{
// recreate if the buffer storage type changed
this.bind();
destroyBufferIdNow(this.id);
this.destroyOldAndCreate(uploadMethod.useBufferStorage);
this.bind();
}
else
{
// Prevent uploading to the null buffer (ID 0).
// This can happen if the buffer was deleted previously.
if (this.id == 0)
{
this.destroyOldAndCreate(this.bufferStorage);
}
this.bind();
}
}
//endregion
//================//
// static cleanup //
//================//
//region
private static void runPhantomReferenceCleanupLoop()
{
while (true)
{
try
{
try
{
Thread.sleep(PHANTOM_REF_CHECK_TIME_IN_MS);
}
catch (InterruptedException ignore) { }
Reference<? extends GLBuffer> phantomRef = PHANTOM_REFERENCE_QUEUE.poll();
while (phantomRef != null)
{
// destroy the buffer if it hasn't been cleared yet
if (PHANTOM_TO_BUFFER_ID.containsKey(phantomRef))
{
int id = PHANTOM_TO_BUFFER_ID.get(phantomRef);
RenderThreadTaskHandler.INSTANCE.queueRunningOnRenderThread("GLBuffer phantom destroy", () -> { destroyBufferIdNow(id); });
//LOGGER.warn("Buffer Phantom collected, ID: ["+id+"]");
}
phantomRef = PHANTOM_REFERENCE_QUEUE.poll();
}
}
catch (Exception e)
{
LOGGER.error("Unexpected error in buffer cleanup thread: [" + e.getMessage() + "].", e);
}
}
}
//endregion
}
@@ -17,16 +17,39 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.util;
package com.seibel.distanthorizons.common.render.openGl.glObject.buffer;
/**
* Added to MC's dynamic textures via mixins
* in order to denote whether a texture is a lightmap or not. <br><br>
*
* If not done any dynamic texture could be used as the lightmap
* which causes some weird rendering bugs.
import org.lwjgl.opengl.GL32;
/**
* AKA the GLElementBuffer
*
* @author James Seibel
* @version 11-20-2021
*/
public interface ILightTextureMarker
public class GLIndexBuffer extends GLBuffer
{
void markLightTexture();
}
/**
* When uploading to a buffer that is too small, recreate it this many times
* bigger than the upload payload
*/
protected int indicesCount = 0;
protected int glType = GL32.GL_UNSIGNED_INT;
public int getGlType() { return this.glType; }
public GLIndexBuffer(boolean isBufferStorage)
{
super(isBufferStorage);
}
@Override
public void destroyAsync()
{
super.destroyAsync();
this.indicesCount = 0;
}
@Override
public int getBufferBindingTarget() { return GL32.GL_ELEMENT_ARRAY_BUFFER; }
}
@@ -0,0 +1,194 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.render.openGl.glObject.buffer;
import java.nio.ByteBuffer;
import com.seibel.distanthorizons.common.render.openGl.glObject.GLProxy;
import com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding.IndexBufferBuilder;
import com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding.LodQuadBuilder;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.render.RenderThreadTaskHandler;
import com.seibel.distanthorizons.core.wrapperInterfaces.render.AbstractDhRenderApiDefinition;
import com.seibel.distanthorizons.core.wrapperInterfaces.render.objects.IVertexBufferWrapper;
import org.lwjgl.opengl.GL32;
import com.seibel.distanthorizons.api.enums.config.EDhApiGpuUploadMethod;
import org.lwjgl.system.MemoryUtil;
/**
* This is a container for a OpenGL
* VBO (Vertex Buffer Object).
*
* @author James Seibel
* @version 11-20-2021
*/
public class GLVertexBuffer extends GLBuffer implements IVertexBufferWrapper
{
private static final AbstractDhRenderApiDefinition RENDER_DEF = SingletonInjector.INSTANCE.get(AbstractDhRenderApiDefinition.class);
/**
* When uploading to a buffer that is too small, recreate it this many times
* bigger than the upload payload
*/
protected int vertexCount = 0;
public int getVertexCount() { return this.vertexCount; }
private GlQuadIndexBuffer quadIBO = null;
private static GlQuadIndexBuffer GLOBAL_QUAD_IBO = null;
public GlQuadIndexBuffer getQuadIBO()
{
if (RENDER_DEF.useSingleIbo())
{
return GLOBAL_QUAD_IBO;
}
else
{
return this.quadIBO;
}
}
//=============//
// constructor //
//=============//
//region
static
{
if (RENDER_DEF.useSingleIbo())
{
RenderThreadTaskHandler.INSTANCE.queueRunningOnRenderThread("Global IBO Creation", () ->
{
GLOBAL_QUAD_IBO = new GlQuadIndexBuffer();
int maxSize = LodQuadBuilder.getMaxBufferByteSize();
int maxVertexCount = maxSize / LodQuadBuilder.BYTES_PER_VERTEX;
int maxQuadCount = (maxVertexCount / 4);
ByteBuffer buffer = IndexBufferBuilder.createBuffer(maxQuadCount);
GLOBAL_QUAD_IBO.upload(buffer, maxQuadCount);
MemoryUtil.memFree(buffer);
});
}
}
public GLVertexBuffer() { this(GLProxy.getInstance().getGpuUploadMethod() == EDhApiGpuUploadMethod.BUFFER_STORAGE); }
public GLVertexBuffer(boolean isBufferStorage) { super(isBufferStorage); }
//endregion
//======================//
// uploading/destroying //
//======================//
//region
@Override
public int getBufferBindingTarget() { return GL32.GL_ARRAY_BUFFER; }
@Override
public void uploadVertexBuffer(ByteBuffer buffer, int vertexCount)
{
EDhApiGpuUploadMethod uploadMethod = GLProxy.getInstance().getGpuUploadMethod();
int maxBufferSize = LodQuadBuilder.getMaxBufferByteSize();
this.uploadBuffer(buffer, vertexCount, uploadMethod, maxBufferSize);
}
/**
* bufferSize is the number of shared verticies. <br>
* This number will be higher when actually rendered since each box's face needs 2 triangles
* with 2 shared verticies.
*/
public void uploadBuffer(ByteBuffer byteBuffer, int vertexCount, EDhApiGpuUploadMethod uploadMethod, int maxExpansionSize)
{
if (vertexCount < 0)
{
throw new IllegalArgumentException("vertexCount is negative!");
}
// If size is zero, just ignore it.
if (byteBuffer.limit() - byteBuffer.position() != 0)
{
super.uploadBuffer(byteBuffer, uploadMethod, maxExpansionSize, uploadMethod.useBufferStorage ? 0 : GL32.GL_STATIC_DRAW);
}
this.vertexCount = vertexCount;
}
@Override
public void uploadIndexBuffer(ByteBuffer buffer, int vertexCount)
{
if (RENDER_DEF.useSingleIbo())
{
// ignore index uploading when running a single IBO
return;
}
// If size is zero, just ignore it.
if (vertexCount == 0)
{
return;
}
if (this.quadIBO != null)
{
this.quadIBO.close();
}
this.quadIBO = new GlQuadIndexBuffer();
int quadCount = (vertexCount / 4);
this.quadIBO.upload(buffer, quadCount);
}
//endregion
//================//
// base overrides //
//================//
//region
@Override
public void close() { this.destroyAsync(); }
@Override
public void destroyAsync()
{
super.destroyAsync();
if (this.quadIBO != null)
{
this.quadIBO.destroyAsync();
}
this.vertexCount = 0;
}
//endregion
}
@@ -0,0 +1,85 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.render.openGl.glObject.buffer;
import com.seibel.distanthorizons.api.enums.config.EDhApiGpuUploadMethod;
import com.seibel.distanthorizons.common.render.openGl.glObject.enums.GLEnums;
import com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding.IndexBufferBuilder;
import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import org.lwjgl.opengl.GL32;
import org.lwjgl.system.MemoryUtil;
import java.nio.ByteBuffer;
/** aka GlQuadElementBuffer */
public class GlQuadIndexBuffer extends GLIndexBuffer
{
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
//=============//
// constructor //
//=============//
//region
public GlQuadIndexBuffer() { super(false); }
public void upload(ByteBuffer buffer, int quadCount)
{
if (quadCount < 0)
{
throw new IllegalArgumentException("quadCount must be greater than 0");
}
if (quadCount == 0)
{
// shouldn't happen, but just in case
return;
}
this.indicesCount = quadCount * 6; // 2 triangles per quad
if (this.indicesCount >= this.getCapacity()
&& this.indicesCount < this.getCapacity() * BUFFER_SHRINK_TRIGGER)
{
return;
}
this.glType = GL32.GL_UNSIGNED_INT;
super.uploadBuffer(buffer, EDhApiGpuUploadMethod.DATA,
this.indicesCount * GLEnums.getTypeSize(this.glType), GL32.GL_STATIC_DRAW);
}
//endregion
//=========//
// getters //
//=========//
//region
public int getCapacity() { return super.getSize() / GLEnums.getTypeSize(this.getGlType()); }
//endregion
}
@@ -0,0 +1,9 @@
package com.seibel.distanthorizons.common.render.openGl.glObject.enums;
public enum EGlVersion
{
GL_11,
GL_12,
GL_30,
GL_31
}
@@ -0,0 +1,261 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.render.openGl.glObject.enums;
import static org.lwjgl.opengl.GL46.*;
// Turns GL int enums back to readable strings
public class GLEnums
{
public static String getString(int glEnum)
{
// blend stuff
switch (glEnum)
{
case GL_ZERO:
return "GL_ZERO";
case GL_ONE:
return "GL_ONE";
case GL_SRC_COLOR:
return "GL_SRC_COLOR";
case GL_ONE_MINUS_SRC_COLOR:
return "GL_ONE_MINUS_SRC_COLOR";
case GL_DST_COLOR:
return "GL_DST_COLOR";
case GL_ONE_MINUS_DST_COLOR:
return "GL_ONE_MINUS_DST_COLOR";
case GL_SRC_ALPHA:
return "GL_SRC_ALPHA";
case GL_ONE_MINUS_SRC_ALPHA:
return "GL_ONE_MINUS_SRC_ALPHA";
case GL_DST_ALPHA:
return "GL_DST_ALPHA";
case GL_ONE_MINUS_DST_ALPHA:
return "GL_ONE_MINUS_DST_ALPHA";
case GL_CONSTANT_COLOR:
return "GL_CONSTANT_COLOR";
case GL_ONE_MINUS_CONSTANT_COLOR:
return "GL_ONE_MINUS_CONSTANT_COLOR";
case GL_CONSTANT_ALPHA:
return "GL_CONSTANT_ALPHA";
case GL_ONE_MINUS_CONSTANT_ALPHA:
return "GL_ONE_MINUS_CONSTANT_ALPHA";
default:
}
// shader stuff
switch (glEnum)
{
case GL_VERTEX_SHADER:
return "GL_VERTEX_SHADER";
case GL_GEOMETRY_SHADER:
return "GL_GEOMETRY_SHADER";
case GL_FRAGMENT_SHADER:
return "GL_FRAGMENT_SHADER";
default:
}
// stencil stuff
switch (glEnum)
{
case GL_KEEP:
return "GL_KEEP";
case GL_ZERO:
return "GL_ZERO";
case GL_REPLACE:
return "GL_REPLACE";
case GL_INCR:
return "GL_INCR";
case GL_DECR:
return "GL_DECR";
case GL_INVERT:
return "GL_INVERT";
case GL_INCR_WRAP:
return "GL_INCR_WRAP";
case GL_DECR_WRAP:
return "GL_DECR_WRAP";
default:
}
// depth stuff
switch (glEnum)
{
case GL_NEVER:
return "GL_NEVER";
case GL_LESS:
return "GL_LESS";
case GL_EQUAL:
return "GL_EQUAL";
case GL_LEQUAL:
return "GL_LEQUAL";
case GL_GREATER:
return "GL_GREATER";
case GL_NOTEQUAL:
return "GL_NOTEQUAL";
case GL_GEQUAL:
return "GL_GEQUAL";
case GL_ALWAYS:
return "GL_ALWAYS";
default:
}
// Texture binding points
switch (glEnum)
{
case GL_TEXTURE0:
return "GL_TEXTURE0";
case GL_TEXTURE1:
return "GL_TEXTURE1";
case GL_TEXTURE2:
return "GL_TEXTURE2";
case GL_TEXTURE3:
return "GL_TEXTURE3";
case GL_TEXTURE4:
return "GL_TEXTURE4";
case GL_TEXTURE5:
return "GL_TEXTURE5";
case GL_TEXTURE6:
return "GL_TEXTURE6";
case GL_TEXTURE7:
return "GL_TEXTURE7";
case GL_TEXTURE8:
return "GL_TEXTURE8";
case GL_TEXTURE9:
return "GL_TEXTURE9";
case GL_TEXTURE10:
return "GL_TEXTURE10";
case GL_TEXTURE11:
return "GL_TEXTURE11";
case GL_TEXTURE12:
return "GL_TEXTURE12";
case GL_TEXTURE13:
return "GL_TEXTURE13";
case GL_TEXTURE14:
return "GL_TEXTURE14";
case GL_TEXTURE15:
return "GL_TEXTURE15";
case GL_TEXTURE16:
return "GL_TEXTURE16";
case GL_TEXTURE17:
return "GL_TEXTURE17";
case GL_TEXTURE18:
return "GL_TEXTURE18";
case GL_TEXTURE19:
return "GL_TEXTURE19";
case GL_TEXTURE20:
return "GL_TEXTURE20";
case GL_TEXTURE21:
return "GL_TEXTURE21";
case GL_TEXTURE22:
return "GL_TEXTURE22";
case GL_TEXTURE23:
return "GL_TEXTURE23";
case GL_TEXTURE24:
return "GL_TEXTURE24";
case GL_TEXTURE25:
return "GL_TEXTURE25";
case GL_TEXTURE26:
return "GL_TEXTURE26";
case GL_TEXTURE27:
return "GL_TEXTURE27";
case GL_TEXTURE28:
return "GL_TEXTURE28";
case GL_TEXTURE29:
return "GL_TEXTURE29";
case GL_TEXTURE30:
return "GL_TEXTURE30";
case GL_TEXTURE31:
return "GL_TEXTURE31";
default:
}
// Polygon modes
switch (glEnum)
{
case GL_POINT:
return "GL_POINT";
case GL_LINE:
return "GL_LINE";
case GL_FILL:
return "GL_FILL";
default:
}
// Culling modes
switch (glEnum)
{
case GL_FRONT:
return "GL_FRONT";
case GL_BACK:
return "GL_BACK";
case GL_FRONT_AND_BACK:
return "GL_FRONT_AND_BACK";
default:
}
// Types
switch (glEnum)
{
case GL_BYTE:
return "GL_BYTE";
case GL_UNSIGNED_BYTE:
return "GL_UNSIGNED_BYTE";
case GL_SHORT:
return "GL_SHORT";
case GL_UNSIGNED_SHORT:
return "GL_UNSIGNED_SHORT";
case GL_INT:
return "GL_INT";
case GL_UNSIGNED_INT:
return "GL_UNSIGNED_INT";
case GL_FLOAT:
return "GL_FLOAT";
case GL_DOUBLE:
return "GL_DOUBLE";
default:
}
return "GL_UNKNOWN(" + glEnum + ")";
}
public static int getTypeSize(int glTypeEnum)
{
switch (glTypeEnum)
{
case GL_BYTE:
case GL_UNSIGNED_BYTE:
return 1;
case GL_SHORT:
case GL_UNSIGNED_SHORT:
return 2;
case GL_INT:
case GL_UNSIGNED_INT:
return 4;
case GL_FLOAT:
return 4;
case GL_DOUBLE:
return 8;
default:
throw new IllegalArgumentException("Unknown type enum: " + getString(glTypeEnum));
}
}
}
@@ -0,0 +1,184 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.render.openGl.glObject.shader;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.ByteBuffer;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import org.lwjgl.PointerBuffer;
import org.lwjgl.opengl.GL32;
import org.lwjgl.opengl.GL32C;
import org.lwjgl.system.MemoryStack;
import org.lwjgl.system.MemoryUtil;
import org.lwjgl.system.NativeType;
/**
* This object holds a OpenGL reference to a shader
* and allows for reading in and compiling a shader file.
*/
public class GlShader
{
private static final DhLogger LOGGER = new DhLoggerBuilder()
.fileLevelConfig(Config.Common.Logging.logRendererGLEventToFile)
.chatLevelConfig(Config.Common.Logging.logRendererGLEventToChat)
.build();
/** OpenGL shader ID */
public final int id;
//==============//
// constructors //
//==============//
//region
/**
* Creates a shader with specified type.
*
* @param type Either GL_VERTEX_SHADER or GL_FRAGMENT_SHADER.
* @param sourceString File path of the shader
* @throws RuntimeException if the shader fails to compile
*/
public GlShader(int type, String sourceString)
{
LOGGER.info("Loading shader with type: ["+type+"]");
LOGGER.debug("Source: \n["+sourceString+"]");
if (sourceString == null || sourceString.isEmpty())
{
throw new IllegalArgumentException("No shader source given.");
}
// Create an empty shader object
this.id = GL32.glCreateShader(type);
if (this.id == 0)
{
throw new IllegalArgumentException("Failed to create shader with type ["+type+"] and Source: \n["+sourceString+"].");
}
safeShaderSource(this.id, sourceString);
GL32.glCompileShader(this.id);
// check if the shader compiled
int status = GL32.glGetShaderi(this.id, GL32.GL_COMPILE_STATUS);
if (status != GL32.GL_TRUE)
{
String message = "Shader compiler error. Details: [" + GL32.glGetShaderInfoLog(this.id) + "]\n";
message += "Source: \n[" + sourceString + "]";
this.free(); // important!
throw new RuntimeException(message);
}
LOGGER.info("Shader loaded sucessfully.");
}
//endregion
//=========//
// helpers //
//=========//
//region
/**
* Identical in function to {@link GL32C#glShaderSource(int, CharSequence)} but
* passes a null pointer for string length to force the driver to rely on the null
* terminator for string length. This is a workaround for an apparent flaw with some
* AMD drivers that don't receive or interpret the length correctly, resulting in
* an access violation when the driver tries to read past the string memory.
*
* <p>Hat tip to fewizz for the find and the fix.
*
* <p>Source: https://github.com/vram-guild/canvas/commit/820bf754092ccaf8d0c169620c2ff575722d7d96
*/
private static void safeShaderSource(@NativeType("GLuint") int glId, @NativeType("GLchar const **") CharSequence source)
{
final MemoryStack stack = MemoryStack.stackGet();
final int stackPointer = stack.getPointer();
try
{
final ByteBuffer sourceBuffer = MemoryUtil.memUTF8(source, true);
final PointerBuffer pointers = stack.mallocPointer(1);
pointers.put(sourceBuffer);
GL32.nglShaderSource(glId, 1, pointers.address0(), 0);
org.lwjgl.system.APIUtil.apiArrayFree(pointers.address0(), 1);
}
finally
{
stack.setPointer(stackPointer);
}
}
public void free() { GL32.glDeleteShader(this.id); }
public static String loadFile(String path, boolean absoluteFilePath)
{
StringBuilder stringBuilder = new StringBuilder();
try
{
// open the file
InputStream in;
if (absoluteFilePath)
{
// Throws FileNotFoundException
in = new FileInputStream(path); // Note: this should use OS path seperator
}
else
{
in = GlShader.class.getClassLoader().getResourceAsStream(path); // Note: path seperator should be '/'
if (in == null)
{
throw new FileNotFoundException("Shader file not found in resource: " + path);
}
}
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
// read in the file
String line;
while ((line = reader.readLine()) != null)
{
stringBuilder.append(line).append("\n");
}
}
catch (IOException e)
{
throw new RuntimeException("Unable to load shader from file [" + path + "]. Error: " + e.getMessage());
}
return stringBuilder.toString();
}
//endregion
}
@@ -0,0 +1,225 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.render.openGl.glObject.shader;
import java.awt.Color;
import java.nio.FloatBuffer;
import com.seibel.distanthorizons.api.objects.math.DhApiVec3i;
import org.lwjgl.opengl.GL32;
import org.lwjgl.system.MemoryStack;
import com.seibel.distanthorizons.core.util.math.Mat4f;
import com.seibel.distanthorizons.core.util.math.Vec3f;
/**
* This object holds the reference to a OpenGL shader program
* and contains a few methods that can be used with OpenGL shader programs.
* The reason for many of these simple wrapper methods is as reminders of what
* can (and needs to be) done with a shader program.
*/
public class GlShaderProgram
{
/** Stores the handle of the program. */
public final int id;
//=============//
// constructor //
//=============//
//region
public GlShaderProgram(String vertResourcePath, String fragResourcePath, String attribute) { this(vertResourcePath, fragResourcePath, new String[]{ attribute }); }
/**
* @param vertResourcePath the relative path the vertex shader should be found
* @param fragResourcePath the relative path the fragment shader should be found
*/
public GlShaderProgram(String vertResourcePath, String fragResourcePath, String[] attributes)
{
this.id = GL32.glCreateProgram();
{
String shaderString = GlShader.loadFile(vertResourcePath, false);
GlShader vertShader = new GlShader(GL32.GL_VERTEX_SHADER, shaderString);
GL32.glAttachShader(this.id, vertShader.id);
vertShader.free();
}
{
String shaderString = GlShader.loadFile(fragResourcePath, false);
GlShader fragShader = new GlShader(GL32.GL_FRAGMENT_SHADER, shaderString);
GL32.glAttachShader(this.id, fragShader.id);
fragShader.free();
}
for (int i = 0; i < attributes.length; i++)
{
GL32.glBindAttribLocation(this.id, i, attributes[i]);
}
GL32.glLinkProgram(this.id);
int status = GL32.glGetProgrami(this.id, GL32.GL_LINK_STATUS);
if (status != GL32.GL_TRUE)
{
String message = "Shader Link Error. Details: " + GL32.glGetProgramInfoLog(this.id);
this.free(); // important!
throw new RuntimeException(message);
}
GL32.glUseProgram(this.id); // This HAVE to be a direct call to prevent calling the overloaded version
}
//endregion
//=========//
// binding //
//=========//
//region
public void bind() { GL32.glUseProgram(this.id); }
public void unbind() { GL32.glUseProgram(0); }
public void free() { GL32.glDeleteProgram(this.id); }
//endregion
//============//
// attributes //
//============//
//region
/**
* WARNING: Slow native call! Cache it if possible!
* Gets the location of an attribute variable with specified name.
* Calls GL20.glGetAttribLocation(id, name)
*
* @param name Attribute name
* @return Location of the attribute
* @throws RuntimeException if attribute not found
*/
public int getAttributeLocation(CharSequence name)
{
int i = GL32.glGetAttribLocation(id, name);
if (i == -1) throw new RuntimeException("Attribute name not found: " + name);
return i;
}
/**
* Same as above but without throwing errors. <br>
* Returns -1 if the attribute doesn't exist or has been optimized out.
*/
public int tryGetAttributeLocation(CharSequence name)
{ return GL32.glGetAttribLocation(this.id, name); }
//endregion
//==========//
// uniforms //
//==========//
//region
/**
* WARNING: Slow native call! Cache it if possible!
* Gets the location of a uniform variable with specified name.
* Calls GL20.glGetUniformLocation(id, name)
*
* @param name Uniform name
* @return Location of the Uniform
* @throws RuntimeException if uniform not found
*/
public int getUniformLocation(CharSequence name) throws RuntimeException
{
int i = GL32.glGetUniformLocation(id, name);
if (i == -1)
{
throw new RuntimeException("Uniform name not found: " + name);
}
return i;
}
// Same as above but without throwing errors.
// Return -1 if uniform doesn't exist or has been optimized out
public int tryGetUniformLocation(CharSequence name)
{ return GL32.glGetUniformLocation(this.id, name); }
/** Requires a bound ShaderProgram. */
public void setUniform(int location, boolean value) { GL32.glUniform1i(location, value ? 1 : 0); }
/** @see GlShaderProgram#setUniform(int, boolean) */
public void trySetUniform(int location, boolean value) { if (location != -1) { this.setUniform(location, value); } }
/** Requires a bound ShaderProgram. */
public void setUniform(int location, int value) { GL32.glUniform1i(location, value); }
/** @see GlShaderProgram#setUniform(int, int) */
public void trySetUniform(int location, int value) { if (location != -1) { this.setUniform(location, value); } }
/** Requires a bound ShaderProgram. */
public void setUniform(int location, float value) { GL32.glUniform1f(location, value); }
/** @see GlShaderProgram#setUniform(int, float) */
public void trySetUniform(int location, float value) { if (location != -1) { this.setUniform(location, value); } }
/** Requires a bound ShaderProgram. */
public void setUniform(int location, Vec3f value) { GL32.glUniform3f(location, value.x, value.y, value.z); }
/** @see GlShaderProgram#setUniform(int, Vec3f) */
public void trySetUniform(int location, Vec3f value) { if (location != -1) { this.setUniform(location, value); } }
/** Requires a bound ShaderProgram. */
public void setUniform(int location, DhApiVec3i value) { GL32.glUniform3i(location, value.x, value.y, value.z); }
/** @see GlShaderProgram#setUniform(int, Mat4f) */
public void trySetUniform(int location, DhApiVec3i value) { if (location != -1) { this.setUniform(location, value); } }
/** Requires a bound ShaderProgram. */
public void setUniform(int location, Mat4f value)
{
try (MemoryStack stack = MemoryStack.stackPush())
{
FloatBuffer buffer = stack.mallocFloat(4 * 4);
value.store(buffer);
GL32.glUniformMatrix4fv(location, false, buffer);
}
}
/** @see GlShaderProgram#setUniform(int, Mat4f) */
public void trySetUniform(int location, Mat4f value) { if (location != -1) { this.setUniform(location, value); } }
/**
* Converts the color's RGBA values into values between 0 and 1. <br>
* Requires a bound ShaderProgram.
*/
public void setUniform(int location, Color value)
{
GL32.glUniform4f(location,
value.getRed() / 256.0f,
value.getGreen() / 256.0f,
value.getBlue() / 256.0f,
value.getAlpha() / 256.0f);
}
/** @see GlShaderProgram#setUniform(int, Color) */
public void trySetUniform(int location, Color value) { if (location != -1) { this.setUniform(location, value); } }
//endregion
}
@@ -0,0 +1,114 @@
package com.seibel.distanthorizons.common.render.openGl.glObject.texture;
import org.jetbrains.annotations.Nullable;
import org.lwjgl.opengl.GL30C;
import org.lwjgl.opengl.GL43C;
public enum EGlDhDepthBufferFormat
{
DEPTH(false),
DEPTH16(false),
DEPTH24(false),
DEPTH32(false),
DEPTH32F(false),
DEPTH_STENCIL(true),
DEPTH24_STENCIL8(true),
DEPTH32F_STENCIL8(true);
private final boolean combinedStencil;
EGlDhDepthBufferFormat(boolean combinedStencil) { this.combinedStencil = combinedStencil; }
@Nullable
public static EGlDhDepthBufferFormat fromGlEnum(int glenum)
{
switch (glenum)
{
case GL30C.GL_DEPTH_COMPONENT:
return EGlDhDepthBufferFormat.DEPTH;
case GL30C.GL_DEPTH_COMPONENT16:
return EGlDhDepthBufferFormat.DEPTH16;
case GL30C.GL_DEPTH_COMPONENT24:
return EGlDhDepthBufferFormat.DEPTH24;
case GL30C.GL_DEPTH_COMPONENT32:
return EGlDhDepthBufferFormat.DEPTH32;
case GL30C.GL_DEPTH_COMPONENT32F:
return EGlDhDepthBufferFormat.DEPTH32F;
case GL30C.GL_DEPTH_STENCIL:
return EGlDhDepthBufferFormat.DEPTH_STENCIL;
case GL30C.GL_DEPTH24_STENCIL8:
return EGlDhDepthBufferFormat.DEPTH24_STENCIL8;
case GL30C.GL_DEPTH32F_STENCIL8:
return EGlDhDepthBufferFormat.DEPTH32F_STENCIL8;
default:
return null;
}
}
public static EGlDhDepthBufferFormat fromGlEnumOrDefault(int glenum)
{
EGlDhDepthBufferFormat format = fromGlEnum(glenum);
if (format == null)
{
// yolo, just assume it's GL_DEPTH_COMPONENT
return EGlDhDepthBufferFormat.DEPTH;
}
return format;
}
public int getGlInternalFormat()
{
switch (this)
{
case DEPTH:
return GL30C.GL_DEPTH_COMPONENT;
case DEPTH16:
return GL30C.GL_DEPTH_COMPONENT16;
case DEPTH24:
return GL30C.GL_DEPTH_COMPONENT24;
case DEPTH32:
return GL30C.GL_DEPTH_COMPONENT32;
case DEPTH32F:
return GL30C.GL_DEPTH_COMPONENT32F;
case DEPTH_STENCIL:
return GL30C.GL_DEPTH_STENCIL;
case DEPTH24_STENCIL8:
return GL30C.GL_DEPTH24_STENCIL8;
case DEPTH32F_STENCIL8:
return GL30C.GL_DEPTH32F_STENCIL8;
}
throw new AssertionError("unreachable");
}
public int getGlType() { return isCombinedStencil() ? GL30C.GL_DEPTH_STENCIL : GL30C.GL_DEPTH_COMPONENT; }
public int getGlFormat()
{
switch (this)
{
case DEPTH:
case DEPTH16:
return GL43C.GL_UNSIGNED_SHORT;
case DEPTH24:
case DEPTH32:
return GL43C.GL_UNSIGNED_INT;
case DEPTH32F:
return GL30C.GL_FLOAT;
case DEPTH_STENCIL:
case DEPTH24_STENCIL8:
return GL30C.GL_UNSIGNED_INT_24_8;
case DEPTH32F_STENCIL8:
return GL30C.GL_FLOAT_32_UNSIGNED_INT_24_8_REV;
}
throw new AssertionError("unreachable");
}
public boolean isCombinedStencil() { return combinedStencil; }
}
@@ -0,0 +1,131 @@
package com.seibel.distanthorizons.common.render.openGl.glObject.texture;
import com.seibel.distanthorizons.common.render.openGl.glObject.enums.EGlVersion;
import org.lwjgl.opengl.GL11C;
import org.lwjgl.opengl.GL30C;
import org.lwjgl.opengl.GL31C;
import java.util.Locale;
import java.util.Optional;
public enum EGlDhInternalTextureFormat
{
RGBA(GL11C.GL_RGBA, EGlVersion.GL_11, EGlDhPixelFormat.RGBA),
// 8-bit normalized
R8(GL30C.GL_R8, EGlVersion.GL_30, EGlDhPixelFormat.RED),
RG8(GL30C.GL_RG8, EGlVersion.GL_30, EGlDhPixelFormat.RG),
RGB8(GL11C.GL_RGB8, EGlVersion.GL_11, EGlDhPixelFormat.RGB),
RGBA8(GL11C.GL_RGBA8, EGlVersion.GL_11, EGlDhPixelFormat.RGBA),
// 8-bit signed normalized
R8_SNORM(GL31C.GL_R8_SNORM, EGlVersion.GL_31, EGlDhPixelFormat.RED),
RG8_SNORM(GL31C.GL_RG8_SNORM, EGlVersion.GL_31, EGlDhPixelFormat.RG),
RGB8_SNORM(GL31C.GL_RGB8_SNORM, EGlVersion.GL_31, EGlDhPixelFormat.RGB),
RGBA8_SNORM(GL31C.GL_RGBA8_SNORM, EGlVersion.GL_31, EGlDhPixelFormat.RGBA),
// 16-bit normalized
R16(GL30C.GL_R16, EGlVersion.GL_30, EGlDhPixelFormat.RED),
RG16(GL30C.GL_RG16, EGlVersion.GL_30, EGlDhPixelFormat.RG),
RGB16(GL11C.GL_RGB16, EGlVersion.GL_11, EGlDhPixelFormat.RGB),
RGBA16(GL11C.GL_RGBA16, EGlVersion.GL_11, EGlDhPixelFormat.RGBA),
// 16-bit signed normalized
R16_SNORM(GL31C.GL_R16_SNORM, EGlVersion.GL_31, EGlDhPixelFormat.RED),
RG16_SNORM(GL31C.GL_RG16_SNORM, EGlVersion.GL_31, EGlDhPixelFormat.RG),
RGB16_SNORM(GL31C.GL_RGB16_SNORM, EGlVersion.GL_31, EGlDhPixelFormat.RGB),
RGBA16_SNORM(GL31C.GL_RGBA16_SNORM, EGlVersion.GL_31, EGlDhPixelFormat.RGBA),
// 16-bit float
R16F(GL30C.GL_R16F, EGlVersion.GL_30, EGlDhPixelFormat.RED),
RG16F(GL30C.GL_RG16F, EGlVersion.GL_30, EGlDhPixelFormat.RG),
RGB16F(GL30C.GL_RGB16F, EGlVersion.GL_30, EGlDhPixelFormat.RGB),
RGBA16F(GL30C.GL_RGBA16F, EGlVersion.GL_30, EGlDhPixelFormat.RGBA),
// 32-bit float
R32F(GL30C.GL_R32F, EGlVersion.GL_30, EGlDhPixelFormat.RED),
RG32F(GL30C.GL_RG32F, EGlVersion.GL_30, EGlDhPixelFormat.RG),
RGB32F(GL30C.GL_RGB32F, EGlVersion.GL_30, EGlDhPixelFormat.RGB),
RGBA32F(GL30C.GL_RGBA32F, EGlVersion.GL_30, EGlDhPixelFormat.RGBA),
// 8-bit integer
R8I(GL30C.GL_R8I, EGlVersion.GL_30, EGlDhPixelFormat.RED_INTEGER),
RG8I(GL30C.GL_RG8I, EGlVersion.GL_30, EGlDhPixelFormat.RG_INTEGER),
RGB8I(GL30C.GL_RGB8I, EGlVersion.GL_30, EGlDhPixelFormat.RGB_INTEGER),
RGBA8I(GL30C.GL_RGBA8I, EGlVersion.GL_30, EGlDhPixelFormat.RGBA_INTEGER),
// 8-bit unsigned integer
R8UI(GL30C.GL_R8UI, EGlVersion.GL_30, EGlDhPixelFormat.RED_INTEGER),
RG8UI(GL30C.GL_RG8UI, EGlVersion.GL_30, EGlDhPixelFormat.RG_INTEGER),
RGB8UI(GL30C.GL_RGB8UI, EGlVersion.GL_30, EGlDhPixelFormat.RGB_INTEGER),
RGBA8UI(GL30C.GL_RGBA8UI, EGlVersion.GL_30, EGlDhPixelFormat.RGBA_INTEGER),
// 16-bit integer
R16I(GL30C.GL_R16I, EGlVersion.GL_30, EGlDhPixelFormat.RED_INTEGER),
RG16I(GL30C.GL_RG16I, EGlVersion.GL_30, EGlDhPixelFormat.RG_INTEGER),
RGB16I(GL30C.GL_RGB16I, EGlVersion.GL_30, EGlDhPixelFormat.RGB_INTEGER),
RGBA16I(GL30C.GL_RGBA16I, EGlVersion.GL_30, EGlDhPixelFormat.RGBA_INTEGER),
// 16-bit unsigned integer
R16UI(GL30C.GL_R16UI, EGlVersion.GL_30, EGlDhPixelFormat.RED_INTEGER),
RG16UI(GL30C.GL_RG16UI, EGlVersion.GL_30, EGlDhPixelFormat.RG_INTEGER),
RGB16UI(GL30C.GL_RGB16UI, EGlVersion.GL_30, EGlDhPixelFormat.RGB_INTEGER),
RGBA16UI(GL30C.GL_RGBA16UI, EGlVersion.GL_30, EGlDhPixelFormat.RGBA_INTEGER),
// 32-bit integer
R32I(GL30C.GL_R32I, EGlVersion.GL_30, EGlDhPixelFormat.RED_INTEGER),
RG32I(GL30C.GL_RG32I, EGlVersion.GL_30, EGlDhPixelFormat.RG_INTEGER),
RGB32I(GL30C.GL_RGB32I, EGlVersion.GL_30, EGlDhPixelFormat.RGB_INTEGER),
RGBA32I(GL30C.GL_RGBA32I, EGlVersion.GL_30, EGlDhPixelFormat.RGBA_INTEGER),
// 32-bit unsigned integer
R32UI(GL30C.GL_R32UI, EGlVersion.GL_30, EGlDhPixelFormat.RED_INTEGER),
RG32UI(GL30C.GL_RG32UI, EGlVersion.GL_30, EGlDhPixelFormat.RG_INTEGER),
RGB32UI(GL30C.GL_RGB32UI, EGlVersion.GL_30, EGlDhPixelFormat.RGB_INTEGER),
RGBA32UI(GL30C.GL_RGBA32UI, EGlVersion.GL_30, EGlDhPixelFormat.RGBA_INTEGER),
// Mixed
R3_G3_B2(GL11C.GL_R3_G3_B2, EGlVersion.GL_11, EGlDhPixelFormat.RGB),
RGB5_A1(GL11C.GL_RGB5_A1, EGlVersion.GL_11, EGlDhPixelFormat.RGBA),
RGB10_A2(GL11C.GL_RGB10_A2, EGlVersion.GL_11, EGlDhPixelFormat.RGBA),
R11F_G11F_B10F(GL30C.GL_R11F_G11F_B10F, EGlVersion.GL_30, EGlDhPixelFormat.RGB),
RGB9_E5(GL30C.GL_RGB9_E5, EGlVersion.GL_30, EGlDhPixelFormat.RGB);
private final int glFormat;
private final EGlVersion minimumGlVersion;
private final EGlDhPixelFormat expectedPixelFormat;
EGlDhInternalTextureFormat(int glFormat, EGlVersion minimumGlVersion, EGlDhPixelFormat expectedPixelFormat)
{
this.glFormat = glFormat;
this.minimumGlVersion = minimumGlVersion;
this.expectedPixelFormat = expectedPixelFormat;
}
public static Optional<EGlDhInternalTextureFormat> fromString(String name)
{
try
{
return Optional.of(EGlDhInternalTextureFormat.valueOf(name.toUpperCase(Locale.US)));
}
catch (IllegalArgumentException e)
{
return Optional.empty();
}
}
public int getGlFormat() { return this.glFormat; }
public EGlDhPixelFormat getPixelFormat() { return this.expectedPixelFormat; }
public EGlVersion getMinimumGlVersion() { return this.minimumGlVersion; }
}
@@ -0,0 +1,61 @@
package com.seibel.distanthorizons.common.render.openGl.glObject.texture;
import com.seibel.distanthorizons.common.render.openGl.glObject.enums.EGlVersion;
import org.lwjgl.opengl.GL11C;
import org.lwjgl.opengl.GL12C;
import org.lwjgl.opengl.GL30C;
import java.util.Locale;
import java.util.Optional;
public enum EGlDhPixelFormat
{
RED(GL11C.GL_RED, EGlVersion.GL_11, false),
RG(GL30C.GL_RG, EGlVersion.GL_30, false),
RGB(GL11C.GL_RGB, EGlVersion.GL_11, false),
BGR(GL12C.GL_BGR, EGlVersion.GL_12, false),
RGBA(GL11C.GL_RGBA, EGlVersion.GL_11, false),
BGRA(GL12C.GL_BGRA, EGlVersion.GL_12, false),
RED_INTEGER(GL30C.GL_RED_INTEGER, EGlVersion.GL_30, true),
RG_INTEGER(GL30C.GL_RG_INTEGER, EGlVersion.GL_30, true),
RGB_INTEGER(GL30C.GL_RGB_INTEGER, EGlVersion.GL_30, true),
BGR_INTEGER(GL30C.GL_BGR_INTEGER, EGlVersion.GL_30, true),
RGBA_INTEGER(GL30C.GL_RGBA_INTEGER, EGlVersion.GL_30, true),
BGRA_INTEGER(GL30C.GL_BGRA_INTEGER, EGlVersion.GL_30, true);
private final int glFormat;
private final EGlVersion minimumGlVersion;
private final boolean isInteger;
EGlDhPixelFormat(int glFormat, EGlVersion minimumGlVersion, boolean isInteger)
{
this.glFormat = glFormat;
this.minimumGlVersion = minimumGlVersion;
this.isInteger = isInteger;
}
public static Optional<EGlDhPixelFormat> fromString(String name)
{
try
{
return Optional.of(EGlDhPixelFormat.valueOf(name.toUpperCase(Locale.US)));
}
catch (IllegalArgumentException e)
{
return Optional.empty();
}
}
public int getGlFormat() { return this.glFormat; }
public EGlVersion getMinimumGlVersion() { return this.minimumGlVersion; }
public boolean isInteger() { return this.isInteger; }
}
@@ -0,0 +1,65 @@
package com.seibel.distanthorizons.common.render.openGl.glObject.texture;
import com.seibel.distanthorizons.common.render.openGl.glObject.enums.EGlVersion;
import org.lwjgl.opengl.GL11C;
import org.lwjgl.opengl.GL12C;
import org.lwjgl.opengl.GL30C;
import java.util.Locale;
import java.util.Optional;
public enum EGlDhPixelType
{
BYTE(GL11C.GL_BYTE, EGlVersion.GL_11),
SHORT(GL11C.GL_SHORT, EGlVersion.GL_11),
INT(GL11C.GL_INT, EGlVersion.GL_11),
HALF_FLOAT(GL30C.GL_HALF_FLOAT, EGlVersion.GL_30),
FLOAT(GL11C.GL_FLOAT, EGlVersion.GL_11),
UNSIGNED_BYTE(GL11C.GL_UNSIGNED_BYTE, EGlVersion.GL_11),
UNSIGNED_BYTE_3_3_2(GL12C.GL_UNSIGNED_BYTE_3_3_2, EGlVersion.GL_12),
UNSIGNED_BYTE_2_3_3_REV(GL12C.GL_UNSIGNED_BYTE_2_3_3_REV, EGlVersion.GL_12),
UNSIGNED_SHORT(GL11C.GL_UNSIGNED_SHORT, EGlVersion.GL_11),
UNSIGNED_SHORT_5_6_5(GL12C.GL_UNSIGNED_SHORT_5_6_5, EGlVersion.GL_12),
UNSIGNED_SHORT_5_6_5_REV(GL12C.GL_UNSIGNED_SHORT_5_6_5_REV, EGlVersion.GL_12),
UNSIGNED_SHORT_4_4_4_4(GL12C.GL_UNSIGNED_SHORT_4_4_4_4, EGlVersion.GL_12),
UNSIGNED_SHORT_4_4_4_4_REV(GL12C.GL_UNSIGNED_SHORT_4_4_4_4_REV, EGlVersion.GL_12),
UNSIGNED_SHORT_5_5_5_1(GL12C.GL_UNSIGNED_SHORT_5_5_5_1, EGlVersion.GL_12),
UNSIGNED_SHORT_1_5_5_5_REV(GL12C.GL_UNSIGNED_SHORT_1_5_5_5_REV, EGlVersion.GL_12),
UNSIGNED_INT(GL11C.GL_UNSIGNED_INT, EGlVersion.GL_11),
UNSIGNED_INT_8_8_8_8(GL12C.GL_UNSIGNED_INT_8_8_8_8, EGlVersion.GL_12),
UNSIGNED_INT_8_8_8_8_REV(GL12C.GL_UNSIGNED_INT_8_8_8_8_REV, EGlVersion.GL_12),
UNSIGNED_INT_10_10_10_2(GL12C.GL_UNSIGNED_INT_10_10_10_2, EGlVersion.GL_12),
UNSIGNED_INT_2_10_10_10_REV(GL12C.GL_UNSIGNED_INT_2_10_10_10_REV, EGlVersion.GL_12);
private final int glFormat;
private final EGlVersion minimumGlVersion;
EGlDhPixelType(int glFormat, EGlVersion minimumGlVersion)
{
this.glFormat = glFormat;
this.minimumGlVersion = minimumGlVersion;
}
public static Optional<EGlDhPixelType> fromString(String name)
{
try
{
return Optional.of(EGlDhPixelType.valueOf(name.toUpperCase(Locale.US)));
}
catch (IllegalArgumentException e)
{
return Optional.empty();
}
}
public int getGlFormat() { return glFormat; }
public EGlVersion getMinimumGlVersion() { return minimumGlVersion; }
}
@@ -0,0 +1,183 @@
package com.seibel.distanthorizons.common.render.openGl.glObject.texture;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper;
import org.joml.Vector2i;
import org.lwjgl.opengl.GL11C;
import org.lwjgl.opengl.GL13C;
import org.lwjgl.opengl.GL43C;
import java.nio.ByteBuffer;
public class GlDhColorTexture
{
private static final MinecraftGLWrapper GLMC = MinecraftGLWrapper.INSTANCE;
private final EGlDhInternalTextureFormat internalFormat;
private final EGlDhPixelFormat format;
private final EGlDhPixelType type;
private int width;
private int height;
private boolean isValid;
/** AKA, the OpenGL name of this texture */
private final int id;
private static final ByteBuffer NULL_BUFFER = null;
//=============//
// constructor //
//=============//
public GlDhColorTexture(Builder builder)
{
this.isValid = true;
this.internalFormat = builder.internalFormat;
this.format = builder.format;
this.type = builder.type;
this.width = builder.width;
this.height = builder.height;
this.id = GL43C.glGenTextures();
boolean isPixelFormatInteger = builder.internalFormat.getPixelFormat().isInteger();
this.setupTexture(this.id, builder.width, builder.height, !isPixelFormatInteger); // this binds the texture
// Clean up after ourselves
// This is strictly defensive to ensure that other buggy code doesn't tamper with our textures
GL43C.glBindTexture(GL43C.GL_TEXTURE_2D, 0);
}
//=========//
// methods //
//=========//
private void setupTexture(int id, int width, int height, boolean allowsLinear)
{
this.resizeTexture(id, width, height);
GL43C.glTexParameteri(GL11C.GL_TEXTURE_2D, GL11C.GL_TEXTURE_MIN_FILTER, allowsLinear ? GL11C.GL_LINEAR : GL11C.GL_NEAREST);
GL43C.glTexParameteri(GL11C.GL_TEXTURE_2D, GL11C.GL_TEXTURE_MAG_FILTER, allowsLinear ? GL11C.GL_LINEAR : GL11C.GL_NEAREST);
GL43C.glTexParameteri(GL11C.GL_TEXTURE_2D, GL11C.GL_TEXTURE_WRAP_S, GL13C.GL_CLAMP_TO_EDGE);
GL43C.glTexParameteri(GL11C.GL_TEXTURE_2D, GL11C.GL_TEXTURE_WRAP_T, GL13C.GL_CLAMP_TO_EDGE);
// disable mip-mapping since DH is just going to draw straight to the screen
GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D, GL43C.GL_TEXTURE_BASE_LEVEL, 0);
GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D, GL43C.GL_TEXTURE_MAX_LEVEL, 0);
}
private void resizeTexture(int texture, int width, int height)
{
GL43C.glBindTexture(GL43C.GL_TEXTURE_2D, texture);
GL43C.glTexImage2D(GL11C.GL_TEXTURE_2D, 0, this.internalFormat.getGlFormat(), width, height, 0, this.format.getGlFormat(), this.type.getGlFormat(), NULL_BUFFER);
}
void resize(Vector2i textureScaleOverride) { this.resize(textureScaleOverride.x, textureScaleOverride.y); }
// Package private, call CompositeRenderTargets#resizeIfNeeded instead.
public void resize(int width, int height)
{
this.throwIfInvalid();
this.width = width;
this.height = height;
this.resizeTexture(this.id, width, height);
}
public EGlDhInternalTextureFormat getInternalFormat() { return this.internalFormat; }
public int getTextureId()
{
this.throwIfInvalid();
return this.id;
}
public int getWidth() { return this.width; }
public int getHeight() { return this.height; }
public void destroy()
{
this.throwIfInvalid();
this.isValid = false;
GLMC.glDeleteTextures(this.id);
}
/** @throws IllegalStateException if the texture isn't valid */
private void throwIfInvalid()
{
if (!this.isValid)
{
throw new IllegalStateException("Attempted to use a deleted composite render target");
}
}
public static Builder builder() { return new Builder(); }
//================//
// helper classes //
//================//
public static class Builder
{
private EGlDhInternalTextureFormat internalFormat = EGlDhInternalTextureFormat.RGBA8;
private int width = 0;
private int height = 0;
private EGlDhPixelFormat format = EGlDhPixelFormat.RGBA;
private EGlDhPixelType type = EGlDhPixelType.UNSIGNED_BYTE;
private Builder()
{
// No-op
}
public Builder setInternalFormat(EGlDhInternalTextureFormat format)
{
this.internalFormat = format;
return this;
}
public Builder setDimensions(int width, int height)
{
if (width <= 0)
{
throw new IllegalArgumentException("Width must be greater than zero");
}
if (height <= 0)
{
throw new IllegalArgumentException("Height must be greater than zero");
}
this.width = width;
this.height = height;
return this;
}
public Builder setPixelFormat(EGlDhPixelFormat pixelFormat)
{
this.format = pixelFormat;
return this;
}
public Builder setPixelType(EGlDhPixelType pixelType)
{
this.type = pixelType;
return this;
}
public GlDhColorTexture build() { return new GlDhColorTexture(this); }
}
}
@@ -0,0 +1,62 @@
package com.seibel.distanthorizons.common.render.openGl.glObject.texture;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper;
import org.lwjgl.opengl.GL11C;
import org.lwjgl.opengl.GL13C;
import org.lwjgl.opengl.GL43C;
import java.nio.ByteBuffer;
public class GlDhDepthTexture
{
private static final MinecraftGLWrapper GLMC = MinecraftGLWrapper.INSTANCE;
private int id;
public GlDhDepthTexture(int width, int height, EGlDhDepthBufferFormat format)
{
this.id = GL43C.glGenTextures();
this.resize(width, height, format);
GL43C.glTexParameteri(GL11C.GL_TEXTURE_2D, GL11C.GL_TEXTURE_MIN_FILTER, GL11C.GL_NEAREST);
GL43C.glTexParameteri(GL11C.GL_TEXTURE_2D, GL11C.GL_TEXTURE_MAG_FILTER, GL11C.GL_NEAREST);
GL43C.glTexParameteri(GL11C.GL_TEXTURE_2D, GL11C.GL_TEXTURE_WRAP_S, GL13C.GL_CLAMP_TO_EDGE);
GL43C.glTexParameteri(GL11C.GL_TEXTURE_2D, GL11C.GL_TEXTURE_WRAP_T, GL13C.GL_CLAMP_TO_EDGE);
// disable mip-mapping since DH is just going to draw straight to the screen
GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D, GL43C.GL_TEXTURE_BASE_LEVEL, 0);
GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D, GL43C.GL_TEXTURE_MAX_LEVEL, 0);
GL43C.glBindTexture(GL43C.GL_TEXTURE_2D, 0);
}
// For internal use by Iris for copying data. Do not use this in DH.
public GlDhDepthTexture(int id) { this.id = id; }
public void resize(int width, int height, EGlDhDepthBufferFormat format)
{
GL43C.glBindTexture(GL43C.GL_TEXTURE_2D, this.getTextureId());
GL43C.glTexImage2D(GL11C.GL_TEXTURE_2D, 0, format.getGlInternalFormat(), width, height, 0,
format.getGlType(), format.getGlFormat(), (ByteBuffer) null);
}
public int getTextureId()
{
if (this.id == -1)
{
throw new IllegalStateException("Depth texture does not exist!");
}
return this.id;
}
public void destroy()
{
GLMC.glDeleteTextures(this.getTextureId());
this.id = -1;
}
}
@@ -0,0 +1,92 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.render.openGl.glObject.vertexAttribute;
import com.seibel.distanthorizons.common.render.openGl.glObject.GLProxy;
import org.lwjgl.opengl.GL32;
/**
* Base for binding/unbinding Vertex Attribute objects (VAO's).
*
* @see GlVertexAttributePostGL43
* @see GlVertexAttributePreGL43
*/
public abstract class GlAbstractVertexAttribute
{
/** Stores the handle of the AbstractVertexAttribute. */
public final int id;
//==============//
// constructors //
//==============//
// This will bind AbstractVertexAttribute
protected GlAbstractVertexAttribute()
{
this.id = GL32.glGenVertexArrays();
GL32.glBindVertexArray(this.id);
}
public static GlAbstractVertexAttribute create()
{
if (GLProxy.getInstance().vertexAttributeBufferBindingSupported)
{
return new GlVertexAttributePostGL43();
}
else
{
return new GlVertexAttributePreGL43();
}
}
//=========//
// binding //
//=========//
public void bind() { GL32.glBindVertexArray(this.id); }
public void unbind() { GL32.glBindVertexArray(0); }
/** Always remember to always free your resources! */
public void free() { GL32.glDeleteVertexArrays(this.id); }
//==================//
// abstract methods //
//==================//
/** Requires both AbstractVertexAttribute and VertexBuffer to be bound */
public abstract void bindBufferToAllBindingPoints(int buffer);
/** Requires both AbstractVertexAttribute and VertexBuffer to be bound */
public abstract void bindBufferToBindingPoint(int buffer, int bindingPoint);
/** Requires both AbstractVertexAttribute to be bound */
public abstract void unbindBuffersFromAllBindingPoint();
/** Requires both AbstractVertexAttribute to be bound */
public abstract void unbindBuffersFromBindingPoint(int bindingPoint);
/** Requires both AbstractVertexAttribute to be bound */
public abstract void setVertexAttribute(int bindingPoint, int attributeIndex, GlVertexPointer attribute);
/** Requires both AbstractVertexAttribute to be bound */
public abstract void completeAndCheck(int expectedStrideSize);
}
@@ -0,0 +1,155 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.render.openGl.glObject.vertexAttribute;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import org.lwjgl.opengl.GL43;
/**
* In OpenGL 4.3 and later, Vertex Attribute got a make-over.
* Now it provides support for buffer binding points natively.
* This means that setting up the VAO is just use ONE native call when
* binding to a buffer. <br><br>
*
* Since I no longer need to implement binding points, I also no
* longer needs to keep track of Pointers.
*/
public final class GlVertexAttributePostGL43 extends GlAbstractVertexAttribute
{
private static final DhLogger LOGGER = new DhLoggerBuilder()
.fileLevelConfig(Config.Common.Logging.logRendererGLEventToFile)
.chatLevelConfig(Config.Common.Logging.logRendererGLEventToChat)
.build();
int numberOfBindingPoints = 0;
int strideSize = 0;
//=============//
// constructor //
//=============//
/** This will bind the {@link GlAbstractVertexAttribute} */
public GlVertexAttributePostGL43()
{
super(); // also bind AbstractVertexAttribute
}
//=========//
// binding //
//=========//
/** Requires both AbstractVertexAttribute and VertexBuffer to be bound */
@Override
public void bindBufferToAllBindingPoints(int buffer)
{
for (int i = 0; i < this.numberOfBindingPoints; i++)
{
GL43.glBindVertexBuffer(i, buffer, 0, this.strideSize);
}
}
/** Requires both AbstractVertexAttribute and VertexBuffer to be bound */
@Override
public void bindBufferToBindingPoint(int buffer, int bindingPoint)
{
GL43.glBindVertexBuffer(bindingPoint, buffer, 0, this.strideSize);
}
//===========//
// unbinding //
//===========//
/** Requires AbstractVertexAttribute to be bound */
@Override
public void unbindBuffersFromAllBindingPoint()
{
for (int i = 0; i < this.numberOfBindingPoints; i++)
{
GL43.glBindVertexBuffer(i, 0, 0, 0);
}
}
/** Requires AbstractVertexAttribute to be bound */
@Override
public void unbindBuffersFromBindingPoint(int bindingPoint)
{
GL43.glBindVertexBuffer(bindingPoint, 0, 0, 0);
}
//==========================//
// manual attribute setting //
//==========================//
/** Requires AbstractVertexAttribute to be bound */
@Override
public void setVertexAttribute(int bindingPoint, int attributeIndex, GlVertexPointer attribute)
{
if (attribute.useInteger)
{
GL43.glVertexAttribIFormat(attributeIndex, attribute.elementCount, attribute.glType, this.strideSize);
}
else
{
GL43.glVertexAttribFormat(attributeIndex, attribute.elementCount, attribute.glType,
attribute.normalized, this.strideSize); // Here strideSize is new attrib offset
}
this.strideSize += attribute.byteSize;
if (this.numberOfBindingPoints <= bindingPoint)
{
this.numberOfBindingPoints = bindingPoint + 1;
}
GL43.glVertexAttribBinding(attributeIndex, bindingPoint);
GL43.glEnableVertexAttribArray(attributeIndex);
}
//============//
// validation //
//============//
/** Requires AbstractVertexAttribute to be bound */
@Override
public void completeAndCheck(int expectedStrideSize)
{
if (this.strideSize != expectedStrideSize)
{
LOGGER.error("Vertex Attribute calculated stride size " + this.strideSize +
" does not match the provided expected stride size " + expectedStrideSize + "!");
throw new IllegalArgumentException("Vertex Attribute Incorrect Format");
}
LOGGER.info("Vertex Attribute (GL43+) completed. It contains " + this.numberOfBindingPoints
+ " binding points and a stride size of " + this.strideSize);
}
}
@@ -0,0 +1,253 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.render.openGl.glObject.vertexAttribute;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.TreeMap;
import java.util.TreeSet;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import org.lwjgl.opengl.GL32;
public final class GlVertexAttributePreGL43 extends GlAbstractVertexAttribute
{
private static final DhLogger LOGGER = new DhLoggerBuilder()
.fileLevelConfig(Config.Common.Logging.logRendererGLEventToFile)
.chatLevelConfig(Config.Common.Logging.logRendererGLEventToChat)
.build();
// I tried to use raw arrays as much as possible since those lookups
// happen every frame, and the speed directly affects fps
int strideSize = 0;
int[][] bindingPointsToIndex;
GlVertexPointer[] pointers;
int[] pointersOffset;
TreeMap<Integer, TreeSet<Integer>> bindingPointsToIndexBuilder;
ArrayList<GlVertexPointer> pointersBuilder;
//=============//
// constructor //
//=============//
/** This will bind the {@link GlAbstractVertexAttribute} */
public GlVertexAttributePreGL43()
{
super(); // also bind AbstractVertexAttribute
this.bindingPointsToIndexBuilder = new TreeMap<>();
this.pointersBuilder = new ArrayList<>();
}
//=========//
// binding //
//=========//
/** Requires both AbstractVertexAttribute and VertexBuffer to be bound */
@Override
public void bindBufferToAllBindingPoints(int buffer)
{
for (int i = 0; i < this.pointers.length; i++)
{
GL32.glEnableVertexAttribArray(i);
}
for (int i = 0; i < this.pointers.length; i++)
{
GlVertexPointer pointer = this.pointers[i];
if (pointer == null)
{
continue;
}
if (pointer.useInteger)
{
GL32.glVertexAttribIPointer(i, pointer.elementCount, pointer.glType,
this.strideSize, this.pointersOffset[i]);
}
else
{
GL32.glVertexAttribPointer(i, pointer.elementCount, pointer.glType,
pointer.normalized, this.strideSize, this.pointersOffset[i]);
}
}
}
/** Requires both AbstractVertexAttribute and VertexBuffer to be bound */
@Override
public void bindBufferToBindingPoint(int buffer, int bindingPoint)
{
int[] bindingPointIndexes = this.bindingPointsToIndex[bindingPoint];
for (int bindingPointIndex : bindingPointIndexes)
{
GL32.glEnableVertexAttribArray(bindingPointIndex);
}
for (int bindingPointIndex : bindingPointIndexes)
{
GlVertexPointer pointer = this.pointers[bindingPointIndex];
if (pointer == null)
{
continue;
}
if (pointer.useInteger)
{
GL32.glVertexAttribIPointer(bindingPointIndex, pointer.elementCount, pointer.glType,
this.strideSize, this.pointersOffset[bindingPointIndex]);
}
else
{
GL32.glVertexAttribPointer(bindingPointIndex, pointer.elementCount, pointer.glType,
pointer.normalized, this.strideSize, this.pointersOffset[bindingPointIndex]);
}
}
}
//===========//
// unbinding //
//===========//
/** Requires AbstractVertexAttribute to be bound */
@Override
public void unbindBuffersFromAllBindingPoint()
{
for (int i = 0; i < this.pointers.length; i++)
{
GL32.glDisableVertexAttribArray(i);
}
}
/** Requires AbstractVertexAttribute to be bound */
@Override
public void unbindBuffersFromBindingPoint(int bindingPoint)
{
int[] bindingPointIndexes = this.bindingPointsToIndex[bindingPoint];
for (int bindingPointIndex : bindingPointIndexes)
{
GL32.glDisableVertexAttribArray(bindingPointIndex);
}
}
//==========================//
// manual attribute setting //
//==========================//
/** Requires AbstractVertexAttribute to be bound */
@Override
public void setVertexAttribute(int bindingPoint, int attributeIndex, GlVertexPointer attribute)
{
TreeSet<Integer> intArray = this.bindingPointsToIndexBuilder.computeIfAbsent(bindingPoint, k -> new TreeSet<>());
intArray.add(attributeIndex);
while (this.pointersBuilder.size() <= attributeIndex)
{
// This is dumb, but ArrayList doesn't have a resize, And this code
// should only be run when it's building the Vertex Attribute anyway.
this.pointersBuilder.add(null);
}
this.pointersBuilder.set(attributeIndex, attribute);
}
//============//
// validation //
//============//
/** Requires AbstractVertexAttribute to be bound */
@Override
public void completeAndCheck(int expectedStrideSize)
{
int maxBindPointNumber = this.bindingPointsToIndexBuilder.lastKey();
this.bindingPointsToIndex = new int[maxBindPointNumber + 1][];
this.bindingPointsToIndexBuilder.forEach((Integer i, TreeSet<Integer> set) ->
{
this.bindingPointsToIndex[i] = new int[set.size()];
Iterator<Integer> iter = set.iterator();
for (int j = 0; j < set.size(); j++)
{
this.bindingPointsToIndex[i][j] = iter.next();
}
});
this.pointers = this.pointersBuilder.toArray(new GlVertexPointer[this.pointersBuilder.size()]);
this.pointersOffset = new int[this.pointers.length];
this.pointersBuilder = null; // Release the builder
this.bindingPointsToIndexBuilder = null; // Release the builder
// Check if all pointers are valid
int currentOffset = 0;
for (int i = 0; i < this.pointers.length; i++)
{
GlVertexPointer pointer = this.pointers[i];
if (pointer == null)
{
LOGGER.warn("Vertex Attribute index " + i + " is not set! No index should be skipped normally!");
continue;
}
this.pointersOffset[i] = currentOffset;
currentOffset += pointer.byteSize;
}
if (currentOffset != expectedStrideSize)
{
LOGGER.error("Vertex Attribute calculated stride size " + currentOffset +
" does not match the provided expected stride size " + expectedStrideSize + "!");
throw new IllegalArgumentException("Vertex Attribute Incorrect Format");
}
this.strideSize = currentOffset;
LOGGER.info("Vertex Attribute (pre GL43) completed.");
// Debug logging
LOGGER.debug("AttributeIndex: ElementCount, glType, normalized, strideSize, offset");
for (int i = 0; i < this.pointers.length; i++)
{
GlVertexPointer pointer = this.pointers[i];
if (pointer == null)
{
LOGGER.debug(i + ": Null!!!!");
}
else
{
LOGGER.debug(i + ": " + pointer.elementCount + ", " +
pointer.glType + ", " + pointer.normalized + ", " + this.strideSize + ", " + this.pointersOffset[i]);
}
}
}
}
@@ -0,0 +1,72 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.render.openGl.glObject.vertexAttribute;
import com.seibel.distanthorizons.coreapi.util.MathUtil;
import org.lwjgl.opengl.GL32;
public final class GlVertexPointer
{
public final int elementCount;
public final int glType;
public final boolean normalized;
public final int byteSize;
public final boolean useInteger;
// basic constructors //
public GlVertexPointer(int elementCount, int glType, boolean normalized, int byteSize, boolean useInteger)
{
this.elementCount = elementCount;
this.glType = glType;
this.normalized = normalized;
this.byteSize = byteSize;
this.useInteger = useInteger;
}
public GlVertexPointer(int elementCount, int glType, boolean normalized, int byteSize)
{
this(elementCount, glType, normalized, byteSize, false);
}
private static int _align(int bytes) { return MathUtil.ceilDiv(bytes, 4) * 4; }
// named constructors //
public static GlVertexPointer addFloatPointer(boolean normalized) { return new GlVertexPointer(1, GL32.GL_FLOAT, normalized, Float.BYTES); }
public static GlVertexPointer addVec2Pointer(boolean normalized) { return new GlVertexPointer(2, GL32.GL_FLOAT, normalized, Float.BYTES * 2); }
public static GlVertexPointer addVec3Pointer(boolean normalized) { return new GlVertexPointer(3, GL32.GL_FLOAT, normalized, Float.BYTES * 3); }
public static GlVertexPointer addVec4Pointer(boolean normalized) { return new GlVertexPointer(4, GL32.GL_FLOAT, normalized, Float.BYTES * 4); }
/** Always aligned to 4 bytes */
public static GlVertexPointer addUnsignedBytePointer(boolean normalized, boolean useInteger) { return new GlVertexPointer(1, GL32.GL_UNSIGNED_BYTE, normalized, 4, useInteger); }
/** aligned to 4 bytes */
public static GlVertexPointer addUnsignedBytesPointer(int elementCount, boolean normalized, boolean useInteger)
{ return new GlVertexPointer(elementCount, GL32.GL_UNSIGNED_BYTE, normalized, _align(elementCount), useInteger); }
public static GlVertexPointer addUnsignedShortsPointer(int elementCount, boolean normalized, boolean useInteger)
{ return new GlVertexPointer(elementCount, GL32.GL_UNSIGNED_SHORT, normalized, _align(elementCount * 2), useInteger); }
public static GlVertexPointer addShortsPointer(int elementCount, boolean normalized, boolean useInteger) { return new GlVertexPointer(elementCount, GL32.GL_SHORT, normalized, _align(elementCount * 2), useInteger); }
public static GlVertexPointer addIntPointer(boolean normalized, boolean useInteger) { return new GlVertexPointer(1, GL32.GL_INT, normalized, 4, useInteger); }
public static GlVertexPointer addIVec2Pointer(boolean normalized, boolean useInteger) { return new GlVertexPointer(2, GL32.GL_INT, normalized, 8, useInteger); }
public static GlVertexPointer addIVec3Pointer(boolean normalized, boolean useInteger) { return new GlVertexPointer(3, GL32.GL_INT, normalized, 12, useInteger); }
public static GlVertexPointer addIVec4Pointer(boolean normalized, boolean useInteger) { return new GlVertexPointer(4, GL32.GL_INT, normalized, 16, useInteger); }
}
@@ -0,0 +1,116 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.render.openGl.postProcessing;
import com.seibel.distanthorizons.api.enums.config.EDhApiGpuUploadMethod;
import com.seibel.distanthorizons.common.render.openGl.glObject.buffer.GLVertexBuffer;
import com.seibel.distanthorizons.common.render.openGl.glObject.vertexAttribute.GlAbstractVertexAttribute;
import com.seibel.distanthorizons.common.render.openGl.glObject.vertexAttribute.GlVertexPointer;
import org.lwjgl.opengl.GL32;
import org.lwjgl.system.MemoryUtil;
import java.nio.ByteBuffer;
/**
* Renders a full-screen textured quad to the screen.
* Used in composite / deferred rendering (IE fog).
*/
public class GlScreenQuad
{
public static GlScreenQuad INSTANCE = new GlScreenQuad();
private static final float[] BOX_VERTICES = {
-1, -1,
1, -1,
1, 1,
-1, -1,
1, 1,
-1, 1,
};
private GLVertexBuffer boxBuffer;
private GlAbstractVertexAttribute va;
private boolean init = false;
//=============//
// constructor //
//=============//
//region
private GlScreenQuad() { }
public void init()
{
if (this.init) return;
this.init = true;
this.va = GlAbstractVertexAttribute.create();
this.va.bind();
// Pos
this.va.setVertexAttribute(0, 0, GlVertexPointer.addVec2Pointer(false));
this.va.completeAndCheck(Float.BYTES * 2);
// Framebuffer
this.createBuffer();
}
private void createBuffer()
{
ByteBuffer buffer = MemoryUtil.memAlloc(BOX_VERTICES.length * Float.BYTES);
buffer.asFloatBuffer().put(BOX_VERTICES);
buffer.rewind();
this.boxBuffer = new GLVertexBuffer(false);
this.boxBuffer.bind();
this.boxBuffer.uploadBuffer(buffer, BOX_VERTICES.length, EDhApiGpuUploadMethod.DATA, BOX_VERTICES.length * Float.BYTES);
MemoryUtil.memFree(buffer);
}
//endregion
//===========//
// rendering //
//===========//
//region
public void render()
{
this.init();
this.boxBuffer.bind();
this.va.bind();
this.va.bindBufferToAllBindingPoints(this.boxBuffer.getId());
GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, GL32.GL_FILL);
GL32.glDrawArrays(GL32.GL_TRIANGLES, 0, 6);
}
//endregion
}
@@ -0,0 +1,190 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.render.openGl.postProcessing.apply;
import com.seibel.distanthorizons.common.render.openGl.GlDhMetaRenderer;
import com.seibel.distanthorizons.common.render.openGl.glObject.GLState;
import com.seibel.distanthorizons.common.render.openGl.glObject.shader.GlShaderProgram;
import com.seibel.distanthorizons.common.render.openGl.postProcessing.GlScreenQuad;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.common.render.openGl.util.GlAbstractShaderRenderer;
import com.seibel.distanthorizons.core.logging.DhLogger;
import org.lwjgl.opengl.GL32;
/**
* Copies {@link com.seibel.distanthorizons.core.render.renderer.LodRenderer}'s currently active color and depth texture to Minecraft's framebuffer.
*/
public class GlDhApplyShader extends GlAbstractShaderRenderer
{
public static GlDhApplyShader INSTANCE = new GlDhApplyShader();
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
private static final MinecraftGLWrapper GLMC = MinecraftGLWrapper.INSTANCE;
// uniforms
public int gDhColorTextureUniform;
public int gDepthMapUniform;
//=======//
// setup //
//=======//
//region
private GlDhApplyShader() { }
@Override
public void onInit()
{
this.shader = new GlShaderProgram(
"assets/distanthorizons/shaders/shared/gl/quad_apply.vert",
"assets/distanthorizons/shaders/shared/gl/apply.frag",
"vPosition"
);
// uniform setup
this.gDhColorTextureUniform = this.shader.getUniformLocation("gDhColorTexture");
this.gDepthMapUniform = this.shader.getUniformLocation("gDhDepthTexture");
}
//endregion
//========//
// render //
//========//
//region
@Override
protected void onRender()
{
if (MC_RENDER.mcRendersToFrameBuffer())
{
this.renderToFrameBuffer();
}
else
{
this.renderToMcTexture();
}
}
private void renderToFrameBuffer()
{
int targetFrameBuffer = MC_RENDER.getTargetFramebuffer();
if (targetFrameBuffer == -1)
{
return;
}
try (GLState state = new GLState())
{
GLMC.disableDepthTest();
// blending isn't needed, we're manually merging the MC and DH textures
// Note: this prevents the sun/moon and stars from rendering through transparent LODs,
// however this also fixes transparent LODs from glowing when rendered against the sky during the day
GLMC.disableBlend();
// old blending logic in case it's ever needed:
//GLMC.enableBlend();
//GL32.glBlendEquation(GL32.GL_FUNC_ADD);
//GLMC.glBlendFunc(GL32.GL_ONE, GL32.GL_ONE_MINUS_SRC_ALPHA);
GLMC.glActiveTexture(GL32.GL_TEXTURE0);
GLMC.glBindTexture(GlDhMetaRenderer.INSTANCE.getActiveColorTextureId());
GL32.glUniform1i(this.gDhColorTextureUniform, 0);
GLMC.glActiveTexture(GL32.GL_TEXTURE1);
GLMC.glBindTexture(GlDhMetaRenderer.INSTANCE.getActiveDepthTextureId());
GL32.glUniform1i(this.gDepthMapUniform, 1);
// Copy to MC's framebuffer
GLMC.glBindFramebuffer(GL32.GL_FRAMEBUFFER, targetFrameBuffer);
GlScreenQuad.INSTANCE.render();
}
// everything's been restored, except at this point the MC framebuffer should now be used instead
GLMC.glBindFramebuffer(GL32.GL_FRAMEBUFFER, targetFrameBuffer);
}
private void renderToMcTexture()
{
int targetColorTextureId = MC_RENDER.getColorTextureId();
if (targetColorTextureId == -1)
{
return;
}
int dhFrameBufferId = GlDhMetaRenderer.INSTANCE.getActiveFramebufferId();
if (dhFrameBufferId == -1)
{
return;
}
int mcFrameBufferId = MC_RENDER.getTargetFramebuffer();
if (mcFrameBufferId == -1)
{
return;
}
try (GLState state = new GLState())
{
GLMC.disableDepthTest();
// blending isn't needed, we're just directly merging the MC and DH textures
// Note: this prevents the sun/moon and stars from rendering through transparent LODs,
// but it also resolves some other issues, so it's likely not an issue
GLMC.disableBlend();
GLMC.glActiveTexture(GL32.GL_TEXTURE0);
GLMC.glBindTexture(GlDhMetaRenderer.INSTANCE.getActiveColorTextureId());
GL32.glUniform1i(this.gDhColorTextureUniform, 0);
GLMC.glActiveTexture(GL32.GL_TEXTURE1);
GLMC.glBindTexture(GlDhMetaRenderer.INSTANCE.getActiveDepthTextureId());
GL32.glUniform1i(this.gDepthMapUniform, 1);
GL32.glFramebufferTexture(GL32.GL_DRAW_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT0, targetColorTextureId, 0);
// Copy to MC's texture via MC's framebuffer
GLMC.glBindFramebuffer(GL32.GL_FRAMEBUFFER, dhFrameBufferId);
GlScreenQuad.INSTANCE.render();
}
// everything's been restored, except at this point the MC framebuffer should now be used instead
GLMC.glBindFramebuffer(GL32.GL_FRAMEBUFFER, mcFrameBufferId);
}
//endregion
}
@@ -0,0 +1,116 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.render.openGl.postProcessing.fade;
import com.seibel.distanthorizons.common.render.openGl.glObject.shader.GlShaderProgram;
import com.seibel.distanthorizons.common.render.openGl.postProcessing.GlScreenQuad;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper;
import com.seibel.distanthorizons.common.render.openGl.util.GlAbstractShaderRenderer;
import com.seibel.distanthorizons.core.render.RenderParams;
import org.lwjgl.opengl.GL32;
/**
* Draws the Fade texture onto Minecraft's FrameBuffer. <br><br>
*
* See Also: <br>
* {@link GlVanillaFadeRenderer} - Parent to this shader. <br>
* {@link GlDhVanillaFadeShader} - draws the Fade texture. <br>
*/
public class GlDhFarFadeApplyShader extends GlAbstractShaderRenderer
{
public static GlDhFarFadeApplyShader INSTANCE = new GlDhFarFadeApplyShader();
private static final MinecraftGLWrapper GLMC = MinecraftGLWrapper.INSTANCE;
public int fadeTexture;
public int readFramebuffer;
public int drawFramebuffer;
// uniforms
public int uFadeColorTextureUniform = -1;
//=============//
// constructor //
//=============//
@Override
public void onInit()
{
this.shader = new GlShaderProgram(
"assets/distanthorizons/shaders/shared/gl/quad_apply.vert",
"assets/distanthorizons/shaders/fade/gl/apply.frag",
"vPosition"
);
// uniform setup
this.uFadeColorTextureUniform = this.shader.getUniformLocation("uFadeColorTextureUniform");
}
//=============//
// render prep //
//=============//
@Override
protected void onApplyUniforms(RenderParams renderParams)
{
GLMC.glActiveTexture(GL32.GL_TEXTURE0);
GLMC.glBindTexture(this.fadeTexture);
GL32.glUniform1i(this.uFadeColorTextureUniform, 0);
}
//========//
// render //
//========//
@Override
protected void onRender()
{
GLMC.disableBlend();
// Depth testing must be disabled otherwise this application shader won't apply anything.
// setting this isn't necessary in vanilla, but some mods may change this, requiring it to be set manually,
// it should be automatically restored after rendering is complete.
GLMC.disableDepthTest();
// apply the rendered Fade to Minecraft's framebuffer
GLMC.glBindFramebuffer(GL32.GL_READ_FRAMEBUFFER, this.readFramebuffer);
GLMC.glBindFramebuffer(GL32.GL_DRAW_FRAMEBUFFER, this.drawFramebuffer);
GlScreenQuad.INSTANCE.render();
GLMC.enableDepthTest();
}
}
@@ -0,0 +1,157 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.render.openGl.postProcessing.fade;
import com.seibel.distanthorizons.common.render.openGl.GlDhMetaRenderer;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.render.RenderParams;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhFarFadeRenderer;
import org.lwjgl.opengl.GL32;
import org.lwjgl.opengl.GL43C;
import java.nio.ByteBuffer;
/**
* Handles fading MC and DH together via {@link GlDhFarFadeShader} and {@link GlDhFarFadeApplyShader}. <br><br>
*
* {@link GlDhFarFadeShader} - draws the Fade to a texture. <br>
* {@link GlDhFarFadeApplyShader} - draws the Fade texture to DH's framebuffer. <br>
*/
public class GlDhFarFadeRenderer implements IDhFarFadeRenderer
{
public static GlDhFarFadeRenderer INSTANCE = new GlDhFarFadeRenderer();
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
private static final MinecraftGLWrapper GLMC = MinecraftGLWrapper.INSTANCE;
private boolean init = false;
private int width = -1;
private int height = -1;
private int fadeFramebuffer = -1;
private int fadeTexture = -1;
//=============//
// constructor //
//=============//
//region
private GlDhFarFadeRenderer() { }
public void init()
{
if (this.init) return;
this.init = true;
GlDhFarFadeShader.INSTANCE.init();
GlDhFarFadeApplyShader.INSTANCE.init();
}
private void createFramebuffer(int width, int height)
{
if (this.fadeFramebuffer != -1)
{
GL32.glDeleteFramebuffers(this.fadeFramebuffer);
this.fadeFramebuffer = -1;
}
this.fadeFramebuffer = GL32.glGenFramebuffers();
GLMC.glBindFramebuffer(GL32.GL_FRAMEBUFFER, this.fadeFramebuffer);
if (this.fadeTexture != -1)
{
GLMC.glDeleteTextures(this.fadeTexture);
this.fadeTexture = -1;
}
this.fadeTexture = GL32.glGenTextures();
{
GLMC.glBindTexture(this.fadeTexture);
GL32.glTexImage2D(GL32.GL_TEXTURE_2D, 0, GL32.GL_RGBA16, width, height, 0, GL32.GL_RGBA, GL32.GL_UNSIGNED_SHORT_4_4_4_4, (ByteBuffer) null);
GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_MIN_FILTER, GL32.GL_LINEAR);
GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_MAG_FILTER, GL32.GL_LINEAR);
// disable mip-mapping since DH is just going to draw straight to the screen
GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D, GL43C.GL_TEXTURE_BASE_LEVEL, 0);
GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D, GL43C.GL_TEXTURE_MAX_LEVEL, 0);
}
GL32.glFramebufferTexture2D(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT0, GL32.GL_TEXTURE_2D, this.fadeTexture, 0);
}
//endregion
//========//
// render //
//========//
//region
@Override
public void render(RenderParams renderParams)
{
try
{
this.init();
// resize the framebuffer if necessary
int width = MC_RENDER.getTargetFramebufferViewportWidth();
int height = MC_RENDER.getTargetFramebufferViewportHeight();
if (this.width != width || this.height != height)
{
this.width = width;
this.height = height;
this.createFramebuffer(width, height);
}
GlDhFarFadeShader.INSTANCE.frameBuffer = this.fadeFramebuffer;
GlDhFarFadeShader.INSTANCE.setProjectionMatrix(renderParams.mcModelViewMatrix, renderParams.mcProjectionMatrix);
GlDhFarFadeShader.INSTANCE.render(renderParams);
GlDhFarFadeApplyShader.INSTANCE.fadeTexture = this.fadeTexture;
GlDhFarFadeApplyShader.INSTANCE.readFramebuffer = GlDhFarFadeShader.INSTANCE.frameBuffer;
GlDhFarFadeApplyShader.INSTANCE.drawFramebuffer = GlDhMetaRenderer.INSTANCE.getActiveFramebufferId();
GlDhFarFadeApplyShader.INSTANCE.render(renderParams);
}
catch (Exception e)
{
LOGGER.error("Unexpected error during fade render, error: ["+e.getMessage()+"].", e);
}
}
//emdregion
}
@@ -0,0 +1,167 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.render.openGl.postProcessing.fade;
import com.seibel.distanthorizons.api.objects.math.DhApiMat4f;
import com.seibel.distanthorizons.common.render.openGl.GlDhMetaRenderer;
import com.seibel.distanthorizons.common.render.openGl.glObject.shader.GlShaderProgram;
import com.seibel.distanthorizons.common.render.openGl.postProcessing.GlScreenQuad;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.common.render.openGl.util.GlAbstractShaderRenderer;
import com.seibel.distanthorizons.core.render.RenderParams;
import com.seibel.distanthorizons.core.util.RenderUtil;
import com.seibel.distanthorizons.core.util.math.Mat4f;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import org.lwjgl.opengl.GL32;
public class GlDhFarFadeShader extends GlAbstractShaderRenderer
{
public static GlDhFarFadeShader INSTANCE = new GlDhFarFadeShader();
private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
private static final MinecraftGLWrapper GLMC = MinecraftGLWrapper.INSTANCE;
public int frameBuffer = -1;
private Mat4f inverseDhMvmProjMatrix;
// Uniforms
/** Inverted Model View Projection matrix */
public int uDhInvMvmProj = -1;
public int uDhDepthTexture = -1;
public int uMcColorTexture = -1;
public int uDhColorTexture = -1;
public int uStartFadeBlockDistance = -1;
public int uEndFadeBlockDistance = -1;
//=============//
// constructor //
//=============//
public GlDhFarFadeShader() { }
@Override
public void onInit()
{
this.shader = new GlShaderProgram(
"assets/distanthorizons/shaders/shared/gl/quad_apply.vert",
"assets/distanthorizons/shaders/fade/gl/dh_fade.frag",
"vPosition"
);
// all uniforms should be tryGet...
// because disabling fade can cause the GLSL to optimize out most (if not all) uniforms
// near fade
this.uDhInvMvmProj = this.shader.tryGetUniformLocation("uDhInvMvmProj");
this.uDhDepthTexture = this.shader.tryGetUniformLocation("uDhDepthTexture");
this.uMcColorTexture = this.shader.tryGetUniformLocation("uMcColorTexture");
this.uDhColorTexture = this.shader.tryGetUniformLocation("uDhColorTexture");
this.uStartFadeBlockDistance = this.shader.tryGetUniformLocation("uStartFadeBlockDistance");
this.uEndFadeBlockDistance = this.shader.tryGetUniformLocation("uEndFadeBlockDistance");
}
//=============//
// render prep //
//=============//
@Override
protected void onApplyUniforms(RenderParams renderParams)
{
this.shader.setUniform(this.uDhInvMvmProj, this.inverseDhMvmProjMatrix);
float dhFarClipDistance = RenderUtil.getFarClipPlaneDistanceInBlocks();
float fadeStartDistance = dhFarClipDistance * 0.5f;
float fadeEndDistance = dhFarClipDistance * 0.9f;
this.shader.setUniform(this.uStartFadeBlockDistance, fadeStartDistance);
this.shader.setUniform(this.uEndFadeBlockDistance, fadeEndDistance);
}
public void setProjectionMatrix(DhApiMat4f mcModelViewMatrix, DhApiMat4f mcProjectionMatrix)
{
Mat4f dhProjectionMatrix = RenderUtil.createLodProjectionMatrix(mcProjectionMatrix);
Mat4f dhModelViewMatrix = RenderUtil.createLodModelViewMatrix(mcModelViewMatrix);
Mat4f inverseDhModelViewProjectionMatrix = new Mat4f(dhProjectionMatrix);
inverseDhModelViewProjectionMatrix.multiply(dhModelViewMatrix);
inverseDhModelViewProjectionMatrix.invert();
this.inverseDhMvmProjMatrix = inverseDhModelViewProjectionMatrix;
}
//========//
// render //
//========//
@Override
protected void onRender()
{
int depthTextureId = GlDhMetaRenderer.INSTANCE.getActiveDepthTextureId();
int colorTextureId = GlDhMetaRenderer.INSTANCE.getActiveColorTextureId();
if (depthTextureId == -1
|| colorTextureId == -1)
{
// the renderer is currently being re-built and/or inactive,
// we don't need to/can't render fading
return;
}
GLMC.glBindFramebuffer(GL32.GL_FRAMEBUFFER, this.frameBuffer);
GLMC.disableScissorTest();
GLMC.disableDepthTest();
GLMC.disableBlend();
GLMC.glActiveTexture(GL32.GL_TEXTURE0);
GLMC.glBindTexture(depthTextureId);
GL32.glUniform1i(this.uDhDepthTexture, 0);
GLMC.glActiveTexture(GL32.GL_TEXTURE1);
GLMC.glBindTexture(MC_RENDER.getColorTextureId());
GL32.glUniform1i(this.uMcColorTexture, 1);
GLMC.glActiveTexture(GL32.GL_TEXTURE2);
GLMC.glBindTexture(colorTextureId);
GL32.glUniform1i(this.uDhColorTexture, 2);
GlScreenQuad.INSTANCE.render();
}
}
@@ -0,0 +1,211 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.render.openGl.postProcessing.fade;
import com.seibel.distanthorizons.api.objects.math.DhApiMat4f;
import com.seibel.distanthorizons.common.render.openGl.GlDhMetaRenderer;
import com.seibel.distanthorizons.common.render.openGl.glObject.shader.GlShaderProgram;
import com.seibel.distanthorizons.common.render.openGl.postProcessing.GlScreenQuad;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.common.render.openGl.util.GlAbstractShaderRenderer;
import com.seibel.distanthorizons.core.render.RenderParams;
import com.seibel.distanthorizons.core.util.RenderUtil;
import com.seibel.distanthorizons.core.util.math.Mat4f;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import org.lwjgl.opengl.GL32;
public class GlDhVanillaFadeShader extends GlAbstractShaderRenderer
{
public static GlDhVanillaFadeShader INSTANCE = new GlDhVanillaFadeShader();
private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
private static final MinecraftGLWrapper GLMC = MinecraftGLWrapper.INSTANCE;
public int frameBuffer = -1;
private Mat4f inverseMcMvmProjMatrix;
private Mat4f inverseDhMvmProjMatrix;
private float levelMaxHeight;
// Uniforms
public int uMcDepthTexture = -1;
public int uDhDepthTexture = -1;
public int uCombinedMcDhColorTexture = -1;
public int uDhColorTexture = -1;
/** Inverted Model View Projection matrix */
public int uDhInvMvmProj = -1;
public int uMcInvMvmProj = -1;
public int uStartFadeBlockDistance = -1;
public int uEndFadeBlockDistance = -1;
public int uMaxLevelHeight = -1;
public int uOnlyRenderLods = -1;
//=============//
// constructor //
//=============//
//region
public GlDhVanillaFadeShader() { }
@Override
public void onInit()
{
this.shader = new GlShaderProgram(
"assets/distanthorizons/shaders/shared/gl/quad_apply.vert",
"assets/distanthorizons/shaders/fade/gl/vanilla_fade.frag",
"vPosition"
);
// all uniforms should be tryGet...
// because disabling fade can cause the GLSL to optimize out most (if not all) uniforms
// near fade
this.uDhInvMvmProj = this.shader.tryGetUniformLocation("uDhInvMvmProj");
this.uMcInvMvmProj = this.shader.tryGetUniformLocation("uMcInvMvmProj");
this.uMcDepthTexture = this.shader.tryGetUniformLocation("uMcDepthTexture");
this.uDhDepthTexture = this.shader.tryGetUniformLocation("uDhDepthTexture");
this.uCombinedMcDhColorTexture = this.shader.tryGetUniformLocation("uCombinedMcDhColorTexture");
this.uDhColorTexture = this.shader.tryGetUniformLocation("uDhColorTexture");
this.uStartFadeBlockDistance = this.shader.tryGetUniformLocation("uStartFadeBlockDistance");
this.uEndFadeBlockDistance = this.shader.tryGetUniformLocation("uEndFadeBlockDistance");
this.uMaxLevelHeight = this.shader.tryGetUniformLocation("uMaxLevelHeight");
this.uOnlyRenderLods = this.shader.tryGetUniformLocation("uOnlyRenderLods");
}
//endregion
//=============//
// render prep //
//=============//
//region
@Override
protected void onApplyUniforms(RenderParams renderParams)
{
this.shader.setUniform(this.uMcInvMvmProj, this.inverseMcMvmProjMatrix);
this.shader.setUniform(this.uDhInvMvmProj, this.inverseDhMvmProjMatrix);
float dhNearClipDistance = RenderUtil.getNearClipPlaneInBlocks();
// this added value prevents the near clip plane and discard circle from touching, which looks bad
dhNearClipDistance += 16f;
// measured in blocks
// these multipliers in James' tests should provide a fairly smooth transition
// without having underdraw issues
float fadeStartDistance = dhNearClipDistance * 1.5f;
float fadeEndDistance = dhNearClipDistance * 1.9f;
this.shader.setUniform(this.uStartFadeBlockDistance, fadeStartDistance);
this.shader.setUniform(this.uEndFadeBlockDistance, fadeEndDistance);
this.shader.setUniform(this.uMaxLevelHeight, this.levelMaxHeight);
this.shader.setUniform(this.uOnlyRenderLods, Config.Client.Advanced.Debugging.lodOnlyMode.get());
}
public void setProjectionMatrix(DhApiMat4f mcModelViewMatrix, DhApiMat4f mcProjectionMatrix)
{
Mat4f inverseMcModelViewProjectionMatrix = new Mat4f(mcProjectionMatrix);
inverseMcModelViewProjectionMatrix.multiply(mcModelViewMatrix);
inverseMcModelViewProjectionMatrix.invert();
this.inverseMcMvmProjMatrix = inverseMcModelViewProjectionMatrix;
Mat4f dhProjectionMatrix = RenderUtil.createLodProjectionMatrix(mcProjectionMatrix);
Mat4f dhModelViewMatrix = RenderUtil.createLodModelViewMatrix(mcModelViewMatrix);
Mat4f inverseDhModelViewProjectionMatrix = new Mat4f(dhProjectionMatrix);
inverseDhModelViewProjectionMatrix.multiply(dhModelViewMatrix);
inverseDhModelViewProjectionMatrix.invert();
this.inverseDhMvmProjMatrix = inverseDhModelViewProjectionMatrix;
}
public void setLevelMaxHeight(int levelMaxHeight) { this.levelMaxHeight = levelMaxHeight; }
//endregion
//========//
// render //
//========//
//region
@Override
protected void onRender()
{
int depthTextureId = GlDhMetaRenderer.INSTANCE.getActiveDepthTextureId();
int colorTextureId = GlDhMetaRenderer.INSTANCE.getActiveColorTextureId();
if (depthTextureId == -1
|| colorTextureId == -1)
{
// the renderer is currently being re-built and/or inactive,
// we don't need to/can't render fading
return;
}
GLMC.glBindFramebuffer(GL32.GL_FRAMEBUFFER, this.frameBuffer);
GLMC.disableScissorTest();
GLMC.disableDepthTest();
GLMC.disableBlend();
GLMC.glActiveTexture(GL32.GL_TEXTURE0);
GLMC.glBindTexture(MC_RENDER.getDepthTextureId());
GL32.glUniform1i(this.uMcDepthTexture, 0);
GLMC.glActiveTexture(GL32.GL_TEXTURE1);
GLMC.glBindTexture(depthTextureId);
GL32.glUniform1i(this.uDhDepthTexture, 1);
GLMC.glActiveTexture(GL32.GL_TEXTURE2);
GLMC.glBindTexture(MC_RENDER.getColorTextureId());
GL32.glUniform1i(this.uCombinedMcDhColorTexture, 2);
GLMC.glActiveTexture(GL32.GL_TEXTURE3);
GLMC.glBindTexture(colorTextureId);
GL32.glUniform1i(this.uDhColorTexture, 3);
GlScreenQuad.INSTANCE.render();
}
//endregion
}
@@ -0,0 +1,194 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.render.openGl.postProcessing.fade;
import com.seibel.distanthorizons.common.render.openGl.GlDhMetaRenderer;
import com.seibel.distanthorizons.common.render.openGl.glObject.GLState;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.render.RenderParams;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhVanillaFadeRenderer;
import com.seibel.distanthorizons.core.logging.DhLogger;
import org.lwjgl.opengl.GL32;
import java.nio.ByteBuffer;
/**
* Handles fading MC and DH together via {@link GlDhVanillaFadeShader} and {@link GlDhFarFadeApplyShader}. <br><br>
*
* {@link GlDhVanillaFadeShader} - draws the Fade to a texture. <br>
* {@link GlDhFarFadeApplyShader} - draws the Fade texture to MC's FrameBuffer. <br>
*/
public class GlVanillaFadeRenderer implements IDhVanillaFadeRenderer
{
public static GlVanillaFadeRenderer INSTANCE = new GlVanillaFadeRenderer();
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
private static final IMinecraftClientWrapper MC_CLIENT = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
private static final MinecraftGLWrapper GLMC = MinecraftGLWrapper.INSTANCE;
private boolean init = false;
private int width = -1;
private int height = -1;
private int fadeFramebuffer = -1;
private int fadeTexture = -1;
//=============//
// constructor //
//=============//
//region
private GlVanillaFadeRenderer() { }
public void init()
{
if (this.init) return;
this.init = true;
GlDhVanillaFadeShader.INSTANCE.init();
GlDhFarFadeApplyShader.INSTANCE.init();
}
private void createFramebuffer(int width, int height)
{
if (this.fadeFramebuffer != -1)
{
GL32.glDeleteFramebuffers(this.fadeFramebuffer);
this.fadeFramebuffer = -1;
}
this.fadeFramebuffer = GL32.glGenFramebuffers();
GLMC.glBindFramebuffer(GL32.GL_FRAMEBUFFER, this.fadeFramebuffer);
// Applying the fade texture is only needed if MC is drawing to their own frame buffer,
// otherwise we can directly render to their texture
if (MC_RENDER.mcRendersToFrameBuffer())
{
if (this.fadeTexture != -1)
{
GLMC.glDeleteTextures(this.fadeTexture);
this.fadeTexture = -1;
}
this.fadeTexture = GL32.glGenTextures();
GLMC.glBindTexture(this.fadeTexture);
GL32.glTexImage2D(GL32.GL_TEXTURE_2D, 0, GL32.GL_RGBA16, width, height, 0, GL32.GL_RGBA, GL32.GL_UNSIGNED_SHORT_4_4_4_4, (ByteBuffer) null);
GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_MIN_FILTER, GL32.GL_LINEAR);
GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_MAG_FILTER, GL32.GL_LINEAR);
GL32.glFramebufferTexture2D(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT0, GL32.GL_TEXTURE_2D, this.fadeTexture, 0);
}
else
{
GL32.glFramebufferTexture2D(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT0, GL32.GL_TEXTURE_2D, MC_RENDER.getColorTextureId(), 0);
}
}
//endregion
//========//
// render //
//========//
//region
@Override
public void render(RenderParams renderParams)
{
int depthTextureId = GlDhMetaRenderer.INSTANCE.getActiveDepthTextureId();
if (depthTextureId == -1)
{
// the renderer hasn't been set up yet
// trying to render fading may cause GL errors
return;
}
IProfilerWrapper profiler = MC_CLIENT.getProfiler();
try (IProfilerWrapper.IProfileBlock fade_profile = profiler.push("DH-Vanilla Fade");
GLState mcState = new GLState())
{
this.init();
// resize the framebuffer if necessary
int width = MC_RENDER.getTargetFramebufferViewportWidth();
int height = MC_RENDER.getTargetFramebufferViewportHeight();
if (this.width != width || this.height != height)
{
this.width = width;
this.height = height;
this.createFramebuffer(width, height);
}
GlDhVanillaFadeShader.INSTANCE.frameBuffer = this.fadeFramebuffer;
GlDhVanillaFadeShader.INSTANCE.setProjectionMatrix(renderParams.mcModelViewMatrix, renderParams.mcProjectionMatrix);
GlDhVanillaFadeShader.INSTANCE.setLevelMaxHeight(renderParams.clientLevelWrapper.getMaxHeight());
GlDhVanillaFadeShader.INSTANCE.render(renderParams);
// Applying the fade texture is only needed if MC is drawing to their own frame buffer,
// otherwise we can directly render to their texture
if (MC_RENDER.mcRendersToFrameBuffer())
{
GlDhFarFadeApplyShader.INSTANCE.fadeTexture = this.fadeTexture;
GlDhFarFadeApplyShader.INSTANCE.readFramebuffer = GlDhVanillaFadeShader.INSTANCE.frameBuffer;
GlDhFarFadeApplyShader.INSTANCE.drawFramebuffer = MC_RENDER.getTargetFramebuffer();
GlDhFarFadeApplyShader.INSTANCE.render(renderParams);
}
}
catch (Exception e)
{
LOGGER.error("Unexpected error during fade render, error: [" + e.getMessage() + "].", e);
}
}
//endregion
//================//
// base overrides //
//================//
//region
public void free()
{
GlDhVanillaFadeShader.INSTANCE.free();
GlDhFarFadeApplyShader.INSTANCE.free();
}
//endregion
}
@@ -0,0 +1,118 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.render.openGl.postProcessing.fog;
import com.seibel.distanthorizons.common.render.openGl.GlDhMetaRenderer;
import com.seibel.distanthorizons.common.render.openGl.glObject.shader.GlShaderProgram;
import com.seibel.distanthorizons.common.render.openGl.postProcessing.GlScreenQuad;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper;
import com.seibel.distanthorizons.common.render.openGl.util.GlAbstractShaderRenderer;
import com.seibel.distanthorizons.core.render.RenderParams;
import org.lwjgl.opengl.GL32;
/**
* Draws the Fog texture onto DH's FrameBuffer. <br><br>
*
* See Also: <br>
* {@link GlDhFogRenderer} - Parent to this shader. <br>
* {@link GlDhFogShader} - draws the Fog texture. <br>
*/
public class GlDhFogApplyShader extends GlAbstractShaderRenderer
{
public static GlDhFogApplyShader INSTANCE = new GlDhFogApplyShader();
private static final MinecraftGLWrapper GLMC = MinecraftGLWrapper.INSTANCE;
public int fogTexture;
// uniforms
public int colorTextureUniform;
public int depthTextureUniform;
//=============//
// constructor //
//=============//
@Override
public void onInit()
{
this.shader = new GlShaderProgram(
"assets/distanthorizons/shaders/shared/gl/quad_apply.vert",
"assets/distanthorizons/shaders/fog/gl/apply.frag",
"vPosition"
);
// uniform setup
this.colorTextureUniform = this.shader.getUniformLocation("uColorTexture");
this.depthTextureUniform = this.shader.getUniformLocation("uDepthTexture");
}
//=============//
// render prep //
//=============//
@Override
protected void onApplyUniforms(RenderParams renderParams)
{
GLMC.glActiveTexture(GL32.GL_TEXTURE0);
GLMC.glBindTexture(this.fogTexture);
GL32.glUniform1i(this.colorTextureUniform, 0);
GLMC.glActiveTexture(GL32.GL_TEXTURE1);
GLMC.glBindTexture(GlDhMetaRenderer.INSTANCE.getActiveDepthTextureId());
GL32.glUniform1i(this.depthTextureUniform, 1);
}
//========//
// render //
//========//
@Override
protected void onRender()
{
GLMC.enableBlend();
GL32.glBlendEquation(GL32.GL_FUNC_ADD);
GLMC.glBlendFuncSeparate(GL32.GL_SRC_ALPHA, GL32.GL_ONE_MINUS_SRC_ALPHA, GL32.GL_ONE, GL32.GL_ONE_MINUS_SRC_ALPHA);
// Depth testing must be disabled otherwise this application shader won't apply anything.
// setting this isn't necessary in vanilla, but some mods may change this, requiring it to be set manually,
// it should be automatically restored after rendering is complete.
GLMC.disableDepthTest();
// apply the rendered Fog to DH's framebuffer
GLMC.glBindFramebuffer(GL32.GL_READ_FRAMEBUFFER, GlDhFogShader.INSTANCE.frameBuffer);
GLMC.glBindFramebuffer(GL32.GL_DRAW_FRAMEBUFFER, GlDhMetaRenderer.INSTANCE.getActiveFramebufferId());
GlScreenQuad.INSTANCE.render();
GLMC.glBindFramebuffer(GL32.GL_READ_FRAMEBUFFER, 0);
}
}
@@ -0,0 +1,156 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.render.openGl.postProcessing.fog;
import com.seibel.distanthorizons.common.render.openGl.glObject.GLState;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.render.RenderParams;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhFogRenderer;
import org.lwjgl.opengl.GL32;
import org.lwjgl.opengl.GL43C;
import java.nio.ByteBuffer;
/**
* Handles adding SSAO via {@link GlDhFogShader} and {@link GlDhFogApplyShader}. <br><br>
*
* {@link GlDhFogShader} - draws the Fog to a texture. <br>
* {@link GlDhFogApplyShader} - draws the Fog texture to DH's FrameBuffer. <br>
*/
public class GlDhFogRenderer implements IDhFogRenderer
{
public static GlDhFogRenderer INSTANCE = new GlDhFogRenderer();
private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
private static final MinecraftGLWrapper GLMC = MinecraftGLWrapper.INSTANCE;
private boolean init = false;
private int width = -1;
private int height = -1;
private int fogFramebuffer = -1;
private int fogTexture = -1;
//=============//
// constructor //
//=============//
private GlDhFogRenderer() { }
public void init()
{
if (this.init) return;
this.init = true;
GlDhFogShader.INSTANCE.init();
GlDhFogApplyShader.INSTANCE.init();
}
private void createFramebuffer(int width, int height)
{
if (this.fogFramebuffer != -1)
{
GL32.glDeleteFramebuffers(this.fogFramebuffer);
this.fogFramebuffer = -1;
}
if (this.fogTexture != -1)
{
GLMC.glDeleteTextures(this.fogTexture);
this.fogTexture = -1;
}
this.fogFramebuffer = GL32.glGenFramebuffers();
GLMC.glBindFramebuffer(GL32.GL_FRAMEBUFFER, this.fogFramebuffer);
this.fogTexture = GLMC.glGenTextures();
{
GLMC.glBindTexture(this.fogTexture);
GL32.glTexImage2D(GL32.GL_TEXTURE_2D, 0, GL32.GL_RGBA16, width, height, 0, GL32.GL_RGBA, GL32.GL_UNSIGNED_SHORT_4_4_4_4, (ByteBuffer) null);
GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_MIN_FILTER, GL32.GL_LINEAR);
GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_MAG_FILTER, GL32.GL_LINEAR);
GL32.glFramebufferTexture2D(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT0, GL32.GL_TEXTURE_2D, this.fogTexture, 0);
// disable mip-mapping since DH is just going to draw straight to the screen
GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D, GL43C.GL_TEXTURE_BASE_LEVEL, 0);
GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D, GL43C.GL_TEXTURE_MAX_LEVEL, 0);
}
}
//========//
// render //
//========//
//region
@Override
public void render(RenderParams renderParams)
{
// GLState needed in MC 1.16.5 probably due to MC not manually setting each GL state they need before the next rendering step
try (GLState state = new GLState())
{
this.init();
// resize the framebuffer if necessary
int width = MC_RENDER.getTargetFramebufferViewportWidth();
int height = MC_RENDER.getTargetFramebufferViewportHeight();
if (this.width != width || this.height != height)
{
this.width = width;
this.height = height;
this.createFramebuffer(width, height);
}
GlDhFogShader.INSTANCE.frameBuffer = this.fogFramebuffer;
GlDhFogShader.INSTANCE.setProjectionMatrix(renderParams.dhMvmProjMatrix);
GlDhFogShader.INSTANCE.render(renderParams);
GlDhFogApplyShader.INSTANCE.fogTexture = this.fogTexture;
GlDhFogApplyShader.INSTANCE.render(renderParams);
}
}
//endregion
//================//
// base overrides //
//================//
//region
public void free()
{
GlDhFogShader.INSTANCE.free();
GlDhFogApplyShader.INSTANCE.free();
}
//endregion
}
@@ -0,0 +1,301 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.render.openGl.postProcessing.fog;
import com.seibel.distanthorizons.api.enums.rendering.EDhApiFogColorMode;
import com.seibel.distanthorizons.api.enums.rendering.EDhApiHeightFogDirection;
import com.seibel.distanthorizons.api.enums.rendering.EDhApiHeightFogMixMode;
import com.seibel.distanthorizons.api.objects.math.DhApiMat4f;
import com.seibel.distanthorizons.common.render.openGl.GlDhMetaRenderer;
import com.seibel.distanthorizons.common.render.openGl.glObject.shader.GlShaderProgram;
import com.seibel.distanthorizons.common.render.openGl.postProcessing.GlScreenQuad;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.common.render.openGl.util.GlAbstractShaderRenderer;
import com.seibel.distanthorizons.core.render.RenderParams;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.util.math.Mat4f;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import org.lwjgl.opengl.GL32;
import java.awt.*;
public class GlDhFogShader extends GlAbstractShaderRenderer
{
public static final GlDhFogShader INSTANCE = new GlDhFogShader();
private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
private static final MinecraftGLWrapper GLMC = MinecraftGLWrapper.INSTANCE;
private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
public int frameBuffer;
private Mat4f inverseMvmProjMatrix;
//==========//
// Uniforms //
//==========//
//region
public int uDepthMap;
/** Inverted Model View Projection matrix */
public int uInvMvmProj;
// fog uniforms
public int uFogColor;
public int uFogScale;
public int uFogVerticalScale;
public int uFogDebugMode;
public int uFogFalloffType;
// far fog
public int uFarFogStart;
public int uFarFogLength;
public int uFarFogMin;
public int uFarFogRange;
public int uFarFogDensity;
// height fog
public int uHeightFogStart;
public int uHeightFogLength;
public int uHeightFogMin;
public int uHeightFogRange;
public int uHeightFogDensity;
public int uHeightFogEnabled;
public int uHeightFogFalloffType;
public int uHeightBasedOnCamera;
public int uHeightFogBaseHeight;
public int uHeightFogAppliesUp;
public int uHeightFogAppliesDown;
public int uUseSphericalFog;
public int uHeightFogMixingMode;
public int uCameraBlockYPos;
//endregion
//=============//
// constructor //
//=============//
//region
public GlDhFogShader() { }
@Override
public void onInit()
{
this.shader = new GlShaderProgram(
"assets/distanthorizons/shaders/shared/gl/quad_apply.vert",
"assets/distanthorizons/shaders/fog/gl/fog.frag",
"vPosition"
);
// all uniforms should be tryGet...
// because disabling fog can cause the GLSL to optimize out most (if not all) uniforms
this.uDepthMap = this.shader.getUniformLocation("uDepthMap");
this.uInvMvmProj = this.shader.getUniformLocation("uInvMvmProj");
// Fog uniforms
this.uFogScale = this.shader.getUniformLocation("uFogScale");
this.uFogVerticalScale = this.shader.getUniformLocation("uFogVerticalScale");
this.uFogColor = this.shader.getUniformLocation("uFogColor");
this.uFogDebugMode = this.shader.getUniformLocation("uFogDebugMode");
this.uFogFalloffType = this.shader.getUniformLocation("uFogFalloffType");
// fog config
this.uFarFogStart = this.shader.getUniformLocation("uFarFogStart");
this.uFarFogLength = this.shader.getUniformLocation("uFarFogLength");
this.uFarFogMin = this.shader.getUniformLocation("uFarFogMin");
this.uFarFogRange = this.shader.getUniformLocation("uFarFogRange");
this.uFarFogDensity = this.shader.getUniformLocation("uFarFogDensity");
// height fog
this.uHeightFogStart = this.shader.getUniformLocation("uHeightFogStart");
this.uHeightFogLength = this.shader.getUniformLocation("uHeightFogLength");
this.uHeightFogMin = this.shader.getUniformLocation("uHeightFogMin");
this.uHeightFogRange = this.shader.getUniformLocation("uHeightFogRange");
this.uHeightFogDensity = this.shader.getUniformLocation("uHeightFogDensity");
this.uHeightFogEnabled = this.shader.getUniformLocation("uHeightFogEnabled");
this.uHeightFogFalloffType = this.shader.getUniformLocation("uHeightFogFalloffType");
this.uHeightBasedOnCamera = this.shader.getUniformLocation("uHeightBasedOnCamera");
this.uHeightFogBaseHeight = this.shader.getUniformLocation("uHeightFogBaseHeight");
this.uHeightFogAppliesUp = this.shader.getUniformLocation("uHeightFogAppliesUp");
this.uHeightFogAppliesDown = this.shader.getUniformLocation("uHeightFogAppliesDown");
this.uUseSphericalFog = this.shader.getUniformLocation("uUseSphericalFog");
this.uHeightFogMixingMode = this.shader.getUniformLocation("uHeightFogMixingMode");
this.uCameraBlockYPos = this.shader.getUniformLocation("uCameraBlockYPos");
}
//endregion
//=============//
// render prep //
//=============//
//region
@Override
protected void onApplyUniforms(RenderParams renderParams)
{
int lodDrawDistance = Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistanceRadius.get() * LodUtil.CHUNK_WIDTH;
if (this.inverseMvmProjMatrix != null)
{
this.shader.setUniform(this.uInvMvmProj, this.inverseMvmProjMatrix);
}
// Fog uniforms
this.shader.setUniform(this.uFogColor, this.getFogColor(renderParams.partialTicks));
this.shader.setUniform(this.uFogScale, 1.f / lodDrawDistance);
this.shader.setUniform(this.uFogVerticalScale, 1.f / MC.getWrappedClientLevel().getMaxHeight());
// only used for debugging
this.shader.setUniform(this.uFogDebugMode, 0); // 1 = render everything with fog color // 7 = use debug rendering
this.shader.setUniform(this.uFogFalloffType, Config.Client.Advanced.Graphics.Fog.farFogFalloff.get().value);
// fog config
float farFogStart = Config.Client.Advanced.Graphics.Fog.farFogStart.get();
float farFogEnd = Config.Client.Advanced.Graphics.Fog.farFogEnd.get();
float farFogMin = Config.Client.Advanced.Graphics.Fog.farFogMin.get();
float farFogMax = Config.Client.Advanced.Graphics.Fog.farFogMax.get();
float farFogDensity = Config.Client.Advanced.Graphics.Fog.farFogDensity.get();
// override fog if underwater
if (MC_RENDER.isFogStateSpecial())
{
// hide everything behind fog
farFogStart = 0.0f;
farFogEnd = 0.0f;
}
this.shader.setUniform(this.uFarFogStart, farFogStart);
this.shader.setUniform(this.uFarFogLength, farFogEnd - farFogStart);
this.shader.setUniform(this.uFarFogMin, farFogMin);
this.shader.setUniform(this.uFarFogRange, farFogMax - farFogMin);
this.shader.setUniform(this.uFarFogDensity, farFogDensity);
// height config
EDhApiHeightFogMixMode heightFogMixingMode = Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogMixMode.get();
boolean heightFogEnabled = heightFogMixingMode != EDhApiHeightFogMixMode.SPHERICAL && heightFogMixingMode != EDhApiHeightFogMixMode.CYLINDRICAL;
boolean useSphericalFog = heightFogMixingMode == EDhApiHeightFogMixMode.SPHERICAL;
EDhApiHeightFogDirection heightFogCameraDirection = Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogDirection.get();
float heightFogStart = Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogStart.get();
float heightFogEnd = Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogEnd.get();
float heightFogMin = Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogMin.get();
float heightFogMax = Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogMax.get();
float heightFogDensity = Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogDensity.get();
this.shader.setUniform(this.uHeightFogStart, heightFogStart);
this.shader.setUniform(this.uHeightFogLength, heightFogEnd - heightFogStart);
this.shader.setUniform(this.uHeightFogMin, heightFogMin);
this.shader.setUniform(this.uHeightFogRange, heightFogMax - heightFogMin);
this.shader.setUniform(this.uHeightFogDensity, heightFogDensity);
this.shader.setUniform(this.uHeightFogEnabled, heightFogEnabled);
this.shader.setUniform(this.uHeightFogFalloffType, Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogFalloff.get().value);
this.shader.setUniform(this.uHeightFogBaseHeight, Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogBaseHeight.get());
this.shader.setUniform(this.uHeightBasedOnCamera, heightFogCameraDirection.basedOnCamera);
this.shader.setUniform(this.uHeightFogAppliesUp, heightFogCameraDirection.fogAppliesUp);
this.shader.setUniform(this.uHeightFogAppliesDown, heightFogCameraDirection.fogAppliesDown);
this.shader.setUniform(this.uUseSphericalFog, useSphericalFog);
this.shader.setUniform(this.uHeightFogMixingMode, heightFogMixingMode.value);
this.shader.setUniform(this.uCameraBlockYPos, (float)MC_RENDER.getCameraExactPosition().y);
}
private Color getFogColor(float partialTicks)
{
Color fogColor;
if (Config.Client.Advanced.Graphics.Fog.colorMode.get() == EDhApiFogColorMode.USE_SKY_COLOR)
{
fogColor = MC_RENDER.getSkyColor();
}
else
{
fogColor = MC_RENDER.getFogColor(partialTicks);
}
return fogColor;
}
public void setProjectionMatrix(DhApiMat4f modelViewProjectionMatrix)
{
this.inverseMvmProjMatrix = new Mat4f(modelViewProjectionMatrix);
this.inverseMvmProjMatrix.invert();
}
//endregion
//========//
// render //
//========//
//region
@Override
protected void onRender()
{
GLMC.glBindFramebuffer(GL32.GL_FRAMEBUFFER, this.frameBuffer);
GLMC.disableScissorTest();
GLMC.disableDepthTest();
GLMC.disableBlend();
GLMC.glActiveTexture(GL32.GL_TEXTURE0);
GLMC.glBindTexture(GlDhMetaRenderer.INSTANCE.getActiveDepthTextureId());
GL32.glUniform1i(this.uDepthMap, 0);
// this is necessary for MC 1.16 (IE Legacy OpenGL)
// otherwise the framebuffer isn't cleared correctly and the fog smears across the screen
if (MC_RENDER.runningLegacyOpenGL())
{
// in another part of the DH code we set the fog color to opaque, here it needs to be transparent
float[] clearColorValues = new float[4];
GL32.glGetFloatv(GL32.GL_COLOR_CLEAR_VALUE, clearColorValues);
GL32.glClearColor(clearColorValues[0], clearColorValues[1], clearColorValues[2], 0.0f);
GL32.glClear(GL32.GL_COLOR_BUFFER_BIT | GL32.GL_DEPTH_BUFFER_BIT);
}
GlScreenQuad.INSTANCE.render();
}
//endregion
}
@@ -0,0 +1,146 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.render.openGl.postProcessing.ssao;
import com.seibel.distanthorizons.common.render.openGl.GlDhMetaRenderer;
import com.seibel.distanthorizons.common.render.openGl.glObject.shader.GlShaderProgram;
import com.seibel.distanthorizons.common.render.openGl.postProcessing.GlScreenQuad;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper;
import com.seibel.distanthorizons.common.render.openGl.util.GlAbstractShaderRenderer;
import com.seibel.distanthorizons.core.render.RenderParams;
import com.seibel.distanthorizons.core.util.RenderUtil;
import org.lwjgl.opengl.GL32;
/**
* Draws the SSAO texture onto DH's FrameBuffer. <br><br>
*
* See Also: <br>
* {@link GlDhSSAORenderer} - Parent to this shader. <br>
* {@link GlDhSSAOShader} - draws the SSAO texture. <br>
*/
public class GlDhSSAOApplyShader extends GlAbstractShaderRenderer
{
public static GlDhSSAOApplyShader INSTANCE = new GlDhSSAOApplyShader();
private static final MinecraftGLWrapper GLMC = MinecraftGLWrapper.INSTANCE;
public int ssaoTexture;
// uniforms
public int gSSAOMapUniform;
public int gDepthMapUniform;
public int gViewSizeUniform;
public int gBlurRadiusUniform;
public int gNearUniform;
public int gFarUniform;
//=============//
// constructor //
//=============//
@Override
public void onInit()
{
this.shader = new GlShaderProgram(
"assets/distanthorizons/shaders/shared/gl/quad_apply.vert",
"assets/distanthorizons/shaders/ssao/gl/apply.frag",
"vPosition"
);
// uniform setup
this.gSSAOMapUniform = this.shader.getUniformLocation("gSSAOMap");
this.gDepthMapUniform = this.shader.getUniformLocation("gDepthMap");
this.gViewSizeUniform = this.shader.tryGetUniformLocation("gViewSize");
this.gBlurRadiusUniform = this.shader.tryGetUniformLocation("gBlurRadius");
this.gNearUniform = this.shader.tryGetUniformLocation("gNear");
this.gFarUniform = this.shader.tryGetUniformLocation("gFar");
}
//=============//
// render prep //
//=============//
@Override
protected void onApplyUniforms(RenderParams renderParams)
{
GLMC.glActiveTexture(GL32.GL_TEXTURE0);
GLMC.glBindTexture(GlDhMetaRenderer.INSTANCE.getActiveDepthTextureId());
GL32.glUniform1i(this.gDepthMapUniform, 0);
GLMC.glActiveTexture(GL32.GL_TEXTURE1);
GLMC.glBindTexture(this.ssaoTexture);
GL32.glUniform1i(this.gSSAOMapUniform, 1);
GL32.glUniform1i(this.gBlurRadiusUniform, 2);
if (this.gViewSizeUniform >= 0)
{
GL32.glUniform2f(this.gViewSizeUniform,
MC_RENDER.getTargetFramebufferViewportWidth(),
MC_RENDER.getTargetFramebufferViewportHeight());
}
if (this.gNearUniform >= 0)
{
GL32.glUniform1f(this.gNearUniform,
RenderUtil.getNearClipPlaneInBlocks());
}
if (this.gFarUniform >= 0)
{
float farClipPlane = RenderUtil.getFarClipPlaneDistanceInBlocks();
GL32.glUniform1f(this.gFarUniform, farClipPlane);
}
}
//========//
// render //
//========//
@Override
protected void onRender()
{
GLMC.enableBlend();
GL32.glBlendEquation(GL32.GL_FUNC_ADD);
GLMC.glBlendFuncSeparate(GL32.GL_ZERO, GL32.GL_SRC_ALPHA, GL32.GL_ZERO, GL32.GL_ONE);
// Depth testing must be disabled otherwise this application shader won't apply anything.
// setting this isn't necessary in vanilla, but some mods may change this, requiring it to be set manually,
// it should be automatically restored after rendering is complete.
GLMC.disableDepthTest();
// apply the rendered SSAO to the LODs
GLMC.glBindFramebuffer(GL32.GL_READ_FRAMEBUFFER, GlDhSSAOShader.INSTANCE.frameBuffer);
GLMC.glBindFramebuffer(GL32.GL_DRAW_FRAMEBUFFER, GlDhMetaRenderer.INSTANCE.getActiveFramebufferId());
GlScreenQuad.INSTANCE.render();
}
}
@@ -0,0 +1,156 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.render.openGl.postProcessing.ssao;
import com.seibel.distanthorizons.common.render.openGl.glObject.GLState;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.render.RenderParams;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhSsaoRenderer;
import org.lwjgl.opengl.GL32;
import org.lwjgl.opengl.GL43C;
import java.nio.ByteBuffer;
/**
* Handles adding SSAO via {@link GlDhSSAOShader} and {@link GlDhSSAOApplyShader}. <br><br>
*
* {@link GlDhSSAOShader} - draws the SSAO to a texture. <br>
* {@link GlDhSSAOApplyShader} - draws the SSAO texture to DH's FrameBuffer. <br>
*/
public class GlDhSSAORenderer implements IDhSsaoRenderer
{
public static GlDhSSAORenderer INSTANCE = new GlDhSSAORenderer();
private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
private static final MinecraftGLWrapper GLMC = MinecraftGLWrapper.INSTANCE;
private boolean init = false;
private int width = -1;
private int height = -1;
private int ssaoFramebuffer = -1;
private int ssaoTexture = -1;
//=============//
// constructor //
//=============//
private GlDhSSAORenderer() { }
public void init()
{
if (this.init) return;
this.init = true;
GlDhSSAOShader.INSTANCE.init();
GlDhSSAOApplyShader.INSTANCE.init();
}
private void createFramebuffer(int width, int height)
{
if (this.ssaoFramebuffer != -1)
{
GL32.glDeleteFramebuffers(this.ssaoFramebuffer);
this.ssaoFramebuffer = -1;
}
if (this.ssaoTexture != -1)
{
GLMC.glDeleteTextures(this.ssaoTexture);
this.ssaoTexture = -1;
}
this.ssaoFramebuffer = GL32.glGenFramebuffers();
GLMC.glBindFramebuffer(GL32.GL_FRAMEBUFFER, this.ssaoFramebuffer);
this.ssaoTexture = GLMC.glGenTextures();
{
GLMC.glBindTexture(this.ssaoTexture);
GL32.glTexImage2D(GL32.GL_TEXTURE_2D, 0, GL32.GL_R16F, width, height, 0, GL32.GL_RED, GL32.GL_HALF_FLOAT, (ByteBuffer) null);
GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_MIN_FILTER, GL32.GL_LINEAR);
GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_MAG_FILTER, GL32.GL_LINEAR);
// disable mip-mapping since DH is just going to draw straight to the screen
GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D, GL43C.GL_TEXTURE_BASE_LEVEL, 0);
GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D, GL43C.GL_TEXTURE_MAX_LEVEL, 0);
}
GL32.glFramebufferTexture2D(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT0, GL32.GL_TEXTURE_2D, this.ssaoTexture, 0);
}
//========//
// render //
//========//
//region
@Override
public void render(RenderParams renderParams)
{
try(GLState state = new GLState())
{
this.init();
// resize the framebuffer if necessary
int width = MC_RENDER.getTargetFramebufferViewportWidth();
int height = MC_RENDER.getTargetFramebufferViewportHeight();
if (this.width != width || this.height != height)
{
this.width = width;
this.height = height;
this.createFramebuffer(width, height);
}
GlDhSSAOShader.INSTANCE.frameBuffer = this.ssaoFramebuffer;
GlDhSSAOShader.INSTANCE.setProjectionMatrix(renderParams.dhProjectionMatrix);
GlDhSSAOShader.INSTANCE.render(renderParams);
GlDhSSAOApplyShader.INSTANCE.ssaoTexture = this.ssaoTexture;
GlDhSSAOApplyShader.INSTANCE.render(renderParams);
}
}
//endregion
//================//
// base overrides //
//================//
//region
public void free()
{
GlDhSSAOShader.INSTANCE.free();
GlDhSSAOApplyShader.INSTANCE.free();
}
//endregion
}
@@ -0,0 +1,144 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.render.openGl.postProcessing.ssao;
import com.seibel.distanthorizons.api.objects.math.DhApiMat4f;
import com.seibel.distanthorizons.common.render.openGl.GlDhMetaRenderer;
import com.seibel.distanthorizons.common.render.openGl.glObject.shader.GlShaderProgram;
import com.seibel.distanthorizons.common.render.openGl.postProcessing.GlScreenQuad;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper;
import com.seibel.distanthorizons.common.render.openGl.util.GlAbstractShaderRenderer;
import com.seibel.distanthorizons.core.render.RenderParams;
import com.seibel.distanthorizons.core.util.math.Mat4f;
import org.lwjgl.opengl.GL32;
/**
* Draws the SSAO to a texture. <br><br>
*
* See Also: <br>
* {@link GlDhSSAORenderer} - Parent to this shader. <br>
* {@link GlDhSSAOApplyShader} - draws the SSAO texture to DH's FrameBuffer. <br>
*/
public class GlDhSSAOShader extends GlAbstractShaderRenderer
{
public static GlDhSSAOShader INSTANCE = new GlDhSSAOShader();
private static final MinecraftGLWrapper GLMC = MinecraftGLWrapper.INSTANCE;
public int frameBuffer;
private Mat4f projection;
private Mat4f invertedProjection;
// uniforms
public int uProj;
public int uInvProj;
public int uSampleCount;
public int uRadius;
public int uStrength;
public int uMinLight;
public int uBias;
public int uDepthMap;
public int uFadeDistanceInBlocks;
//=============//
// constructor //
//=============//
@Override
public void onInit()
{
this.shader = new GlShaderProgram(
"assets/distanthorizons/shaders/shared/gl/quad_apply.vert",
"assets/distanthorizons/shaders/ssao/gl/ao.frag",
"vPosition"
);
// uniform setup
this.uProj = this.shader.getUniformLocation("uProj");
this.uInvProj = this.shader.getUniformLocation("uInvProj");
this.uSampleCount = this.shader.getUniformLocation("uSampleCount");
this.uRadius = this.shader.getUniformLocation("uRadius");
this.uStrength = this.shader.getUniformLocation("uStrength");
this.uMinLight = this.shader.getUniformLocation("uMinLight");
this.uBias = this.shader.getUniformLocation("uBias");
this.uDepthMap = this.shader.getUniformLocation("uDepthMap");
this.uFadeDistanceInBlocks = this.shader.getUniformLocation("uFadeDistanceInBlocks");
}
//=============//
// render prep //
//=============//
public void setProjectionMatrix(DhApiMat4f projectionMatrix)
{
this.projection = new Mat4f(projectionMatrix);
this.invertedProjection = new Mat4f(projectionMatrix);
this.invertedProjection.invert();
}
@Override
protected void onApplyUniforms(RenderParams renderParams)
{
this.shader.setUniform(this.uProj, this.projection);
this.shader.setUniform(this.uInvProj, this.invertedProjection);
this.shader.setUniform(this.uSampleCount, 6);
this.shader.setUniform(this.uRadius, 4.0f);
this.shader.setUniform(this.uStrength, 0.2f);
this.shader.setUniform(this.uMinLight, 0.25f);
this.shader.setUniform(this.uBias, 0.02f);
this.shader.setUniform(this.uFadeDistanceInBlocks, 1_600.0f);
GL32.glUniform1i(this.uDepthMap, 0);
}
//========//
// render //
//========//
@Override
protected void onRender()
{
GLMC.glBindFramebuffer(GL32.GL_FRAMEBUFFER, this.frameBuffer);
GLMC.disableScissorTest();
GLMC.disableDepthTest();
GLMC.disableBlend();
GLMC.glActiveTexture(GL32.GL_TEXTURE0);
GLMC.glBindTexture(GlDhMetaRenderer.INSTANCE.getActiveDepthTextureId());
GlScreenQuad.INSTANCE.render();
}
}
@@ -0,0 +1,380 @@
package com.seibel.distanthorizons.common.render.openGl.terrain;
import com.seibel.distanthorizons.api.interfaces.override.rendering.IDhApiShaderProgram;
import com.seibel.distanthorizons.api.methods.events.abstractEvents.*;
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam;
import com.seibel.distanthorizons.api.objects.math.DhApiVec3f;
import com.seibel.distanthorizons.common.render.openGl.GlDhMetaRenderer;
import com.seibel.distanthorizons.common.render.openGl.glObject.GLProxy;
import com.seibel.distanthorizons.common.render.openGl.glObject.buffer.GLVertexBuffer;
import com.seibel.distanthorizons.common.render.openGl.glObject.shader.GlShaderProgram;
import com.seibel.distanthorizons.common.render.openGl.glObject.vertexAttribute.GlAbstractVertexAttribute;
import com.seibel.distanthorizons.common.render.openGl.glObject.vertexAttribute.GlVertexAttributePostGL43;
import com.seibel.distanthorizons.common.render.openGl.glObject.vertexAttribute.GlVertexAttributePreGL43;
import com.seibel.distanthorizons.common.render.openGl.glObject.vertexAttribute.GlVertexPointer;
import com.seibel.distanthorizons.common.render.openGl.util.vertexFormat.GlLodVertexFormat;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper;
import com.seibel.distanthorizons.common.wrappers.misc.LightMapWrapper;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding.LodBufferContainer;
import com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding.LodQuadBuilder;
import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector;
import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.render.RenderParams;
import com.seibel.distanthorizons.core.util.RenderUtil;
import com.seibel.distanthorizons.core.util.math.Mat4f;
import com.seibel.distanthorizons.core.util.math.Vec3d;
import com.seibel.distanthorizons.core.util.math.Vec3f;
import com.seibel.distanthorizons.core.util.objects.SortedArraySet;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IIrisAccessor;
import com.seibel.distanthorizons.core.wrapperInterfaces.render.objects.IVertexBufferWrapper;
import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector;
import org.lwjgl.opengl.GL32;
/**
* Handles rendering the normal LOD terrain.
* @see LodQuadBuilder
*/
public class GlDhTerrainShaderProgram extends GlShaderProgram implements IDhApiShaderProgram
{
public static final DhLogger LOGGER = new DhLoggerBuilder()
.fileLevelConfig(Config.Common.Logging.logRendererEventToFile)
.build();
private static final MinecraftGLWrapper GLMC = MinecraftGLWrapper.INSTANCE;
private static final IIrisAccessor IRIS_ACCESSOR = ModAccessorInjector.INSTANCE.get(IIrisAccessor.class);
private boolean init = false;
public GlAbstractVertexAttribute vao;
// uniforms //
//region
public int uCombinedMatrix = -1;
public int uModelOffset = -1;
public int uWorldYOffset = -1;
public int uMircoOffset = -1;
public int uEarthRadius = -1;
public int uLightMap = -1;
// fragment shader uniforms
public int uClipDistance = -1;
public int uDitherDhRendering = -1;
// Noise Uniforms
public int uNoiseEnabled = -1;
public int uNoiseSteps = -1;
public int uNoiseIntensity = -1;
public int uNoiseDropoff = -1;
// Debug Uniform
public int uIsWhiteWorld = -1;
//endregion
//=============//
// constructor //
//=============//
//region
public GlDhTerrainShaderProgram()
{
super(
"assets/distanthorizons/shaders/shared/gl/standard.vert",
"assets/distanthorizons/shaders/shared/gl/flat_shaded.frag",
new String[]{"vPosition", "color"}
);
}
public void tryInit()
{
if (this.init)
{
return;
}
this.uCombinedMatrix = this.getUniformLocation("uCombinedMatrix");
this.uModelOffset = this.getUniformLocation("uModelOffset");
this.uWorldYOffset = this.getUniformLocation("uWorldYOffset");
this.uDitherDhRendering = this.getUniformLocation("uDitherDhRendering");
this.uMircoOffset = this.getUniformLocation("uMircoOffset");
this.uEarthRadius = this.getUniformLocation("uEarthRadius");
this.uLightMap = this.getUniformLocation("uLightMap");
// Fog/Clip Uniforms
this.uClipDistance = this.getUniformLocation("uClipDistance");
// Noise Uniforms
this.uNoiseEnabled = this.getUniformLocation("uNoiseEnabled");
this.uNoiseSteps = this.getUniformLocation("uNoiseSteps");
this.uNoiseIntensity = this.getUniformLocation("uNoiseIntensity");
this.uNoiseDropoff = this.getUniformLocation("uNoiseDropoff");
// Debug Uniform
this.uIsWhiteWorld = this.getUniformLocation("uIsWhiteWorld");
if (GLProxy.getInstance().vertexAttributeBufferBindingSupported)
{
this.vao = new GlVertexAttributePostGL43(); // also binds AbstractVertexAttribute
}
else
{
this.vao = new GlVertexAttributePreGL43(); // also binds AbstractVertexAttribute
}
this.vao.bind();
// short: x, y, z, meta
// meta: byte skylight, byte blocklight, byte microOffset
this.vao.setVertexAttribute(0, 0, GlVertexPointer.addUnsignedShortsPointer(4, false, true));
// byte: r, g, b, a
this.vao.setVertexAttribute(0, 1, GlVertexPointer.addUnsignedBytesPointer(4, true, false));
// byte: iris material ID, normal index, 2 spacers
this.vao.setVertexAttribute(0, 2, GlVertexPointer.addUnsignedBytesPointer(4, true, true));
try
{
int vertexByteCount = GlLodVertexFormat.DH_VERTEX_FORMAT.getByteSize();
this.vao.completeAndCheck(vertexByteCount);
}
catch (RuntimeException e)
{
System.out.println(GlLodVertexFormat.DH_VERTEX_FORMAT);
throw e;
}
// unbinding here is necessary to fix an issue when running on Legacy GL
this.vao.unbind();
this.init = true;
}
//endregion
//=============//
// API methods //
//=============//
//region
@Override
public void bind()
{
this.tryInit();
super.bind();
this.vao.bind();
}
@Override
public void unbind()
{
super.unbind();
this.vao.unbind();
}
@Override
public void free()
{
this.vao.free();
super.free();
}
@Override
public void bindVertexBuffer(int vbo) { this.vao.bindBufferToAllBindingPoints(vbo); }
@Override
public void fillUniformData(DhApiRenderParam renderParameters)
{
Mat4f combinedMatrix = new Mat4f(renderParameters.dhProjectionMatrix);
combinedMatrix.multiply(renderParameters.dhModelViewMatrix);
super.bind();
// uniforms
this.setUniform(this.uCombinedMatrix, combinedMatrix);
this.setUniform(this.uMircoOffset, 0.01f); // 0.01 block offset
this.setUniform(this.uLightMap, LightMapWrapper.GL_BOUND_INDEX);
this.setUniform(this.uWorldYOffset, (float) renderParameters.worldYOffset);
this.setUniform(this.uDitherDhRendering, Config.Client.Advanced.Graphics.Quality.ditherDhFade.get());
float curveRatio = Config.Client.Advanced.Graphics.Experimental.earthCurveRatio.get();
if (curveRatio < -1.0f || curveRatio > 1.0f)
{
curveRatio = /*6371KM*/ 6371000.0f / curveRatio;
}
else
{
// disable curvature if the config value is between -1 and 1
curveRatio = 0.0f;
}
this.setUniform(this.uEarthRadius, curveRatio);
// Noise Uniforms
this.setUniform(this.uNoiseEnabled, Config.Client.Advanced.Graphics.NoiseTexture.enableNoiseTexture.get());
this.setUniform(this.uNoiseSteps, Config.Client.Advanced.Graphics.NoiseTexture.noiseSteps.get());
this.setUniform(this.uNoiseIntensity, Config.Client.Advanced.Graphics.NoiseTexture.noiseIntensity.get());
this.setUniform(this.uNoiseDropoff, Config.Client.Advanced.Graphics.NoiseTexture.noiseDropoff.get());
// Debug
this.setUniform(this.uIsWhiteWorld, Config.Client.Advanced.Debugging.enableWhiteWorld.get());
// Clip Uniform
float dhNearClipDistance = RenderUtil.getNearClipPlaneInBlocks();
if (!Config.Client.Advanced.Debugging.lodOnlyMode.get())
{
// this added value prevents the near clip plane and discard circle from touching, which looks bad
dhNearClipDistance += 16f;
}
this.setUniform(this.uClipDistance, dhNearClipDistance);
}
@Override
public void setModelOffsetPos(DhApiVec3f modelOffsetPos) { this.setUniform(this.uModelOffset, new Vec3f(modelOffsetPos)); }
@Override
public int getId() { return this.id; }
/** The base DH render program should always render */
@Override
public boolean overrideThisFrame() { return true; }
//endregion
//===========//
// rendering //
//===========//
//region
public void render(RenderParams renderEventParam, boolean opaquePass, SortedArraySet<LodBufferContainer> bufferContainers, IProfilerWrapper profiler)
{
//=======================//
// debug wireframe setup //
//=======================//
boolean renderWireframe = Config.Client.Advanced.Debugging.renderWireframe.get();
if (renderWireframe)
{
GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, GL32.GL_LINE);
GLMC.disableFaceCulling();
}
else
{
GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, GL32.GL_FILL);
GLMC.enableFaceCulling();
}
if (!opaquePass)
{
GLMC.enableBlend();
GLMC.enableDepthTest();
GL32.glBlendEquation(GL32.GL_FUNC_ADD);
GLMC.glBlendFuncSeparate(GL32.GL_SRC_ALPHA, GL32.GL_ONE_MINUS_SRC_ALPHA, GL32.GL_ONE, GL32.GL_ONE_MINUS_SRC_ALPHA);
}
else
{
GLMC.disableBlend();
}
//===========//
// rendering //
//===========//
if (IRIS_ACCESSOR != null)
{
// done to fix a bug with Iris where face culling isn't properly set or reverted in the MC state manager
// which causes Sodium to render some water chunks with their normals inverted
// https://github.com/IrisShaders/Iris/issues/2582
// https://github.com/IrisShaders/Iris/blob/1.21.9/common/src/main/java/net/irisshaders/iris/compat/dh/LodRendererEvents.java#L346
GLMC.enableFaceCulling();
}
if (bufferContainers != null)
{
for (int lodIndex = 0; lodIndex < bufferContainers.size(); lodIndex++)
{
LodBufferContainer bufferContainer = bufferContainers.get(lodIndex);
// set uniforms and fire events
{
Vec3d camPos = renderEventParam.exactCameraPosition;
Vec3f modelPos = new Vec3f(
(float) (bufferContainer.minCornerBlockPos.getX() - camPos.x),
(float) (bufferContainer.minCornerBlockPos.getY() - camPos.y),
(float) (bufferContainer.minCornerBlockPos.getZ() - camPos.z));
GlDhMetaRenderer.INSTANCE.shaderProgramForThisFrame.bind();
GlDhMetaRenderer.INSTANCE.shaderProgramForThisFrame.setModelOffsetPos(modelPos);
ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeBufferRenderEvent.class, new DhApiBeforeBufferRenderEvent.EventParam(renderEventParam, modelPos));
}
IVertexBufferWrapper[] vertexBuffers = (opaquePass ? bufferContainer.vboOpaqueWrappers : bufferContainer.vboTransparentWrappers);
for (int vboIndex = 0; vboIndex < vertexBuffers.length; vboIndex++)
{
GLVertexBuffer vbo = (GLVertexBuffer) vertexBuffers[vboIndex];
if (vbo == null)
{
continue;
}
if (vbo.getVertexCount() == 0)
{
continue;
}
// 4 vertices per face, but 6 indices (IE 2 triangles) per face, aka need to multiply by 1.5
int indexCount = (int)(vbo.getVertexCount() * 1.5);
vbo.bind();
vbo.getQuadIBO().bind();
GlDhMetaRenderer.INSTANCE.shaderProgramForThisFrame.bindVertexBuffer(vbo.getId());
GL32.glDrawElements(
GL32.GL_TRIANGLES,
indexCount,
vbo.getQuadIBO().getGlType(), 0);
vbo.unbind();
vbo.getQuadIBO().unbind();
}
}
}
//=========================//
// debug wireframe cleanup //
//=========================//
if (renderWireframe)
{
// default back to GL_FILL since all other rendering uses it
GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, GL32.GL_FILL);
GLMC.enableFaceCulling();
}
}
//endregion
}
@@ -0,0 +1,146 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.render.openGl.test;
import com.seibel.distanthorizons.api.enums.config.EDhApiGpuUploadMethod;
import com.seibel.distanthorizons.common.render.openGl.postProcessing.apply.GlDhApplyShader;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.common.render.openGl.glObject.buffer.GLVertexBuffer;
import com.seibel.distanthorizons.common.render.openGl.glObject.shader.GlShaderProgram;
import com.seibel.distanthorizons.common.render.openGl.glObject.vertexAttribute.GlAbstractVertexAttribute;
import com.seibel.distanthorizons.common.render.openGl.glObject.vertexAttribute.GlVertexPointer;
import com.seibel.distanthorizons.core.render.RenderParams;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhTestTriangleRenderer;
import org.lwjgl.opengl.GL32;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
/**
* Renders a UV colored quad
* to the center of the screen to confirm DH's
* apply shader is running correctly
*/
public class GlTestTriangleRenderer implements IDhTestTriangleRenderer
{
public static final DhLogger LOGGER = new DhLoggerBuilder().build();
private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
private static final MinecraftGLWrapper GLMC = MinecraftGLWrapper.INSTANCE;
public static final GlTestTriangleRenderer INSTANCE = new GlTestTriangleRenderer();
// Render a square with uv color
private static final float[] VERTICES =
{
// PosX,Y, ColorR,G,B,A
-0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f,
0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f,
0.0f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f,
};
GlShaderProgram basicShader;
GLVertexBuffer vbo;
GlAbstractVertexAttribute va;
boolean init = false;
//=============//
// constructor //
//=============//
//region
private GlTestTriangleRenderer() { }
public void init()
{
if (this.init)
{
return;
}
LOGGER.info("init");
this.init = true;
this.va = GlAbstractVertexAttribute.create();
this.va.bind();
// Pos
this.va.setVertexAttribute(0, 0, GlVertexPointer.addVec2Pointer(false));
// Color
this.va.setVertexAttribute(0, 1, GlVertexPointer.addVec4Pointer(false));
this.va.completeAndCheck(Float.BYTES * 6);
this.basicShader = new GlShaderProgram(
"assets/distanthorizons/shaders/test/gl/vert.vert",
"assets/distanthorizons/shaders/test/gl/frag.frag",
new String[]{"vPosition", "color"});
this.createBuffer();
}
private void createBuffer()
{
ByteBuffer buffer = ByteBuffer.allocateDirect(VERTICES.length * Float.BYTES);
// Fill buffer with vertices.
buffer.order(ByteOrder.nativeOrder());
buffer.asFloatBuffer().put(VERTICES);
buffer.rewind();
this.vbo = new GLVertexBuffer(false);
this.vbo.bind();
this.vbo.uploadBuffer(buffer, 3, EDhApiGpuUploadMethod.DATA, VERTICES.length * Float.BYTES);
}
//endregion
//========//
// render //
//========//
//region
@Override
public void render(RenderParams renderParams)
{
this.init();
this.basicShader.bind();
this.va.bind();
this.vbo.bind();
this.va.bindBufferToAllBindingPoints(this.vbo.getId());
GL32.glDrawArrays(GL32.GL_TRIANGLES, 0, 3);
GlDhApplyShader.INSTANCE.render(renderParams);
}
//endregion
}
@@ -0,0 +1,112 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.render.openGl.util;
import com.seibel.distanthorizons.common.render.openGl.glObject.shader.GlShaderProgram;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.render.RenderParams;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import org.lwjgl.opengl.GL32;
public abstract class GlAbstractShaderRenderer
{
protected static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
protected GlShaderProgram shader;
protected boolean init = false;
//=======//
// setup //
//=======//
//region
protected GlAbstractShaderRenderer() {}
public void init()
{
if (this.init) return;
this.init = true;
this.onInit();
}
//endregion
//==================//
// abstract methods //
//==================//
//region
protected void onInit() {}
protected void onApplyUniforms(RenderParams renderParams) {}
protected void onRender() {}
//endregion
//===========//
// rendering //
//===========//
//region
public void render(RenderParams renderParams)
{
this.init();
this.shader.bind();
this.onApplyUniforms(renderParams);
int width = MC_RENDER.getTargetFramebufferViewportWidth();
int height = MC_RENDER.getTargetFramebufferViewportHeight();
GL32.glViewport(0, 0, width, height);
this.onRender();
this.shader.unbind();
}
//endregion
//================//
// base overrides //
//================//
//region
public void free()
{
if (this.shader != null)
{
this.shader.free();
}
}
// endregion
}
@@ -0,0 +1,110 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.render.openGl.util.vertexFormat;
import java.util.stream.Collectors;
import com.google.common.collect.ImmutableList;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
/**
* This is used to represent a single vertex
* stored in GPU memory,
* <p>
* A (almost) exact copy of Minecraft's
* VertexFormat class, several methods
* were commented out since we didn't need them.
*
* @author James Seibel
* @version 12-9-2021
*/
public class GlLodVertexFormat
{
/** the format of data stored in the GPU buffers */
public static final GlLodVertexFormat DH_VERTEX_FORMAT = GlVertexFormats.POSITION_COLOR_BLOCK_LIGHT_SKY_LIGHT_MATERIAL_ID_NORMAL_INDEX;
private final ImmutableList<GlLodVertexFormatElement> elements;
private final IntList offsets = new IntArrayList();
private final int byteSize;
public GlLodVertexFormat(ImmutableList<GlLodVertexFormatElement> elementList)
{
this.elements = elementList;
int i = 0;
for (GlLodVertexFormatElement LodVertexFormatElement : elementList)
{
this.offsets.add(i);
i += LodVertexFormatElement.getByteSize();
}
this.byteSize = i;
}
public int getByteSize()
{
return this.byteSize;
}
public ImmutableList<GlLodVertexFormatElement> getElements()
{
return this.elements;
}
// Forge added method
public int getOffset(int index)
{
return offsets.getInt(index);
}
@Override
public String toString() { return "format: " + this.elements.size() + " elements: " + this.elements.stream().map(Object::toString).collect(Collectors.joining(" ")); }
@Override
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
else if (obj != null && this.getClass() == obj.getClass())
{
GlLodVertexFormat vertexFormat = (GlLodVertexFormat) obj;
return this.byteSize == vertexFormat.byteSize && this.elements.equals(vertexFormat.elements);
}
else
{
return false;
}
}
@Override
public int hashCode() { return this.elements.hashCode(); }
}
@@ -0,0 +1,168 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.render.openGl.util.vertexFormat;
import org.lwjgl.opengl.GL32;
/**
* This object is used to build LodVertexFormats.
* <p>
* A (almost) exact copy of Minecraft's
* VertexFormatElement class. <br>
* A number of things were removed from the original
* object since we didn't need them, specifically "usage".
*
* @author James Seibel
* @version 11-13-2021
*/
public class GlLodVertexFormatElement
{
private final GlLodVertexFormatElement.DataType dataType;
/** James isn't sure what index is for */
private final int index;
private final int count;
private final int byteSize;
private final boolean isPadding;
public GlLodVertexFormatElement(int newIndex, GlLodVertexFormatElement.DataType newType, int newCount, boolean isPadding)
{
this.dataType = newType;
this.index = newIndex;
this.count = newCount;
this.byteSize = newType.getSize() * this.count;
this.isPadding = isPadding;
}
public final boolean getIsPadding()
{
return isPadding;
}
public final GlLodVertexFormatElement.DataType getType()
{
return this.dataType;
}
public final int getIndex()
{
return this.index;
}
public final int getByteSize()
{
return this.byteSize;
}
// added by Forge
public int getElementCount()
{
return count;
}
public enum DataType
{
FLOAT(4, "Float", GL32.GL_FLOAT),
UBYTE(1, "Unsigned Byte", GL32.GL_UNSIGNED_BYTE),
BYTE(1, "Byte", GL32.GL_BYTE),
USHORT(2, "Unsigned Short", GL32.GL_UNSIGNED_SHORT),
SHORT(2, "Short", GL32.GL_SHORT),
UINT(4, "Unsigned Int", GL32.GL_UNSIGNED_INT),
INT(4, "Int", GL32.GL_INT);
private final int size;
private final String name;
private final int glType;
DataType(int sizeInBytes, String newName, int openGlDataType)
{
this.size = sizeInBytes;
this.name = newName;
this.glType = openGlDataType;
}
public int getSize()
{
return this.size;
}
public String getName()
{
return this.name;
}
public int getGlType()
{
return this.glType;
}
}
@Override
public int hashCode()
{
int i = this.dataType.hashCode();
i = 31 * i + this.index;
return 31 * i + this.count;
}
@Override
public String toString()
{
return this.count + "," + this.dataType.getName();
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
else if (obj != null && this.getClass() == obj.getClass())
{
GlLodVertexFormatElement LodVertexFormatElement = (GlLodVertexFormatElement) obj;
if (this.count != LodVertexFormatElement.count)
{
return false;
}
else if (this.index != LodVertexFormatElement.index)
{
return false;
}
else if (this.dataType != LodVertexFormatElement.dataType)
{
return false;
}
else
{
return false;
}
}
else
{
return false;
}
}
}
@@ -0,0 +1,50 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.render.openGl.util.vertexFormat;
import com.google.common.collect.ImmutableList;
/**
* A (almost) exact copy of MC's
* DefaultVertexFormats class.
*/
public class GlVertexFormats
{
public static final GlLodVertexFormatElement ELEMENT_POSITION = new GlLodVertexFormatElement(3, GlLodVertexFormatElement.DataType.USHORT, 3, false);
public static final GlLodVertexFormatElement ELEMENT_COLOR = new GlLodVertexFormatElement(0, GlLodVertexFormatElement.DataType.UBYTE, 4, false);
public static final GlLodVertexFormatElement ELEMENT_BYTE_PADDING = new GlLodVertexFormatElement(0, GlLodVertexFormatElement.DataType.BYTE, 1, true);
public static final GlLodVertexFormatElement ELEMENT_LIGHT = new GlLodVertexFormatElement(0, GlLodVertexFormatElement.DataType.UBYTE, 1, false);
public static final GlLodVertexFormatElement ELEMENT_IRIS_MATERIAL_INDEX = new GlLodVertexFormatElement(0, GlLodVertexFormatElement.DataType.BYTE, 1, false);
public static final GlLodVertexFormatElement ELEMENT_IRIS_NORMAL_INDEX = new GlLodVertexFormatElement(0, GlLodVertexFormatElement.DataType.BYTE, 1, false);
public static final GlLodVertexFormat POSITION_COLOR_BLOCK_LIGHT_SKY_LIGHT_MATERIAL_ID_NORMAL_INDEX = new GlLodVertexFormat(ImmutableList.<GlLodVertexFormatElement>builder()
.add(ELEMENT_POSITION)
.add(ELEMENT_BYTE_PADDING)
.add(ELEMENT_LIGHT)
.add(ELEMENT_COLOR)
.add(ELEMENT_IRIS_MATERIAL_INDEX)
.add(ELEMENT_IRIS_NORMAL_INDEX)
.add(ELEMENT_BYTE_PADDING)
.add(ELEMENT_BYTE_PADDING) // padding is to make sure the format is a multiple of 4
.build());
}
@@ -19,12 +19,19 @@
package com.seibel.distanthorizons.common.wrappers;
import com.seibel.distanthorizons.common.wrappers.gui.ClassicConfigGUI;
import com.seibel.distanthorizons.api.enums.config.EDhApiRenderApi;
import com.seibel.distanthorizons.api.interfaces.render.IDhApiCustomRenderObjectFactory;
import com.seibel.distanthorizons.common.render.blaze.BlazeDhRenderApiDefinition;
import com.seibel.distanthorizons.common.render.openGl.GlDhRenderApiDefinition;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.render.renderer.GenericRenderObjectFactory;
import com.seibel.distanthorizons.common.wrappers.gui.classicConfig.ClassicConfigGUI;
import com.seibel.distanthorizons.common.wrappers.gui.LangWrapper;
import com.seibel.distanthorizons.common.wrappers.level.KeyedClientLevelManager;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftServerWrapper;
import com.seibel.distanthorizons.core.level.IKeyedClientLevelManager;
import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.wrapperInterfaces.config.IConfigGui;
import com.seibel.distanthorizons.core.wrapperInterfaces.config.ILangWrapper;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftClientWrapper;
@@ -33,9 +40,9 @@ import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.wrapperInterfaces.IVersionConstants;
import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftGLWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftSharedWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.render.AbstractDhRenderApiDefinition;
/**
* Binds all necessary dependencies, so we
@@ -49,6 +56,9 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftSha
*/
public class DependencySetup
{
protected static final DhLogger LOGGER = new DhLoggerBuilder().build();
public static void createSharedBindings()
{
@@ -56,6 +66,7 @@ public class DependencySetup
SingletonInjector.INSTANCE.bind(IVersionConstants.class, VersionConstants.INSTANCE);
SingletonInjector.INSTANCE.bind(IWrapperFactory.class, WrapperFactory.INSTANCE);
SingletonInjector.INSTANCE.bind(IKeyedClientLevelManager.class, KeyedClientLevelManager.INSTANCE);
SingletonInjector.INSTANCE.bind(IDhApiCustomRenderObjectFactory.class, GenericRenderObjectFactory.INSTANCE);
}
public static void createServerBindings()
@@ -66,8 +77,61 @@ public class DependencySetup
SingletonInjector.INSTANCE.bind(IMinecraftClientWrapper.class, MinecraftClientWrapper.INSTANCE);
SingletonInjector.INSTANCE.bind(IMinecraftSharedWrapper.class, MinecraftClientWrapper.INSTANCE);
SingletonInjector.INSTANCE.bind(IMinecraftRenderWrapper.class, MinecraftRenderWrapper.INSTANCE);
SingletonInjector.INSTANCE.bind(IMinecraftGLWrapper.class, MinecraftGLWrapper.INSTANCE);
SingletonInjector.INSTANCE.bind(IConfigGui.class, ClassicConfigGUI.CONFIG_CORE_INTERFACE);
}
/** will be called from a DH thread, not the render thread */
public static void setRenderingApiBindings()
{
EDhApiRenderApi renderingApiEnum = Config.Client.Advanced.Graphics.Experimental.renderingApi.get();
if (renderingApiEnum == EDhApiRenderApi.AUTO)
{
IVersionConstants versionConstants = SingletonInjector.INSTANCE.get(IVersionConstants.class);
renderingApiEnum = versionConstants.getDefaultRenderingApi();
}
LOGGER.info("Setting DH Rendering API to: ["+renderingApiEnum+"].");
boolean validApi;
AbstractDhRenderApiDefinition renderDefinition;
if (renderingApiEnum == EDhApiRenderApi.OPEN_GL)
{
validApi = true;
renderDefinition = new GlDhRenderApiDefinition();
}
else if (renderingApiEnum == EDhApiRenderApi.BLAZE_3D)
{
#if MC_VER <= MC_1_21_10
validApi = false;
renderDefinition = null;
#else
validApi = true;
renderDefinition = new BlazeDhRenderApiDefinition();
#endif
}
else
{
String message = "No ["+ AbstractDhRenderApiDefinition.class.getSimpleName()+"] concrete implementation found for the value: ["+renderingApiEnum+"].";
LOGGER.fatal(message);
throw new IllegalStateException(message);
}
// crash if an invalid API is set
if (!validApi)
{
String message = "["+renderingApiEnum+"] is not supported on this version of Minecraft, reverting to ["+EDhApiRenderApi.AUTO+"].";
LOGGER.fatal(message);
Config.Client.Advanced.Graphics.Experimental.renderingApi.set(EDhApiRenderApi.AUTO);
throw new IllegalStateException(message);
}
renderDefinition.bindRenderers();
}
}
@@ -46,7 +46,6 @@ public class McObjectConverter
/** 4x4 float matrix converter */
@Deprecated
public static Mat4f Convert(
#if MC_VER < MC_1_19_4 com.mojang.math.Matrix4f
#elif MC_VER < MC_1_21_6 org.joml.Matrix4f
@@ -140,7 +139,16 @@ public class McObjectConverter
}
public static BlockPos Convert(DhBlockPos wrappedPos) { return new BlockPos(wrappedPos.getX(), wrappedPos.getY(), wrappedPos.getZ()); }
public static ChunkPos Convert(DhChunkPos wrappedPos) { return new ChunkPos(wrappedPos.getX(), wrappedPos.getZ()); }
public static DhChunkPos Convert(ChunkPos mcPos)
{
#if MC_VER <= MC_1_21_11
return new DhChunkPos(mcPos.x, mcPos.z);
#else
return new DhChunkPos(mcPos.x(), mcPos.z());
#endif
}
public static Direction Convert(EDhDirection lodDirection) { return directions[lodDirection.ordinal()]; }
public static EDhDirection Convert(Direction direction) { return lodDirections[direction.ordinal()]; }
@@ -19,22 +19,30 @@
package com.seibel.distanthorizons.common.wrappers;
import com.seibel.distanthorizons.api.enums.config.EDhApiRenderApi;
import com.seibel.distanthorizons.core.wrapperInterfaces.IVersionConstants;
/**
* @author James Seibel
* @version 12-11-2021
*/
public class VersionConstants implements IVersionConstants
{
public static final VersionConstants INSTANCE = new VersionConstants();
private VersionConstants()
{
}
//=============//
// constructor //
//=============//
//region
private VersionConstants() { }
//endregion
//=========//
// methods //
//=========//
//region
@Override
public String getMinecraftVersion()
@@ -85,10 +93,26 @@ public class VersionConstants implements IVersionConstants
return "1.21.10";
#elif MC_VER == MC_1_21_11
return "1.21.11";
#elif MC_VER == MC_26_1_2
return "26.1.2";
#else
ERROR MC version constant missing
#endif
}
@Override
public EDhApiRenderApi getDefaultRenderingApi()
{
#if MC_VER <= MC_1_21_11
return EDhApiRenderApi.OPEN_GL;
#else
return EDhApiRenderApi.BLAZE_3D;
#endif
}
//endregion
}
@@ -30,28 +30,33 @@ import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.level.IDhLevel;
import com.seibel.distanthorizons.core.level.IDhServerLevel;
import com.seibel.distanthorizons.core.wrapperInterfaces.render.AbstractDhRenderApiDefinition;
import com.seibel.distanthorizons.core.wrapperInterfaces.render.objects.IDhGenericObjectVertexBufferContainer;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory;
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.render.objects.ILodContainerUniformBufferWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhGenericRenderer;
import com.seibel.distanthorizons.core.wrapperInterfaces.render.objects.IVertexBufferWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.worldGeneration.IBatchGeneratorEnvironmentWrapper;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import net.minecraft.client.multiplayer.ClientLevel;
#if MC_VER > MC_1_17_1
import net.minecraft.core.Holder;
#endif
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.ChunkAccess;
import java.io.IOException;
import java.util.HashSet;
/**
* This handles creating abstract wrapper objects.
@@ -62,9 +67,32 @@ public class WrapperFactory implements IWrapperFactory
//=====================//
// internal properties //
//=====================//
//region
private AbstractDhRenderApiDefinition renderDefinition;
private AbstractDhRenderApiDefinition getRenderDefinition()
{
// delayed get to make sure we don't accidentally set the variable before it's bound
if (this.renderDefinition != null)
{
return this.renderDefinition;
}
this.renderDefinition = SingletonInjector.INSTANCE.get(AbstractDhRenderApiDefinition.class);
return this.renderDefinition;
}
//endregion
//==============//
// core methods //
//==============//
//region
@Override
public IBatchGeneratorEnvironmentWrapper createBatchGenerator(IDhLevel targetLevel)
@@ -103,7 +131,7 @@ public class WrapperFactory implements IWrapperFactory
@Override
public IBiomeWrapper deserializeBiomeWrapper(String str, ILevelWrapper levelWrapper) throws IOException { return BiomeWrapper.deserialize(str, levelWrapper); }
@Override
public IBiomeWrapper getPlainsBiomeWrapper(ILevelWrapper levelWrapper) // TODO is there a way we could get this without the levelWrapper? it isn't necessary but would clean up the code a bit
public IBiomeWrapper getPlainsBiomeWrapper(ILevelWrapper levelWrapper)
{
try
{
@@ -119,16 +147,19 @@ public class WrapperFactory implements IWrapperFactory
public IBlockStateWrapper deserializeBlockStateWrapper(String str, ILevelWrapper levelWrapper) throws IOException { return BlockStateWrapper.deserialize(str, levelWrapper); }
@Override
public IBlockStateWrapper getAirBlockStateWrapper() { return BlockStateWrapper.AIR; }
@Override
public IBlockStateWrapper getWaterBlockStateWrapper(ILevelWrapper levelWrapper) { return BlockStateWrapper.getWaterBlockStateWrapper(levelWrapper); }
@Override
public ObjectOpenHashSet<IBlockStateWrapper> getRendererIgnoredBlocks(ILevelWrapper levelWrapper) { return BlockStateWrapper.getRendererIgnoredBlocks(levelWrapper); }
@Override
public ObjectOpenHashSet<IBlockStateWrapper> getRendererIgnoredCaveBlocks(ILevelWrapper levelWrapper) { return BlockStateWrapper.getRendererIgnoredCaveBlocks(levelWrapper); }
@Override
public ObjectOpenHashSet<IBlockStateWrapper> getWaterSubsurfaceReplacementBlocks(ILevelWrapper levelWrapper) { return BlockStateWrapper.getWaterSubsurfaceReplacementBlocks(levelWrapper); }
@Override
public ObjectOpenHashSet<IBlockStateWrapper> getWaterSurfaceReplacementBlocks(ILevelWrapper levelWrapper) { return BlockStateWrapper.getWaterSurfaceReplacementBlocks(levelWrapper); }
@Override
public HashSet<IBlockStateWrapper> getRendererIgnoredBlocks(ILevelWrapper levelWrapper) { return BlockStateWrapper.getRendererIgnoredBlocks(levelWrapper); }
@Override
public HashSet<IBlockStateWrapper> getRendererIgnoredCaveBlocks(ILevelWrapper levelWrapper) { return BlockStateWrapper.getRendererIgnoredCaveBlocks(levelWrapper); }
@Override
public void resetRendererIgnoredCaveBlocks() { BlockStateWrapper.clearRendererIgnoredCaveBlocks(); }
@Override
public void resetRendererIgnoredBlocksSet() { BlockStateWrapper.clearRendererIgnoredBlocks(); }
public void resetCachedIgnoredBlocksSets() { BlockStateWrapper.clearCachedIgnoreBlocks(); }
/**
@@ -211,10 +242,19 @@ public class WrapperFactory implements IWrapperFactory
}
@Override public IVertexBufferWrapper createVboWrapper(String name) { return this.getRenderDefinition().createVboWrapper(name); }
@Override public ILodContainerUniformBufferWrapper createLodContainerUniformWrapper() { return this.getRenderDefinition().createLodContainerUniformWrapper(); }
@Override public IDhGenericObjectVertexBufferContainer createGenericObjectVboContainer() { return this.getRenderDefinition().createGenericVboContainer(); }
@Override public IDhGenericRenderer createGenericRenderer() { return this.getRenderDefinition().createGenericRenderer(); }
//endregion
//=============//
// api methods //
//=============//
//region
// documentation should be in the API interface
@@ -313,12 +353,15 @@ public class WrapperFactory implements IWrapperFactory
return createWrapperErrorMessage("BlockState wrapper", expectedClassNames, objectArray);
}
//endregion
//================//
// helper methods //
//================//
//region
private static String createWrapperErrorMessage(String wrapperName, String[] expectedClassNames, Object[] objectArray)
{
@@ -354,4 +397,8 @@ public class WrapperFactory implements IWrapperFactory
return message.toString();
}
//endregion
}
@@ -6,15 +6,13 @@ import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSour
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pos.DhSectionPos;
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPosMutable;
import com.seibel.distanthorizons.core.util.ColorUtil;
import com.seibel.distanthorizons.coreapi.util.ColorUtil;
import com.seibel.distanthorizons.core.util.FullDataPointUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import it.unimi.dsi.fastutil.longs.LongArrayList;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.BlockAndTintGetter;
import net.minecraft.world.level.ColorResolver;
import net.minecraft.world.level.biome.Biome;
@@ -30,6 +28,11 @@ import org.jetbrains.annotations.Nullable;
import net.minecraft.core.Holder;
#endif
#if MC_VER <= MC_1_21_11
import net.minecraft.world.level.BlockAndTintGetter;
#else
import net.minecraft.client.renderer.block.BlockAndTintGetter;
#endif
public abstract class AbstractDhTintGetter implements BlockAndTintGetter
{
@@ -43,7 +46,7 @@ public abstract class AbstractDhTintGetter implements BlockAndTintGetter
private static final ConcurrentHashMap<BlockBiomeWrapperPair, Integer> COLOR_BY_BLOCK_BIOME_PAIR = new ConcurrentHashMap<>();
/** returned if the color cache is incomplete */
public static final int INVALID_COLOR = Integer.MIN_VALUE;
public static final int INVALID_COLOR = -1;
protected BiomeWrapper biomeWrapper;
@@ -57,6 +60,7 @@ public abstract class AbstractDhTintGetter implements BlockAndTintGetter
//=============//
// constructor //
//=============//
//region
public AbstractDhTintGetter() { }
@@ -73,11 +77,14 @@ public abstract class AbstractDhTintGetter implements BlockAndTintGetter
this.smoothingRadiusInBlocks = Config.Client.Advanced.Graphics.Quality.lodBiomeBlending.get();
}
//endregion
//================//
// shared methods //
//================//
//===============//
// color getters //
//===============//
//region
/** Called by MC's tint getter */
@Override
@@ -192,7 +199,7 @@ public abstract class AbstractDhTintGetter implements BlockAndTintGetter
BlockBiomeWrapperPair pair = BlockBiomeWrapperPair.get(this.blockStateWrapper, biomeWrapper);
// use the cached color if possible
Integer cachedColor = COLOR_BY_BLOCK_BIOME_PAIR.get(pair); // explicit Integer return here reduces unnecessary allocations
Integer cachedColor = COLOR_BY_BLOCK_BIOME_PAIR.get(pair);
if (cachedColor != null)
{
return cachedColor;
@@ -332,6 +339,27 @@ public abstract class AbstractDhTintGetter implements BlockAndTintGetter
});
}
//endregion
//===========//
// set color //
//===========//
//region
/**
* can be used in newer MC versions
* where the color getting logic is a bit more manual
*/
public static void setStaticColor(BlockStateWrapper blockStateWrapper, BiomeWrapper biomeWrapper, Integer colorInt)
{
BlockBiomeWrapperPair pair = BlockBiomeWrapperPair.get(blockStateWrapper, biomeWrapper);
COLOR_BY_BLOCK_BIOME_PAIR.put(pair, colorInt);
}
//endregion
}
@@ -49,6 +49,7 @@ import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceLocation;
#else
import net.minecraft.resources.Identifier;
import net.minecraft.core.component.DataComponentMap;
#endif
import net.minecraft.world.level.biome.Biome;
@@ -221,26 +222,27 @@ public class BiomeWrapper implements IBiomeWrapper
Level level = (Level)levelWrapper.getWrappedMcObject();
net.minecraft.core.RegistryAccess registryAccess = level.registryAccess();
#if MC_VER < MC_1_21_11
#if MC_VER <= MC_1_21_10
ResourceLocation resourceLocation;
#else
Identifier resourceLocation;
#endif
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
#if MC_VER <= MC_1_17_1
resourceLocation = registryAccess.registryOrThrow(Registry.BIOME_REGISTRY).getKey(this.biome);
#elif MC_VER == MC_1_18_2 || MC_VER == MC_1_19_2
#elif MC_VER <= MC_1_19_2
resourceLocation = registryAccess.registryOrThrow(Registry.BIOME_REGISTRY).getKey(this.biome.value());
#elif MC_VER < MC_1_21_3
#elif MC_VER <= MC_1_21_1
resourceLocation = registryAccess.registryOrThrow(Registries.BIOME).getKey(this.biome.value());
#else
resourceLocation = registryAccess.lookupOrThrow(Registries.BIOME).getKey(this.biome.value());
#endif
if (resourceLocation == null)
{
String biomeName;
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
#if MC_VER <= MC_1_17_1
biomeName = this.biome.toString();
#else
biomeName = this.biome.value().toString();
@@ -258,7 +260,6 @@ public class BiomeWrapper implements IBiomeWrapper
return this.serialString;
}
// TODO would it be worth while to cache these objects in a ConcurrentHashMap<string, IBiomeWrapper>?
public static IBiomeWrapper deserialize(String resourceLocationString, ILevelWrapper levelWrapper) throws IOException
{
// we need the final string for the concurrent hash map later
@@ -355,18 +356,18 @@ public class BiomeWrapper implements IBiomeWrapper
boolean success;
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
#if MC_VER <= MC_1_17_1
Biome biome = registryAccess.registryOrThrow(Registry.BIOME_REGISTRY).get(resourceLocation);
success = (biome != null);
#elif MC_VER == MC_1_18_2 || MC_VER == MC_1_19_2
#elif MC_VER <= MC_1_19_2
Biome unwrappedBiome = registryAccess.registryOrThrow(Registry.BIOME_REGISTRY).get(resourceLocation);
success = (unwrappedBiome != null);
Holder<Biome> biome = new Holder.Direct<>(unwrappedBiome);
#elif MC_VER < MC_1_21_3
#elif MC_VER <= MC_1_21_1
Biome unwrappedBiome = registryAccess.registryOrThrow(Registries.BIOME).get(resourceLocation);
success = (unwrappedBiome != null);
Holder<Biome> biome = new Holder.Direct<>(unwrappedBiome);
#else
#elif MC_VER <= MC_1_21_11
Holder<Biome> biome;
Optional<Holder.Reference<Biome>> optionalBiomeHolder = registryAccess.lookupOrThrow(Registries.BIOME).get(resourceLocation);
if (optionalBiomeHolder.isPresent())
@@ -380,6 +381,20 @@ public class BiomeWrapper implements IBiomeWrapper
success = false;
biome = null;
}
#else
Holder<Biome> biome;
Optional<Holder.Reference<Biome>> optionalBiomeHolder = registryAccess.lookupOrThrow(Registries.BIOME).get(resourceLocation);
if (optionalBiomeHolder.isPresent())
{
Biome unwrappedBiome = optionalBiomeHolder.get().value();
success = (unwrappedBiome != null);
biome = new Holder.Direct<>(unwrappedBiome, DataComponentMap.EMPTY);
}
else
{
success = false;
biome = null;
}
#endif
return new BiomeDeserializeResult(success, biome);
@@ -20,14 +20,18 @@
package com.seibel.distanthorizons.common.wrappers.block;
import com.seibel.distanthorizons.api.enums.rendering.EDhApiBlockMaterial;
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiBlockStateWrapperCreatedEvent;
import com.seibel.distanthorizons.common.wrappers.WrapperFactory;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.config.types.ConfigEntry;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.util.ColorUtil;
import com.seibel.distanthorizons.coreapi.util.ColorUtil;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import net.minecraft.tags.BlockTags;
import net.minecraft.world.level.block.BeaconBeamBlock;
import net.minecraft.world.level.block.Block;
@@ -41,6 +45,7 @@ import java.io.IOException;
import java.util.*;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Stream;
import org.jetbrains.annotations.Nullable;
@@ -48,13 +53,16 @@ import org.jetbrains.annotations.Nullable;
import net.minecraft.core.Registry;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.EmptyBlockGetter;
import net.minecraft.world.level.Level;
#elif MC_VER == MC_1_18_2 || MC_VER == MC_1_19_2
import net.minecraft.tags.TagKey;
import net.minecraft.client.Minecraft;
import net.minecraft.world.level.Level;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Registry;
import net.minecraft.world.level.EmptyBlockGetter;
#else
import net.minecraft.tags.TagKey;
import net.minecraft.world.level.Level;
import net.minecraft.core.BlockPos;
import net.minecraft.core.registries.Registries;
@@ -83,13 +91,16 @@ public class BlockStateWrapper implements IBlockStateWrapper
public static final ConcurrentHashMap<String, BlockStateWrapper> WRAPPER_BY_RESOURCE_LOCATION = new ConcurrentHashMap<>();
public static final String AIR_STRING = "AIR";
public static final BlockStateWrapper AIR = new BlockStateWrapper(null, null);
public static final BlockStateWrapper AIR = new BlockStateWrapper(null, null, null);
public static final String DIRT_RESOURCE_LOCATION_STRING = "minecraft:dirt";
public static final String WATER_RESOURCE_LOCATION_STRING = "minecraft:water";
public static HashSet<IBlockStateWrapper> rendererIgnoredBlocks = null;
public static HashSet<IBlockStateWrapper> rendererIgnoredCaveBlocks = null;
public static ObjectOpenHashSet<IBlockStateWrapper> rendererIgnoredBlocks = null;
public static ObjectOpenHashSet<IBlockStateWrapper> rendererIgnoredCaveBlocks = null;
public static ObjectOpenHashSet<IBlockStateWrapper> waterSubsurfaceReplacementBlocks = null;
public static ObjectOpenHashSet<IBlockStateWrapper> waterSurfaceReplacementBlocks = null;
public static IBlockStateWrapper waterBlock = null;
/** keep track of broken blocks so we don't log every time */
#if MC_VER <= MC_1_21_10
@@ -115,6 +126,9 @@ public class BlockStateWrapper implements IBlockStateWrapper
private final boolean isBeaconBlock;
private final boolean isBeaconBaseBlock;
private final boolean allowsBeaconBeamPassage;
private final boolean isSolid;
private final boolean isLiquid;
private final boolean allowApiColorOverride;
/** null if this block can't tint beacons */
private final Color beaconTintColor;
private final Color mapColor;
@@ -124,6 +138,7 @@ public class BlockStateWrapper implements IBlockStateWrapper
//==============//
// constructors //
//==============//
//region
public static BlockStateWrapper fromBlockState(BlockState blockState, ILevelWrapper levelWrapper)
{
@@ -139,7 +154,7 @@ public class BlockStateWrapper implements IBlockStateWrapper
}
else
{
BlockStateWrapper newWrapper = new BlockStateWrapper(blockState, levelWrapper);
BlockStateWrapper newWrapper = createNewWrapper(blockState, levelWrapper);
WRAPPER_BY_BLOCK_STATE.put(blockState, newWrapper);
return newWrapper;
}
@@ -165,30 +180,114 @@ public class BlockStateWrapper implements IBlockStateWrapper
}
}
private BlockStateWrapper(BlockState blockState, ILevelWrapper levelWrapper)
private static BlockStateWrapper createNewWrapper(@Nullable BlockState blockState, ILevelWrapper levelWrapper)
{
// create a wrapper specifically for the API event to use
BlockStateWrapper apiWrapper = new BlockStateWrapper(blockState, levelWrapper, null);
DhApiBlockStateWrapperCreatedEvent.EventParam eventParam = new DhApiBlockStateWrapperCreatedEvent.EventParam(apiWrapper);
ApiEventInjector.INSTANCE.fireAllEvents(DhApiBlockStateWrapperCreatedEvent.class, eventParam);
if (!eventParam.getOverridesSet())
{
// no changes needed, use the existing object
return apiWrapper;
}
// create a new wrapper using whatever overrides the API user set
BlockStateWrapper returnWrapper = new BlockStateWrapper(blockState, levelWrapper, eventParam);
return returnWrapper;
}
private BlockStateWrapper(@Nullable BlockState blockState, ILevelWrapper levelWrapper, @Nullable DhApiBlockStateWrapperCreatedEvent.EventParam overrideEventParam)
{
this.blockState = blockState;
this.serialString = this.serialize(levelWrapper);
this.hashCode = Objects.hash(this.serialString);
this.blockMaterialId = this.calculateEDhApiBlockMaterialId().index;
this.opacity = this.calculateOpacity();
String lowercaseSerial = this.serialString.toLowerCase();
// beacon blocks
boolean isBeaconBaseBlock = false;
for (int i = 0; i < LodUtil.BEACON_BASE_BLOCK_NAME_LIST.size(); i++)
// allow overriding if present
if (overrideEventParam != null
&& overrideEventParam.getBlockMaterial() != null)
{
String baseBlockName = LodUtil.BEACON_BASE_BLOCK_NAME_LIST.get(i);
if (lowercaseSerial.contains(baseBlockName))
this.blockMaterialId = overrideEventParam.getBlockMaterial().index;
}
else
{
// no API override, use the base logic
this.blockMaterialId = this.calculateEDhApiBlockMaterialId().index;
}
// allow overriding if present
if (overrideEventParam != null
&& overrideEventParam.getOpacity() != null)
{
this.opacity = overrideEventParam.getOpacity();
}
else
{
this.opacity = this.calculateOpacity();
}
// allow overriding if present
if (overrideEventParam != null
&& overrideEventParam.getAllowApiColorOverride() != null)
{
this.allowApiColorOverride = overrideEventParam.getAllowApiColorOverride();
}
else
{
this.allowApiColorOverride = false;
}
String lowerCaseSerial = this.serialString.toLowerCase();
// beacon base blocks
#if MC_VER <= MC_1_18_2
// Used to handle older MC versions that don't have an simple way of getting the block's tags
List<String> oldBeaconBaseBlockNameList = Arrays.asList(
"iron_block",
"gold_block",
"diamond_block",
"emerald_block",
"netherite_block"
);
// Older MC versions are harder to get block tags, so just use a static list to determine beacon blocks
boolean isBeaconBaseBlock = false;
for (int i = 0; i < oldBeaconBaseBlockNameList.size(); i++)
{
String baseBlockName = oldBeaconBaseBlockNameList.get(i);
if (lowerCaseSerial.contains(baseBlockName))
{
isBeaconBaseBlock = true;
break;
}
}
this.isBeaconBaseBlock = isBeaconBaseBlock;
this.isBeaconBlock = lowercaseSerial.contains("minecraft:beacon");
#else
if (blockState != null)
{
// check if this block has any tags
Stream<TagKey<Block>> tags;
#if MC_VER <= MC_1_21_11
tags = blockState.getTags();
#else
tags = blockState.tags();
#endif
this.isBeaconBaseBlock = tags.anyMatch((TagKey<Block> tag) -> tag.location().getPath().toLowerCase().contains("beacon_base_blocks"));
}
else
{
this.isBeaconBaseBlock = false;
}
#endif
// beacon block
this.isBeaconBlock = lowerCaseSerial.contains("minecraft:beacon");
// beacon tint color
Color beaconTintColor = null;
@@ -220,11 +319,17 @@ public class BlockStateWrapper implements IBlockStateWrapper
boolean canOcclude = this.getCanOcclude();
boolean propagatesSkyLightDown = this.getPropagatesSkyLightDown();
if (lowercaseSerial.contains("minecraft:bedrock"))
if (lowerCaseSerial.contains("minecraft:bedrock"))
{
// bedrock is a special case fully opaque block that does allow beacons through
allowsBeaconBeamPassage = true;
}
else if (lowerCaseSerial.contains("minecraft:tinted_glass"))
{
// tinted glass is a special case where it isn't fully opaque,
// but should block beacons
allowsBeaconBeamPassage = false;
}
else if (propagatesSkyLightDown || !canOcclude)
{
// stairs, cake, fences, etc.
@@ -245,14 +350,17 @@ public class BlockStateWrapper implements IBlockStateWrapper
this.allowsBeaconBeamPassage = allowsBeaconBeamPassage;
int mcColor = 0;
// map color
if (this.blockState != null)
{
int mcColor = 0;
#if MC_VER < MC_1_20_1
mcColor = this.blockState.getMaterial().getColor().col;
#else
mcColor = this.blockState.getMapColor(EmptyBlockGetter.INSTANCE, BlockPos.ZERO).col;
#endif
this.mapColor = ColorUtil.toColorObjRGB(mcColor);
}
else
@@ -260,20 +368,57 @@ public class BlockStateWrapper implements IBlockStateWrapper
this.mapColor = new Color(0,0,0,0);
}
//LOGGER.trace("Created BlockStateWrapper ["+this.serialString+"] for ["+blockState+"] with material ID ["+this.EDhApiBlockMaterialId+"]");
// is solid
if (this.isAir()
|| this.blockState == null) // "== null" isn't necessary since its handled in isAir() but is here to prevent intellij from complaining
{
this.isSolid = false;
}
else
{
#if MC_VER < MC_1_20_1
this.isSolid = this.blockState.getMaterial().isSolid();
#else
this.isSolid = !this.blockState.getCollisionShape(EmptyBlockGetter.INSTANCE, BlockPos.ZERO).isEmpty();
#endif
}
// is liquid
if (this.isAir()
|| this.blockState == null) // == null isn't necessary since its handled in isAir() but is here to prevent intellij from complaining
{
this.isLiquid = false;
}
else
{
#if MC_VER < MC_1_20_1
this.isLiquid = this.blockState.getMaterial().isLiquid() || !this.blockState.getFluidState().isEmpty();
#else
this.isLiquid = !this.blockState.getFluidState().isEmpty();
#endif
}
}
//endregion
//====================//
// LodBuilder methods //
//====================//
//region
/**
* Requires a {@link ILevelWrapper} since {@link BlockStateWrapper#deserialize(String,ILevelWrapper)} also requires one.
/**
* Each of the following methods require
* a {@link ILevelWrapper} since {@link BlockStateWrapper#deserialize(String,ILevelWrapper)} also requires one.
* This way the method won't accidentally be called before the deserialization can be completed.
*/
public static HashSet<IBlockStateWrapper> getRendererIgnoredBlocks(ILevelWrapper levelWrapper)
public static ObjectOpenHashSet<IBlockStateWrapper> getRendererIgnoredBlocks(ILevelWrapper levelWrapper)
{
// use the cached version if possible
if (rendererIgnoredBlocks != null)
@@ -281,16 +426,12 @@ public class BlockStateWrapper implements IBlockStateWrapper
return rendererIgnoredBlocks;
}
HashSet<String> baseIgnoredBlock = new HashSet<>();
ObjectOpenHashSet<String> baseIgnoredBlock = new ObjectOpenHashSet<>();
baseIgnoredBlock.add(AIR_STRING);
rendererIgnoredBlocks = getAllBlockWrappers(Config.Client.Advanced.Graphics.Culling.ignoredRenderBlockCsv, baseIgnoredBlock, levelWrapper);
return rendererIgnoredBlocks;
}
/**
* Requires a {@link ILevelWrapper} since {@link BlockStateWrapper#deserialize(String,ILevelWrapper)} also requires one.
* This way the method won't accidentally be called before the deserialization can be completed.
*/
public static HashSet<IBlockStateWrapper> getRendererIgnoredCaveBlocks(ILevelWrapper levelWrapper)
public static ObjectOpenHashSet<IBlockStateWrapper> getRendererIgnoredCaveBlocks(ILevelWrapper levelWrapper)
{
// use the cached version if possible
if (rendererIgnoredCaveBlocks != null)
@@ -298,23 +439,69 @@ public class BlockStateWrapper implements IBlockStateWrapper
return rendererIgnoredCaveBlocks;
}
HashSet<String> baseIgnoredBlock = new HashSet<>();
ObjectOpenHashSet<String> baseIgnoredBlock = new ObjectOpenHashSet<>();
baseIgnoredBlock.add(AIR_STRING);
rendererIgnoredCaveBlocks = getAllBlockWrappers(Config.Client.Advanced.Graphics.Culling.ignoredRenderCaveBlockCsv, baseIgnoredBlock, levelWrapper);
return rendererIgnoredCaveBlocks;
}
public static ObjectOpenHashSet<IBlockStateWrapper> getWaterSurfaceReplacementBlocks(ILevelWrapper levelWrapper)
{
// use the cached version if possible
if (waterSurfaceReplacementBlocks != null)
{
return waterSurfaceReplacementBlocks;
}
ObjectOpenHashSet<String> baseIgnoredBlock = new ObjectOpenHashSet<>();
waterSurfaceReplacementBlocks = getAllBlockWrappers(Config.Client.Advanced.Graphics.Culling.waterSurfaceBlockReplacementCsv, baseIgnoredBlock, levelWrapper);
return waterSurfaceReplacementBlocks;
}
public static ObjectOpenHashSet<IBlockStateWrapper> getWaterSubsurfaceReplacementBlocks(ILevelWrapper levelWrapper)
{
// use the cached version if possible
if (waterSubsurfaceReplacementBlocks != null)
{
return waterSubsurfaceReplacementBlocks;
}
ObjectOpenHashSet<String> baseIgnoredBlock = new ObjectOpenHashSet<>();
waterSubsurfaceReplacementBlocks = getAllBlockWrappers(Config.Client.Advanced.Graphics.Culling.waterSubSurfaceBlockReplacementCsv, baseIgnoredBlock, levelWrapper);
return waterSubsurfaceReplacementBlocks;
}
public static IBlockStateWrapper getWaterBlockStateWrapper(ILevelWrapper levelWrapper)
{
// use the cached version if possible
if (waterBlock != null)
{
return waterBlock;
}
waterBlock = WrapperFactory.INSTANCE.deserializeBlockStateWrapperOrGetDefault("minecraft:water", levelWrapper);
return waterBlock;
}
public static void clearRendererIgnoredBlocks() { rendererIgnoredBlocks = null; }
public static void clearRendererIgnoredCaveBlocks() { rendererIgnoredCaveBlocks = null; }
public static void clearCachedIgnoreBlocks()
{
rendererIgnoredBlocks = null;
rendererIgnoredCaveBlocks = null;
waterSurfaceReplacementBlocks = null;
waterSubsurfaceReplacementBlocks = null;
waterBlock = null;
}
//endregion
//=====================//
// lod builder helpers //
//=====================//
//region
private static HashSet<IBlockStateWrapper> getAllBlockWrappers(ConfigEntry<String> config, HashSet<String> baseResourceLocations, ILevelWrapper levelWrapper)
private static ObjectOpenHashSet<IBlockStateWrapper> getAllBlockWrappers(ConfigEntry<String> config, ObjectOpenHashSet<String> baseResourceLocations, ILevelWrapper levelWrapper)
{
// get the base blocks
HashSet<String> blockStringList = new HashSet<>();
ObjectOpenHashSet<String> blockStringList = new ObjectOpenHashSet<>();
if (baseResourceLocations != null)
{
blockStringList.addAll(baseResourceLocations);
@@ -329,10 +516,10 @@ public class BlockStateWrapper implements IBlockStateWrapper
return getAllBlockWrappers(blockStringList, levelWrapper);
}
private static HashSet<IBlockStateWrapper> getAllBlockWrappers(HashSet<String> blockResourceLocationSet, ILevelWrapper levelWrapper)
private static ObjectOpenHashSet<IBlockStateWrapper> getAllBlockWrappers(ObjectOpenHashSet<String> blockResourceLocationSet, ILevelWrapper levelWrapper)
{
// deserialize each of the given resource locations
HashSet<IBlockStateWrapper> blockStateWrappers = new HashSet<>();
ObjectOpenHashSet<IBlockStateWrapper> blockStateWrappers = new ObjectOpenHashSet<>();
for (String blockResourceLocation : blockResourceLocationSet)
{
try
@@ -381,11 +568,14 @@ public class BlockStateWrapper implements IBlockStateWrapper
return blockStateWrappers;
}
//endregion
//=================//
// wrapper methods //
//=================//
//region
@Override
public int getOpacity() { return this.opacity; }
@@ -457,7 +647,6 @@ public class BlockStateWrapper implements IBlockStateWrapper
@Override
public int getLightEmission() { return (this.blockState != null) ? this.blockState.getLightEmission() : 0; }
@@ -494,35 +683,9 @@ public class BlockStateWrapper implements IBlockStateWrapper
public boolean isAir(BlockState blockState) { return blockState == null || blockState.isAir(); }
@Override
public boolean isSolid()
{
if (this.isAir())
{
return false;
}
#if MC_VER < MC_1_20_1
return this.blockState.getMaterial().isSolid();
#else
return !this.blockState.getCollisionShape(EmptyBlockGetter.INSTANCE, BlockPos.ZERO).isEmpty();
#endif
}
public boolean isSolid() { return this.isSolid; }
@Override
public boolean isLiquid()
{
if (this.isAir())
{
return false;
}
#if MC_VER < MC_1_20_1
return this.blockState.getMaterial().isLiquid() || !this.blockState.getFluidState().isEmpty();
#else
return !this.blockState.getFluidState().isEmpty();
#endif
}
public boolean isLiquid() { return this.isLiquid; }
@Override
public boolean isBeaconBlock() { return this.isBeaconBlock; }
@Override
@@ -531,6 +694,8 @@ public class BlockStateWrapper implements IBlockStateWrapper
public boolean isBeaconTintBlock() { return this.beaconTintColor != null; }
@Override
public boolean allowsBeaconBeamPassage() { return this.allowsBeaconBeamPassage; }
@Override
public boolean allowApiColorOverride() { return this.allowApiColorOverride; }
@Override
public Color getMapColor() { return this.mapColor; }
@@ -543,11 +708,14 @@ public class BlockStateWrapper implements IBlockStateWrapper
@Override
public String toString() { return this.getSerialString(); }
//endregion
//=======================//
// serialization methods //
//=======================//
//region
private String serialize(ILevelWrapper levelWrapper)
{
@@ -559,22 +727,23 @@ public class BlockStateWrapper implements IBlockStateWrapper
// older versions of MC have a static registry
#if MC_VER > MC_1_17_1
#if MC_VER <= MC_1_16_5
#else
Level level = (Level)levelWrapper.getWrappedMcObject();
net.minecraft.core.RegistryAccess registryAccess = level.registryAccess();
#endif
#if MC_VER < MC_1_21_11
#if MC_VER <= MC_1_21_10
ResourceLocation resourceLocation;
#else
Identifier resourceLocation;
#endif
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
#if MC_VER <= MC_1_17_1
resourceLocation = Registry.BLOCK.getKey(this.blockState.getBlock());
#elif MC_VER == MC_1_18_2 || MC_VER == MC_1_19_2
#elif MC_VER <= MC_1_19_2
resourceLocation = registryAccess.registryOrThrow(Registry.BLOCK_REGISTRY).getKey(this.blockState.getBlock());
#elif MC_VER < MC_1_21_3
#elif MC_VER <= MC_1_21_1
resourceLocation = registryAccess.registryOrThrow(Registries.BLOCK).getKey(this.blockState.getBlock());
#else
resourceLocation = registryAccess.lookupOrThrow(Registries.BLOCK).getKey(this.blockState.getBlock());
@@ -663,18 +832,19 @@ public class BlockStateWrapper implements IBlockStateWrapper
try
{
#if MC_VER > MC_1_17_1
#if MC_VER <= MC_1_16_5
#else
LodUtil.assertTrue(levelWrapper != null && levelWrapper.getWrappedMcObject() != null);
Level level = (Level)levelWrapper.getWrappedMcObject();
#endif
Block block;
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
#if MC_VER <= MC_1_17_1
block = Registry.BLOCK.get(resourceLocation);
#elif MC_VER == MC_1_18_2 || MC_VER == MC_1_19_2
#elif MC_VER <= MC_1_19_2
net.minecraft.core.RegistryAccess registryAccess = level.registryAccess();
block = registryAccess.registryOrThrow(Registry.BLOCK_REGISTRY).get(resourceLocation);
#elif MC_VER < MC_1_21_3
#elif MC_VER <= MC_1_21_1
net.minecraft.core.RegistryAccess registryAccess = level.registryAccess();
block = registryAccess.registryOrThrow(Registries.BLOCK).get(resourceLocation);
#else
@@ -729,7 +899,7 @@ public class BlockStateWrapper implements IBlockStateWrapper
foundState = block.defaultBlockState();
}
foundWrapper = new BlockStateWrapper(foundState, levelWrapper);
foundWrapper = createNewWrapper(foundState, levelWrapper);
return foundWrapper;
}
catch (Exception e)
@@ -775,11 +945,14 @@ public class BlockStateWrapper implements IBlockStateWrapper
return stringBuilder.toString();
}
//endregion
//==============//
// Iris methods //
//==============//
//region
private EDhApiBlockMaterial calculateEDhApiBlockMaterialId()
{
@@ -829,7 +1002,10 @@ public class BlockStateWrapper implements IBlockStateWrapper
{
return EDhApiBlockMaterial.METAL;
}
else if (serialString.contains("grass_block"))
else if (
serialString.contains("grass_block")
|| serialString.contains("grass_slab")
)
{
return EDhApiBlockMaterial.GRASS;
}
@@ -884,4 +1060,7 @@ public class BlockStateWrapper implements IBlockStateWrapper
}
}
//endregion
}
@@ -19,28 +19,24 @@
package com.seibel.distanthorizons.common.wrappers.block;
import com.seibel.distanthorizons.api.interfaces.block.IDhApiBlockStateWrapper;
import com.seibel.distanthorizons.api.interfaces.world.IDhApiLevelWrapper;
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiBlockColorOverrideEvent;
import com.seibel.distanthorizons.common.wrappers.McObjectConverter;
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPosMutable;
import com.seibel.distanthorizons.core.util.ColorUtil;
import com.seibel.distanthorizons.coreapi.util.ColorUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.core.Direction;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.FlowerBlock;
import net.minecraft.world.level.block.LeavesBlock;
import net.minecraft.world.level.block.RotatedPillarBlock;
#if MC_VER >= MC_1_19_2
import net.minecraft.util.RandomSource;
#else
import java.util.Random;
#endif
import net.minecraft.world.level.block.*;
import net.minecraft.world.level.block.state.BlockState;
import com.seibel.distanthorizons.core.logging.DhLogger;
import net.minecraft.world.level.block.state.properties.SlabType;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
@@ -48,9 +44,22 @@ import java.util.HashSet;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;
#if MC_VER < MC_1_21_5
#if MC_VER >= MC_1_19_2
import net.minecraft.util.RandomSource;
#else
import java.util.Random;
#endif
#if MC_VER < MC_1_21_5
import net.minecraft.client.renderer.block.model.BakedQuad;
#elif MC_VER <= MC_1_21_11
import net.minecraft.client.renderer.block.model.BlockModelPart;
import net.minecraft.client.renderer.block.model.BakedQuad;
#else
import net.minecraft.client.renderer.block.dispatch.BlockStateModelPart;
import net.minecraft.client.resources.model.geometry.BakedQuad;
import net.minecraft.core.BlockPos;
import net.minecraft.client.color.block.BlockTintSource;
#endif
/**
@@ -64,6 +73,8 @@ public class ClientBlockStateColorCache
{
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
private static final Minecraft MC = Minecraft.getInstance();
private static final HashSet<BlockState> BLOCK_STATES_THAT_NEED_LEVEL = new HashSet<>();
private static final HashSet<BlockState> BROKEN_BLOCK_STATES = new HashSet<>();
@@ -79,7 +90,16 @@ public class ClientBlockStateColorCache
/** This is the order each direction on a block is processed when attempting to get the texture/color */
private static final Direction[] COLOR_RESOLUTION_DIRECTION_ORDER = { Direction.UP, Direction.NORTH, Direction.EAST, Direction.WEST, Direction.SOUTH, Direction.DOWN };
private static final @Nullable Direction[] COLOR_RESOLUTION_DIRECTION_ORDER =
{
Direction.UP,
null, // null represents "unculled" faces, IE the top of farmland
Direction.NORTH,
Direction.EAST,
Direction.WEST,
Direction.SOUTH,
Direction.DOWN
};
private static final int FLOWER_COLOR_SCALE = 5;
@@ -106,6 +126,7 @@ public class ClientBlockStateColorCache
//===========//
// constants //
//===========//
//region
private static final int MIN_SRGB_BITS = 0x39000000; // 2^(-13)
private static final int MAX_SRGB_BITS = 0x3f7fffff; // 1.0 - f32::EPSILON
@@ -114,6 +135,7 @@ public class ClientBlockStateColorCache
private static final int[] linearToSrgbTable = new int[]
{
//region
0x0073000d, 0x007a000d, 0x0080000d, 0x0087000d, 0x008d000d, 0x0094000d, 0x009a000d, 0x00a1000d,
0x00a7001a, 0x00b4001a, 0x00c1001a, 0x00ce001a, 0x00da001a, 0x00e7001a, 0x00f4001a, 0x0101001a,
0x010e0033, 0x01280033, 0x01410033, 0x015b0033, 0x01750033, 0x018f0033, 0x01a80033, 0x01c20033,
@@ -127,9 +149,11 @@ public class ClientBlockStateColorCache
0x31d105b0, 0x34a80555, 0x37520507, 0x39d504c5, 0x3c37048b, 0x3e7c0458, 0x40a8042a, 0x42bd0401,
0x44c20798, 0x488e071e, 0x4c1c06b6, 0x4f76065d, 0x52a50610, 0x55ac05cc, 0x5892058f, 0x5b590559,
0x5e0c0a23, 0x631c0980, 0x67db08f6, 0x6c55087f, 0x70940818, 0x74a007bd, 0x787d076c, 0x7c330723,
//endregion
};
private static final float[] srgbToLinearTable = new float[]
//region
{
0.0f, 0.000303527f, 0.000607054f, 0.00091058103f, 0.001214108f, 0.001517635f, 0.0018211621f, 0.002124689f,
0.002428216f, 0.002731743f, 0.00303527f, 0.0033465356f, 0.003676507f, 0.004024717f, 0.004391442f,
@@ -163,16 +187,22 @@ public class ClientBlockStateColorCache
0.78353804f, 0.79129815f, 0.79910296f, 0.8069525f, 0.8148468f, 0.822786f, 0.8307701f, 0.83879924f, 0.84687346f,
0.8549928f, 0.8631574f, 0.87136734f, 0.8796226f, 0.8879232f, 0.89626956f, 0.90466136f, 0.913099f, 0.92158204f,
0.93011117f, 0.9386859f, 0.9473069f, 0.9559735f, 0.9646866f, 0.9734455f, 0.98225087f, 0.9911022f, 1.0f
//endregion
};
private static final ThreadLocal<TintWithoutLevelOverrider> TintWithoutLevelOverrideGetter = ThreadLocal.withInitial(() -> new TintWithoutLevelOverrider());
private static final ThreadLocal<TintGetterOverride> TintOverrideGetter = ThreadLocal.withInitial(() -> new TintGetterOverride());
// these are threadlocals since AbstractDhTintGetter use local variables to handle color queries
private static final ThreadLocal<TintWithoutLevelOverrider> TintWithoutLevelOverrideGetter = ThreadLocal.withInitial(TintWithoutLevelOverrider::new);
private static final ThreadLocal<TintGetterOverride> TintOverrideGetter = ThreadLocal.withInitial(TintGetterOverride::new);
private static final ThreadLocal<DhApiBlockColorOverrideEvent.EventParam> ColorOverrideEventParamGetter = ThreadLocal.withInitial(DhApiBlockColorOverrideEvent.EventParam::new);
//endregion
//=============//
// constructor //
//=============//
//region
public ClientBlockStateColorCache(BlockState blockState, IClientLevelWrapper clientLevelWrapper)
{
@@ -183,6 +213,8 @@ public class ClientBlockStateColorCache
this.resolveColors();
}
//endregion
//===================//
@@ -230,11 +262,18 @@ public class ClientBlockStateColorCache
{
BakedQuad firstQuad = quads.get(0);
#if MC_VER <= MC_1_21_11
this.needPostTinting = firstQuad.isTinted();
#else
this.needPostTinting = firstQuad.materialInfo().isTinted();
#endif
#if MC_VER <= MC_1_21_4
this.tintIndex = firstQuad.getTintIndex();
#else
#elif MC_VER <= MC_1_21_11
this.tintIndex = firstQuad.tintIndex();
#else
this.tintIndex = firstQuad.materialInfo().tintIndex();
#endif
#if MC_VER < MC_1_17_1
@@ -245,10 +284,14 @@ public class ClientBlockStateColorCache
this.baseColor = calculateColorFromTexture(
firstQuad.getSprite(),
EColorMode.getColorMode(this.blockState.getBlock()));
#else
#elif MC_VER <= MC_1_21_11
this.baseColor = calculateColorFromTexture(
firstQuad.sprite(),
EColorMode.getColorMode(this.blockState.getBlock()));
#else
this.baseColor = calculateColorFromTexture(
firstQuad.materialInfo().sprite(),
EColorMode.getColorMode(this.blockState.getBlock()));
#endif
}
else
@@ -281,14 +324,23 @@ public class ClientBlockStateColorCache
@Nullable
private List<BakedQuad> getQuadsForDirection(@Nullable Direction direction)
{
List<BakedQuad> quads = null;
BlockState effectiveBlockState = this.blockState;
// if this block is a slab, use it's double variant so we can get the top face,
// otherwise the color will use the side, which isn't as accurate
if (this.blockState.getBlock() instanceof SlabBlock)
{
effectiveBlockState = this.blockState.setValue( SlabBlock.TYPE, SlabType.DOUBLE );
}
List<BakedQuad> quads;
#if MC_VER < MC_1_21_5
quads = Minecraft.getInstance().getModelManager().getBlockModelShaper().
getBlockModel(this.blockState).getQuads(this.blockState, direction, RANDOM);
#else
List<BlockModelPart> blockModelPartList = Minecraft.getInstance().getModelManager().getBlockModelShaper().
getBlockModel(this.blockState).collectParts(RANDOM);
quads = MC.getModelManager().getBlockModelShaper().
getBlockModel(effectiveBlockState).getQuads(effectiveBlockState, direction, RANDOM);
#elif MC_VER <= MC_1_21_11
List<BlockModelPart> blockModelPartList = MC.getModelManager().getBlockModelShaper().
getBlockModel(effectiveBlockState).collectParts(RANDOM);
quads = new ArrayList<>();
if (blockModelPartList != null)
@@ -299,12 +351,23 @@ public class ClientBlockStateColorCache
quads.addAll(blockModelPartList.get(i).getQuads(direction));
}
}
#else
List<BlockStateModelPart> blockModelPartList = new ArrayList<>();
MC.getModelManager().getBlockStateModelSet()
.get(effectiveBlockState).collectParts(RANDOM, blockModelPartList);
quads = new ArrayList<>();
for (int i = 0; i < blockModelPartList.size(); i++)
{
// if direction is null this will return the unculled quads
quads.addAll(blockModelPartList.get(i).getQuads(direction));
}
#endif
return quads;
}
//TODO: Perhaps make this not just use the first frame?
/** if multiple frames are present, just the first one will be used */
private static int calculateColorFromTexture(TextureAtlasSprite texture, EColorMode colorMode)
{
int count = 0;
@@ -439,7 +502,11 @@ public class ClientBlockStateColorCache
private int getParticleIconColor()
{
return calculateColorFromTexture(
Minecraft.getInstance().getModelManager().getBlockModelShaper().getParticleIcon(this.blockState),
#if MC_VER <= MC_1_21_11
Minecraft.getInstance().getModelManager().getBlockModelShaper().getParticleIcon(this.blockState),
#else
Minecraft.getInstance().getModelManager().getBlockStateModelSet().get(this.blockState).particleMaterial().sprite(),
#endif
EColorMode.getColorMode(this.blockState.getBlock()));
}
@@ -452,52 +519,89 @@ public class ClientBlockStateColorCache
public int getColor(BiomeWrapper biomeWrapper, FullDataSourceV2 fullDataSource, DhBlockPos blockPos)
{
// only get the tint if the block needs to be tinted
if (!this.needPostTinting)
int tintColor = AbstractDhTintGetter.INVALID_COLOR;
if (this.needPostTinting)
{
return this.baseColor;
}
// don't try tinting blocks that don't support our method of tint getting
if (BROKEN_BLOCK_STATES.contains(this.blockState))
{
return this.baseColor;
}
// attempt to get the tint
int tintColor = -1;
try
{
// try to use the fast tint getter logic first
if (!BLOCK_STATES_THAT_NEED_LEVEL.contains(this.blockState))
// don't try tinting blocks that don't support our method of tint getting
if (BROKEN_BLOCK_STATES.contains(this.blockState))
{
try
return this.baseColor;
}
// attempt to get the tint
try
{
// try to use the fast tint getter logic first
if (!BLOCK_STATES_THAT_NEED_LEVEL.contains(this.blockState))
{
TintWithoutLevelOverrider tintOverride = TintWithoutLevelOverrideGetter.get();
tintOverride.update(biomeWrapper, this.blockStateWrapper, fullDataSource, this.clientLevelWrapper);
// try using DH's cached tint values first if possible
tintColor = tintOverride.tryGetBlockTint(new DhBlockPosMutable(blockPos));
if (tintColor == AbstractDhTintGetter.INVALID_COLOR)
try
{
// one or more tint values weren't calculated,
// we need MC's color resolver
TintWithoutLevelOverrider tintOverride = TintWithoutLevelOverrideGetter.get();
tintOverride.update(biomeWrapper, this.blockStateWrapper, fullDataSource, this.clientLevelWrapper);
// try using DH's cached tint values first if possible
tintColor = tintOverride.tryGetBlockTint(new DhBlockPosMutable(blockPos));
if (tintColor == AbstractDhTintGetter.INVALID_COLOR)
{
// one or more tint values weren't calculated,
// we need MC's color resolver
#if MC_VER <= MC_1_21_11
tintColor = Minecraft.getInstance()
.getBlockColors()
.getColor(this.blockState,
tintOverride,
tintOverride, // tintOverride will save the result of this query to speed up future queries
McObjectConverter.Convert(blockPos),
this.tintIndex);
#else
BlockTintSource tintSource = Minecraft.getInstance()
.getBlockColors()
.getTintSource(this.blockState, this.tintIndex);
// a tint source may be null for blocks that don't actually need tinting
// in that case the base color should be sufficient
// Example: cherry blossom leaves
if (tintSource != null)
{
BlockPos mcPos = McObjectConverter.Convert(blockPos);
tintColor = tintSource.colorInWorld(this.blockState, tintOverride, mcPos);
if (tintColor == -1)
{
tintColor = tintSource.colorAsTerrainParticle(this.blockState, tintOverride, mcPos);
}
}
if (tintColor == -1)
{
// no color found, use the base color
tintColor = AbstractDhTintGetter.INVALID_COLOR;
}
// save this color to speed up future queries
TintWithoutLevelOverrider.setStaticColor(this.blockStateWrapper, biomeWrapper, tintColor);
// try to get the blended color with this new information
tintColor = tintOverride.tryGetBlockTint(new DhBlockPosMutable(blockPos));
#endif
}
}
}
catch (UnsupportedOperationException e)
{
catch (Exception e)
{
#if MC_VER <= MC_1_21_11
// this exception generally occurs if the tint requires other blocks besides itself
LOGGER.debug("Unable to use ["+ TintWithoutLevelOverrider.class.getSimpleName()+"] to get the block tint for block: [" + this.blockState + "] and biome: [" + biomeWrapper + "] at pos: " + blockPos + ". Error: [" + e.getMessage() + "]. Attempting to use backup method...", e);
BLOCK_STATES_THAT_NEED_LEVEL.add(this.blockState);
#else
// only display the error once per block/biome type to reduce log spam
if (!BROKEN_BLOCK_STATES.contains(this.blockState))
{
LOGGER.warn("Failed to get block color for block: [" + this.blockState + "] and biome: [" + biomeWrapper + "] at pos: " + blockPos + ". Error: [" + e.getMessage() + "]. Note: future errors for this block/biome will be ignored.", e);
BROKEN_BLOCK_STATES.add(this.blockState);
}
#endif
}
}
}
// level-specific logic is only needed for MC 1.21.11 and older
#if MC_VER <= MC_1_21_11
// use the level logic only if requested
if (BLOCK_STATES_THAT_NEED_LEVEL.contains(this.blockState))
{
@@ -518,28 +622,49 @@ public class ClientBlockStateColorCache
this.tintIndex);
}
}
}
catch (Exception e)
{
// only display the error once per block/biome type to reduce log spam
if (!BROKEN_BLOCK_STATES.contains(this.blockState))
#endif
}
catch (Exception e)
{
LOGGER.warn("Failed to get block color for block: [" + this.blockState + "] and biome: [" + biomeWrapper + "] at pos: " + blockPos + ". Error: ["+e.getMessage() + "]. Note: future errors for this block/biome will be ignored.", e);
BROKEN_BLOCK_STATES.add(this.blockState);
// only display the error once per block/biome type to reduce log spam
if (!BROKEN_BLOCK_STATES.contains(this.blockState))
{
LOGGER.warn("Failed to get block color for block: [" + this.blockState + "] and biome: [" + biomeWrapper + "] at pos: " + blockPos + ". Error: [" + e.getMessage() + "]. Note: future errors for this block/biome will be ignored.", e);
BROKEN_BLOCK_STATES.add(this.blockState);
}
}
}
if (tintColor != -1)
int returnColor;
if (tintColor != AbstractDhTintGetter.INVALID_COLOR)
{
return ColorUtil.multiplyARGBwithRGB(this.baseColor, tintColor);
returnColor = ColorUtil.multiplyARGBwithRGB(this.baseColor, tintColor);
}
else
{
// unable to get the tinted color, use the base color instead
return this.baseColor;
returnColor = this.baseColor;
}
// only fire an API event if needed
// (this is done to reduce GC pressure and speed up color getting)
if (this.blockStateWrapper.allowApiColorOverride())
{
DhApiBlockColorOverrideEvent.EventParam eventParam = ColorOverrideEventParamGetter.get();
eventParam.update(
this.clientLevelWrapper,
this.blockStateWrapper, returnColor,
blockPos.getX(), blockPos.getY(), blockPos.getZ()
);
ApiEventInjector.INSTANCE.fireAllEvents(DhApiBlockColorOverrideEvent.class, eventParam);
// let the API user override this color
returnColor = eventParam.getColorAsInt();
}
return returnColor;
}
@@ -547,6 +672,7 @@ public class ClientBlockStateColorCache
//================//
// helper classes //
//================//
//region
private enum EColorMode
{
@@ -558,14 +684,28 @@ public class ClientBlockStateColorCache
static EColorMode getColorMode(Block block)
{
if (block instanceof LeavesBlock) return Leaves;
if (block instanceof FlowerBlock) return Flower;
if (block.toString().contains("glass")) return Glass;
if (block.toString().equals("Block{chiselsandbits:chiseled}")) return Chisel;
if (block instanceof LeavesBlock)
{
return Leaves;
}
if (block instanceof FlowerBlock)
{
return Flower;
}
if (block.toString().contains("glass"))
{
return Glass;
}
if (block.toString().equals("Block{chiselsandbits:chiseled}"))
{
return Chisel;
}
return Default;
}
}
//endregion
}
@@ -19,12 +19,13 @@
package com.seibel.distanthorizons.common.wrappers.block;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
#if MC_VER < MC_1_17_1
#elif MC_VER < MC_1_21_3
#else
import com.seibel.distanthorizons.core.util.ColorUtil;
import com.seibel.distanthorizons.coreapi.util.ColorUtil;
import net.minecraft.client.renderer.texture.SpriteContents;
#endif
@@ -51,10 +51,10 @@ public class TintGetterOverride extends AbstractDhTintGetter
public TintGetterOverride() { }
public void update(LevelReader parent, BiomeWrapper biomeWrapper, BlockStateWrapper blockStateWrapper, FullDataSourceV2 fullDataSource, IClientLevelWrapper clientLevelWrapper)
public void update(BiomeWrapper biomeWrapper, BlockStateWrapper blockStateWrapper, FullDataSourceV2 fullDataSource, IClientLevelWrapper clientLevelWrapper)
{
super.update(biomeWrapper, blockStateWrapper, fullDataSource, clientLevelWrapper);
this.parent = parent;
this.parent = (LevelReader)this.clientLevelWrapper.getWrappedMcObject();
}
@@ -63,8 +63,10 @@ public class TintGetterOverride extends AbstractDhTintGetter
// methods //
//=========//
#if MC_VER <= MC_1_21_11
@Override
public float getShade(Direction direction, boolean bl) { return this.parent.getShade(direction, bl); }
#endif
@Override
public LevelLightEngine getLightEngine() { return this.parent.getLightEngine(); }
@@ -175,6 +177,13 @@ public class TintGetterOverride extends AbstractDhTintGetter
@Override
public int getSectionYFromSectionIndex(int i) { return this.parent.getSectionYFromSectionIndex(i); }
#endif
#if MC_VER <= MC_1_21_11
#else
@Override
public CardinalLighting cardinalLighting() { return CardinalLighting.DEFAULT; }
#endif
@@ -19,8 +19,6 @@
package com.seibel.distanthorizons.common.wrappers.block;
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.level.LevelReader;
@@ -30,6 +28,12 @@ import net.minecraft.world.level.lighting.LevelLightEngine;
import net.minecraft.world.level.material.FluidState;
import org.jetbrains.annotations.Nullable;
#if MC_VER <= MC_1_21_11
#else
import net.minecraft.world.level.CardinalLighting;
#endif
public class TintWithoutLevelOverrider extends AbstractDhTintGetter
{
@@ -46,9 +50,12 @@ public class TintWithoutLevelOverrider extends AbstractDhTintGetter
// methods //
//=========//
#if MC_VER <= MC_1_21_11
@Override
public float getShade(Direction direction, boolean shade)
{ throw new UnsupportedOperationException("ERROR: getShade() called on TintWithoutLevelOverrider. Object is for tinting only."); }
#endif
@Override
public LevelLightEngine getLightEngine()
{ throw new UnsupportedOperationException("ERROR: getLightEngine() called on TintWithoutLevelOverrider. Object is for tinting only."); }
@@ -87,4 +94,18 @@ public class TintWithoutLevelOverrider extends AbstractDhTintGetter
#endif
//=================//
// post MC 1.21.11 //
//=================//
#if MC_VER <= MC_1_21_11
#else
@Override
public CardinalLighting cardinalLighting()
{ throw new UnsupportedOperationException("ERROR: cardinalLighting() called on TintWithoutLevelOverrider. Object is for tinting only."); }
#endif
}
@@ -18,6 +18,7 @@
*/
package com.seibel.distanthorizons.common.wrappers.chunk;
import com.seibel.distanthorizons.api.DhApi;
import com.seibel.distanthorizons.common.wrappers.block.BiomeWrapper;
import com.seibel.distanthorizons.common.wrappers.block.BlockStateWrapper;
import com.seibel.distanthorizons.common.wrappers.misc.MutableBlockPosWrapper;
@@ -40,6 +41,7 @@ import net.minecraft.world.level.levelgen.Heightmap;
import com.seibel.distanthorizons.core.logging.DhLogger;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
#if MC_VER >= MC_1_17_1
import net.minecraft.core.QuartPos;
@@ -80,6 +82,10 @@ public class ChunkWrapper implements IChunkWrapper
private static final ThreadLocal<BlockPos.MutableBlockPos> MUTABLE_BLOCK_POS_REF = ThreadLocal.withInitial(() -> new BlockPos.MutableBlockPos());
private static final ThreadLocal<MutableBlockPosWrapper> MUTABLE_BLOCK_POS_WRAPPER_REF = ThreadLocal.withInitial(() -> new MutableBlockPosWrapper());
public static final Set<String> LOGGED_BLOCK_GET_ERRORS = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
private static boolean heightmapThreadWarningLogged = false;
private final ChunkAccess chunk;
private final DhChunkPos chunkPos;
@@ -107,22 +113,26 @@ public class ChunkWrapper implements IChunkWrapper
// constructor //
//=============//
/**
* Note: this constructor should be very
* fast since it will be called frequently on the MC
* server thread and a slow method will cause server lag.
*/
public ChunkWrapper(ChunkAccess chunk, ILevelWrapper wrappedLevel)
{
this(chunk, wrappedLevel, true);
}
public ChunkWrapper(ChunkAccess chunk, ILevelWrapper wrappedLevel, boolean recreateHeightmaps)
{
this.chunk = chunk;
this.wrappedLevel = wrappedLevel;
this.chunkPos = new DhChunkPos(chunk.getPos().x, chunk.getPos().z);
if (recreateHeightmaps)
{
this.createDhHeightMaps();
}
#if MC_VER <= MC_1_21_11
this.chunkPos = new DhChunkPos(chunk.getPos().x, chunk.getPos().z);
#else
this.chunkPos = new DhChunkPos(chunk.getPos().x(), chunk.getPos().z());
#endif
}
@Override
public ChunkWrapper copy() { return new ChunkWrapper(this.chunk, this.wrappedLevel); }
//=========//
@@ -242,11 +252,16 @@ public class ChunkWrapper implements IChunkWrapper
}
private int getChunkSectionMinHeight(int index) { return (index * 16) + this.getInclusiveMinBuildHeight(); }
@Override
public void createDhHeightMaps()
{
// re-calculate the min/max heights for consistency (during world gen these may be wrong)
this.minNonEmptyHeight = Integer.MIN_VALUE;
this.maxNonEmptyHeight = Integer.MAX_VALUE;
if (heightmapThreadWarningLogged
&& !DhApi.isDhThread())
{
heightmapThreadWarningLogged = true;
LOGGER.warn("ChunkWrapper Height maps created on non-DH thread ["+Thread.currentThread().getName()+"]. This may cause stuttering.");
}
this.solidHeightMap = new int[LodUtil.CHUNK_WIDTH][LodUtil.CHUNK_WIDTH];
@@ -365,8 +380,19 @@ public class ChunkWrapper implements IChunkWrapper
blockPos.setY(relY);
blockPos.setZ(relZ);
// TODO copy into pooled array, this isn't thread safe and can cause MC to throw errors if the chunk is loaded
return BlockStateWrapper.fromBlockState(this.chunk.getBlockState(blockPos), this.wrappedLevel);
try
{
return BlockStateWrapper.fromBlockState(this.chunk.getBlockState(blockPos), this.wrappedLevel);
}
catch (Exception e)
{
if (LOGGED_BLOCK_GET_ERRORS.add(e.getMessage()))
{
LOGGER.warn("Failed to get block from chunk ["+this.chunkPos+"] at relative block pos ["+relX+","+relY+","+relZ+"], air will be used instead. This error message will only be logged once. error: ["+e.getMessage()+"].", e);
}
return BlockStateWrapper.AIR;
}
}
@Override
@@ -378,8 +404,20 @@ public class ChunkWrapper implements IChunkWrapper
pos.setX(relX);
pos.setY(relY);
pos.setZ(relZ);
return BlockStateWrapper.fromBlockState(this.chunk.getBlockState(pos), this.wrappedLevel, guess);
try
{
return BlockStateWrapper.fromBlockState(this.chunk.getBlockState(pos), this.wrappedLevel, guess);
}
catch (Exception e)
{
if (LOGGED_BLOCK_GET_ERRORS.add(e.getMessage()))
{
LOGGER.warn("Failed to get block from chunk ["+this.chunkPos+"] at relative block pos ["+relX+","+relY+","+relZ+"], air will be used instead. This error message will only be logged once. error: ["+e.getMessage()+"].", e);
}
return BlockStateWrapper.AIR;
}
}
/**
@@ -407,8 +445,10 @@ public class ChunkWrapper implements IChunkWrapper
LevelChunkSection section = sections[i];
if (section != null)
{
// TODO implement section cloning for older MC versions, only 1.21.4 MC (and maybe other semi recent versions) have a clean way to handle this
// TODO we probably want a wrapper object instead
// Implementation notes:
// implement section cloning for older MC versions, only 1.21.4 MC (and maybe other semi recent versions) have a clean way to handle this
// we probably want a wrapper object instead
#if MC_VER < MC_1_21_4
this.levelChunkSections[i] = section;
#else
@@ -614,15 +654,6 @@ public class ChunkWrapper implements IChunkWrapper
//===============//
// other methods //
//===============//
@Override
public boolean isStillValid() { return this.wrappedLevel.tryGetChunk(this.chunkPos) == this; }
//================//
// base overrides //
//================//

Some files were not shown because too many files have changed in this diff Show More