From c6b1c06a3f9e3ab82b7cbf04c33592a2c2a1f90d Mon Sep 17 00:00:00 2001 From: thatmidcoder393 <73651803+ThatMG393@users.noreply.github.com> Date: Sun, 15 Dec 2024 14:10:14 +0800 Subject: [PATCH] Upstreamed/combine draw commands (#8) * rename some things for clarity * fix waterlogged glass panes (once again, but more this time) by avoiding distance sorting through the detection of primary intersectors when geometry is intersecting and then sorting them in a fixed order * use Mth.clamp for clarity * refactor buffer and sort result handling, buffers are now freed immediately instead of keeping them to avoid memory usage buffer caching would be a better solution but that's complicated and doesn't currently work correctly * reduce number of unique triggers by around 5 percent without impacting sorting or building performance * importantly sort a little farther away, sort tasks are fast * use defer zero frames for important sort tasks by default * fix build * clarify authorship of BitArray * fix bug with radix sort for SNR heuristic in BSP partition generating wrong indexes * combine draw commands * correctly reset accumulated element count * remove draw call combining for indexed rendering as it's broken and hard to fix * skip heuristic if there's no quads * refactor primary intersector detection to handle large cases better, also removed the warning message about unpartitionable geometry as it seems to not be a relevant problem * fix topo sorting in some situations where the dot product was wrongly not recalculated when the normal is quantized. also fixed aligned quads not receiving the more accurate center based on the average of the unique vertexes. * reorder vertex ranges before uploaded to optimize for combined draw commands * tune primary intersector detection to handle situations where only a small amount of geometry is intersecting * Correctly handle colorization on NeoForge * Combine the vertex position attributes (#2753) This improves terrain rendering performance significantly on Intel Xe-LP graphics under Linux. * Add option for Fullscreen Resolution (#2642) The resolution controls would not fit in the allocated space, so the rendering of slider controls was changed to enable rendering the slider bar and the value text on separate lines. Co-authored-by: MeeniMc <68366846+MeeniMc@users.noreply.github.com> * Only enable Fullscreen Resolution option on Windows Additionally, adjust the rendering of the controls to be less confusing when disabled, and provide an explanation as to what the option does. * fix draw command combining, remove aggressive non-empty command skipping because it seems broken * Use consistent vertex ordering in entity rendering Some core shaders were relying on the model part faces being written out in a specific order. We still don't support core shaders, but the fix here is trivial enough. Fixes #2745 * Add check for NeoForge per-quad AO flag * Disable mod entrypoint on Forge when running on servers (#2773) * fix graphical corruption when there's a lot of geometry by appropriately picking the size of the required shared index buffer * cleanup unused and broken code * cleanup calculation of mask bit and element count * cleanup meshing, storage, and renderer * fix translucent rendering by correctly decoding vertex segments * cleanup misc, remove unused code * refactor translucent AnyOrderData to not generate its own trivial index buffer and instead share this type of data within regions * add Index Pool arena size * add arena content caching * Fix excessively large allocations in chunk meshing The requested capacity was being multiplied by the vertex stride more than once, which resulted in far too much memory being allocated. Closes #2792 * Fix some issues with Uint32 representation This increases the maximum size of vertex and index buffers to 4 billion elements, since the Uint32 types stored in memory are now safely represented with Int64. For vertex buffers, this increases their maximum size to 80 GiB, and index buffers have a maximum size of 16 GiB, whereas both were limited to 2 GiB prior. * refactor storage to cope with larger amounts of geometry and use less ugly hacks, rename a bunch of methods to be consistent and clearer * remove debug code * Fix cull bitmask ordering in entity rendering Closes #2788 * Add support for Maven Local publishing * Fix incorrect warning message when D3DKMT is not supported * Add Flawless Frames handler for NeoForge * Add angle-based section visibility path occlusion (#2811) This eliminates 8-13% of the rendered sections at higher render distances on average in testing, and correspondingly reduces graph search time by a similar amount. * Disable material downgrading on Intel Gen8 and older Fixes #2830 * Delay normal face calculation to use This potentially fixes some cases of #2835. * Skip particle rendering optimizations for incompatible mods Fixes #2827 * Update project URLs in source and documentation We're no longer a Fabric-exclusive mod, so let's get rid of the suffix. * Add third-party license notice for Fabric API * Optimizations for some block models (#2508) Co-authored-by: muzikbike <52297970+muzikbike@users.noreply.github.com> * Fix sorting failures on rotated cuboids (#2812) Use the accurate vertex positions for unaligned and aligned (but rotated) quads. * Rework the Gradle build scripts for multi-loader * Shared logic is moved into a build plugin where possible * Build time is significantly improved when the Gradle daemon is warmed * Mixins are remapped in-place now, eliminating the need for refmaps at runtime. This also gets rid of some warning messages at startup. * Module relationships are now correctly represented in IDEA for other source sets (fixes a lot of code analysis features) * Split Java source and resources into different configurations * Run configurations are now consistent between NeoForge/Fabric * The common project is no longer remapped unnecessarily * Updated Gradle and build plugins * Restore versioning schema to build script * Make organization of platform mixin packages consistent Fixes #2688 * Exclude README documentation from processed resources These files are only meant to be in the source distribution, and Minecraft doesn't like them. * Don't try to load a refmap from the mixin plugin * Enumerate additional PCI classes in the graphics adapter probe Some integrated GPUs, such as RDNA3.5, appear to use the PCI_CLASS_DISPLAY_OTHER class. * Remove KDE and GNOME specific backends for browsing URLs The bugs with xdg-open have been resolved upstream and most Linux distributions are shipping the patches. Also, make sure we get a successful exit code from the XDG implementation. * Remove Minecraft from classpath of the pre-launch source set This will help to avoid class-load issues and makes the code more hygienic. * Update to Minecraft 1.21.3 * Ensure tooltips are constrained to the screen (#2845) * Update authors and contributors list * Remove leftover popPose * Trust existing fog color * Block the Overwolf Overlay due to graphical corruption The overlay does not correctly restore the texture unit state in OpenGL, which causes problems when Minecraft thinks a texture has already been bound to a slot. Since disabling the OpenGL state cache globally is not an acceptable solution (it would severely hurt performance) and their software doesn't give us any method to detect the problematic version, we block all versions. gep_minecraft.dll is the payload they actually inject, which has no version information or description. Fixes #2862 * Avoid static memory allocation in EntityRenderer Just allocate on the stack, since it's a small amount of memory (<1 KiB) and avoids needing complex finalizers. * Fix y-offset calculation for back face culling in cloud rendering (#2864) * Fix memory leak and double free in CloudRenderer * Improve color mixing functions The existing functions did not implement rounding correctly (often leading to off-by-one errors). Additionally, the improved variants are both slightly faster and easier to understand. * Fixup documentation in ColorMixer * Unify color mixing/swizzling utilities The Fabric integration code was re-implementing a lot of the utilities that already exist in Sodium unnecessarily. Also, improve the documentation so that ABGR and RGBA are not used interchangeably. * Add optimized function for bi-linear interpolation This reduces the number of ALU ops significantly and creates a common utility function in the project. * Reduce time complexity for box blurs Measuring the time spent per box blur in biome blending, the following results were observed. Radius Before After % Improvement 7 blocks 9100ns 3700ns 59% 3 blocks 5400ns 3200ns 41% 1 blocks 3700ns 2600ns 29% * Revert detection of Overwolf Overlay They claim this has since been fixed. We will re-examine in the future if we see additional reports. This reverts commit e7ea6f7dd5207c8fd0dac584a1134a16025a85de. * Bump version to 0.6.0-beta.5 * Bump version to 0.6.0-final * Update render code for chunk status map Fixes #2881 * Rollup of fixes and improvements for cloud rendering Some changes were made to cloud rendering in newer versions that needed to be replicated in Sodium. - The alpha cutoff for clouds was changed to (a < 10). - Texture loading can now gracefully fail, and it is expected that rendering is skipped when this happens. - The movement/positioning of clouds was slightly changed. - The render pass system now needs to be told about render target usages (fixes #2883). This commit also improves mesh building time by around 35% on a fast processor (AMD Ryzen HX AI 370) through various micro-optimizations. * Fix culling behavior between transparent and opaque blocks Minecraft 1.21.2 changed some of the rules, and this was causing the faces of transparent blocks to be rendered even when they were hidden by full opaque blocks. Fixes #2850 * Fix precision issues in cloud rendering at far distances * Use alternative workaround for NVIDIA drivers The NVIDIA driver enables a driver feature called "Threaded Optimizations" when it finds Minecraft, which causes severe performance issues and sometimes even crashes. Newer versions of the driver seem to use a slightly different heuristic which our workaround doesn't address. So, instead, use an alternative method that enables GL_DEBUG_OUTPUT_SYNCHRONOUS. This seems to reliably disable the functionality *even if* the user has configured it otherwise in their driver settings. Additionally, on Windows, we now always indicate to the driver that Minecraft is running, so that users with hybrid graphics don't see regressed performance. * Sort render lists for regions and sections after traversal (#2780) Render sections and regions are sorted after the graph traversal is performed. This decouples their ordering from the graph, which isn't entirely correct for draw call sorting. Fixes #2266 * Add support for new NeoForge fluid overlay API * Ensure depth test is configured when rendering clouds The state of the depth test prior to cloud rendering is undefined. After rendering, it is expected to be disabled again. * Fix rounding error in ColorMixer#mix Rounding of the values now happens after the 16-bit intermediaries are added together. This affected some animated textures, causing them to exhibit flickering behavior. * Add additional optimized block models This covers the following additional blocks: - Cauldrons - Brewing Stands - Bells Co-authored-by: JellySquid * Avoid marking the section graph as dirty if state didn't change (#2886) Avoids rebuilding the render lists and doing a graph search more often than necessary by checking if the section actually changed in a way that's relevant to the graph search. For worlds that update their blocks frequently (every tick or every redstone tick) this avoids half the graph searches. Some graph searches are still necessary to schedule rebuild tasks, but when the task results come back, this doesn't do another graph search unless the section's visibility data or build state changed in a way that needs the render list to be updated. * Use larger bounding box for nearby sections in frustum check (#2879) This fixes some problems where very large block entities in nearby sections may be incorrectly culled. But it does not comprehensively fix the problem for all other sections, since that would require visiting the 27-neighborhood of every section, which is too slow. * Bump version to 0.6.1 * Bump dependency versions * Update mod manifest * Ensure ItemRenderContext.isDefaultTranslucent is initialized * Update compatible mods listing * Update mod manifest to restrict Minecraft versions * Update to Minecraft 1.21.4 * Update NeoForge manifest for Minecraft 1.21.4 * Fix glyph effect orientation * Fix hidden surface elimination in fluid rendering for waterlogged blocks (#2907) * Fix detection for specific Intel OpenGL ICDs The OpenGL ICD name now includes the file extension, which the regex expressions were not matching. * Avoid showing the incompatible driver error in some cases For systems with hybrid graphics, it may be the case that an incompatible graphics driver is installed, but that it isn't used for the OpenGL context. We can avoid showing errors in this situation by checking the vendor string of the context immediately after creation. This is not the most robust check, but in practice, a single system should not have multiple graphics drivers installed from the same vendor, so checking the string should be relatively safe. * Fix lambda mappings * Bump version and dependency requirements * Use correct coordinates for sorting chunk sections (#2924) Fix section and region sorting by using the correct section coordinate instead of the integer part of the camera transform, which is incorrect near the origin. Closes #2918 * Update README.md --------- Co-authored-by: douira Co-authored-by: IMS212 Co-authored-by: JellySquid <1363084+jellysquid3@users.noreply.github.com> Co-authored-by: douira Co-authored-by: MeeniMc <68366846+MeeniMc@users.noreply.github.com> Co-authored-by: JellySquid Co-authored-by: IThundxr Co-authored-by: muzikbike <52297970+muzikbike@users.noreply.github.com> --- .github/ISSUE_TEMPLATE/bug_report.yml | 4 +- .github/ISSUE_TEMPLATE/config.yml | 2 +- .github/ISSUE_TEMPLATE/feature_request.yml | 2 +- .github/workflows/build-commit.yml | 2 +- .github/workflows/build-pull-request.yml | 2 +- .github/workflows/build-release.yml | 3 +- .github/workflows/build-tag.yml | 2 +- README.md | 85 +- build.gradle.kts | 80 -- buildSrc/build.gradle.kts | 8 + buildSrc/src/main/kotlin/BuildConfig.kt | 41 + .../main/kotlin/multiloader-base.gradle.kts | 44 + .../kotlin/multiloader-platform.gradle.kts | 43 + common/build.gradle.kts | 155 ++-- .../mods/sodium/api/util/ColorABGR.java | 58 +- .../mods/sodium/api/util/ColorARGB.java | 55 +- .../mods/sodium/api/util/ColorMixer.java | 187 +++-- .../api/vertex/buffer/VertexBufferWriter.java | 2 +- .../mods/sodium/desktop/LaunchWarn.java | 2 +- .../utils/browse/BrowseUrlHandler.java | 9 - .../desktop/utils/browse/GNOMEImpl.java | 16 - .../sodium/desktop/utils/browse/KDEImpl.java | 16 - .../sodium/desktop/utils/browse/XDGImpl.java | 15 +- .../client/checks/ResourcePackScanner.java | 6 +- .../checks/SodiumResourcePackMetadata.java | 2 +- .../client/data/config/MixinConfig.java | 4 +- .../sodium/client/gl/arena/GlBufferArena.java | 94 ++- .../client/gl/arena/GlBufferSegment.java | 75 +- .../gl/arena/PendingBufferCopyCommand.java | 35 +- .../client/gl/buffer/IndexedVertexData.java | 16 - .../client/gl/device/MultiDrawBatch.java | 1 + .../gl/shader/uniform/GlUniformFloat4v.java | 4 + .../client/gui/SodiumGameOptionPages.java | 44 +- .../sodium/client/gui/SodiumOptionsGUI.java | 15 +- .../sodium/client/gui/options/OptionFlag.java | 1 + .../sodium/client/gui/options/OptionImpl.java | 16 +- .../gui/options/control/ControlElement.java | 51 +- .../control/ControlValueFormatter.java | 16 + .../gui/options/control/SliderControl.java | 64 +- .../client/gui/widgets/AbstractWidget.java | 3 + .../client/gui/widgets/FlatButtonWidget.java | 5 + .../model/color/DefaultColorProviders.java | 4 +- .../color/interop/ItemColorsExtension.java | 8 - .../model/light/data/LightDataAccess.java | 4 +- .../client/model/quad/BakedQuadView.java | 2 + .../sodium/client/model/quad/ModelQuad.java | 15 +- .../client/model/quad/ModelQuadView.java | 13 +- .../model/quad/ModelQuadViewMutable.java | 4 +- .../quad/blender/BlendedColorProvider.java | 85 +- .../client/render/SodiumWorldRenderer.java | 14 +- .../render/chunk/DefaultChunkRenderer.java | 124 +-- .../client/render/chunk/RenderSection.java | 30 +- .../render/chunk/RenderSectionManager.java | 52 +- .../render/chunk/ShaderChunkRenderer.java | 5 +- .../render/chunk/SharedQuadIndexBuffer.java | 9 + .../chunk/compile/ChunkBuildBuffers.java | 91 +- .../render/chunk/compile/ChunkSortOutput.java | 36 +- .../chunk/compile/executor/ChunkBuilder.java | 13 +- .../compile/pipeline/BlockOcclusionCache.java | 129 ++- .../chunk/compile/pipeline/BlockRenderer.java | 25 +- .../pipeline/DefaultFluidRenderer.java | 55 +- .../tasks/ChunkBuilderMeshingTask.java | 33 +- .../tasks/ChunkBuilderSortingTask.java | 8 + .../chunk/data/BuiltSectionMeshParts.java | 19 +- .../chunk/data/SectionRenderDataStorage.java | 159 +++- .../chunk/data/SectionRenderDataUnsafe.java | 69 +- .../render/chunk/lists/ChunkRenderList.java | 40 +- .../render/chunk/lists/SortedRenderLists.java | 42 - .../chunk/lists/VisibleChunkCollector.java | 65 +- .../chunk/occlusion/OcclusionCuller.java | 96 ++- .../chunk/occlusion/VisibilityEncoding.java | 2 +- .../render/chunk/region/RenderRegion.java | 28 +- .../chunk/region/RenderRegionManager.java | 65 +- .../shader/ChunkShaderBindingPoints.java | 9 +- .../chunk/shader/ChunkShaderFogComponent.java | 8 +- .../chunk/translucent_sorting/SortType.java | 13 +- .../chunk/translucent_sorting/TQuad.java | 74 +- .../TranslucentGeometryCollector.java | 47 +- .../translucent_sorting/bsp_tree/BSPNode.java | 2 +- .../bsp_tree/InnerPartitionBSPNode.java | 4 +- .../data/AnyOrderData.java | 16 +- .../data/DynamicSorter.java | 2 +- .../data/DynamicTopoData.java | 1 - .../data/PresentSorter.java | 23 + .../data/SharedIndexSorter.java | 27 + .../translucent_sorting/data/SortData.java | 5 - .../translucent_sorting/data/Sorter.java | 17 +- .../data/StaticNormalRelativeData.java | 9 +- .../data/StaticSorter.java | 2 +- .../data/TopoGraphSorting.java | 99 ++- .../trigger/GeometryPlanes.java | 4 +- .../builder/ChunkMeshBufferBuilder.java | 52 +- .../format/impl/CompactChunkVertex.java | 7 +- .../impl/DefaultChunkMeshAttributes.java | 3 +- .../client/render/frapi/SodiumRenderer.java | 10 +- .../render/frapi/helper/ColorHelper.java | 70 +- .../render/frapi/helper/GeometryHelper.java | 5 +- .../render/frapi/helper/NormalHelper.java | 2 +- .../frapi/material/MaterialFinderImpl.java | 13 +- .../frapi/material/MaterialViewImpl.java | 28 +- .../frapi/material/RenderMaterialImpl.java | 9 + .../render/frapi/mesh/EncodingFormat.java | 3 +- .../render/frapi/mesh/MeshBuilderImpl.java | 84 -- .../client/render/frapi/mesh/MeshImpl.java | 45 +- .../render/frapi/mesh/MutableMeshImpl.java | 93 +++ .../frapi/mesh/MutableQuadViewImpl.java | 117 ++- .../render/frapi/mesh/QuadViewImpl.java | 13 +- .../render/AbstractBlockRenderContext.java | 91 +- .../frapi/render/AbstractRenderContext.java | 65 +- .../frapi/render/ItemRenderContext.java | 261 ++---- .../render/NonTerrainBlockRenderContext.java | 18 +- .../render/immediate/CloudRenderer.java | 789 +++++++++++------- .../immediate/model/BakedModelEncoder.java | 17 +- .../immediate/model/EntityRenderer.java | 44 +- .../render/immediate/model/ModelCuboid.java | 36 +- .../client/services/PlatformBlockAccess.java | 9 + .../mods/sodium/client/util/UInt32.java | 22 + .../util/WeightedRandomListExtension.java | 7 + .../client/util/collections/BitArray.java | 4 +- .../sodium/client/util/color/BoxBlur.java | 91 +- .../sodium/client/util/color/ColorSRGB.java | 5 +- .../mods/sodium/client/world/LevelSlice.java | 4 +- .../client/world/biome/BiomeColorMaps.java | 2 +- .../client/world/biome/LevelBiomeSlice.java | 4 +- .../client/world/biome/LevelColorCache.java | 42 +- .../world/cloned/ClonedChunkSection.java | 2 +- .../mods/sodium/mixin/SodiumMixinPlugin.java | 2 +- .../sodium/mixin/core/MinecraftMixin.java | 3 +- .../mods/sodium/mixin/core/WindowMixin.java | 25 +- .../core/model/colors/ItemColorsMixin.java | 33 - .../SheetedDecalTextureGeneratorMixin.java | 2 +- .../render/world/EntityRendererAccessor.java | 13 + .../core/render/world/LevelRendererMixin.java | 13 +- .../core/world/biome/ClientLevelMixin.java | 3 +- .../core/world/map/ClientChunkCacheMixin.java | 2 +- .../world/map/ClientPacketListenerMixin.java | 2 +- .../gui/hooks/console/GameRendererMixin.java | 10 +- .../gui/screen/LevelLoadingScreenMixin.java | 69 +- .../options/weather/LevelRendererMixin.java | 5 +- .../render/compositing/RenderTargetMixin.java | 26 - .../entity/cull/EntityRendererMixin.java | 5 +- .../render/frapi/BakedModelMixin.java | 24 +- .../render/frapi/ItemRendererAccessor.java | 9 +- .../render/frapi/ItemRendererMixin.java | 21 +- .../render/gui/font/BakedGlyphMixin.java | 58 +- .../gui/outlines/LevelRendererMixin.java | 3 +- .../render/immediate/DirectionMixin.java | 2 +- .../intrinsics/BufferBuilderMixin.java | 2 +- .../render/model/item/ItemRendererMixin.java | 38 +- .../particle/SingleQuadParticleMixin.java | 47 +- .../world/clouds/LevelRendererMixin.java | 47 +- .../render/world/sky/FogRendererMixin.java | 2 +- .../render/world/sky/LevelRendererMixin.java | 11 +- .../shader/uniform/ShaderInstanceMixin.java | 45 +- .../animations/tracking/GuiGraphicsMixin.java | 19 +- .../tracking/SpriteContentsTickerMixin.java | 2 +- .../SpriteContentsInterpolationMixin.java | 4 +- .../mipmaps/MipmapGeneratorMixin.java | 30 +- .../textures/mipmaps/SpriteContentsMixin.java | 13 +- .../context_creation/WindowMixin.java | 53 +- .../assets/minecraft/models/block/README.txt | 8 + .../assets/minecraft/models/block/beacon.json | 46 + .../minecraft/models/block/bell_floor.json | 43 + .../minecraft/models/block/brewing_stand.json | 57 ++ .../minecraft/models/block/cauldron.json | 214 +++++ .../minecraft/models/block/composter.json | 85 ++ .../minecraft/models/block/flower_pot.json | 88 ++ .../models/block/flower_pot_cross.json | 108 +++ .../minecraft/models/block/heavy_core.json | 39 + .../assets/minecraft/models/block/hopper.json | 107 +++ .../minecraft/models/block/hopper_side.json | 107 +++ .../minecraft/models/block/inner_stairs.json | 61 ++ .../minecraft/models/block/lightning_rod.json | 42 + .../models/block/lightning_rod_on.json | 33 + .../block/mangrove_propagule_hanging_0.json | 75 ++ .../block/mangrove_propagule_hanging_1.json | 85 ++ .../block/mangrove_propagule_hanging_2.json | 103 +++ .../block/mangrove_propagule_hanging_3.json | 103 +++ .../block/mangrove_propagule_hanging_4.json | 103 +++ .../minecraft/models/block/potted_bamboo.json | 110 +++ .../block/potted_mangrove_propagule.json | 129 +++ .../assets/minecraft/models/block/stairs.json | 62 ++ .../models/block/template_anvil.json | 60 ++ .../block/template_cake_with_candle.json | 51 ++ .../models/block/template_cauldron_full.json | 221 +++++ .../block/template_cauldron_level1.json | 221 +++++ .../block/template_cauldron_level2.json | 221 +++++ .../models/block/template_chorus_flower.json | 46 + .../block/template_four_turtle_eggs.json | 57 ++ .../block/template_hanging_lantern.json | 51 ++ .../models/block/template_item_frame.json | 85 ++ .../models/block/template_item_frame_map.json | 57 ++ .../block/template_potted_azalea_bush.json | 146 ++++ .../models/block/tinted_flower_pot_cross.json | 108 +++ .../assets/minecraft/shaders/core/clouds.json | 23 - .../resources/assets/sodium/lang/en_us.json | 1 + .../core => sodium/shaders}/clouds.fsh | 0 .../assets/sodium/shaders/clouds.json | 21 + .../core => sodium/shaders}/clouds.vsh | 0 .../sodium/shaders/include/chunk_vertex.glsl | 11 +- .../resources/sodium-common.accesswidener | 21 + ....mixins.json => sodium-common.mixins.json} | 2 +- .../checks/GraphicsDriverChecks.java | 63 ++ .../compatibility/checks/ModuleScanner.java | 37 +- .../checks/PostLaunchChecks.java | 14 +- .../compatibility/checks/PreLaunchChecks.java | 191 +---- .../environment/GLContextInfo.java | 20 - .../environment/GlContextInfo.java | 19 + .../compatibility/environment/OsUtils.java | 27 +- .../probe/GraphicsAdapterProbe.java | 75 +- .../probe/GraphicsAdapterVendor.java | 65 +- .../workarounds/Workarounds.java | 68 +- .../workarounds/intel/IntelWorkarounds.java | 59 ++ .../workarounds/nvidia/NvidiaWorkarounds.java | 155 +++- .../sodium/client/platform/MessageBox.java | 35 +- .../client/platform/NativeWindowHandle.java | 5 + .../client/platform/PlatformHelper.java | 29 + .../client/platform/windows/api/Gdi32.java | 18 +- .../platform/windows/api/d3dkmt/D3DKMT.java | 38 +- fabric/build.gradle.kts | 96 +-- .../mods/sodium/fabric/SodiumFabricMod.java | 4 +- .../mods/sodium/fabric/SodiumPreLaunch.java | 3 +- .../fabric/block/FabricBlockAccess.java | 5 + .../mixin/core/model/quad/BakedQuadMixin.java | 26 +- .../model/MultiPartBakedModelMixin.java | 10 +- .../model/WeightedBakedModelMixin.java | 35 +- .../model/WeightedRandomListMixin.java | 44 + .../model/block/ModelBlockRendererMixin.java | 4 +- .../features/world/biome/BiomeMixin.java | 2 +- fabric/src/main/resources/fabric.mod.json | 80 +- .../resources/sodium-fabric.accesswidener | 0 .../main/resources/sodium-fabric.mixins.json | 14 +- gradle/wrapper/gradle-wrapper.properties | 2 +- neoforge/build.gradle.kts | 180 ++-- .../core/model/quad/BakedQuadMixin.java | 42 +- .../model/MultiPartBakedModelMixin.java | 25 +- .../model/WeightedBakedModelMixin.java | 45 +- .../model/WeightedRandomListMixin.java | 44 + .../model/block/ModelBlockRendererMixin.java | 4 +- .../features/world/biome/BiomeMixin.java | 2 +- .../AbstractBlockRenderContextMixin.java | 37 + .../neoforge}/AuxiliaryLightManagerMixin.java | 2 +- .../neoforge}/ChunkRenderTypeSetAccessor.java | 2 +- .../platform/neoforge}/ClientHooksMixin.java | 2 +- .../platform/neoforge}/EntrypointMixin.java | 2 +- .../platform/neoforge}/LevelSliceMixin.java | 3 +- .../platform/neoforge}/ModelDataMixin.java | 2 +- .../neoforge}/ResourcePackLoaderMixin.java | 2 +- .../neoforge}/SimpleBakedModelAccessor.java | 2 +- .../neoforge/NeoForgeRuntimeInformation.java | 3 +- .../mods/sodium/neoforge/SodiumForgeMod.java | 32 +- .../neoforge/block/NeoForgeBlockAccess.java | 10 +- .../AbstractBlockRenderContextMixin.java | 28 - .../resources/META-INF/neoforge.mods.toml | 19 +- .../main/resources/sodium-forge.mixins.json | 28 - .../resources/sodium-neoforge.mixins.json | 28 + .../sodium/service/SodiumWorkarounds.java | 10 +- settings.gradle.kts | 1 + thirdparty/NOTICE.txt | 9 +- thirdparty/licenses/LICENSE-APACHE-2.0.txt | 202 +++++ 260 files changed, 7865 insertions(+), 3056 deletions(-) create mode 100644 buildSrc/build.gradle.kts create mode 100644 buildSrc/src/main/kotlin/BuildConfig.kt create mode 100644 buildSrc/src/main/kotlin/multiloader-base.gradle.kts create mode 100644 buildSrc/src/main/kotlin/multiloader-platform.gradle.kts delete mode 100644 common/src/desktop/java/net/caffeinemc/mods/sodium/desktop/utils/browse/GNOMEImpl.java delete mode 100644 common/src/desktop/java/net/caffeinemc/mods/sodium/desktop/utils/browse/KDEImpl.java delete mode 100644 common/src/main/java/net/caffeinemc/mods/sodium/client/gl/buffer/IndexedVertexData.java delete mode 100644 common/src/main/java/net/caffeinemc/mods/sodium/client/model/color/interop/ItemColorsExtension.java create mode 100644 common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/translucent_sorting/data/PresentSorter.java create mode 100644 common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/translucent_sorting/data/SharedIndexSorter.java delete mode 100644 common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/translucent_sorting/data/SortData.java delete mode 100644 common/src/main/java/net/caffeinemc/mods/sodium/client/render/frapi/mesh/MeshBuilderImpl.java create mode 100644 common/src/main/java/net/caffeinemc/mods/sodium/client/render/frapi/mesh/MutableMeshImpl.java create mode 100644 common/src/main/java/net/caffeinemc/mods/sodium/client/util/UInt32.java create mode 100644 common/src/main/java/net/caffeinemc/mods/sodium/client/util/WeightedRandomListExtension.java delete mode 100644 common/src/main/java/net/caffeinemc/mods/sodium/mixin/core/model/colors/ItemColorsMixin.java create mode 100644 common/src/main/java/net/caffeinemc/mods/sodium/mixin/core/render/world/EntityRendererAccessor.java create mode 100644 common/src/main/resources/assets/minecraft/models/block/README.txt create mode 100644 common/src/main/resources/assets/minecraft/models/block/beacon.json create mode 100644 common/src/main/resources/assets/minecraft/models/block/bell_floor.json create mode 100644 common/src/main/resources/assets/minecraft/models/block/brewing_stand.json create mode 100644 common/src/main/resources/assets/minecraft/models/block/cauldron.json create mode 100644 common/src/main/resources/assets/minecraft/models/block/composter.json create mode 100644 common/src/main/resources/assets/minecraft/models/block/flower_pot.json create mode 100644 common/src/main/resources/assets/minecraft/models/block/flower_pot_cross.json create mode 100644 common/src/main/resources/assets/minecraft/models/block/heavy_core.json create mode 100644 common/src/main/resources/assets/minecraft/models/block/hopper.json create mode 100644 common/src/main/resources/assets/minecraft/models/block/hopper_side.json create mode 100644 common/src/main/resources/assets/minecraft/models/block/inner_stairs.json create mode 100644 common/src/main/resources/assets/minecraft/models/block/lightning_rod.json create mode 100644 common/src/main/resources/assets/minecraft/models/block/lightning_rod_on.json create mode 100644 common/src/main/resources/assets/minecraft/models/block/mangrove_propagule_hanging_0.json create mode 100644 common/src/main/resources/assets/minecraft/models/block/mangrove_propagule_hanging_1.json create mode 100644 common/src/main/resources/assets/minecraft/models/block/mangrove_propagule_hanging_2.json create mode 100644 common/src/main/resources/assets/minecraft/models/block/mangrove_propagule_hanging_3.json create mode 100644 common/src/main/resources/assets/minecraft/models/block/mangrove_propagule_hanging_4.json create mode 100644 common/src/main/resources/assets/minecraft/models/block/potted_bamboo.json create mode 100644 common/src/main/resources/assets/minecraft/models/block/potted_mangrove_propagule.json create mode 100644 common/src/main/resources/assets/minecraft/models/block/stairs.json create mode 100644 common/src/main/resources/assets/minecraft/models/block/template_anvil.json create mode 100644 common/src/main/resources/assets/minecraft/models/block/template_cake_with_candle.json create mode 100644 common/src/main/resources/assets/minecraft/models/block/template_cauldron_full.json create mode 100644 common/src/main/resources/assets/minecraft/models/block/template_cauldron_level1.json create mode 100644 common/src/main/resources/assets/minecraft/models/block/template_cauldron_level2.json create mode 100644 common/src/main/resources/assets/minecraft/models/block/template_chorus_flower.json create mode 100644 common/src/main/resources/assets/minecraft/models/block/template_four_turtle_eggs.json create mode 100644 common/src/main/resources/assets/minecraft/models/block/template_hanging_lantern.json create mode 100644 common/src/main/resources/assets/minecraft/models/block/template_item_frame.json create mode 100644 common/src/main/resources/assets/minecraft/models/block/template_item_frame_map.json create mode 100644 common/src/main/resources/assets/minecraft/models/block/template_potted_azalea_bush.json create mode 100644 common/src/main/resources/assets/minecraft/models/block/tinted_flower_pot_cross.json delete mode 100644 common/src/main/resources/assets/minecraft/shaders/core/clouds.json rename common/src/main/resources/assets/{minecraft/shaders/core => sodium/shaders}/clouds.fsh (100%) create mode 100644 common/src/main/resources/assets/sodium/shaders/clouds.json rename common/src/main/resources/assets/{minecraft/shaders/core => sodium/shaders}/clouds.vsh (100%) create mode 100644 common/src/main/resources/sodium-common.accesswidener rename common/src/main/resources/{sodium.mixins.json => sodium-common.mixins.json} (98%) create mode 100644 common/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/checks/GraphicsDriverChecks.java delete mode 100644 common/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/environment/GLContextInfo.java create mode 100644 common/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/environment/GlContextInfo.java create mode 100644 common/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/workarounds/intel/IntelWorkarounds.java create mode 100644 common/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/NativeWindowHandle.java create mode 100644 common/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/PlatformHelper.java rename {neoforge/src/main/java/net/caffeinemc/mods/sodium/neoforge => fabric/src/main/java/net/caffeinemc/mods/sodium}/mixin/core/model/quad/BakedQuadMixin.java (85%) rename fabric/src/main/java/net/caffeinemc/mods/sodium/mixin/{fabric => }/features/model/MultiPartBakedModelMixin.java (86%) rename fabric/src/main/java/net/caffeinemc/mods/sodium/mixin/{fabric => }/features/model/WeightedBakedModelMixin.java (54%) create mode 100644 fabric/src/main/java/net/caffeinemc/mods/sodium/mixin/features/model/WeightedRandomListMixin.java rename fabric/src/main/java/net/caffeinemc/mods/sodium/mixin/{fabric => }/features/render/model/block/ModelBlockRendererMixin.java (97%) rename fabric/src/main/java/net/caffeinemc/mods/sodium/mixin/{fabric => }/features/world/biome/BiomeMixin.java (97%) rename common/src/main/resources/sodium.accesswidener => fabric/src/main/resources/sodium-fabric.accesswidener (100%) rename {fabric/src/main/java/net/caffeinemc/mods/sodium/mixin/fabric => neoforge/src/main/java/net/caffeinemc/mods/sodium/mixin}/core/model/quad/BakedQuadMixin.java (74%) rename neoforge/src/main/java/net/caffeinemc/mods/sodium/{neoforge => }/mixin/features/model/MultiPartBakedModelMixin.java (82%) rename neoforge/src/main/java/net/caffeinemc/mods/sodium/{neoforge => }/mixin/features/model/WeightedBakedModelMixin.java (51%) create mode 100644 neoforge/src/main/java/net/caffeinemc/mods/sodium/mixin/features/model/WeightedRandomListMixin.java rename neoforge/src/main/java/net/caffeinemc/mods/sodium/{neoforge => }/mixin/features/render/model/block/ModelBlockRendererMixin.java (97%) rename neoforge/src/main/java/net/caffeinemc/mods/sodium/{neoforge => }/mixin/features/world/biome/BiomeMixin.java (97%) create mode 100644 neoforge/src/main/java/net/caffeinemc/mods/sodium/mixin/platform/neoforge/AbstractBlockRenderContextMixin.java rename neoforge/src/main/java/net/caffeinemc/mods/sodium/{neoforge/mixin => mixin/platform/neoforge}/AuxiliaryLightManagerMixin.java (83%) rename neoforge/src/main/java/net/caffeinemc/mods/sodium/{neoforge/mixin => mixin/platform/neoforge}/ChunkRenderTypeSetAccessor.java (88%) rename neoforge/src/main/java/net/caffeinemc/mods/sodium/{neoforge/mixin => mixin/platform/neoforge}/ClientHooksMixin.java (92%) rename neoforge/src/main/java/net/caffeinemc/mods/sodium/{neoforge/mixin => mixin/platform/neoforge}/EntrypointMixin.java (94%) rename neoforge/src/main/java/net/caffeinemc/mods/sodium/{neoforge/mixin => mixin/platform/neoforge}/LevelSliceMixin.java (95%) rename neoforge/src/main/java/net/caffeinemc/mods/sodium/{neoforge/mixin => mixin/platform/neoforge}/ModelDataMixin.java (80%) rename neoforge/src/main/java/net/caffeinemc/mods/sodium/{neoforge/mixin => mixin/platform/neoforge}/ResourcePackLoaderMixin.java (96%) rename neoforge/src/main/java/net/caffeinemc/mods/sodium/{neoforge/mixin => mixin/platform/neoforge}/SimpleBakedModelAccessor.java (85%) delete mode 100644 neoforge/src/main/java/net/caffeinemc/mods/sodium/neoforge/mixin/AbstractBlockRenderContextMixin.java delete mode 100644 neoforge/src/main/resources/sodium-forge.mixins.json create mode 100644 neoforge/src/main/resources/sodium-neoforge.mixins.json create mode 100644 thirdparty/licenses/LICENSE-APACHE-2.0.txt diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index a80e3ed44b..d01ccf8aa3 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -17,10 +17,10 @@ body: - **Have you ensured that all of your mods (including Sodium) are up-to-date?** The latest version of Sodium can always be found [on Modrinth](https://modrinth.com/mod/sodium). - - **Have you read the [list of known driver incompatibilities](https://github.com/CaffeineMC/sodium-fabric/wiki/Driver-Compatibility)?** Most problems + - **Have you read the [list of known driver incompatibilities](https://github.com/CaffeineMC/sodium/wiki/Driver-Compatibility)?** Most problems (including "poor performance") are caused by out-of-date or incompatible graphics drivers. - - **Have you used the [search tool](https://github.com/CaffeineMC/sodium-fabric/issues) to check whether your issue + - **Have you used the [search tool](https://github.com/CaffeineMC/sodium/issues) to check whether your issue has already been reported?** If it has been, then consider adding more information to the existing issue instead. - **Have you determined the minimum set of instructions to reproduce the issue?** If your problem only occurs diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 1abfdc6fba..5097a676da 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -3,4 +3,4 @@ contact_links: - name: For help with other issues, join our Discord community url: https://caffeinemc.net/discord about: This is the best option for getting help with mod installation, performance issues, and any other support inquiries - # Copied from https://github.com/CaffeineMC/sodium-fabric#community + # Copied from https://github.com/CaffeineMC/sodium#community diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index 0c88dc3550..991291427e 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -17,7 +17,7 @@ body: - type: markdown attributes: value: >- - Make sure you have used the [search tool](https://github.com/CaffeineMC/sodium-fabric/issues) to see if a similar + Make sure you have used the [search tool](https://github.com/CaffeineMC/sodium/issues) to see if a similar request already exists. If we have previously closed a feature request, then please do not create another request. - type: textarea id: description diff --git a/.github/workflows/build-commit.yml b/.github/workflows/build-commit.yml index 7b4621a81b..b73702a561 100644 --- a/.github/workflows/build-commit.yml +++ b/.github/workflows/build-commit.yml @@ -38,4 +38,4 @@ jobs: uses: actions/upload-artifact@v4 with: name: sodium-artifacts-${{ steps.ref.outputs.branch }} - path: build/libs/*.jar \ No newline at end of file + path: build/mods/*.jar \ No newline at end of file diff --git a/.github/workflows/build-pull-request.yml b/.github/workflows/build-pull-request.yml index 1cf8d3e7d7..045dd53920 100644 --- a/.github/workflows/build-pull-request.yml +++ b/.github/workflows/build-pull-request.yml @@ -28,4 +28,4 @@ jobs: uses: actions/upload-artifact@v4 with: name: sodium-artifacts-${{ steps.ref.outputs.branch }} - path: build/libs/*.jar \ No newline at end of file + path: build/mods/*.jar \ No newline at end of file diff --git a/.github/workflows/build-release.yml b/.github/workflows/build-release.yml index ebf1d9ea13..ab751d49b9 100644 --- a/.github/workflows/build-release.yml +++ b/.github/workflows/build-release.yml @@ -28,6 +28,5 @@ jobs: - name: Upload assets to GitHub uses: AButler/upload-release-assets@v2.0 with: - # Filter built files to disregard -sources and -dev, and leave only the minecraft-compatible jars. - files: 'build/libs/*[0-9].jar;LICENSE' + files: 'build/mods/*.jar' repo-token: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/build-tag.yml b/.github/workflows/build-tag.yml index 4ac8946fd9..478c3b3d2b 100644 --- a/.github/workflows/build-tag.yml +++ b/.github/workflows/build-tag.yml @@ -36,4 +36,4 @@ jobs: uses: actions/upload-artifact@v4 with: name: sodium-artifacts-${{ steps.ref.outputs.branch }} - path: build/libs/*.jar \ No newline at end of file + path: build/mods/*.jar \ No newline at end of file diff --git a/README.md b/README.md index 995884d091..e8af77f8ca 100644 --- a/README.md +++ b/README.md @@ -5,25 +5,53 @@ Sodium is a powerful rendering engine and optimization mod for the Minecraft client which improves frame rates and reduces micro-stutter, while fixing many graphical issues in Minecraft. -### 📥 Installation +**This mod is the result of thousands of hours of development, and is made possible thanks to players like you.** If you +would like to show a token of your appreciation for my work, and help support the development of Sodium in the process, +then consider [buying me a coffee](https://caffeinemc.net/donate). -The latest version of Sodium can be downloaded from our official [Modrinth](https://modrinth.com/mod/sodium) and -[CurseForge](https://www.curseforge.com/minecraft/mc-mods/sodium) pages. + + +--- + +### 📥 Downloads + +#### Stable builds + +The latest stable release of Sodium can be downloaded from our official [Modrinth](https://modrinth.com/mod/sodium) and +[CurseForge](https://www.curseforge.com/minecraft/mc-mods/sodium) pages. + +#### Nightly builds (for developers) + +We also provide bleeding-edge builds ("nightlies") which are useful for testing the very latest changes before they're +packaged into a release. These builds are only provided for other mod developers and users with expert skills, and do +not come with any support or warranty. It is often the case they have issues and lack compatibility with other mods. + +The latest nightly build for each current branch of development can be downloaded below. + +- Minecraft 1.21.4 (latest): [Download nightly](https://nightly.link/CaffeineMC/sodium/workflows/build-commit/dev/sodium-artifacts-dev.zip) or [View all builds](https://github.com/CaffeineMC/sodium/actions/workflows/build-commit.yml?query=branch%3Adev) +- Minecraft 1.21.3: [Download nightly](https://nightly.link/CaffeineMC/sodium/workflows/build-commit/1.21.3%2Fstable/sodium-artifacts-1.21.3-stable.zip) or [View all builds](https://github.com/CaffeineMC/sodium/actions/workflows/build-commit.yml?query=branch%3A1.21.3%2Fstable) +- Minecraft 1.21.1: [Download nightly](https://nightly.link/CaffeineMC/sodium/workflows/build-commit/1.21.1%2Fstable/sodium-artifacts-1.21.1-stable.zip) or [View all builds](https://github.com/CaffeineMC/sodium/actions/workflows/build-commit.yml?query=branch%3A1.21.1%2Fstable) + +### 🖥️ Installation Since the release of Sodium 0.6.0, both the _Fabric_ and _NeoForge_ mod loaders are supported. We generally recommend that new users prefer to use the _Fabric_ mod loader, since it is more lightweight and stable (for the time being.) -For more information about downloading and installing the mod, please refer to our [Installation Guide](https://github.com/CaffeineMC/sodium-fabric/wiki/Installation). +For more information about downloading and installing the mod, please refer to our [Installation Guide](https://github.com/CaffeineMC/sodium/wiki/Installation). -### 🐛 Reporting Issues +### 🙇 Getting Help -You can report bugs and crashes by opening an issue on our [issue tracker](https://github.com/CaffeineMC/sodium-fabric/issues). -Before opening a new issue, use the search tool to make sure that your issue has not already been reported and ensure -that you have completely filled out the issue template. Issues that are duplicates or do not contain the necessary -information to triage and debug may be closed. +For technical support (including help with mod installation problems and game crashes), please use our +[official Discord server](https://caffeinemc.net/discord). + +### 📬 Reporting Issues + +If you do not need technical support and would like to report an issue (bug, crash, etc.) or otherwise request changes +(for mod compatibility, new features, etc.), then we encourage you to open an issue on the +[project issue tracker](https://github.com/CaffeineMC/sodium/issues). Please note that while the issue tracker is open to feature requests, development is primarily focused on -improving hardware compatibility, performance, and finishing any unimplemented features necessary for parity with +improving compatibility, performance, and finishing any unimplemented features necessary for parity with the vanilla renderer. ### 💬 Join the Community @@ -36,15 +64,16 @@ We have an [official Discord community](https://caffeinemc.net/discord) for all ## ✅ Hardware Compatibility -We only provide support for graphics cards which have up-to-date drivers for OpenGL 4.6. Most graphics cards which have -been released since year 2010 are supported, such as the... +We only provide official support for graphics cards which have up-to-date drivers that are compatible with OpenGL 4.5 +or newer. Most graphics cards released in the past 12 years will meet these requirements, including the following: - AMD Radeon HD 7000 Series (GCN 1) or newer - NVIDIA GeForce 400 Series (Fermi) or newer - Intel HD Graphics 500 Series (Skylake) or newer -In some cases, older graphics cards may also work (so long as they have up-to-date drivers which have support for -OpenGL 3.3), but they are not officially supported, and may not be compatible with future versions of Sodium. +Nearly all graphics cards that are already compatible with Minecraft (which requires OpenGL 3.3) should also work +with Sodium. But our team cannot ensure compatibility or provide support for older graphics cards, and they may +not work with future versions of Sodium. #### OpenGL Compatibility Layers @@ -52,29 +81,23 @@ Devices which need to use OpenGL translation layers (such as GL4ES, ANGLE, etc) not work with Sodium. These translation layers do not implement required functionality and they suffer from underlying driver bugs which cannot be worked around. -## 🛠️ Developer Guide - -### Building from sources +## 🛠️ Building from sources -Sodium uses a typical Gradle project structure and can be compiled by simply running the default `build` task. The build -artifacts (typical mod binaries, and their sources) can be found in the `build/libs` directory. +Sodium uses the [Gradle build tool](https://gradle.org/) and can be built with the `gradle build` command. The build +artifacts (production binaries and their source bundles) can be found in the `build/mods` directory. -#### Requirements +The [Gradle wrapper](https://docs.gradle.org/current/userguide/gradle_wrapper.html#sec:using_wrapper) is provided for ease of use and will automatically download and install the +appropriate version of Gradle for the project build. To use the Gradle wrapper, substitute `gradle` in build commands +with `./gradlew.bat` (Windows) or `./gradlew` (macOS and Linux). -We recommend using a package manager (such as [SDKMAN](https://sdkman.io/)) to manage toolchain dependencies and keep -them up to date. For many Linux distributions, these dependencies will be standard packages in your software -repositories. +### Build Requirements - OpenJDK 21 - - We recommend using the [Eclipse Temurin](https://adoptium.net/) distribution, as it's known to be high quality - and to work without issues. -- Gradle 8.6.x (optional) - - The [Gradle wrapper](https://docs.gradle.org/current/userguide/gradle_wrapper.html#sec:using_wrapper) is provided - in this repository can be used instead of installing a suitable version of Gradle yourself. However, if you are - building many projects, you may prefer to install it yourself through a suitable package manager as to save disk - space and to avoid many different Gradle daemons sitting around in memory. + - We recommend using the [Eclipse Temurin](https://adoptium.net/) distribution as it's regularly tested by our developers and known + to be of high quality. +- Gradle 8.10.x - Typically, newer versions of Gradle will work without issues, but the build script is only tested against the - version specified by the wrapper script. + version used by the [wrapper script](/gradle/wrapper/gradle-wrapper.properties). ## 📜 License diff --git a/build.gradle.kts b/build.gradle.kts index d1a181b9da..e69de29bb2 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,80 +0,0 @@ -plugins { - id("java") - id("fabric-loom") version ("1.7.3") apply (false) -} - -val MINECRAFT_VERSION by extra { "1.21.1" } -val NEOFORGE_VERSION by extra { "21.1.46" } -val FABRIC_LOADER_VERSION by extra { "0.16.4" } -val FABRIC_API_VERSION by extra { "0.103.0+1.21.1" } - -// This value can be set to null to disable Parchment. -// TODO: Re-add Parchment -val PARCHMENT_VERSION by extra { null } - -// https://semver.org/ -val MOD_VERSION by extra { "0.6.0-beta.2" } - -allprojects { - apply(plugin = "java") - apply(plugin = "maven-publish") -} - -tasks.withType { - options.encoding = "UTF-8" -} - -tasks.jar { - enabled = false -} - -subprojects { - apply(plugin = "maven-publish") - - java.toolchain.languageVersion = JavaLanguageVersion.of(21) - - - fun createVersionString(): String { - val builder = StringBuilder() - - val isReleaseBuild = project.hasProperty("build.release") - val buildId = System.getenv("GITHUB_RUN_NUMBER") - - if (isReleaseBuild) { - builder.append(MOD_VERSION) - } else { - builder.append(MOD_VERSION.substringBefore('-')) - builder.append("-snapshot") - } - - builder.append("+mc").append(MINECRAFT_VERSION) - - if (!isReleaseBuild) { - if (buildId != null) { - builder.append("-build.${buildId}") - } else { - builder.append("-local") - } - } - - return builder.toString() - } - - tasks.processResources { - filesMatching("META-INF/neoforge.mods.toml") { - expand(mapOf("version" to createVersionString())) - } - } - - version = createVersionString() - group = "net.caffeinemc.mods" - - tasks.withType { - options.encoding = "UTF-8" - options.release.set(21) - } - - tasks.withType().configureEach { - enabled = false - } -} diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts new file mode 100644 index 0000000000..0a2aabdef4 --- /dev/null +++ b/buildSrc/build.gradle.kts @@ -0,0 +1,8 @@ +plugins { + `kotlin-dsl` +} + +repositories { + mavenCentral() + gradlePluginPortal() +} diff --git a/buildSrc/src/main/kotlin/BuildConfig.kt b/buildSrc/src/main/kotlin/BuildConfig.kt new file mode 100644 index 0000000000..61c7fe177e --- /dev/null +++ b/buildSrc/src/main/kotlin/BuildConfig.kt @@ -0,0 +1,41 @@ + import org.gradle.api.Project + +object BuildConfig { + val MINECRAFT_VERSION: String = "1.21.4" + val NEOFORGE_VERSION: String = "21.4.3-beta" + val FABRIC_LOADER_VERSION: String = "0.16.9" + val FABRIC_API_VERSION: String = "0.110.5+1.21.4" + + // This value can be set to null to disable Parchment. + // TODO: Re-add Parchment + val PARCHMENT_VERSION: String? = null + + // https://semver.org/ + var MOD_VERSION: String = "0.6.3" + + fun createVersionString(project: Project): String { + val builder = StringBuilder() + + val isReleaseBuild = project.hasProperty("build.release") + val buildId = System.getenv("GITHUB_RUN_NUMBER") + + if (isReleaseBuild) { + builder.append(MOD_VERSION) + } else { + builder.append(MOD_VERSION.substringBefore('-')) + builder.append("-snapshot") + } + + builder.append("+mc").append(MINECRAFT_VERSION) + + if (!isReleaseBuild) { + if (buildId != null) { + builder.append("-build.${buildId}") + } else { + builder.append("-local") + } + } + + return builder.toString() + } +} \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/multiloader-base.gradle.kts b/buildSrc/src/main/kotlin/multiloader-base.gradle.kts new file mode 100644 index 0000000000..c23e78a4c6 --- /dev/null +++ b/buildSrc/src/main/kotlin/multiloader-base.gradle.kts @@ -0,0 +1,44 @@ +plugins { + id("java-library") + id("idea") +} + +group = "net.caffeinemc" +version = BuildConfig.createVersionString(project) + +java.toolchain.languageVersion = JavaLanguageVersion.of(21) + +tasks.withType { + options.encoding = "UTF-8" + options.release.set(21) +} + +tasks.withType().configureEach { + enabled = false +} + +repositories { + exclusiveContent { + forRepository { + maven { + name = "Modrinth" + url = uri("https://api.modrinth.com/maven") + } + } + filter { + includeGroup("maven.modrinth") + } + } + + exclusiveContent { + forRepository { + maven { + name = "Parchment" + url = uri("https://maven.parchmentmc.org") + } + } + filter { + includeGroup("org.parchmentmc.data") + } + } +} diff --git a/buildSrc/src/main/kotlin/multiloader-platform.gradle.kts b/buildSrc/src/main/kotlin/multiloader-platform.gradle.kts new file mode 100644 index 0000000000..006f9f3500 --- /dev/null +++ b/buildSrc/src/main/kotlin/multiloader-platform.gradle.kts @@ -0,0 +1,43 @@ +plugins { + id("multiloader-base") + id("maven-publish") +} + +val configurationDesktopIntegrationJava: Configuration = configurations.create("commonDesktopIntegration") { + isCanBeResolved = true +} + +dependencies { + configurationDesktopIntegrationJava(project(path = ":common", configuration = "commonDesktopJava")) +} + +tasks { + processResources { + inputs.property("version", version) + + filesMatching(listOf("fabric.mod.json", "META-INF/neoforge.mods.toml")) { + expand(mapOf("version" to version)) + } + } + + jar { + duplicatesStrategy = DuplicatesStrategy.FAIL + from(rootDir.resolve("LICENSE.md")) + + // Entry-point for desktop integration when the file is executed directly + from(configurationDesktopIntegrationJava) + manifest.attributes["Main-Class"] = "net.caffeinemc.mods.sodium.desktop.LaunchWarn" + } +} + +publishing { + publications { + create("maven") { + groupId = project.group as String + artifactId = project.name as String + version = version + + from(components["java"]) + } + } +} diff --git a/common/build.gradle.kts b/common/build.gradle.kts index aba0458673..6595341f1e 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -1,48 +1,22 @@ plugins { - id("java") - id("idea") - id("fabric-loom") version ("1.7.3") -} + id("multiloader-base") + id("java-library") -repositories { - maven("https://maven.parchmentmc.org/") + id("fabric-loom") version ("1.8.9") } -val MINECRAFT_VERSION: String by rootProject.extra -val PARCHMENT_VERSION: String? by rootProject.extra -val FABRIC_LOADER_VERSION: String by rootProject.extra -val FABRIC_API_VERSION: String by rootProject.extra - -dependencies { - minecraft(group = "com.mojang", name = "minecraft", version = MINECRAFT_VERSION) - mappings(loom.layered() { - officialMojangMappings() - if (PARCHMENT_VERSION != null) { - parchment("org.parchmentmc.data:parchment-${MINECRAFT_VERSION}:${PARCHMENT_VERSION}@zip") - } - }) - compileOnly("io.github.llamalad7:mixinextras-common:0.3.5") - annotationProcessor("io.github.llamalad7:mixinextras-common:0.3.5") - compileOnly("net.fabricmc:sponge-mixin:0.13.2+mixin.0.8.5") - - fun addDependentFabricModule(name: String) { - val module = fabricApi.module(name, FABRIC_API_VERSION) - modCompileOnly(module) - } - - addDependentFabricModule("fabric-api-base") - addDependentFabricModule("fabric-block-view-api-v2") - addDependentFabricModule("fabric-renderer-api-v1") - addDependentFabricModule("fabric-rendering-data-attachment-v1") +base { + archivesName = "sodium-common" +} - modCompileOnly("net.fabricmc.fabric-api:fabric-renderer-api-v1:3.2.9+1172e897d7") +val configurationPreLaunch = configurations.create("preLaunchDeps") { + isCanBeResolved = true } sourceSets { val main = getByName("main") val api = create("api") val workarounds = create("workarounds") - val desktop = create("desktop") api.apply { java { @@ -52,13 +26,7 @@ sourceSets { workarounds.apply { java { - compileClasspath += main.compileClasspath - } - } - - desktop.apply { - java { - srcDir("src/desktop/java") + compileClasspath += configurationPreLaunch } } @@ -66,53 +34,94 @@ sourceSets { java { compileClasspath += api.output compileClasspath += workarounds.output - runtimeClasspath += api.output } } -} -loom { - mixin { - defaultRefmapName = "sodium.refmap.json" - } + create("desktop") +} - accessWidenerPath = file("src/main/resources/sodium.accesswidener") +dependencies { + minecraft(group = "com.mojang", name = "minecraft", version = BuildConfig.MINECRAFT_VERSION) + mappings(loom.layered { + officialMojangMappings() - mods { - val main by creating { // to match the default mod generated for Forge - sourceSet("api") - sourceSet("desktop") - sourceSet("main") + if (BuildConfig.PARCHMENT_VERSION != null) { + parchment("org.parchmentmc.data:parchment-${BuildConfig.MINECRAFT_VERSION}:${BuildConfig.PARCHMENT_VERSION}@zip") } + }) + + compileOnly("io.github.llamalad7:mixinextras-common:0.3.5") + annotationProcessor("io.github.llamalad7:mixinextras-common:0.3.5") + + compileOnly("net.fabricmc:sponge-mixin:0.13.2+mixin.0.8.5") + compileOnly("net.fabricmc:fabric-loader:${BuildConfig.FABRIC_LOADER_VERSION}") + + fun addDependentFabricModule(name: String) { + modCompileOnly(fabricApi.module(name, BuildConfig.FABRIC_API_VERSION)) } + + addDependentFabricModule("fabric-api-base") + addDependentFabricModule("fabric-block-view-api-v2") + addDependentFabricModule("fabric-renderer-api-v1") + addDependentFabricModule("fabric-rendering-data-attachment-v1") + + // We need to be careful during pre-launch that we don't touch any Minecraft classes, since other mods + // will not yet have an opportunity to apply transformations. + configurationPreLaunch("org.lwjgl:lwjgl:3.3.3") + configurationPreLaunch("org.lwjgl:lwjgl-opengl:3.3.3") + configurationPreLaunch("net.java.dev.jna:jna:5.14.0") + configurationPreLaunch("net.java.dev.jna:jna-platform:5.14.0") + configurationPreLaunch("org.slf4j:slf4j-api:2.0.9") + configurationPreLaunch("org.jetbrains:annotations:25.0.0") } -tasks { - getByName("compileDesktopJava") { - sourceCompatibility = JavaVersion.VERSION_1_8.toString() - targetCompatibility = JavaVersion.VERSION_1_8.toString() +loom { + accessWidenerPath = file("src/main/resources/sodium-common.accesswidener") + + mixin { + useLegacyMixinAp = false } +} - jar { - from(rootDir.resolve("LICENSE.md")) +fun exportSourceSetJava(name: String, sourceSet: SourceSet) { + val configuration = configurations.create("${name}Java") { + isCanBeResolved = true + isCanBeConsumed = true + } - val api = sourceSets.getByName("api") - from(api.output.classesDirs) - from(api.output.resourcesDir) + val compileTask = tasks.getByName(sourceSet.compileJavaTaskName) + artifacts.add(configuration.name, compileTask.destinationDirectory) { + builtBy(compileTask) + } +} - val workarounds = sourceSets.getByName("workarounds") - from(workarounds.output.classesDirs) - from(workarounds.output.resourcesDir) +fun exportSourceSetResources(name: String, sourceSet: SourceSet) { + val configuration = configurations.create("${name}Resources") { + isCanBeResolved = true + isCanBeConsumed = true + } - val desktop = sourceSets.getByName("desktop") - from(desktop.output.classesDirs) - from(desktop.output.resourcesDir) + val compileTask = tasks.getByName(sourceSet.processResourcesTaskName) + compileTask.apply { + exclude("**/README.txt") + exclude("/*.accesswidener") + } - manifest.attributes["Main-Class"] = "net.caffeinemc.mods.sodium.desktop.LaunchWarn" + artifacts.add(configuration.name, compileTask.destinationDir) { + builtBy(compileTask) } } -// This trick hides common tasks in the IDEA list. -tasks.configureEach { - group = null -} \ No newline at end of file +// Exports the compiled output of the source set to the named configuration. +fun exportSourceSet(name: String, sourceSet: SourceSet) { + exportSourceSetJava(name, sourceSet) + exportSourceSetResources(name, sourceSet) +} + +exportSourceSet("commonMain", sourceSets["main"]) +exportSourceSet("commonApi", sourceSets["api"]) +exportSourceSet("commonEarlyLaunch", sourceSets["workarounds"]) +exportSourceSet("commonDesktop", sourceSets["desktop"]) + +tasks.jar { enabled = false } +tasks.remapJar { enabled = false } \ No newline at end of file diff --git a/common/src/api/java/net/caffeinemc/mods/sodium/api/util/ColorABGR.java b/common/src/api/java/net/caffeinemc/mods/sodium/api/util/ColorABGR.java index a7e86c52c9..6869755ae0 100644 --- a/common/src/api/java/net/caffeinemc/mods/sodium/api/util/ColorABGR.java +++ b/common/src/api/java/net/caffeinemc/mods/sodium/api/util/ColorABGR.java @@ -1,5 +1,7 @@ package net.caffeinemc.mods.sodium.api.util; +import java.nio.ByteOrder; + /** * Provides some utilities for packing and unpacking color components from packed integer colors in ABGR format, which * is used by OpenGL for color vectors. @@ -9,11 +11,16 @@ * | Alpha | Blue | Green | Red | */ public class ColorABGR implements ColorU8 { - private static final int RED_COMPONENT_OFFSET = 0; + private static final int RED_COMPONENT_OFFSET = 0; private static final int GREEN_COMPONENT_OFFSET = 8; - private static final int BLUE_COMPONENT_OFFSET = 16; + private static final int BLUE_COMPONENT_OFFSET = 16; private static final int ALPHA_COMPONENT_OFFSET = 24; + private static final int RED_COMPONENT_MASK = COMPONENT_MASK << RED_COMPONENT_OFFSET; + private static final int GREEN_COMPONENT_MASK = COMPONENT_MASK << GREEN_COMPONENT_OFFSET; + private static final int BLUE_COMPONENT_MASK = COMPONENT_MASK << BLUE_COMPONENT_OFFSET; + private static final int ALPHA_COMPONENT_MASK = COMPONENT_MASK << ALPHA_COMPONENT_OFFSET; + /** * Packs the specified color components into ABGR format. The alpha component is fully opaque. * @param r The red component of the color @@ -98,4 +105,51 @@ public static int unpackBlue(int color) { public static int unpackAlpha(int color) { return (color >> ALPHA_COMPONENT_OFFSET) & COMPONENT_MASK; } + + /** + * Multiplies the RGB components of the color with the provided factor. The alpha component is not modified. + * + * @param color The packed 32-bit ABGR color to be multiplied + * @param factor The darkening factor (in the range of 0..255) to multiply with + */ + public static int mulRGB(int color, int factor) { + return (ColorMixer.mul(color, factor) & ~ALPHA_COMPONENT_MASK) | (color & ALPHA_COMPONENT_MASK); + } + + /** + * See {@link #mulRGB(int, int)}. This function is identical, but it accepts a float in [0.0, 1.0] instead, which + * is then mapped to [0, 255]. + * + * @param color The packed 32-bit ABGR color to be multiplied + * @param factor The darkening factor (in the range of 0.0..1.0) to multiply with + */ + public static int mulRGB(int color, float factor) { + return mulRGB(color, ColorU8.normalizedFloatToByte(factor)); + } + + private static final boolean BIG_ENDIAN = ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN; + + /** + * Shuffles the ordering of the ABGR color so that it can then be written out to memory in the platform's native + * byte ordering. This should be used when writing the packed color to memory (in native-order) as a 32-bit word. + */ + public static int fromNativeByteOrder(int color) { + if (BIG_ENDIAN) { + return Integer.reverseBytes(color); + } else { + return color; + } + } + + /** + * Shuffles the ordering of the ABGR color from the platform's native byte ordering. This should be used when reading + * the packed color from memory (in native-order) as a 32-bit word. + */ + public static int toNativeByteOrder(int color) { + if (BIG_ENDIAN) { + return Integer.reverseBytes(color); + } else { + return color; + } + } } diff --git a/common/src/api/java/net/caffeinemc/mods/sodium/api/util/ColorARGB.java b/common/src/api/java/net/caffeinemc/mods/sodium/api/util/ColorARGB.java index f93880555e..2d7899111f 100644 --- a/common/src/api/java/net/caffeinemc/mods/sodium/api/util/ColorARGB.java +++ b/common/src/api/java/net/caffeinemc/mods/sodium/api/util/ColorARGB.java @@ -15,6 +15,11 @@ public class ColorARGB implements ColorU8 { private static final int GREEN_COMPONENT_OFFSET = 8; private static final int BLUE_COMPONENT_OFFSET = 0; + private static final int RED_COMPONENT_MASK = COMPONENT_MASK << RED_COMPONENT_OFFSET; + private static final int GREEN_COMPONENT_MASK = COMPONENT_MASK << GREEN_COMPONENT_OFFSET; + private static final int BLUE_COMPONENT_MASK = COMPONENT_MASK << BLUE_COMPONENT_OFFSET; + private static final int ALPHA_COMPONENT_MASK = COMPONENT_MASK << ALPHA_COMPONENT_OFFSET; + /** * Packs the specified color components into big-endian format for consumption by OpenGL. * @param r The red component of the color @@ -73,23 +78,40 @@ public static int unpackBlue(int color) { } /** - * Re-packs the ARGB color into an ABGR color with the specified alpha component. + * Swizzles from ARGB format into ABGR format, replacing the alpha component with {@param alpha}. */ - public static int toABGR(int color, float alpha) { - return Integer.reverseBytes(color << 8 | ColorU8.normalizedFloatToByte(alpha)); + public static int toABGR(int color, int alpha) { + // shl(ARGB, 8) -> RGB0 + // or(RGB0, 000A) -> RGBA + return Integer.reverseBytes(color << 8 | alpha); } /** - * Re-packs the ARGB color into a aBGR color with the specified alpha component. + * Swizzles from ARGB format into ABGR format, replacing the alpha component with {@param alpha}. The alpha + * component is mapped from [0.0, 1.0] to [0, 255]. */ - public static int toABGR(int color, int alpha) { - return Integer.reverseBytes(color << 8 | alpha); + public static int toABGR(int color, float alpha) { + return toABGR(color, ColorU8.normalizedFloatToByte(alpha)); } + /** + * Swizzles from ARGB format into ABGR format. + */ public static int toABGR(int color) { + // rotateLeft(ARGB, 8) -> RGBA + // reverseBytes(RGBA) -> ABGR return Integer.reverseBytes(Integer.rotateLeft(color, 8)); } + /** + * Swizzles from ABGR format into ARGB format. + */ + public static int fromABGR(int color) { + // reverseBytes(ABGR) -> RGBA + // rotateRight(RGBA, 8) -> ARGB + return Integer.rotateRight(Integer.reverseBytes(color), 8); + } + /** * Packs the specified color components into ARGB format. * @param rgb The red/green/blue component of the color @@ -98,4 +120,25 @@ public static int toABGR(int color) { public static int withAlpha(int rgb, int alpha) { return (alpha << ALPHA_COMPONENT_OFFSET) | (rgb & ~(COMPONENT_MASK << ALPHA_COMPONENT_OFFSET)); } + + /** + * Multiplies the RGB components of the color with the provided factor. The alpha component is not modified. + * + * @param color The packed 32-bit ABGR color to be multiplied + * @param factor The darkening factor (in the range of 0..255) to multiply with + */ + public static int mulRGB(int color, int factor) { + return (ColorMixer.mul(color, factor) & ~ALPHA_COMPONENT_MASK) | (color & ALPHA_COMPONENT_MASK); + } + + /** + * See {@link #mulRGB(int, int)}. This function is identical, but it accepts a float in [0.0, 1.0] instead, which + * is then mapped to [0, 255]. + * + * @param color The packed 32-bit ABGR color to be multiplied + * @param factor The darkening factor (in the range of 0.0..1.0) to multiply with + */ + public static int mulRGB(int color, float factor) { + return mulRGB(color, ColorU8.normalizedFloatToByte(factor)); + } } diff --git a/common/src/api/java/net/caffeinemc/mods/sodium/api/util/ColorMixer.java b/common/src/api/java/net/caffeinemc/mods/sodium/api/util/ColorMixer.java index a1934c8b44..220eae17ca 100644 --- a/common/src/api/java/net/caffeinemc/mods/sodium/api/util/ColorMixer.java +++ b/common/src/api/java/net/caffeinemc/mods/sodium/api/util/ColorMixer.java @@ -1,77 +1,146 @@ package net.caffeinemc.mods.sodium.api.util; +import org.jetbrains.annotations.ApiStatus; + +/** + * A collection of optimized color mixing functions which directly operate on packed color values. These functions are + * agnostic to the ordering of color channels, and the output value will always use the same channel ordering as + * the input values. + */ public class ColorMixer { - private static final int CHANNEL_MASK = 0x00FF00FF; + /** + *

Linearly interpolate between the {@param start} and {@param end} points, represented as packed unsigned 8-bit + * values within a 32-bit integer. The result is computed as

(start * weight) + (end * (255 - weight))
+ * using fixed-point arithmetic and round-to-nearest behavior.

+ * + *

The results are undefined if {@param weight} is not within the interval [0, 255].

+ * + *

If {@param start} and {@param end} are the same value, the result of this function will always be that value, + * regardless of {@param weight}.

+ * + * @param start The value at the start of the range to interpolate + * @param end The value at the end of the range to interpolate + * @param weight The weight value used to interpolate between color values (in 0..255 range) + * @return The color that was interpolated between the start and end points + */ + public static int mix(int start, int end, int weight) { + // De-interleave the 8-bit component lanes into high and low halves for each point. + // Multiply the start point by alpha, and the end point by 1-alpha, to produce Q8.8 fixed-point intermediates. + // Add the Q8.8 fixed-point intermediaries together to obtain the mixed values. + final long hi = ((start & 0x00FF00FFL) * weight) + ((end & 0x00FF00FFL) * (ColorU8.COMPONENT_MASK - weight)); + final long lo = ((start & 0xFF00FF00L) * weight) + ((end & 0xFF00FF00L) * (ColorU8.COMPONENT_MASK - weight)); + + // Round the fixed-point values to the nearest integer, and interleave the high and low halves to + // produce the final packed result. + final long result = + (((hi + 0x00FF00FFL) >>> 8) & 0x00FF00FFL) | + (((lo + 0xFF00FF00L) >>> 8) & 0xFF00FF00L); + + return (int) result; + } /** - * Mixes a 32-bit color (with packed 8-bit components) into another using the given ratio. This is equivalent to - * (color1 * ratio) + (color2 * (1.0 - ratio)) but uses bitwise trickery for maximum performance. - *

- * The order of the channels within the packed color does not matter, and the output color will always - * have the same ordering as the input colors. + *

This function is identical to {@link ColorMixer#mix(int, int, int)}, but {@param weight} is a normalized + * floating-point value within the interval of [0.0, 1.0].

+ * + *

The results are undefined if {@param weight} is not within the interval [0.0, 1.0].

* - * @param aColor The color to mix towards - * @param bColor The color to mix away from - * @param ratio The percentage (in 0.0..1.0 range) to mix the first color into the second color - * @return The mixed color in packed 32-bit format + * @param start The start of the range to interpolate + * @param end The end of the range to interpolate + * @param weight The weight value used to interpolate between color values (in 0.0..1.0 range) + * @return The color that was interpolated between the start and end points */ - public static int mix(int aColor, int bColor, float ratio) { - int aRatio = (int) (256 * ratio); // int(ratio) - int bRatio = 256 - aRatio; // int(1.0 - ratio) - - // Mask off and shift two components from each color into a packed vector of 16-bit components, where the - // high 8 bits are all zeroes. - int a1 = (aColor >> 0) & CHANNEL_MASK; - int b1 = (bColor >> 0) & CHANNEL_MASK; - int a2 = (aColor >> 8) & CHANNEL_MASK; - int b2 = (bColor >> 8) & CHANNEL_MASK; - - // Multiply the packed 16-bit components against each mix factor, and add the components of each color - // to produce the mixed result. This will never overflow since both 16-bit integers are in 0..255 range. - - // Then, shift the high 8 bits of each packed 16-bit component into the low 8 bits, and mask off the high bits of - // each 16-bit component to produce a vector of packed 8-bit components, where every other component is empty. - int c1 = (((a1 * aRatio) + (b1 * bRatio)) >> 8) & CHANNEL_MASK; - int c2 = (((a2 * aRatio) + (b2 * bRatio)) >> 8) & CHANNEL_MASK; - - // Join the color components into the original order - return ((c1 << 0) | (c2 << 8)); + public static int mix(int start, int end, float weight) { + return mix(start, end, ColorU8.normalizedFloatToByte(weight)); } /** - * Multiplies the 32-bit colors (with packed 8-bit components) together. - *

- * The order of the channels within the packed color does not matter, and the output color will always - * have the same ordering as the input colors. + *

Performs bi-linear interpolation on a 2x2 matrix of color values to derive the point (x, y). This is more + * efficient than chaining {@link #mul(int, float)} calls.

* - * @param a The first color to multiply - * @param b The second color to multiply - * @return The multiplied color in packed 32-bit format + *

The results are undefined if {@param x} and {@param y} are not within the interval [0.0, 1.0].

+ * + * @param m00 The packed color value for (0, 0) + * @param m01 The packed color value for (0, 1) + * @param m10 The packed color value for (1, 0) + * @param m11 The packed color value for (1, 1) + * @param x The amount to interpolate between x=0 and x=1 + * @param y The amount to interpolate between y=0 and y=1 + * @return The interpolated color value */ - public static int mul(int a, int b) { - // Take each 8-bit component pair, multiply them together to create intermediate 16-bit integers, - // and then shift the high half of each 16-bit integer into 8-bit integers. - int c0 = (((a >> 0) & 0xFF) * ((b >> 0) & 0xFF)) >> 8; - int c1 = (((a >> 8) & 0xFF) * ((b >> 8) & 0xFF)) >> 8; - int c2 = (((a >> 16) & 0xFF) * ((b >> 16) & 0xFF)) >> 8; - int c3 = (((a >> 24) & 0xFF) * ((b >> 24) & 0xFF)) >> 8; - - // Pack the components - return (c0 << 0) | (c1 << 8) | (c2 << 16) | (c3 << 24); + @ApiStatus.Experimental + public static int mix2d(int m00, int m01, int m10, int m11, float x, float y) { + // The weights for each row and column in the matrix + int x1 = ColorU8.normalizedFloatToByte(x), x0 = 255 - x1; + int y1 = ColorU8.normalizedFloatToByte(y), y0 = 255 - y1; + + // Blend across the X-axis + // (M00 * X0) + (M10 * X1) + long row0a = ((((m00 & 0x00FF00FFL) * x0) + (((m10 & 0x00FF00FFL) * x1)) + 0x00FF00FFL) >>> 8) & 0x00FF00FFL; + long row0b = ((((m00 & 0xFF00FF00L) * x0) + (((m10 & 0xFF00FF00L) * x1)) + 0xFF00FF00L) >>> 8) & 0xFF00FF00L; + + // (M10 * X0) + (M11 * X1) + long row1a = ((((m01 & 0x00FF00FFL) * x0) + (((m11 & 0x00FF00FFL) * x1)) + 0x00FF00FFL) >>> 8) & 0x00FF00FFL; + long row1b = ((((m01 & 0xFF00FF00L) * x0) + (((m11 & 0xFF00FF00L) * x1)) + 0xFF00FF00L) >>> 8) & 0xFF00FF00L; + + // Blend across the Y-axis + // (ROW0 * Y0) + (ROW1 * Y1) + long result = ((((row0a * y0) + ((row1a * y1)) + 0x00FF00FFL) >>> 8) & 0x00FF00FFL) | + ((((row0b * y0) + ((row1b * y1)) + 0xFF00FF00L) >>> 8) & 0xFF00FF00L); + + return (int) result; } /** - * Multiplies the 32-bit colors with one component + *

Multiplies the packed 8-bit values component-wise to produce 16-bit intermediaries, and then round to the + * nearest 8-bit representation (similar to floating-point.)

+ * + * @param color0 The first color to multiply + * @param color1 The second color to multiply + * @return The product of the two colors + */ + public static int mulComponentWise(int color0, int color1) { + int comp0 = ((((color0 >>> 0) & 0xFF) * ((color1 >>> 0) & 0xFF)) + 0xFF) >>> 8; + int comp1 = ((((color0 >>> 8) & 0xFF) * ((color1 >>> 8) & 0xFF)) + 0xFF) >>> 8; + int comp2 = ((((color0 >>> 16) & 0xFF) * ((color1 >>> 16) & 0xFF)) + 0xFF) >>> 8; + int comp3 = ((((color0 >>> 24) & 0xFF) * ((color1 >>> 24) & 0xFF)) + 0xFF) >>> 8; + + return (comp0 << 0) | (comp1 << 8) | (comp2 << 16) | (comp3 << 24); + } + + /** + *

Multiplies each 8-bit component against the factor to produce 16-bit intermediaries, and then round to the + * nearest 8-bit representation (similar to floating-point.)

+ * + *

The results are undefined if {@param factor} is not within the interval [0, 255].

+ * + * @param color The packed color values + * @param factor The multiplication factor (in 0..255 range) + * @return The result of the multiplication + */ + public static int mul(int color, int factor) { + // De-interleave the 8-bit component lanes into high and low halves. + // Perform 8-bit multiplication to produce Q8.8 fixed-point intermediaries. + final long hi = (color & 0x00FF00FFL) * factor; + final long lo = (color & 0xFF00FF00L) * factor; + + // Round the Q8.8 fixed-point values to the nearest integer, and interleave the high and low halves to + // produce the packed result. + final long result = + (((hi + 0x00FF00FFL) >>> 8) & 0x00FF00FFL) | + (((lo + 0xFF00FF00L) >>> 8) & 0xFF00FF00L); + + return (int) result; + + } + + /** + * See {@link #mul(int, int)}, which this function is identical to, except that it takes a floating point value in + * the interval of [0.0, 1.0] and maps it to [0, 255]. + * + *

The results are undefined if {@param factor} is not within the interval [0.0, 1.0].

*/ - public static int mulSingle(int a, int b) { - // Take each 8-bit component pair, multiply them together to create intermediate 16-bit integers, - // and then shift the high half of each 16-bit integer into 8-bit integers. - int c0 = (((a) & 0xFF) * b) >> 8; - int c1 = (((a >> 8) & 0xFF) * b) >> 8; - int c2 = (((a >> 16) & 0xFF) * b) >> 8; - int c3 = (((a >> 24) & 0xFF) * b) >> 8; - - // Pack the components - return (c0 << 0) | (c1 << 8) | (c2 << 16) | (c3 << 24); + public static int mul(int color, float factor) { + return mul(color, ColorU8.normalizedFloatToByte(factor)); } } diff --git a/common/src/api/java/net/caffeinemc/mods/sodium/api/vertex/buffer/VertexBufferWriter.java b/common/src/api/java/net/caffeinemc/mods/sodium/api/vertex/buffer/VertexBufferWriter.java index 3b467b27cf..002a2bd986 100644 --- a/common/src/api/java/net/caffeinemc/mods/sodium/api/vertex/buffer/VertexBufferWriter.java +++ b/common/src/api/java/net/caffeinemc/mods/sodium/api/vertex/buffer/VertexBufferWriter.java @@ -43,7 +43,7 @@ private static RuntimeException createUnsupportedVertexConsumerThrowable(VertexC var name = clazz.getName(); return new IllegalArgumentException(("The class %s does not implement interface VertexBufferWriter, " + - "which is required for compatibility with Sodium (see: https://github.com/CaffeineMC/sodium-fabric/issues/1620)").formatted(name)); + "which is required for compatibility with Sodium (see: https://github.com/CaffeineMC/sodium/issues/1620)").formatted(name)); } /** diff --git a/common/src/desktop/java/net/caffeinemc/mods/sodium/desktop/LaunchWarn.java b/common/src/desktop/java/net/caffeinemc/mods/sodium/desktop/LaunchWarn.java index a1d55cb71f..577c762e52 100644 --- a/common/src/desktop/java/net/caffeinemc/mods/sodium/desktop/LaunchWarn.java +++ b/common/src/desktop/java/net/caffeinemc/mods/sodium/desktop/LaunchWarn.java @@ -7,7 +7,7 @@ import java.io.IOException; public class LaunchWarn { - private static final String HELP_URL = "https://github.com/CaffeineMC/sodium-fabric/wiki/Installation"; + private static final String HELP_URL = "https://github.com/CaffeineMC/sodium/wiki/Installation"; private static final String RICH_MESSAGE = "" + diff --git a/common/src/desktop/java/net/caffeinemc/mods/sodium/desktop/utils/browse/BrowseUrlHandler.java b/common/src/desktop/java/net/caffeinemc/mods/sodium/desktop/utils/browse/BrowseUrlHandler.java index 633021bf50..e7e3e989d9 100644 --- a/common/src/desktop/java/net/caffeinemc/mods/sodium/desktop/utils/browse/BrowseUrlHandler.java +++ b/common/src/desktop/java/net/caffeinemc/mods/sodium/desktop/utils/browse/BrowseUrlHandler.java @@ -8,15 +8,6 @@ public interface BrowseUrlHandler { static BrowseUrlHandler createImplementation() { // OpenJDK doesn't use xdg-open and fails to provide an implementation on most systems. if (XDGImpl.isSupported()) { - // Apparently xdg-open is just broken for some desktop environments, because *for some reason* - // setting a default browser is complicated. - if (KDEImpl.isSupported()) { - return new KDEImpl(); - } else if (GNOMEImpl.isSupported()) { - return new GNOMEImpl(); - } - - // If the user's desktop environment isn't KDE or GNOME, then we can only rely on xdg-open being present. return new XDGImpl(); } else if (CrossPlatformImpl.isSupported()) { return new CrossPlatformImpl(); diff --git a/common/src/desktop/java/net/caffeinemc/mods/sodium/desktop/utils/browse/GNOMEImpl.java b/common/src/desktop/java/net/caffeinemc/mods/sodium/desktop/utils/browse/GNOMEImpl.java deleted file mode 100644 index b453efe2cf..0000000000 --- a/common/src/desktop/java/net/caffeinemc/mods/sodium/desktop/utils/browse/GNOMEImpl.java +++ /dev/null @@ -1,16 +0,0 @@ -package net.caffeinemc.mods.sodium.desktop.utils.browse; - -import java.io.IOException; -import java.util.Objects; - -class GNOMEImpl implements BrowseUrlHandler { - public static boolean isSupported() { - return XDGImpl.isSupported() && Objects.equals(System.getenv("XDG_CURRENT_DESKTOP"), "GNOME"); - } - - @Override - public void browseTo(String url) throws IOException { - Runtime.getRuntime() - .exec(new String[] { "gnome-open", url }); - } -} diff --git a/common/src/desktop/java/net/caffeinemc/mods/sodium/desktop/utils/browse/KDEImpl.java b/common/src/desktop/java/net/caffeinemc/mods/sodium/desktop/utils/browse/KDEImpl.java deleted file mode 100644 index 61fb07d037..0000000000 --- a/common/src/desktop/java/net/caffeinemc/mods/sodium/desktop/utils/browse/KDEImpl.java +++ /dev/null @@ -1,16 +0,0 @@ -package net.caffeinemc.mods.sodium.desktop.utils.browse; - -import java.io.IOException; -import java.util.Objects; - -class KDEImpl implements BrowseUrlHandler { - public static boolean isSupported() { - return XDGImpl.isSupported() && Objects.equals(System.getenv("XDG_CURRENT_DESKTOP"), "KDE"); - } - - @Override - public void browseTo(String url) throws IOException { - Runtime.getRuntime() - .exec(new String[] { "kde-open", url }); - } -} diff --git a/common/src/desktop/java/net/caffeinemc/mods/sodium/desktop/utils/browse/XDGImpl.java b/common/src/desktop/java/net/caffeinemc/mods/sodium/desktop/utils/browse/XDGImpl.java index 42adc65c1d..5c5884a552 100644 --- a/common/src/desktop/java/net/caffeinemc/mods/sodium/desktop/utils/browse/XDGImpl.java +++ b/common/src/desktop/java/net/caffeinemc/mods/sodium/desktop/utils/browse/XDGImpl.java @@ -1,7 +1,5 @@ package net.caffeinemc.mods.sodium.desktop.utils.browse; -import net.caffeinemc.mods.sodium.desktop.utils.browse.BrowseUrlHandler; - import java.io.IOException; import java.util.Locale; @@ -15,7 +13,18 @@ public static boolean isSupported() { @Override public void browseTo(String url) throws IOException { - Runtime.getRuntime() + var process = Runtime.getRuntime() .exec(new String[] { "xdg-open", url }); + + try { + int result = process.waitFor(); + + if (result != 0 /* success */) { + throw new IOException("xdg-open exited with code: %d".formatted(result)); + } + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/checks/ResourcePackScanner.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/checks/ResourcePackScanner.java index 56e0512812..6f9ad8117c 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/checks/ResourcePackScanner.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/checks/ResourcePackScanner.java @@ -47,7 +47,7 @@ public class ResourcePackScanner { ); /** - * #1569 + * #1569 * Iterate through all active resource packs, and detect resource packs which contain files matching the blacklist. * An error message is shown for resource packs which replace terrain core shaders. * A warning is shown for resource packs which replace the default light.glsl and fog.glsl shaders. @@ -115,14 +115,14 @@ private static void printCompatibilityReport(Collection sca if (!entry.shaderPrograms.isEmpty()) { emitProblem(builder, "The resource pack replaces terrain shaders, which are not supported", - "https://github.com/CaffeineMC/sodium-fabric/wiki/Resource-Packs", + "https://github.com/CaffeineMC/sodium/wiki/Resource-Packs", entry.shaderPrograms); } if (!entry.shaderIncludes.isEmpty()) { emitProblem(builder, "The resource pack modifies shader include files, which are not fully supported", - "https://github.com/CaffeineMC/sodium-fabric/wiki/Resource-Packs", + "https://github.com/CaffeineMC/sodium/wiki/Resource-Packs", entry.shaderIncludes); } } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/checks/SodiumResourcePackMetadata.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/checks/SodiumResourcePackMetadata.java index 44898479b0..5a8a608588 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/checks/SodiumResourcePackMetadata.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/checks/SodiumResourcePackMetadata.java @@ -17,5 +17,5 @@ public record SodiumResourcePackMetadata(List ignoredShaders) { .apply(instance, SodiumResourcePackMetadata::new) ); public static final MetadataSectionType SERIALIZER = - MetadataSectionType.fromCodec("sodium", CODEC); + new MetadataSectionType<>("sodium", CODEC); } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/data/config/MixinConfig.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/data/config/MixinConfig.java index b9ad8c36e8..c4434f2105 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/data/config/MixinConfig.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/data/config/MixinConfig.java @@ -12,7 +12,7 @@ import java.util.Properties; /** - * Documentation of these options... + * Documentation of these options... */ @SuppressWarnings("CanBeFinal") public class MixinConfig { @@ -230,7 +230,7 @@ private static void writeDefaultConfig(File file) throws IOException { writer.write("# This is the configuration file for Sodium.\n"); writer.write("#\n"); writer.write("# You can find information on editing this file and all the available options here:\n"); - writer.write("# https://github.com/CaffeineMC/sodium-fabric/wiki/Configuration-File\n"); + writer.write("# https://github.com/CaffeineMC/sodium/wiki/Configuration-File\n"); writer.write("#\n"); writer.write("# By default, this file will be empty except for this notice.\n"); } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/gl/arena/GlBufferArena.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/gl/arena/GlBufferArena.java index f26cf032ce..f24feb54b7 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/gl/arena/GlBufferArena.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/gl/arena/GlBufferArena.java @@ -1,10 +1,13 @@ package net.caffeinemc.mods.sodium.client.gl.arena; +import it.unimi.dsi.fastutil.longs.Long2ReferenceOpenHashMap; import net.caffeinemc.mods.sodium.client.gl.arena.staging.StagingBuffer; import net.caffeinemc.mods.sodium.client.gl.buffer.GlBuffer; import net.caffeinemc.mods.sodium.client.gl.buffer.GlBufferUsage; import net.caffeinemc.mods.sodium.client.gl.buffer.GlMutableBuffer; import net.caffeinemc.mods.sodium.client.gl.device.CommandList; +import net.jpountz.xxhash.XXHash64; +import net.jpountz.xxhash.XXHashFactory; import java.nio.ByteBuffer; import java.util.ArrayList; @@ -26,12 +29,17 @@ public class GlBufferArena { private GlBufferSegment head; - private int capacity; - private int used; + private static final XXHash64 NATIVE_HASH = XXHashFactory.fastestInstance().hash64(); + private static final XXHash64 JAVA_HASH = XXHashFactory.fastestJavaInstance().hash64(); + private static final int NATIVE_HASH_BYTES_THRESHOLD = 512; // TODO: tune this? + private final Long2ReferenceOpenHashMap cache; + + private long capacity; + private long used; private final int stride; - public GlBufferArena(CommandList commands, int initialCapacity, int stride, StagingBuffer stagingBuffer) { + public GlBufferArena(CommandList commands, int initialCapacity, int stride, StagingBuffer stagingBuffer, boolean enableCache) { this.capacity = initialCapacity; this.resizeIncrement = initialCapacity / 16; @@ -44,16 +52,22 @@ public GlBufferArena(CommandList commands, int initialCapacity, int stride, Stag commands.allocateStorage(this.arenaBuffer, this.capacity * stride, BUFFER_USAGE); this.stagingBuffer = stagingBuffer; + + if (enableCache) { + this.cache = new Long2ReferenceOpenHashMap<>(); + } else { + this.cache = null; + } } - private void resize(CommandList commandList, int newCapacity) { + private void resize(CommandList commandList, long newCapacity) { if (this.used > newCapacity) { throw new UnsupportedOperationException("New capacity must be larger than used size"); } this.checkAssertions(); - int tail = newCapacity - this.used; + long tail = newCapacity - this.used; List usedSegments = this.getUsedSegments(); List pendingCopies = this.buildTransferList(usedSegments, tail); @@ -66,7 +80,7 @@ private void resize(CommandList commandList, int newCapacity) { if (usedSegments.isEmpty()) { this.head.setNext(null); } else { - this.head.setNext(usedSegments.get(0)); + this.head.setNext(usedSegments.getFirst()); this.head.getNext() .setPrev(this.head); } @@ -74,23 +88,23 @@ private void resize(CommandList commandList, int newCapacity) { this.checkAssertions(); } - private List buildTransferList(List usedSegments, int base) { + private List buildTransferList(List usedSegments, long base) { List pendingCopies = new ArrayList<>(); PendingBufferCopyCommand currentCopyCommand = null; - int writeOffset = base; + long writeOffset = base; for (int i = 0; i < usedSegments.size(); i++) { GlBufferSegment s = usedSegments.get(i); - if (currentCopyCommand == null || currentCopyCommand.readOffset + currentCopyCommand.length != s.getOffset()) { + if (currentCopyCommand == null || currentCopyCommand.getReadOffset() + currentCopyCommand.getLength() != s.getOffset()) { if (currentCopyCommand != null) { pendingCopies.add(currentCopyCommand); } currentCopyCommand = new PendingBufferCopyCommand(s.getOffset(), writeOffset, s.getLength()); } else { - currentCopyCommand.length += s.getLength(); + currentCopyCommand.setLength(currentCopyCommand.getLength() + s.getLength()); } s.setOffset(writeOffset); @@ -117,7 +131,11 @@ private List buildTransferList(List u return pendingCopies; } - private void transferSegments(CommandList commandList, Collection list, int capacity) { + private void transferSegments(CommandList commandList, Collection list, long capacity) { + if (capacity >= (1L << 32)) { + throw new IllegalArgumentException("Maximum arena buffer size is 4 GiB"); + } + GlMutableBuffer srcBufferObj = this.arenaBuffer; GlMutableBuffer dstBufferObj = commandList.createMutableBuffer(); @@ -125,9 +143,9 @@ private void transferSegments(CommandList commandList, Collection getUsedSegments() { return used; } - public int getDeviceUsedMemory() { + public long getDeviceUsedMemory() { return this.used * this.stride; } - public int getDeviceAllocatedMemory() { + public long getDeviceAllocatedMemory() { return this.capacity * this.stride; } @@ -222,6 +240,10 @@ public void free(GlBufferSegment entry) { throw new IllegalStateException("Already freed"); } + if (entry.isHashed()) { + this.cache.remove(entry.getHash()); + } + entry.setFree(true); this.used -= entry.getLength(); @@ -261,7 +283,7 @@ public boolean upload(CommandList commandList, Stream stream) { // A linked list is used as we'll be randomly removing elements and want O(1) performance List queue = stream.collect(Collectors.toCollection(LinkedList::new)); - // Try to upload all of the data into free segments first + // Try to upload all the data into free segments first this.tryUploads(commandList, queue); // If we weren't able to upload some buffers, they will have been left behind in the queue @@ -293,18 +315,46 @@ private void tryUploads(CommandList commandList, List queue) { this.stagingBuffer.flush(commandList); } + private long getBufferHash(ByteBuffer data) { + var seed = System.identityHashCode(this); + var length = data.remaining(); + if (length < NATIVE_HASH_BYTES_THRESHOLD) { + return JAVA_HASH.hash(data, 0, length, seed); + } else { + return NATIVE_HASH.hash(data, 0, length, seed); + } + } + private boolean tryUpload(CommandList commandList, PendingUpload upload) { - ByteBuffer data = upload.getDataBuffer() - .getDirectBuffer(); + ByteBuffer data = upload.getDataBuffer().getDirectBuffer(); int elementCount = data.remaining() / this.stride; + // return a buffer segment with the same content if there is one based on the hash of the incoming content + GlBufferSegment matchingSegment = null; + long hash = 0; + if (this.cache != null) { + hash = this.getBufferHash(data); + matchingSegment = this.cache.get(hash); + } + if (matchingSegment != null) { + upload.setResult(matchingSegment); + matchingSegment.addRef(); + return true; + } + GlBufferSegment dst = this.alloc(elementCount); if (dst == null) { return false; } + // if a new segment was needed (cache miss), set the calculated hash on the segment + if (this.cache != null) { + dst.setHash(hash); + this.cache.put(hash, dst); + } + // Copy the data into our staging buffer, then copy it into the arena's buffer this.stagingBuffer.enqueueCopy(commandList, data, this.arenaBuffer, dst.getOffset() * this.stride); @@ -313,11 +363,11 @@ private boolean tryUpload(CommandList commandList, PendingUpload upload) { return true; } - public void ensureCapacity(CommandList commandList, int elementCount) { + public void ensureCapacity(CommandList commandList, long elementCount) { // Re-sizing the arena results in a compaction, so any free space in the arena will be // made into one contiguous segment, joined with the new segment of free space we're asking for // We calculate the number of free elements in our arena and then subtract that from the total requested - int elementsNeeded = elementCount - (this.capacity - this.used); + long elementsNeeded = elementCount - (this.capacity - this.used); // Try to allocate some extra buffer space unless this is an unusually large allocation this.resize(commandList, Math.max(this.capacity + this.resizeIncrement, this.capacity + elementsNeeded)); @@ -331,7 +381,7 @@ private void checkAssertions() { private void checkAssertions0() { GlBufferSegment seg = this.head; - int used = 0; + long used = 0; while (seg != null) { if (seg.getOffset() < 0) { diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/gl/arena/GlBufferSegment.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/gl/arena/GlBufferSegment.java index 480727ee92..c32f739620 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/gl/arena/GlBufferSegment.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/gl/arena/GlBufferSegment.java @@ -1,56 +1,77 @@ package net.caffeinemc.mods.sodium.client.gl.arena; +import net.caffeinemc.mods.sodium.client.util.UInt32; + public class GlBufferSegment { private final GlBufferArena arena; private boolean free = false; + private int refCount = 1; + private long hash; + private boolean isHashed = false; - private int offset; - private int length; + private int offset; /* Uint32 */ + private int length; /* Uint32 */ private GlBufferSegment next; private GlBufferSegment prev; - public GlBufferSegment(GlBufferArena arena, int offset, int length) { + public GlBufferSegment(GlBufferArena arena, long offset, long length) { this.arena = arena; - this.offset = offset; - this.length = length; + this.offset = UInt32.downcast(offset); + this.length = UInt32.downcast(length); } - public void delete() { - this.arena.free(this); + /* Uint32 */ + protected long getEnd() { + return this.getOffset() + this.getLength(); } - protected int getEnd() { - return this.offset + this.length; + /* Uint32 */ + public long getOffset() { + return UInt32.upcast(this.offset); } - public int getLength() { - return this.length; + /* Uint32 */ + public long getLength() { + return UInt32.upcast(this.length); } - protected void setLength(int len) { - if (len <= 0) { - throw new IllegalArgumentException("len <= 0"); - } + protected void setOffset(long offset /* Uint32 */) { + this.offset = UInt32.downcast(offset); + } - this.length = len; + protected void setLength(long length /* Uint32 */) { + this.length = UInt32.downcast(length); } - public int getOffset() { - return this.offset; + public void setHash(long hash) { + this.hash = hash; + this.isHashed = true; } - protected void setOffset(int offset) { - if (offset < 0) { - throw new IllegalArgumentException("start < 0"); - } + public long getHash() { + return this.hash; + } + + public boolean isHashed() { + return this.isHashed; + } - this.offset = offset; + public void addRef() { + if (this.isFree()) { + throw new IllegalStateException("Cannot add ref to free segment"); + } + this.refCount++; } protected void setFree(boolean free) { this.free = free; + if (this.free) { + this.refCount = 0; + } else { + this.refCount = Math.max(this.refCount, 1); + } } protected boolean isFree() { @@ -73,6 +94,14 @@ protected void setPrev(GlBufferSegment prev) { this.prev = prev; } + public void delete() { + // only actually free if there's no more users + if (--this.refCount == 0) { + this.arena.free(this); + this.isHashed = false; + } + } + protected void mergeInto(GlBufferSegment entry) { this.setLength(this.getLength() + entry.getLength()); this.setNext(entry.getNext()); diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/gl/arena/PendingBufferCopyCommand.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/gl/arena/PendingBufferCopyCommand.java index e7be6b80fb..8402d475c4 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/gl/arena/PendingBufferCopyCommand.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/gl/arena/PendingBufferCopyCommand.java @@ -1,14 +1,35 @@ package net.caffeinemc.mods.sodium.client.gl.arena; +import net.caffeinemc.mods.sodium.client.util.UInt32; + class PendingBufferCopyCommand { - public final int readOffset; - public final int writeOffset; + private final int readOffset; /* Uint32 */ + private final int writeOffset; /* Uint32 */ + + private int length; - public int length; + PendingBufferCopyCommand(long readOffset, long writeOffset, long length) { + this.readOffset = UInt32.downcast(readOffset); + this.writeOffset = UInt32.downcast(writeOffset); + this.length = UInt32.downcast(length); + } + + /* Uint32 */ + public long getReadOffset() { + return UInt32.upcast(this.readOffset); + } + + /* Uint32 */ + public long getWriteOffset() { + return UInt32.upcast(this.writeOffset); + } + + /* Uint32 */ + public long getLength() { + return UInt32.upcast(this.length); + } - PendingBufferCopyCommand(int readOffset, int writeOffset, int length) { - this.readOffset = readOffset; - this.writeOffset = writeOffset; - this.length = length; + public void setLength(long length /* Uint32 */) { + this.length = UInt32.downcast(length); } } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/gl/buffer/IndexedVertexData.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/gl/buffer/IndexedVertexData.java deleted file mode 100644 index 12b879ba99..0000000000 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/gl/buffer/IndexedVertexData.java +++ /dev/null @@ -1,16 +0,0 @@ -package net.caffeinemc.mods.sodium.client.gl.buffer; - -import net.caffeinemc.mods.sodium.client.gl.attribute.GlVertexFormat; -import net.caffeinemc.mods.sodium.client.util.NativeBuffer; - -/** - * Helper type for tagging the vertex format alongside the raw buffer data. - */ -public record IndexedVertexData(GlVertexFormat vertexFormat, - NativeBuffer vertexBuffer, - NativeBuffer indexBuffer) { - public void delete() { - this.vertexBuffer.free(); - this.indexBuffer.free(); - } -} diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/gl/device/MultiDrawBatch.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/gl/device/MultiDrawBatch.java index ceda21721a..0eb9a381fb 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/gl/device/MultiDrawBatch.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/gl/device/MultiDrawBatch.java @@ -53,6 +53,7 @@ public boolean isEmpty() { public int getIndexBufferSize() { int elements = 0; + // since there's command combining, all facings might be rendered at the same time with a single command which requires a bigger index buffer for (var index = 0; index < this.size; index++) { elements = Math.max(elements, MemoryUtil.memGetInt(this.pElementCount + ((long) index * Integer.BYTES))); } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/gl/shader/uniform/GlUniformFloat4v.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/gl/shader/uniform/GlUniformFloat4v.java index c968f69bdb..b8668e7f76 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/gl/shader/uniform/GlUniformFloat4v.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/gl/shader/uniform/GlUniformFloat4v.java @@ -15,4 +15,8 @@ public void set(float[] value) { GL30C.glUniform4fv(this.index, value); } + + public void set(float x, float y, float z, float w) { + GL30C.glUniform4f(this.index, x, y, z, w); + } } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/gui/SodiumGameOptionPages.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/gui/SodiumGameOptionPages.java index b512dddc4e..6a43897da0 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/gui/SodiumGameOptionPages.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/gui/SodiumGameOptionPages.java @@ -2,37 +2,41 @@ import com.google.common.collect.ImmutableList; import com.mojang.blaze3d.pipeline.RenderTarget; +import com.mojang.blaze3d.platform.Monitor; +import com.mojang.blaze3d.platform.VideoMode; import com.mojang.blaze3d.platform.Window; +import net.caffeinemc.mods.sodium.client.compatibility.environment.OsUtils; import net.caffeinemc.mods.sodium.client.gl.arena.staging.MappedStagingBuffer; import net.caffeinemc.mods.sodium.client.gl.device.RenderDevice; import net.caffeinemc.mods.sodium.client.gui.options.*; import net.caffeinemc.mods.sodium.client.gui.options.binding.compat.VanillaBooleanOptionBinding; -import net.caffeinemc.mods.sodium.client.gui.options.control.ControlValueFormatter; -import net.caffeinemc.mods.sodium.client.gui.options.control.CyclingControl; -import net.caffeinemc.mods.sodium.client.gui.options.control.SliderControl; -import net.caffeinemc.mods.sodium.client.gui.options.control.TickBoxControl; +import net.caffeinemc.mods.sodium.client.gui.options.control.*; import net.caffeinemc.mods.sodium.client.gui.options.storage.MinecraftOptionsStorage; import net.caffeinemc.mods.sodium.client.gui.options.storage.SodiumOptionsStorage; import net.caffeinemc.mods.sodium.client.compatibility.workarounds.Workarounds; import net.caffeinemc.mods.sodium.client.services.PlatformRuntimeInformation; import net.minecraft.client.AttackIndicatorStatus; +import net.minecraft.client.InactivityFpsLimit; import net.minecraft.client.CloudStatus; import net.minecraft.client.GraphicsStatus; import net.minecraft.client.Minecraft; -import net.minecraft.client.ParticleStatus; import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ParticleStatus; import org.lwjgl.opengl.GL; import org.lwjgl.opengl.GLCapabilities; import java.util.ArrayList; import java.util.List; +import java.util.Optional; // TODO: Rename in Sodium 0.6 public class SodiumGameOptionPages { private static final SodiumOptionsStorage sodiumOpts = new SodiumOptionsStorage(); private static final MinecraftOptionsStorage vanillaOpts = new MinecraftOptionsStorage(); + private static final Window window = Minecraft.getInstance().getWindow(); public static OptionPage general() { + Monitor monitor = window.findBestMonitor(); List groups = new ArrayList<>(); groups.add(OptionGroup.createBuilder() @@ -90,6 +94,26 @@ public static OptionPage general() { } }, (opts) -> opts.fullscreen().get()) .build()) + .add(OptionImpl.createBuilder(int.class, vanillaOpts) + .setName(Component.translatable("options.fullscreen.resolution")) + .setTooltip(Component.translatable("sodium.options.fullscreen_resolution.tooltip")) + .setControl(option -> new SliderControl(option, 0, null != monitor? monitor.getModeCount(): 0, 1, ControlValueFormatter.resolution())) + .setBinding((options, value) -> { + if (null != monitor) { + window.setPreferredFullscreenVideoMode(0 == value? Optional.empty(): Optional.of(monitor.getMode(value - 1))); + } + }, options -> { + if (null == monitor) { + return 0; + } + else { + Optional optional = window.getPreferredFullscreenVideoMode(); + return optional.map((videoMode) -> monitor.getVideoModeIndex(videoMode) + 1).orElse(0); + } + }) + .setEnabled(() -> OsUtils.getOs() == OsUtils.OperatingSystem.WIN && Minecraft.getInstance().getWindow().findBestMonitor() != null) + .setFlags(OptionFlag.REQUIRES_VIDEOMODE_RELOAD) + .build()) .add(OptionImpl.createBuilder(boolean.class, vanillaOpts) .setName(Component.translatable("options.vsync")) .setTooltip(Component.translatable("sodium.options.v_sync.tooltip")) @@ -103,7 +127,7 @@ public static OptionPage general() { .setControl(option -> new SliderControl(option, 10, 260, 10, ControlValueFormatter.fpsLimit())) .setBinding((opts, value) -> { opts.framerateLimit().set(value); - Minecraft.getInstance().getWindow().setFramerateLimit(value); + Minecraft.getInstance().getFramerateLimitTracker().setFramerateLimit(value); }, opts -> opts.framerateLimit().get()) .build()) .build()); @@ -159,7 +183,7 @@ public static OptionPage quality() { if (Minecraft.useShaderTransparency()) { RenderTarget framebuffer = Minecraft.getInstance().levelRenderer.getCloudsTarget(); if (framebuffer != null) { - framebuffer.clear(Minecraft.ON_OSX); + framebuffer.clear(); } } }, opts -> opts.cloudStatus().get()) @@ -311,6 +335,12 @@ public static OptionPage performance() { .setEnabled(SodiumGameOptionPages::supportsNoErrorContext) .setFlags(OptionFlag.REQUIRES_GAME_RESTART) .build()) + .add(OptionImpl.createBuilder(InactivityFpsLimit.class, vanillaOpts) + .setName(Component.translatable("options.inactivityFpsLimit")) + .setTooltip(v -> Component.translatable(v.getId() == 0 ? "options.inactivityFpsLimit.minimized.tooltip" : "options.inactivityFpsLimit.afk.tooltip")) + .setControl(option -> new CyclingControl<>(option, InactivityFpsLimit.class, new Component[] { Component.translatable("options.inactivityFpsLimit.minimized"), Component.translatable("options.inactivityFpsLimit.afk") })) + .setBinding((opts, value) -> opts.inactivityFpsLimit().set(value), opts -> opts.inactivityFpsLimit().get()) + .build()) .build()); if (PlatformRuntimeInformation.getInstance().isDevelopmentEnvironment()) { diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/gui/SodiumOptionsGUI.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/gui/SodiumOptionsGUI.java index 876bdb44bd..aba63b7b0b 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/gui/SodiumOptionsGUI.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/gui/SodiumOptionsGUI.java @@ -295,18 +295,21 @@ private void renderOptionTooltip(GuiGraphics graphics, ControlElement element int textPadding = 3; int boxPadding = 3; - int boxWidth = 200; - int boxY = dim.y(); int boxX = dim.getLimitX() + boxPadding; + int boxWidth = Math.min(200, this.width - boxX - boxPadding); + Option option = element.getOption(); - List tooltip = new ArrayList<>(this.font.split(option.getTooltip(), boxWidth - (textPadding * 2))); + var splitWidth = boxWidth - (textPadding * 2); + List tooltip = new ArrayList<>(this.font.split(option.getTooltip(),splitWidth)); OptionImpact impact = option.getImpact(); if (impact != null) { - tooltip.add(Language.getInstance().getVisualOrder(Component.translatable("sodium.options.performance_impact_string", impact.getLocalizedName()).withStyle(ChatFormatting.GRAY))); + var impactText = Component.translatable("sodium.options.performance_impact_string", + impact.getLocalizedName()); + tooltip.addAll(this.font.split(impactText.withStyle(ChatFormatting.GRAY), splitWidth)); } int boxHeight = (tooltip.size() * 12) + boxPadding; @@ -355,6 +358,10 @@ private void applyChanges() { client.delayTextureReload(); } + if (flags.contains(OptionFlag.REQUIRES_VIDEOMODE_RELOAD)) { + client.getWindow().changeFullscreenVideoMode(); + } + if (flags.contains(OptionFlag.REQUIRES_GAME_RESTART)) { Console.instance().logMessage(MessageLevel.WARN, "sodium.console.game_restart", true, 10.0); diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/gui/options/OptionFlag.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/gui/options/OptionFlag.java index 8ea3e5b0bf..17568af5a5 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/gui/options/OptionFlag.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/gui/options/OptionFlag.java @@ -4,5 +4,6 @@ public enum OptionFlag { REQUIRES_RENDERER_RELOAD, REQUIRES_RENDERER_UPDATE, REQUIRES_ASSET_RELOAD, + REQUIRES_VIDEOMODE_RELOAD, REQUIRES_GAME_RESTART } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/gui/options/OptionImpl.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/gui/options/OptionImpl.java index 893bea4363..ee5d3b2892 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/gui/options/OptionImpl.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/gui/options/OptionImpl.java @@ -23,7 +23,7 @@ public class OptionImpl implements Option { private final EnumSet flags; private final Component name; - private final Component tooltip; + private final Function tooltip; private final OptionImpact impact; @@ -34,7 +34,7 @@ public class OptionImpl implements Option { private OptionImpl(OptionStorage storage, Component name, - Component tooltip, + Function tooltip, OptionBinding binding, Function, Control> control, EnumSet flags, @@ -59,7 +59,7 @@ public Component getName() { @Override public Component getTooltip() { - return this.tooltip; + return this.tooltip.apply(this.modifiedValue); } @Override @@ -121,7 +121,7 @@ public static OptionImpl.Builder createBuilder(@SuppressWarnings("u public static class Builder { private final OptionStorage storage; private Component name; - private Component tooltip; + private Function tooltip; private OptionBinding binding; private Function, Control> control; private OptionImpact impact; @@ -143,6 +143,14 @@ public Builder setName(Component name) { public Builder setTooltip(Component tooltip) { Validate.notNull(tooltip, "Argument must not be null"); + this.tooltip = t -> tooltip; + + return this; + } + + public Builder setTooltip(Function tooltip) { + Validate.notNull(tooltip, "Argument must not be null"); + this.tooltip = tooltip; return this; diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/gui/options/control/ControlElement.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/gui/options/control/ControlElement.java index 3df9998470..fbdb182130 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/gui/options/control/ControlElement.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/gui/options/control/ControlElement.java @@ -8,6 +8,7 @@ import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.navigation.FocusNavigationEvent; import net.minecraft.client.gui.navigation.ScreenRectangle; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public class ControlElement extends AbstractWidget { @@ -20,18 +21,28 @@ public ControlElement(Option option, Dim2i dim) { this.dim = dim; } + public int getContentWidth() { + return this.option.getControl().getMaxWidth(); + } + @Override public void render(GuiGraphics graphics, int mouseX, int mouseY, float delta) { String name = this.option.getName().getString(); - String label; - if ((this.hovered || this.isFocused()) && this.font.width(name) > (this.dim.width() - this.option.getControl().getMaxWidth())) { - name = name.substring(0, Math.min(name.length(), 10)) + "..."; + // add the star suffix before truncation to prevent it from overlapping with the label text + if (this.option.isAvailable() && this.option.hasChanged()) { + name = name + " *"; + } + + // on focus or hover truncate the label to never overlap with the control's content + if (this.hovered || this.isFocused()) { + name = truncateLabelToFit(name); } + String label; if (this.option.isAvailable()) { if (this.option.hasChanged()) { - label = ChatFormatting.ITALIC + name + " *"; + label = ChatFormatting.ITALIC + name; } else { label = ChatFormatting.WHITE + name; } @@ -49,6 +60,33 @@ public void render(GuiGraphics graphics, int mouseX, int mouseY, float delta) { } } + private @NotNull String truncateLabelToFit(String name) { + var suffix = "..."; + var suffixWidth = this.font.width(suffix); + var nameFontWidth = this.font.width(name); + var targetWidth = this.dim.width() - this.getContentWidth() - 20; + if (nameFontWidth > targetWidth) { + targetWidth -= suffixWidth; + int maxLabelChars = name.length() - 3; + int minLabelChars = 1; + + // binary search on how many chars fit + while (maxLabelChars - minLabelChars > 1) { + var mid = (maxLabelChars + minLabelChars) / 2; + var midName = name.substring(0, mid); + var midWidth = this.font.width(midName); + if (midWidth > targetWidth) { + maxLabelChars = mid; + } else { + minLabelChars = mid; + } + } + + name = name.substring(0, minLabelChars).trim() + suffix; + } + return name; + } + public Option getOption() { return this.option; } @@ -68,4 +106,9 @@ public Dim2i getDimensions() { public ScreenRectangle getRectangle() { return new ScreenRectangle(this.dim.x(), this.dim.y(), this.dim.width(), this.dim.height()); } + + @Override + public boolean isMouseOver(double x, double y) { + return this.dim.containsCursor(x, y); + } } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/gui/options/control/ControlValueFormatter.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/gui/options/control/ControlValueFormatter.java index e72a682b1b..c7d8e30507 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/gui/options/control/ControlValueFormatter.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/gui/options/control/ControlValueFormatter.java @@ -1,5 +1,8 @@ package net.caffeinemc.mods.sodium.client.gui.options.control; +import com.mojang.blaze3d.platform.Monitor; +import net.caffeinemc.mods.sodium.client.compatibility.environment.OsUtils; +import net.minecraft.client.Minecraft; import net.minecraft.network.chat.Component; public interface ControlValueFormatter { @@ -7,6 +10,19 @@ static ControlValueFormatter guiScale() { return (v) -> (v == 0) ? Component.translatable("options.guiScale.auto") : Component.literal(v + "x"); } + static ControlValueFormatter resolution() { + return (v) -> { + Monitor monitor = Minecraft.getInstance().getWindow().findBestMonitor(); + + if (OsUtils.getOs() != OsUtils.OperatingSystem.WIN || monitor == null) { + return Component.translatable("options.fullscreen.unavailable"); + } else if (0 == v) { + return Component.translatable("options.fullscreen.current"); + } else { + return Component.literal(monitor.getMode(v - 1).toString().replace(" (24bit)","")); + } + }; + } static ControlValueFormatter fpsLimit() { return (v) -> (v == 260) ? Component.translatable("options.framerateLimit.max") : Component.translatable("options.framerate", v); } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/gui/options/control/SliderControl.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/gui/options/control/SliderControl.java index 402191ee52..99c379b493 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/gui/options/control/SliderControl.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/gui/options/control/SliderControl.java @@ -3,9 +3,10 @@ import com.mojang.blaze3d.platform.InputConstants; import net.caffeinemc.mods.sodium.client.gui.options.Option; import net.caffeinemc.mods.sodium.client.util.Dim2i; +import net.minecraft.ChatFormatting; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.renderer.Rect2i; -import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.Style; import net.minecraft.util.Mth; import org.apache.commons.lang3.Validate; @@ -48,6 +49,7 @@ private static class Button extends ControlElement { private static final int THUMB_WIDTH = 2, TRACK_HEIGHT = 1; private final Rect2i sliderBounds; + private int contentWidth; private final ControlValueFormatter formatter; private final int min; @@ -75,48 +77,52 @@ public Button(Option option, Dim2i dim, int min, int max, int interval, @Override public void render(GuiGraphics graphics, int mouseX, int mouseY, float delta) { - super.render(graphics, mouseX, mouseY, delta); - - if (this.option.isAvailable() && (this.hovered || this.isFocused())) { - this.renderSlider(graphics); - } else { - this.renderStandaloneValue(graphics); - } - } - - private void renderStandaloneValue(GuiGraphics graphics) { int sliderX = this.sliderBounds.getX(); int sliderY = this.sliderBounds.getY(); int sliderWidth = this.sliderBounds.getWidth(); int sliderHeight = this.sliderBounds.getHeight(); - Component label = this.formatter.format(this.option.getValue()); - int labelWidth = this.font.width(label); + var label = this.formatter.format(this.option.getValue()) + .copy(); - this.drawString(graphics, label, sliderX + sliderWidth - labelWidth, sliderY + (sliderHeight / 2) - 4, 0xFFFFFFFF); - } + if (!this.option.isAvailable()) { + label.setStyle(Style.EMPTY + .withColor(ChatFormatting.GRAY) + .withItalic(true)); + } - private void renderSlider(GuiGraphics graphics) { - int sliderX = this.sliderBounds.getX(); - int sliderY = this.sliderBounds.getY(); - int sliderWidth = this.sliderBounds.getWidth(); - int sliderHeight = this.sliderBounds.getHeight(); + int labelWidth = this.font.width(label); - this.thumbPosition = this.getThumbPositionForValue(this.option.getValue()); + boolean drawSlider = this.option.isAvailable() && (this.hovered || this.isFocused()); + if (drawSlider) { + this.contentWidth = sliderWidth + labelWidth; + } else { + this.contentWidth = labelWidth; + } - double thumbOffset = Mth.clamp((double) (this.getIntValue() - this.min) / this.range * sliderWidth, 0, sliderWidth); + // render the label first and then the slider to prevent the highlight rect from darkening the slider + super.render(graphics, mouseX, mouseY, delta); - int thumbX = (int) (sliderX + thumbOffset - THUMB_WIDTH); - int trackY = (int) (sliderY + (sliderHeight / 2f) - ((double) TRACK_HEIGHT / 2)); + if (drawSlider) { + this.thumbPosition = this.getThumbPositionForValue(this.option.getValue()); - this.drawRect(graphics, thumbX, sliderY, thumbX + (THUMB_WIDTH * 2), sliderY + sliderHeight, 0xFFFFFFFF); - this.drawRect(graphics, sliderX, trackY, sliderX + sliderWidth, trackY + TRACK_HEIGHT, 0xFFFFFFFF); + double thumbOffset = Mth.clamp((double) (this.getIntValue() - this.min) / this.range * sliderWidth, 0, sliderWidth); - Component label = this.formatter.format(this.getIntValue()); + int thumbX = (int) (sliderX + thumbOffset - THUMB_WIDTH); + int trackY = (int) (sliderY + (sliderHeight / 2f) - ((double) TRACK_HEIGHT / 2)); - int labelWidth = this.font.width(label); + this.drawRect(graphics, thumbX, sliderY, thumbX + (THUMB_WIDTH * 2), sliderY + sliderHeight, 0xFFFFFFFF); + this.drawRect(graphics, sliderX, trackY, sliderX + sliderWidth, trackY + TRACK_HEIGHT, 0xFFFFFFFF); - this.drawString(graphics, label, sliderX - labelWidth - 6, sliderY + (sliderHeight / 2) - 4, 0xFFFFFFFF); + this.drawString(graphics, label, sliderX - labelWidth - 6, sliderY + (sliderHeight / 2) - 4, 0xFFFFFFFF); + } else { + this.drawString(graphics, label, sliderX + sliderWidth - labelWidth, sliderY + (sliderHeight / 2) - 4, 0xFFFFFFFF); + } + } + + @Override + public int getContentWidth() { + return this.contentWidth; } public int getIntValue() { diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/gui/widgets/AbstractWidget.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/gui/widgets/AbstractWidget.java index cf494b3e2c..30a3d5d3b2 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/gui/widgets/AbstractWidget.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/gui/widgets/AbstractWidget.java @@ -102,4 +102,7 @@ protected void drawBorder(GuiGraphics graphics, int x1, int y1, int x2, int y2, graphics.fill(x1, y1, x1 + 1, y2, color); graphics.fill(x2 - 1, y1, x2, y2, color); } + + @Override + public abstract boolean isMouseOver(double x, double y); } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/gui/widgets/FlatButtonWidget.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/gui/widgets/FlatButtonWidget.java index 7d753555d1..4bdd04e73a 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/gui/widgets/FlatButtonWidget.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/gui/widgets/FlatButtonWidget.java @@ -121,6 +121,11 @@ public Component getLabel() { return super.nextFocusPath(event); } + @Override + public boolean isMouseOver(double x, double y) { + return this.dim.containsCursor(x, y); + } + @Override public ScreenRectangle getRectangle() { return new ScreenRectangle(this.dim.x(), this.dim.y(), this.dim.width(), this.dim.height()); diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/model/color/DefaultColorProviders.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/model/color/DefaultColorProviders.java index 848896141b..c80d52022c 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/model/color/DefaultColorProviders.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/model/color/DefaultColorProviders.java @@ -7,7 +7,7 @@ import net.minecraft.client.renderer.BiomeColors; import net.minecraft.core.BlockPos; import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.material.FluidState; + import java.util.Arrays; public class DefaultColorProviders { @@ -50,7 +50,7 @@ private VanillaAdapter(BlockColor color) { @Override public void getColors(LevelSlice slice, BlockPos pos, BlockPos.MutableBlockPos scratchPos, BlockState state, ModelQuadView quad, int[] output) { - Arrays.fill(output, 0xFF000000 | this.color.getColor(state, slice, pos, quad.getColorIndex())); + Arrays.fill(output, 0xFF000000 | this.color.getColor(state, slice, pos, quad.getTintIndex())); } } } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/model/color/interop/ItemColorsExtension.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/model/color/interop/ItemColorsExtension.java deleted file mode 100644 index cee52b119f..0000000000 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/model/color/interop/ItemColorsExtension.java +++ /dev/null @@ -1,8 +0,0 @@ -package net.caffeinemc.mods.sodium.client.model.color.interop; - -import net.minecraft.client.color.item.ItemColor; -import net.minecraft.world.item.ItemStack; - -public interface ItemColorsExtension { - ItemColor sodium$getColorProvider(ItemStack stack); -} diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/model/light/data/LightDataAccess.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/model/light/data/LightDataAccess.java index 0ded5bc8c9..1b04155e03 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/model/light/data/LightDataAccess.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/model/light/data/LightDataAccess.java @@ -65,8 +65,8 @@ protected int compute(int x, int y, int z) { BlockState state = level.getBlockState(pos); boolean em = state.emissiveRendering(level, pos); - boolean op = state.isViewBlocking(level, pos) && state.getLightBlock(level, pos) != 0; - boolean fo = state.isSolidRender(level, pos); + boolean op = state.isViewBlocking(level, pos) && state.getLightBlock() != 0; + boolean fo = state.isSolidRender(); boolean fc = state.isCollisionShapeFullBlock(level, pos); int lu = PlatformBlockAccess.getInstance().getLightEmission(state, level, pos); diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/model/quad/BakedQuadView.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/model/quad/BakedQuadView.java index 8acff21468..27673a7f99 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/model/quad/BakedQuadView.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/model/quad/BakedQuadView.java @@ -8,4 +8,6 @@ public interface BakedQuadView extends ModelQuadView { int getFaceNormal(); boolean hasShade(); + + boolean hasAO(); } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/model/quad/ModelQuad.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/model/quad/ModelQuad.java index d5f650313d..54a01fd3bf 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/model/quad/ModelQuad.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/model/quad/ModelQuad.java @@ -15,7 +15,7 @@ public class ModelQuad implements ModelQuadViewMutable { private TextureAtlasSprite sprite; private Direction direction; - private int colorIdx; + private int tintIdx; private int faceNormal; @Override @@ -74,8 +74,8 @@ public void setSprite(TextureAtlasSprite sprite) { } @Override - public void setColorIndex(int index) { - this.colorIdx = index; + public void setTintIndex(int index) { + this.tintIdx = index; } @Override @@ -84,8 +84,8 @@ public void setLightFace(Direction direction) { } @Override - public int getColorIndex() { - return this.colorIdx; + public int getTintIndex() { + return this.tintIdx; } @Override @@ -147,4 +147,9 @@ public TextureAtlasSprite getSprite() { public Direction getLightFace() { return this.direction; } + + @Override + public int getMaxLightQuad(int idx) { + return getLight(idx); + } } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/model/quad/ModelQuadView.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/model/quad/ModelQuadView.java index 08369fb0af..f04685e987 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/model/quad/ModelQuadView.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/model/quad/ModelQuadView.java @@ -60,9 +60,9 @@ public interface ModelQuadView { int getFlags(); /** - * @return The color index of this quad. + * @return The tint index of this quad. */ - int getColorIndex(); + int getTintIndex(); /** * @return The sprite texture used by this quad, or null if none is attached @@ -75,7 +75,7 @@ public interface ModelQuadView { Direction getLightFace(); default boolean hasColor() { - return this.getColorIndex() != -1; + return this.getTintIndex() != -1; } default int calculateNormal() { @@ -127,4 +127,11 @@ default int getAccurateNormal(int i) { return normal == 0 ? getFaceNormal() : normal; } + + /** + * Gets the maximum light value for this vertex. + * @param idx The vertex index. + * @return Lightmap value. + */ + int getMaxLightQuad(int idx); } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/model/quad/ModelQuadViewMutable.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/model/quad/ModelQuadViewMutable.java index 30753db6ca..02063e2cb3 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/model/quad/ModelQuadViewMutable.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/model/quad/ModelQuadViewMutable.java @@ -69,7 +69,7 @@ public interface ModelQuadViewMutable extends ModelQuadView { void setLightFace(Direction direction); /** - * Sets the color index used by this quad + * Sets the tint index used by this quad */ - void setColorIndex(int index); + void setTintIndex(int index); } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/model/quad/blender/BlendedColorProvider.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/model/quad/blender/BlendedColorProvider.java index b1d53d164a..24014df9b9 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/model/quad/blender/BlendedColorProvider.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/model/quad/blender/BlendedColorProvider.java @@ -3,7 +3,6 @@ import net.caffeinemc.mods.sodium.client.model.quad.ModelQuadView; import net.caffeinemc.mods.sodium.client.model.color.ColorProvider; import net.caffeinemc.mods.sodium.client.world.LevelSlice; -import net.caffeinemc.mods.sodium.api.util.ColorARGB; import net.caffeinemc.mods.sodium.api.util.ColorMixer; import net.minecraft.core.BlockPos; import net.minecraft.util.Mth; @@ -17,56 +16,40 @@ public void getColors(LevelSlice slice, BlockPos pos, BlockPos.MutableBlockPos s } private int getVertexColor(LevelSlice slice, BlockPos pos, BlockPos.MutableBlockPos scratchPos, ModelQuadView quad, T state, int vertexIndex) { - // Offset the position by -0.5f to align smooth blending with flat blending. - final float posX = quad.getX(vertexIndex) - 0.5f; - final float posY = quad.getY(vertexIndex) - 0.5f; - final float posZ = quad.getZ(vertexIndex) - 0.5f; - - // Floor the positions here to always get the largest integer below the input - // as negative values by default round toward zero when casting to an integer. - // Which would cause negative ratios to be calculated in the interpolation later on. - final int posIntX = Mth.floor(posX); - final int posIntY = Mth.floor(posY); - final int posIntZ = Mth.floor(posZ); - - // Integer component of position vector - final int blockIntX = pos.getX() + posIntX; - final int blockIntY = pos.getY() + posIntY; - final int blockIntZ = pos.getZ() + posIntZ; - - // Retrieve the color values for each neighboring block - final int c00 = this.getColor(slice, state, scratchPos.set(blockIntX + 0, blockIntY, blockIntZ + 0)); - final int c01 = this.getColor(slice, state, scratchPos.set(blockIntX + 0, blockIntY, blockIntZ + 1)); - final int c10 = this.getColor(slice, state, scratchPos.set(blockIntX + 1, blockIntY, blockIntZ + 0)); - final int c11 = this.getColor(slice, state, scratchPos.set(blockIntX + 1, blockIntY, blockIntZ + 1)); - - // Linear interpolation across the Z-axis - int z0; - - if (c00 != c01) { - z0 = ColorMixer.mix(c00, c01, posZ - posIntZ); - } else { - z0 = c00; - } - - int z1; - - if (c10 != c11) { - z1 = ColorMixer.mix(c10, c11, posZ - posIntZ); - } else { - z1 = c10; - } - - // Linear interpolation across the X-axis - int x0; - - if (z0 != z1) { - x0 = ColorMixer.mix(z0, z1, posX - posIntX); - } else { - x0 = z0; - } - - return x0; + // The vertex position + // We add a half-texel offset since we are sampling points within a color texture + final float x = quad.getX(vertexIndex) - 0.5f; + final float y = quad.getY(vertexIndex) - 0.5f; + final float z = quad.getZ(vertexIndex) - 0.5f; + + // Integer component of vertex position + final int intX = Mth.floor(x); + final int intY = Mth.floor(y); + final int intZ = Mth.floor(z); + + // Fractional component of vertex position + final float fracX = x - intX; + final float fracY = y - intY; + final float fracZ = z - intZ; + + // Block coordinates (in world space) which the vertex is located within + // This is calculated after converting from floating point to avoid precision loss with large coordinates + final int blockX = pos.getX() + intX; + final int blockY = pos.getY() + intY; + final int blockZ = pos.getZ() + intZ; + + // Retrieve the color values for each neighboring value + // This creates a 2x2 matrix which is then sampled during interpolation + final int m00 = this.getColor(slice, state, scratchPos.set(blockX + 0, blockY, blockZ + 0)); + final int m01 = this.getColor(slice, state, scratchPos.set(blockX + 0, blockY, blockZ + 1)); + final int m10 = this.getColor(slice, state, scratchPos.set(blockX + 1, blockY, blockZ + 0)); + final int m11 = this.getColor(slice, state, scratchPos.set(blockX + 1, blockY, blockZ + 1)); + + // Perform interpolation across the X-axis, and then Y-axis + // y0 = (m00 * (1.0 - x)) + (m10 * x) + // y1 = (m01 * (1.0 - x)) + (m11 * x) + // result = (y0 * (1.0 - y)) + (y1 * y) + return ColorMixer.mix2d(m00, m01, m10, m11, fracX, fracZ); } protected abstract int getColor(LevelSlice slice, T state, BlockPos pos); diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/SodiumWorldRenderer.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/SodiumWorldRenderer.java index 428b7eed1c..c2e72fb2e4 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/SodiumWorldRenderer.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/SodiumWorldRenderer.java @@ -22,6 +22,7 @@ import net.caffeinemc.mods.sodium.client.services.PlatformBlockAccess; import net.caffeinemc.mods.sodium.client.util.NativeBuffer; import net.caffeinemc.mods.sodium.client.world.LevelRendererExtension; +import net.caffeinemc.mods.sodium.mixin.core.render.world.EntityRendererAccessor; import net.minecraft.client.Camera; import net.minecraft.client.Minecraft; import net.minecraft.client.multiplayer.ClientLevel; @@ -31,11 +32,14 @@ import net.minecraft.client.renderer.RenderBuffers; import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.blockentity.BlockEntityRenderDispatcher; +import net.minecraft.client.renderer.entity.EntityRenderer; +import net.minecraft.client.renderer.entity.state.EntityRenderState; import net.minecraft.client.resources.model.ModelBakery; import net.minecraft.core.BlockPos; import net.minecraft.core.SectionPos; import net.minecraft.server.level.BlockDestructionProgress; import net.minecraft.util.Mth; +import net.minecraft.util.profiling.Profiler; import net.minecraft.util.profiling.ProfilerFiller; import net.minecraft.world.entity.Entity; import net.minecraft.world.level.block.entity.BlockEntity; @@ -172,7 +176,7 @@ public void setupTerrain(Camera camera, this.reload(); } - ProfilerFiller profiler = this.client.getProfiler(); + ProfilerFiller profiler = Profiler.get(); profiler.push("camera_setup"); LocalPlayer player = this.client.player; @@ -186,7 +190,7 @@ public void setupTerrain(Camera camera, Matrix4f projectionMatrix = new Matrix4f(RenderSystem.getProjectionMatrix()); float pitch = camera.getXRot(); float yaw = camera.getYRot(); - float fogDistance = RenderSystem.getShaderFogEnd(); + float fogDistance = RenderSystem.getShaderFog().end(); if (this.lastCameraPos == null) { this.lastCameraPos = new Vector3d(pos); @@ -476,7 +480,7 @@ public void iterateVisibleBlockEntities(Consumer blockEntityConsume * Returns whether or not the entity intersects with any visible chunks in the graph. * @return True if the entity is visible, otherwise false */ - public boolean isEntityVisible(Entity entity) { + public boolean isEntityVisible(EntityRenderer renderer, T entity) { if (!this.useEntityCulling) { return true; } @@ -486,7 +490,7 @@ public boolean isEntityVisible(Entity entity) { return true; } - AABB bb = entity.getBoundingBoxForCulling(); + AABB bb = ((EntityRendererAccessor) renderer).getCullingBox(entity); // bail on very large entities to avoid checking many sections double entityVolume = (bb.maxX - bb.minX) * (bb.maxY - bb.minY) * (bb.maxZ - bb.minZ); @@ -501,7 +505,7 @@ public boolean isEntityVisible(Entity entity) { public boolean isBoxVisible(double x1, double y1, double z1, double x2, double y2, double z2) { // Boxes outside the valid level height will never map to a rendered chunk // Always render these boxes, or they'll be culled incorrectly! - if (y2 < this.level.getMinBuildHeight() + 0.5D || y1 > this.level.getMaxBuildHeight() - 0.5D) { + if (y2 < this.level.getMinY() + 0.5D || y1 > this.level.getMaxY() - 0.5D) { return true; } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/DefaultChunkRenderer.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/DefaultChunkRenderer.java index 19b131f4e0..cee84de9c7 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/DefaultChunkRenderer.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/DefaultChunkRenderer.java @@ -1,7 +1,6 @@ package net.caffeinemc.mods.sodium.client.render.chunk; import net.caffeinemc.mods.sodium.client.SodiumClientMod; -import net.caffeinemc.mods.sodium.client.gl.attribute.GlVertexAttributeBinding; import net.caffeinemc.mods.sodium.client.gl.device.CommandList; import net.caffeinemc.mods.sodium.client.gl.device.DrawCommandList; import net.caffeinemc.mods.sodium.client.gl.device.MultiDrawBatch; @@ -16,13 +15,13 @@ import net.caffeinemc.mods.sodium.client.render.chunk.lists.ChunkRenderList; import net.caffeinemc.mods.sodium.client.render.chunk.lists.ChunkRenderListIterable; import net.caffeinemc.mods.sodium.client.render.chunk.region.RenderRegion; -import net.caffeinemc.mods.sodium.client.render.chunk.shader.ChunkShaderBindingPoints; import net.caffeinemc.mods.sodium.client.render.chunk.shader.ChunkShaderInterface; import net.caffeinemc.mods.sodium.client.render.chunk.terrain.TerrainRenderPass; import net.caffeinemc.mods.sodium.client.render.chunk.translucent_sorting.SortBehavior; import net.caffeinemc.mods.sodium.client.render.chunk.vertex.format.ChunkVertexType; import net.caffeinemc.mods.sodium.client.render.viewport.CameraTransform; import net.caffeinemc.mods.sodium.client.util.BitwiseMath; +import net.caffeinemc.mods.sodium.client.util.UInt32; import org.lwjgl.system.MemoryUtil; import java.util.Iterator; @@ -71,7 +70,7 @@ public void render(ChunkRenderMatrices matrices, continue; } - fillCommandBuffer(this.batch, region, storage, renderList, camera, renderPass, useBlockFaceCulling); + fillCommandBuffer(this.batch, region, storage, renderList, camera, renderPass, useBlockFaceCulling, useIndexedTessellation); if (this.batch.isEmpty()) { continue; @@ -108,7 +107,8 @@ private static void fillCommandBuffer(MultiDrawBatch batch, ChunkRenderList renderList, CameraTransform camera, TerrainRenderPass pass, - boolean useBlockFaceCulling) { + boolean useBlockFaceCulling, + boolean useIndexedTessellation) { batch.clear(); var iterator = renderList.sectionsWithGeometryIterator(pass.isTranslucent()); @@ -148,44 +148,48 @@ private static void fillCommandBuffer(MultiDrawBatch batch, continue; } - addDrawCommands(batch, pMeshData, slices); - } - } - - /** - * Add the draw command into the multi draw batch of the current region for one - * section. The section's mesh data is given as a pointer into the render data - * storage's allocated memory. It goes through each direction and writes the - * offsets and lengths of the already uploaded vertex and index data. The multi - * draw batch provides pointers to arrays where each of the section's data is - * stored. The batch's size counts how many commands it contains. - */ - private static void addDrawCommands(MultiDrawBatch batch, long pMeshData, int mask) { - int elementOffset = SectionRenderDataUnsafe.getBaseElement(pMeshData); - - // If high bit is set, the indices should be sourced from the arena's index buffer - if ((elementOffset & SectionRenderDataUnsafe.BASE_ELEMENT_MSB) != 0) { - addIndexedDrawCommands(batch, pMeshData, mask); - } else { - addNonIndexedDrawCommands(batch, pMeshData, mask); + // it's necessary to sometimes not the locally-indexed command generator even for indexed tessellations since + // sometimes the index buffer is shared, but not globally shared. This means that translucent sections that + // are sharing an index buffer amongst them need to use the shared index command generator since it sets the + // same element offset for each draw command and doesn't increment it. Recall that in each draw command the indexing + // of the elements needs to start at 0 and thus starting somewhere further into the shared index buffer is invalid. + // there's also the optimization that draw commands can be combined when using a shared index buffer, be it + // globally shared or just shared within the region, which isn't possible with the locally-indexed command generator. + if (useIndexedTessellation && SectionRenderDataUnsafe.isLocalIndex(pMeshData)) { + addLocalIndexedDrawCommands(batch, pMeshData, slices); + } else { + addSharedIndexedDrawCommands(batch, pMeshData, slices); + } } } /** - * Generates the draw commands for a chunk's meshes using the shared index buffer. + * Generates the draw commands for a chunk's meshes, where each mesh has a separate index buffer. This is used + * when rendering translucent geometry, as each geometry set needs a sorted index buffer. */ @SuppressWarnings("IntegerMultiplicationImplicitCastToLong") - private static void addNonIndexedDrawCommands(MultiDrawBatch batch, long pMeshData, int mask) { + private static void addLocalIndexedDrawCommands(MultiDrawBatch batch, long pMeshData, int mask) { final var pElementPointer = batch.pElementPointer; final var pBaseVertex = batch.pBaseVertex; final var pElementCount = batch.pElementCount; int size = batch.size; + long elementOffset = SectionRenderDataUnsafe.getBaseElement(pMeshData); + long baseVertex = SectionRenderDataUnsafe.getBaseVertex(pMeshData); + for (int facing = 0; facing < ModelQuadFacing.COUNT; facing++) { - MemoryUtil.memPutInt(pBaseVertex + (size << 2), SectionRenderDataUnsafe.getVertexOffset(pMeshData, facing)); - MemoryUtil.memPutInt(pElementCount + (size << 2), SectionRenderDataUnsafe.getElementCount(pMeshData, facing)); - MemoryUtil.memPutAddress(pElementPointer + (size << 3), 0 /* using a shared index buffer */); + final long vertexCount = SectionRenderDataUnsafe.getVertexCount(pMeshData, facing); + final long elementCount = (vertexCount >> 2) * 6; + + MemoryUtil.memPutInt(pElementCount + (size << 2), UInt32.uncheckedDowncast(elementCount)); + MemoryUtil.memPutInt(pBaseVertex + (size << 2), UInt32.uncheckedDowncast(baseVertex)); + + // * 4 to convert to bytes (the index buffer contains integers) + MemoryUtil.memPutAddress(pElementPointer + (size << 3), elementOffset << 2); + + baseVertex += vertexCount; + elementOffset += elementCount; size += (mask >> facing) & 1; } @@ -194,33 +198,57 @@ private static void addNonIndexedDrawCommands(MultiDrawBatch batch, long pMeshDa } /** - * Generates the draw commands for a chunk's meshes, where each mesh has a separate index buffer. This is used - * when rendering translucent geometry, as each geometry set needs a sorted index buffer. + * Generates the draw commands for a chunk's meshes using the shared index buffer. */ @SuppressWarnings("IntegerMultiplicationImplicitCastToLong") - private static void addIndexedDrawCommands(MultiDrawBatch batch, long pMeshData, int mask) { + private static void addSharedIndexedDrawCommands(MultiDrawBatch batch, long pMeshData, int mask) { final var pElementPointer = batch.pElementPointer; final var pBaseVertex = batch.pBaseVertex; final var pElementCount = batch.pElementCount; - int size = batch.size; - - int elementOffset = SectionRenderDataUnsafe.getBaseElement(pMeshData) - & ~SectionRenderDataUnsafe.BASE_ELEMENT_MSB; - - for (int facing = 0; facing < ModelQuadFacing.COUNT; facing++) { - final var elementCount = SectionRenderDataUnsafe.getElementCount(pMeshData, facing); + // this is either zero (global shared index buffer) or the offset to the location of the shared element buffer (region shared index buffer) + final var elementOffsetBytes = SectionRenderDataUnsafe.getBaseElement(pMeshData) << 2; + final var facingList = SectionRenderDataUnsafe.getFacingList(pMeshData); - MemoryUtil.memPutInt(pBaseVertex + (size << 2), SectionRenderDataUnsafe.getVertexOffset(pMeshData, facing)); - MemoryUtil.memPutInt(pElementCount + (size << 2), elementCount); + int size = batch.size; + long groupVertexCount = 0; + long baseVertex = SectionRenderDataUnsafe.getBaseVertex(pMeshData); + int lastMaskBit = 0; + + for (int i = 0; i <= ModelQuadFacing.COUNT; i++) { + var maskBit = 0; + long vertexCount = 0; + if (i < ModelQuadFacing.COUNT) { + vertexCount = SectionRenderDataUnsafe.getVertexCount(pMeshData, i); + + // if there's no vertexes, the mask bit is just 0 + if (vertexCount != 0) { + var facing = (facingList >>> (i * 8)) & 0xFF; + maskBit = (mask >>> facing) & 1; + } + } - // * 4 to convert to bytes (the buffer contains 32-bit integers) - // the section render data storage for the indices stores the offset in indices (also called elements) - MemoryUtil.memPutAddress(pElementPointer + (size << 3), elementOffset << 2); + if (maskBit == 0) { + if (lastMaskBit == 1) { + // delay writing out draw command if there's a zero-size group + if (i < ModelQuadFacing.COUNT && vertexCount == 0) { + continue; + } + + MemoryUtil.memPutInt(pElementCount + (size << 2), UInt32.uncheckedDowncast((groupVertexCount >> 2) * 6)); + MemoryUtil.memPutInt(pBaseVertex + (size << 2), UInt32.uncheckedDowncast(baseVertex)); + MemoryUtil.memPutAddress(pElementPointer + (size << 3), elementOffsetBytes); + size++; + baseVertex += groupVertexCount; + groupVertexCount = 0; + } + + baseVertex += vertexCount; + } else { + groupVertexCount += vertexCount; + } - // adding the number of elements works because the index data has one index per element (which are the indices) - elementOffset += elementCount; - size += (mask >> facing) & 1; + lastMaskBit = maskBit; } batch.size = size; @@ -235,7 +263,7 @@ private static void addIndexedDrawCommands(MultiDrawBatch batch, long pMeshData, private static final int MODEL_NEG_Y = ModelQuadFacing.NEG_Y.ordinal(); private static final int MODEL_NEG_Z = ModelQuadFacing.NEG_Z.ordinal(); - private static int getVisibleFaces(int originX, int originY, int originZ, int chunkX, int chunkY, int chunkZ) { + public static int getVisibleFaces(int originX, int originY, int originZ, int chunkX, int chunkY, int chunkZ) { // This is carefully written so that we can keep everything branch-less. // // Normally, this would be a ridiculous way to handle the problem. But the Hotspot VM's diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/RenderSection.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/RenderSection.java index 0103850155..9aa933474c 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/RenderSection.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/RenderSection.java @@ -69,9 +69,9 @@ public RenderSection(RenderRegion region, int chunkX, int chunkY, int chunkZ) { this.chunkY = chunkY; this.chunkZ = chunkZ; - int rX = this.getChunkX() & (RenderRegion.REGION_WIDTH - 1); - int rY = this.getChunkY() & (RenderRegion.REGION_HEIGHT - 1); - int rZ = this.getChunkZ() & (RenderRegion.REGION_LENGTH - 1); + int rX = this.getChunkX() & RenderRegion.REGION_WIDTH_M; + int rY = this.getChunkY() & RenderRegion.REGION_HEIGHT_M; + int rZ = this.getChunkZ() & RenderRegion.REGION_LENGTH_M; this.sectionIndex = LocalSectionIndex.pack(rX, rY, rZ); @@ -139,30 +139,44 @@ public void delete() { this.disposed = true; } - public void setInfo(@Nullable BuiltSectionInfo info) { + public boolean setInfo(@Nullable BuiltSectionInfo info) { if (info != null) { - this.setRenderState(info); + return this.setRenderState(info); } else { - this.clearRenderState(); + return this.clearRenderState(); } } - private void setRenderState(@NotNull BuiltSectionInfo info) { + private boolean setRenderState(@NotNull BuiltSectionInfo info) { + var prevBuilt = this.built; + var prevFlags = this.flags; + var prevVisibilityData = this.visibilityData; + this.built = true; this.flags = info.flags; this.visibilityData = info.visibilityData; + this.globalBlockEntities = info.globalBlockEntities; this.culledBlockEntities = info.culledBlockEntities; this.animatedSprites = info.animatedSprites; + + // the section is marked as having received graph-relevant changes if it's build state, flags, or connectedness has changed. + // the entities and sprites don't need to be checked since whether they exist is encoded in the flags. + return !prevBuilt || prevFlags != this.flags || prevVisibilityData != this.visibilityData; } - private void clearRenderState() { + private boolean clearRenderState() { + var wasBuilt = this.built; + this.built = false; this.flags = RenderSectionFlags.NONE; this.visibilityData = VisibilityEncoding.NULL; this.globalBlockEntities = null; this.culledBlockEntities = null; this.animatedSprites = null; + + // changes to data if it moves from built to not built don't matter, so only build state changes matter + return wasBuilt; } /** diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/RenderSectionManager.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/RenderSectionManager.java index 21aae7a7e1..97f57148f7 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/RenderSectionManager.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/RenderSectionManager.java @@ -146,7 +146,7 @@ private void createTerrainRenderList(Camera camera, Viewport viewport, int frame this.occlusionCuller.findVisible(visitor, viewport, searchDistance, useOcclusionCulling, frame); - this.renderLists = visitor.createRenderLists(); + this.renderLists = visitor.createRenderLists(viewport); this.taskLists = visitor.getRebuildLists(); } @@ -167,7 +167,7 @@ private boolean shouldUseOcclusionCulling(Camera camera, boolean spectator) { BlockPos origin = camera.getBlockPosition(); if (spectator && this.level.getBlockState(origin) - .isSolidRender(this.level, origin)) + .isSolidRender()) { useOcclusionCulling = false; } else { @@ -209,6 +209,7 @@ public void onSectionAdded(int x, int y, int z) { this.connectNeighborNodes(renderSection); + // force update to schedule build task this.needsGraphUpdate = true; } @@ -235,6 +236,7 @@ public void onSectionRemoved(int x, int y, int z) { section.delete(); + // force update to remove section from render lists this.needsGraphUpdate = true; } @@ -301,7 +303,7 @@ public void uploadChunks() { // (sort results never change the graph) // generally there's no sort results without a camera movement, which would also trigger // a graph update, but it can sometimes happen because of async task execution - this.needsGraphUpdate = this.needsGraphUpdate || this.processChunkBuildResults(results); + this.needsGraphUpdate |= this.processChunkBuildResults(results); for (var result : results) { result.destroy(); @@ -317,8 +319,7 @@ private boolean processChunkBuildResults(ArrayList results) { for (var result : filtered) { TranslucentData oldData = result.render.getTranslucentData(); if (result instanceof ChunkBuildOutput chunkBuildOutput) { - this.updateSectionInfo(result.render, chunkBuildOutput.info); - touchedSectionInfo = true; + touchedSectionInfo |= this.updateSectionInfo(result.render, chunkBuildOutput.info); if (chunkBuildOutput.translucentData != null) { this.sortTriggering.integrateTranslucentData(oldData, chunkBuildOutput.translucentData, this.cameraPosition, this::scheduleSort); @@ -327,9 +328,9 @@ private boolean processChunkBuildResults(ArrayList results) { result.render.setTranslucentData(chunkBuildOutput.translucentData); } } else if (result instanceof ChunkSortOutput sortOutput - && sortOutput.getTopoSorter() != null + && sortOutput.getDynamicSorter() != null && result.render.getTranslucentData() instanceof DynamicTopoData data) { - this.sortTriggering.applyTriggerChanges(data, sortOutput.getTopoSorter(), result.render.getPosition(), this.cameraPosition); + this.sortTriggering.applyTriggerChanges(data, sortOutput.getDynamicSorter(), result.render.getPosition(), this.cameraPosition); } var job = result.render.getTaskCancellationToken(); @@ -346,13 +347,13 @@ private boolean processChunkBuildResults(ArrayList results) { return touchedSectionInfo; } - private void updateSectionInfo(RenderSection render, BuiltSectionInfo info) { - render.setInfo(info); + private boolean updateSectionInfo(RenderSection render, BuiltSectionInfo info) { + var infoChanged = render.setInfo(info); if (info == null || ArrayUtils.isEmpty(info.globalBlockEntities)) { - this.sectionsWithGlobalEntities.remove(render); + return this.sectionsWithGlobalEntities.remove(render) || infoChanged; } else { - this.sectionsWithGlobalEntities.add(render); + return this.sectionsWithGlobalEntities.add(render) || infoChanged; } } @@ -609,6 +610,7 @@ public void scheduleRebuild(int x, int y, int z, boolean important) { if (pendingUpdate != null) { section.setPendingUpdate(pendingUpdate); + // force update to schedule rebuild task on this section this.needsGraphUpdate = true; } } @@ -626,13 +628,13 @@ private static boolean allowImportantRebuilds() { } private float getEffectiveRenderDistance() { - var color = RenderSystem.getShaderFogColor(); - var distance = RenderSystem.getShaderFogEnd(); + var alpha = RenderSystem.getShaderFog().alpha(); + var distance = RenderSystem.getShaderFog().end(); var renderDistance = this.getRenderDistance(); // The fog must be fully opaque in order to skip rendering of chunks behind it - if (!Mth.equal(color[3], 1.0f)) { + if (!Mth.equal(alpha, 1.0f)) { return renderDistance; } @@ -676,8 +678,10 @@ public Collection getDebugStrings() { int count = 0; - long deviceUsed = 0; - long deviceAllocated = 0; + long geometryDeviceUsed = 0; + long geometryDeviceAllocated = 0; + long indexDeviceUsed = 0; + long indexDeviceAllocated = 0; for (var region : this.regions.getLoadedRegions()) { var resources = region.getResources(); @@ -686,15 +690,19 @@ public Collection getDebugStrings() { continue; } - var buffer = resources.getGeometryArena(); + var geometryArena = resources.getGeometryArena(); + geometryDeviceUsed += geometryArena.getDeviceUsedMemory(); + geometryDeviceAllocated += geometryArena.getDeviceAllocatedMemory(); - deviceUsed += buffer.getDeviceUsedMemory(); - deviceAllocated += buffer.getDeviceAllocatedMemory(); + var indexArena = resources.getIndexArena(); + indexDeviceUsed += indexArena.getDeviceUsedMemory(); + indexDeviceAllocated += indexArena.getDeviceAllocatedMemory(); count++; } - list.add(String.format("Geometry Pool: %d/%d MiB (%d buffers)", MathUtil.toMib(deviceUsed), MathUtil.toMib(deviceAllocated), count)); + list.add(String.format("Geometry Pool: %d/%d MiB (%d buffers)", MathUtil.toMib(geometryDeviceUsed), MathUtil.toMib(geometryDeviceAllocated), count)); + list.add(String.format("Index Pool: %d/%d MiB", MathUtil.toMib(indexDeviceUsed), MathUtil.toMib(indexDeviceAllocated))); list.add(String.format("Transfer Queue: %s", this.regions.getStagingBuffer().toString())); list.add(String.format("Chunk Builder: Permits=%02d (E %03d) | Busy=%02d | Total=%02d", @@ -723,13 +731,13 @@ public boolean isSectionBuilt(int x, int y, int z) { } public void onChunkAdded(int x, int z) { - for (int y = this.level.getMinSection(); y < this.level.getMaxSection(); y++) { + for (int y = this.level.getMinSectionY(); y <= this.level.getMaxSectionY(); y++) { this.onSectionAdded(x, y, z); } } public void onChunkRemoved(int x, int z) { - for (int y = this.level.getMinSection(); y < this.level.getMaxSection(); y++) { + for (int y = this.level.getMinSectionY(); y <= this.level.getMaxSectionY(); y++) { this.onSectionRemoved(x, y, z); } } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/ShaderChunkRenderer.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/ShaderChunkRenderer.java index fecce3ba8f..675e8ae632 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/ShaderChunkRenderer.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/ShaderChunkRenderer.java @@ -42,7 +42,7 @@ private GlProgram createShader(String path, ChunkShaderOpt GlShader vertShader = ShaderLoader.loadShader(ShaderType.VERTEX, ResourceLocation.fromNamespaceAndPath("sodium", path + ".vsh"), constants); - + GlShader fragShader = ShaderLoader.loadShader(ShaderType.FRAGMENT, ResourceLocation.fromNamespaceAndPath("sodium", path + ".fsh"), constants); @@ -50,8 +50,7 @@ private GlProgram createShader(String path, ChunkShaderOpt return GlProgram.builder(ResourceLocation.fromNamespaceAndPath("sodium", "chunk_shader")) .attachShader(vertShader) .attachShader(fragShader) - .bindAttribute("a_PositionHi", ChunkShaderBindingPoints.ATTRIBUTE_POSITION_HI) - .bindAttribute("a_PositionLo", ChunkShaderBindingPoints.ATTRIBUTE_POSITION_LO) + .bindAttribute("a_Position", ChunkShaderBindingPoints.ATTRIBUTE_POSITION) .bindAttribute("a_Color", ChunkShaderBindingPoints.ATTRIBUTE_COLOR) .bindAttribute("a_TexCoord", ChunkShaderBindingPoints.ATTRIBUTE_TEXTURE) .bindAttribute("a_LightAndData", ChunkShaderBindingPoints.ATTRIBUTE_LIGHT_MATERIAL_INDEX) diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/SharedQuadIndexBuffer.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/SharedQuadIndexBuffer.java index e21dc96476..1526d6408f 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/SharedQuadIndexBuffer.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/SharedQuadIndexBuffer.java @@ -7,6 +7,7 @@ import net.caffeinemc.mods.sodium.client.gl.device.CommandList; import net.caffeinemc.mods.sodium.client.gl.tessellation.GlIndexType; import net.caffeinemc.mods.sodium.client.gl.util.EnumBitField; +import net.caffeinemc.mods.sodium.client.util.NativeBuffer; import java.nio.ByteBuffer; import java.nio.IntBuffer; @@ -55,6 +56,14 @@ private void grow(CommandList commandList, int primitiveCount) { this.maxPrimitives = primitiveCount; } + public static NativeBuffer createIndexBuffer(IndexType indexType, int primitiveCount) { + var bufferSize = primitiveCount * indexType.getBytesPerElement() * ELEMENTS_PER_PRIMITIVE; + var buffer = new NativeBuffer(bufferSize); + + indexType.createIndexBuffer(buffer.getDirectBuffer(), primitiveCount); + + return buffer; + } public GlBuffer getBufferObject() { return this.buffer; diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/compile/ChunkBuildBuffers.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/compile/ChunkBuildBuffers.java index eae5389deb..189a5ad3ff 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/compile/ChunkBuildBuffers.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/compile/ChunkBuildBuffers.java @@ -6,6 +6,7 @@ import net.caffeinemc.mods.sodium.client.render.chunk.compile.buffers.ChunkModelBuilder; import net.caffeinemc.mods.sodium.client.render.chunk.data.BuiltSectionInfo; import net.caffeinemc.mods.sodium.client.render.chunk.data.BuiltSectionMeshParts; +import net.caffeinemc.mods.sodium.client.render.chunk.data.SectionRenderDataUnsafe; import net.caffeinemc.mods.sodium.client.render.chunk.terrain.DefaultTerrainRenderPasses; import net.caffeinemc.mods.sodium.client.render.chunk.terrain.TerrainRenderPass; import net.caffeinemc.mods.sodium.client.render.chunk.terrain.material.Material; @@ -13,10 +14,6 @@ import net.caffeinemc.mods.sodium.client.render.chunk.vertex.format.ChunkVertexType; import net.caffeinemc.mods.sodium.client.util.NativeBuffer; -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.List; - /** * A collection of temporary buffers for each worker thread which will be used to build chunk meshes for given render * passes. This makes a best-effort attempt to pick a suitable size for each scratch buffer, but will never try to @@ -60,47 +57,79 @@ public ChunkModelBuilder get(TerrainRenderPass pass) { * have been rendered to pass the finished meshes over to the graphics card. This function can be called multiple * times to return multiple copies. */ - public BuiltSectionMeshParts createMesh(TerrainRenderPass pass, boolean forceUnassigned) { + public BuiltSectionMeshParts createMesh(TerrainRenderPass pass, int visibleSlices, boolean forceUnassigned, boolean sliceReordering) { var builder = this.builders.get(pass); + int[] vertexSegments = new int[ModelQuadFacing.COUNT << 1]; + int vertexTotal = 0; - List vertexBuffers = new ArrayList<>(); - int[] vertexCounts = new int[ModelQuadFacing.COUNT]; + // get the total vertex count to initialize the buffer + for (ModelQuadFacing facing : ModelQuadFacing.VALUES) { + vertexTotal += builder.getVertexBuffer(facing).count(); + } - int vertexSum = 0; + if (vertexTotal == 0) { + return null; + } - for (ModelQuadFacing facing : ModelQuadFacing.VALUES) { - var ordinal = facing.ordinal(); - var buffer = builder.getVertexBuffer(facing); + var mergedBuffer = new NativeBuffer(vertexTotal * this.vertexType.getVertexFormat().getStride()); + var mergedBufferBuilder = mergedBuffer.getDirectBuffer(); - if (buffer.isEmpty()) { - continue; + if (sliceReordering) { + // sliceReordering implies !forceUnassigned + + // write all currently visible slices first, and then the rest. + // start with unassigned as it will never become invisible + var unassignedBuffer = builder.getVertexBuffer(ModelQuadFacing.UNASSIGNED); + int vertexSegmentCount = 0; + vertexSegments[vertexSegmentCount++] = unassignedBuffer.count(); + vertexSegments[vertexSegmentCount++] = ModelQuadFacing.UNASSIGNED.ordinal(); + if (!unassignedBuffer.isEmpty()) { + mergedBufferBuilder.put(unassignedBuffer.slice()); } - vertexBuffers.add(buffer.slice()); - var bufferCount = buffer.count(); - if (!forceUnassigned) { - vertexCounts[ordinal] = bufferCount; - } + // write all visible and then invisible slices + for (var step = 0; step < 2; step++) { + for (ModelQuadFacing facing : ModelQuadFacing.VALUES) { + var facingIndex = facing.ordinal(); + if (facing == ModelQuadFacing.UNASSIGNED || ((visibleSlices >> facingIndex) & 1) == step) { + continue; + } - vertexSum += bufferCount; - } + var buffer = builder.getVertexBuffer(facing); - if (vertexSum == 0) { - return null; - } + // generate empty ranges to prevent SectionRenderData storage from making up indexes for null ranges + vertexSegments[vertexSegmentCount++] = buffer.count(); + vertexSegments[vertexSegmentCount++] = facingIndex; - if (forceUnassigned) { - vertexCounts[ModelQuadFacing.UNASSIGNED.ordinal()] = vertexSum; - } + if (!buffer.isEmpty()) { + mergedBufferBuilder.put(buffer.slice()); + } + } + } + } else { + // forceUnassigned implies !sliceReordering - var mergedBuffer = new NativeBuffer(vertexSum * this.vertexType.getVertexFormat().getStride()); - var mergedBufferBuilder = mergedBuffer.getDirectBuffer(); + if (forceUnassigned) { + var segmentIndex = ModelQuadFacing.UNASSIGNED.ordinal() << 1; + vertexSegments[segmentIndex] = vertexTotal; + vertexSegments[segmentIndex + 1] = ModelQuadFacing.UNASSIGNED.ordinal(); + } - for (var buffer : vertexBuffers) { - mergedBufferBuilder.put(buffer); + for (ModelQuadFacing facing : ModelQuadFacing.VALUES) { + var buffer = builder.getVertexBuffer(facing); + if (!buffer.isEmpty()) { + if (!forceUnassigned) { + var facingIndex = facing.ordinal(); + var segmentIndex = facingIndex << 1; + vertexSegments[segmentIndex] = buffer.count(); + vertexSegments[segmentIndex + 1] = facingIndex; + } + mergedBufferBuilder.put(buffer.slice()); + } + } } - return new BuiltSectionMeshParts(mergedBuffer, vertexCounts); + return new BuiltSectionMeshParts(mergedBuffer, vertexSegments); } public void destroy() { diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/compile/ChunkSortOutput.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/compile/ChunkSortOutput.java index 52236a161e..1e17585537 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/compile/ChunkSortOutput.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/compile/ChunkSortOutput.java @@ -2,14 +2,11 @@ import net.caffeinemc.mods.sodium.client.render.chunk.RenderSection; import net.caffeinemc.mods.sodium.client.render.chunk.translucent_sorting.data.DynamicTopoData; -import net.caffeinemc.mods.sodium.client.render.chunk.translucent_sorting.data.SortData; import net.caffeinemc.mods.sodium.client.render.chunk.translucent_sorting.data.Sorter; -import net.caffeinemc.mods.sodium.client.util.NativeBuffer; -public class ChunkSortOutput extends BuilderTaskOutput implements SortData { - private NativeBuffer indexBuffer; +public class ChunkSortOutput extends BuilderTaskOutput { + private Sorter sorter; private boolean reuseUploadedIndexData; - private DynamicTopoData.DynamicTopoSorter topoSorter; public ChunkSortOutput(RenderSection render, int buildTime) { super(render, buildTime); @@ -17,41 +14,34 @@ public ChunkSortOutput(RenderSection render, int buildTime) { public ChunkSortOutput(RenderSection render, int buildTime, Sorter data) { this(render, buildTime); - this.copyResultFrom(data); + this.setSorter(data); } - public void copyResultFrom(Sorter sorter) { - this.indexBuffer = sorter.getIndexBuffer(); + public void setSorter(Sorter sorter) { + this.sorter = sorter; this.reuseUploadedIndexData = false; - if (sorter instanceof DynamicTopoData.DynamicTopoSorter topoSorterInstance) { - this.topoSorter = topoSorterInstance; - } } - public void markAsReusingUploadedData() { - this.reuseUploadedIndexData = true; + public Sorter getSorter() { + return this.sorter; } - @Override - public NativeBuffer getIndexBuffer() { - return this.indexBuffer; + public void markAsReusingUploadedData() { + this.reuseUploadedIndexData = true; } - @Override public boolean isReusingUploadedIndexData() { return this.reuseUploadedIndexData; } - public DynamicTopoData.DynamicTopoSorter getTopoSorter() { - return this.topoSorter; + public DynamicTopoData.DynamicTopoSorter getDynamicSorter() { + return this.sorter instanceof DynamicTopoData.DynamicTopoSorter dynamicSorter ? dynamicSorter : null; } - @Override public void destroy() { super.destroy(); - - if (this.indexBuffer != null) { - this.indexBuffer.free(); + if (this.sorter != null) { + this.sorter.destroy(); } } } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/compile/executor/ChunkBuilder.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/compile/executor/ChunkBuilder.java index 792903a5b5..e251f27e89 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/compile/executor/ChunkBuilder.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/compile/executor/ChunkBuilder.java @@ -1,10 +1,13 @@ package net.caffeinemc.mods.sodium.client.render.chunk.compile.executor; +import com.mojang.jtracy.TracyClient; +import com.mojang.jtracy.Zone; import net.caffeinemc.mods.sodium.client.SodiumClientMod; import net.caffeinemc.mods.sodium.client.render.chunk.compile.BuilderTaskOutput; import net.caffeinemc.mods.sodium.client.render.chunk.compile.ChunkBuildContext; import net.caffeinemc.mods.sodium.client.render.chunk.compile.tasks.ChunkBuilderTask; import net.caffeinemc.mods.sodium.client.render.chunk.vertex.format.ChunkVertexType; +import net.minecraft.SharedConstants; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.util.Mth; import org.apache.commons.lang3.Validate; @@ -48,7 +51,7 @@ public ChunkBuilder(ClientLevel level, ChunkVertexType vertexType) { for (int i = 0; i < count; i++) { ChunkBuildContext context = new ChunkBuildContext(level, vertexType); - WorkerRunnable worker = new WorkerRunnable(context); + WorkerRunnable worker = new WorkerRunnable("Chunk Render Task Executor #" + i, context); Thread thread = new Thread(worker, "Chunk Render Task Executor #" + i); thread.setPriority(Math.max(0, Thread.NORM_PRIORITY - 2)); @@ -184,9 +187,11 @@ public int getTotalThreadCount() { private class WorkerRunnable implements Runnable { // Making this thread-local provides a small boost to performance by avoiding the overhead in synchronizing // caches between different CPU cores + private final String name; private final ChunkBuildContext context; - public WorkerRunnable(ChunkBuildContext context) { + public WorkerRunnable(String name, ChunkBuildContext context) { + this.name = name; this.context = context; } @@ -209,6 +214,8 @@ public void run() { ChunkBuilder.this.busyThreadCount.getAndIncrement(); + Zone zone = TracyClient.beginZone(name, SharedConstants.IS_RUNNING_IN_IDE); + try { job.execute(this.context); } finally { @@ -216,6 +223,8 @@ public void run() { ChunkBuilder.this.busyThreadCount.decrementAndGet(); } + + zone.close(); } } } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/compile/pipeline/BlockOcclusionCache.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/compile/pipeline/BlockOcclusionCache.java index 294b2b96e4..f157fac5b1 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/compile/pipeline/BlockOcclusionCache.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/compile/pipeline/BlockOcclusionCache.java @@ -8,6 +8,7 @@ import net.minecraft.core.Direction; import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.material.FluidState; import net.minecraft.world.phys.shapes.BooleanOp; import net.minecraft.world.phys.shapes.Shapes; import net.minecraft.world.phys.shapes.VoxelShape; @@ -30,53 +31,131 @@ public BlockOcclusionCache() { } /** - * @param selfState The state of the block in the level - * @param view The block view for this render context - * @param selfPos The position of the block - * @param facing The facing direction of the side to check + * @param selfBlockState The state of the block in the level + * @param view The block view for this render context + * @param selfPos The position of the block + * @param facing The facing direction of the side to check * @return True if the block side facing {@param dir} is not occluded, otherwise false */ - public boolean shouldDrawSide(BlockState selfState, BlockGetter view, BlockPos selfPos, Direction facing) { - BlockPos.MutableBlockPos otherPos = this.cachedPositionObject; - otherPos.set(selfPos.getX() + facing.getStepX(), selfPos.getY() + facing.getStepY(), selfPos.getZ() + facing.getStepZ()); + public boolean shouldDrawSide(BlockState selfBlockState, BlockGetter view, BlockPos selfPos, Direction facing) { + BlockPos.MutableBlockPos neighborPos = this.cachedPositionObject; + neighborPos.setWithOffset(selfPos, facing); - BlockState otherState = view.getBlockState(otherPos); + // The block state of the neighbor + BlockState neighborBlockState = view.getBlockState(neighborPos); + + // The cull shape of the neighbor between the block being rendered and it + VoxelShape neighborShape = neighborBlockState.getFaceOcclusionShape(DirectionUtil.getOpposite(facing)); + + // Minecraft enforces that if the neighbor has a full-block occlusion shape, the face is always hidden + if (isFullShape(neighborShape)) { + return false; + } - // Blocks can define special behavior to control whether faces are rendered. + // Blocks can define special behavior to control whether their faces are rendered. // This is mostly used by transparent blocks (Leaves, Glass, etc.) to not render interior faces between blocks // of the same type. - if (selfState.skipRendering(otherState, facing) || PlatformBlockAccess.getInstance().shouldSkipRender(view, selfState, otherState, selfPos, otherPos, facing)) { + if (selfBlockState.skipRendering(neighborBlockState, facing)) { + return false; + } else if (PlatformBlockAccess.getInstance() + .shouldSkipRender(view, selfBlockState, neighborBlockState, selfPos, neighborPos, facing)) { return false; } - // If the other block is transparent, then it is unable to hide any geometry. - if (!otherState.canOcclude()) { + // After any custom behavior has been handled, check if the neighbor block is transparent or has an empty + // cull shape. These blocks cannot hide any geometry. + if (isEmptyShape(neighborShape) || !neighborBlockState.canOcclude()) { return true; } - // The cull shape of the block being rendered - VoxelShape selfShape = selfState.getFaceOcclusionShape(view, selfPos, facing); + // The cull shape between of the block being rendered, between it and the neighboring block + VoxelShape selfShape = selfBlockState.getFaceOcclusionShape(facing); - // If the block being rendered has an empty cull shape, intersection tests will always fail - if (selfShape.isEmpty()) { + // If the block being rendered has an empty cull shape, there will be no intersection with the neighboring + // block's cull shape, so no geometry can be hidden. + if (isEmptyShape(selfShape)) { return true; } - // The cull shape of the block neighboring the one being rendered - VoxelShape otherShape = otherState.getFaceOcclusionShape(view, otherPos, DirectionUtil.getOpposite(facing)); + // No other simplifications apply, so we need to perform a full shape comparison, which is very slow + return this.lookup(selfShape, neighborShape); + } - // If the other block has an empty cull shape, then it cannot hide any geometry - if (otherShape.isEmpty()) { - return true; + private static boolean isFullShape(VoxelShape selfShape) { + return selfShape == Shapes.block(); + } + + private static boolean isEmptyShape(VoxelShape voxelShape) { + return voxelShape == Shapes.empty() || voxelShape.isEmpty(); + } + + /** + * Checks if a face of a fluid block should be rendered. It takes into account both occluding fluid face against its own waterlogged block state and the neighboring block state. This is an approximation that doesn't check voxel for shapes between the fluid and the neighboring block since that is handled by the fluid renderer separately and more accurately using actual fluid heights. It only uses voxel shape comparison for checking self-occlusion with the waterlogged block state. + * + * @param selfBlockState The state of the block in the level + * @param view The block view for this render context + * @param selfPos The position of the fluid + * @param facing The facing direction of the side to check + * @param fluid The fluid state + * @param fluidShape The non-empty shape of the fluid + * @return True if the fluid side facing {@param dir} is not occluded, otherwise false + */ + public boolean shouldDrawFullBlockFluidSide(BlockState selfBlockState, BlockGetter view, BlockPos selfPos, Direction facing, FluidState fluid, VoxelShape fluidShape) { + var fluidShapeIsBlock = fluidShape == Shapes.block(); + + // only perform self-occlusion if the own block state can't occlude + if (selfBlockState.canOcclude()) { + var selfShape = selfBlockState.getFaceOcclusionShape(facing); + + // only a non-empty self-shape can occlude anything + if (!isEmptyShape(selfShape)) { + // a full self-shape occludes everything + if (isFullShape(selfShape) && fluidShapeIsBlock) { + return false; + } + + // perform occlusion of the fluid by the block it's contained in + if (!this.lookup(fluidShape, selfShape)) { + return false; + } + } + } + + // perform occlusion against the neighboring block + BlockPos.MutableBlockPos otherPos = this.cachedPositionObject; + otherPos.set(selfPos.getX() + facing.getStepX(), selfPos.getY() + facing.getStepY(), selfPos.getZ() + facing.getStepZ()); + BlockState otherState = view.getBlockState(otherPos); + + // don't render anything if the other blocks is the same fluid + if (otherState.getFluidState() == fluid) { + return false; } - // If both blocks use a full-cube cull shape, then they will always hide the faces between each other - if (selfShape == Shapes.block() && otherShape == Shapes.block()) { + // check for special fluid occlusion behavior + if (PlatformBlockAccess.getInstance().shouldOccludeFluid(facing.getOpposite(), otherState, fluid)) { return false; } - // No other simplifications apply, so we need to perform a full shape comparison, which is very slow - return this.lookup(selfShape, otherShape); + // the up direction doesn't do occlusion with other block shapes + if (facing == Direction.UP) { + return true; + } + + // only occlude against blocks that can potentially occlude in the first place + if (!otherState.canOcclude()) { + return true; + } + + var otherShape = otherState.getFaceOcclusionShape(facing.getOpposite()); + + // If the other block has an empty cull shape, then it cannot hide any geometry + if (isEmptyShape(otherShape)) { + return true; + } + + // If both blocks use a full-cube cull shape, then they will always hide the faces between each other. + // No voxel shape comparison is done after this point because it's redundant with the later more accurate check. + return !isFullShape(otherShape) || !fluidShapeIsBlock; } private boolean lookup(VoxelShape self, VoxelShape other) { diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/compile/pipeline/BlockRenderer.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/compile/pipeline/BlockRenderer.java index 23cf5303aa..e042e129d7 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/compile/pipeline/BlockRenderer.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/compile/pipeline/BlockRenderer.java @@ -2,6 +2,8 @@ import net.caffeinemc.mods.sodium.api.util.ColorABGR; import net.caffeinemc.mods.sodium.api.util.ColorARGB; +import net.caffeinemc.mods.sodium.api.util.ColorMixer; +import net.caffeinemc.mods.sodium.client.compatibility.workarounds.Workarounds; import net.caffeinemc.mods.sodium.client.model.color.ColorProvider; import net.caffeinemc.mods.sodium.client.model.color.ColorProviderRegistry; import net.caffeinemc.mods.sodium.client.model.light.LightMode; @@ -19,7 +21,6 @@ import net.caffeinemc.mods.sodium.client.render.chunk.translucent_sorting.TranslucentGeometryCollector; import net.caffeinemc.mods.sodium.client.render.chunk.vertex.builder.ChunkMeshBufferBuilder; import net.caffeinemc.mods.sodium.client.render.chunk.vertex.format.ChunkVertexEncoder; -import net.caffeinemc.mods.sodium.client.render.frapi.helper.ColorHelper; import net.caffeinemc.mods.sodium.client.render.frapi.mesh.MutableQuadViewImpl; import net.caffeinemc.mods.sodium.client.render.frapi.render.AbstractBlockRenderContext; import net.caffeinemc.mods.sodium.client.render.texture.SpriteFinderCache; @@ -84,7 +85,7 @@ public void renderModel(BakedModel model, BlockState state, BlockPos pos, BlockP this.posOffset.set(origin.getX(), origin.getY(), origin.getZ()); if (state.hasOffsetFunction()) { - Vec3 modelOffset = state.getOffset(this.level, pos); + Vec3 modelOffset = state.getOffset(pos); this.posOffset.add((float) modelOffset.x, (float) modelOffset.y, (float) modelOffset.z); } @@ -101,7 +102,7 @@ public void renderModel(BakedModel model, BlockState state, BlockPos pos, BlockP for (RenderType type : renderTypes) { this.type = type; - ((FabricBakedModel) model).emitBlockQuads(this.level, state, pos, this.randomSupplier, this); + ((FabricBakedModel) model).emitBlockQuads(getEmitter(), this.level, state, pos, this.randomSupplier, this::isFaceCulled); } type = null; @@ -114,7 +115,6 @@ public void renderModel(BakedModel model, BlockState state, BlockPos pos, BlockP @Override protected void processQuad(MutableQuadViewImpl quad) { final RenderMaterial mat = quad.material(); - final int colorIndex = mat.disableColorIndex() ? -1 : quad.colorIndex(); final TriState aoMode = mat.ambientOcclusion(); final ShadeMode shadeMode = mat.shadeMode(); final LightMode lightMode; @@ -134,13 +134,15 @@ protected void processQuad(MutableQuadViewImpl quad) { material = DefaultMaterials.forRenderLayer(blendMode.blockRenderLayer == null ? type : blendMode.blockRenderLayer); } - this.colorizeQuad(quad, colorIndex); + this.tintQuad(quad); this.shadeQuad(quad, lightMode, emissive, shadeMode); this.bufferQuad(quad, this.quadLightData.br, material); } - private void colorizeQuad(MutableQuadViewImpl quad, int colorIndex) { - if (colorIndex != -1) { + private void tintQuad(MutableQuadViewImpl quad) { + int tintIndex = quad.tintIndex(); + + if (tintIndex != -1) { ColorProvider colorProvider = this.colorProvider; if (colorProvider != null) { @@ -148,7 +150,7 @@ private void colorizeQuad(MutableQuadViewImpl quad, int colorIndex) { colorProvider.getColors(this.slice, this.pos, this.scratchPos, this.state, quad, vertexColors); for (int i = 0; i < 4; i++) { - quad.color(i, ColorHelper.multiplyColor(vertexColors[i], quad.color(i))); + quad.color(i, ColorMixer.mulComponentWise(vertexColors[i], quad.color(i))); } } } @@ -184,6 +186,7 @@ private void bufferQuad(MutableQuadViewImpl quad, float[] brightnesses, Material // attempt render pass downgrade if possible var pass = material.pass; + var downgradedPass = attemptPassDowngrade(atlasSprite, pass); if (downgradedPass != null) { pass = downgradedPass; @@ -225,7 +228,11 @@ private boolean validateQuadUVs(TextureAtlasSprite atlasSprite) { return true; } - private TerrainRenderPass attemptPassDowngrade(TextureAtlasSprite sprite, TerrainRenderPass pass) { + private @Nullable TerrainRenderPass attemptPassDowngrade(TextureAtlasSprite sprite, TerrainRenderPass pass) { + if (Workarounds.isWorkaroundEnabled(Workarounds.Reference.INTEL_DEPTH_BUFFER_COMPARISON_UNRELIABLE)) { + return null; + } + boolean attemptDowngrade = true; boolean hasNonOpaqueVertex = false; diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/compile/pipeline/DefaultFluidRenderer.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/compile/pipeline/DefaultFluidRenderer.java index b49cde9421..cf89774909 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/compile/pipeline/DefaultFluidRenderer.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/compile/pipeline/DefaultFluidRenderer.java @@ -4,7 +4,6 @@ import net.caffeinemc.mods.sodium.api.util.ColorARGB; import net.caffeinemc.mods.sodium.api.util.NormI8; import net.caffeinemc.mods.sodium.client.model.color.ColorProvider; -import net.caffeinemc.mods.sodium.client.model.color.ColorProviderRegistry; import net.caffeinemc.mods.sodium.client.model.light.LightMode; import net.caffeinemc.mods.sodium.client.model.light.LightPipeline; import net.caffeinemc.mods.sodium.client.model.light.LightPipelineProvider; @@ -28,8 +27,6 @@ import net.minecraft.tags.FluidTags; import net.minecraft.util.Mth; import net.minecraft.world.level.BlockAndTintGetter; -import net.minecraft.world.level.block.LiquidBlock; -import net.minecraft.world.level.block.SupportType; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.material.Fluid; import net.minecraft.world.level.material.FluidState; @@ -49,6 +46,8 @@ public class DefaultFluidRenderer { private final MutableFloat scratchHeight = new MutableFloat(0); private final MutableInt scratchSamples = new MutableInt(); + private final BlockOcclusionCache occlusionCache = new BlockOcclusionCache(); + private final ModelQuadViewMutable quad = new ModelQuad(); private final LightPipelineProvider lighters; @@ -65,21 +64,10 @@ public DefaultFluidRenderer(LightPipelineProvider lighters) { this.lighters = lighters; } - private boolean isFluidOccluded(BlockAndTintGetter world, int x, int y, int z, Direction dir, BlockState blockState, Fluid fluid) { - //Test own block state first, this prevents waterlogged blocks from having hidden internal geometry - // which can result in z-fighting - var pos = this.scratchPos.set(x, y, z); - if (blockState.canOcclude() && blockState.isFaceSturdy(world, pos, dir, SupportType.FULL)) { - return true; - } - - //Test neighboring block state - var adjPos = this.scratchPos.set(x + dir.getStepX(), y + dir.getStepY(), z + dir.getStepZ()); - BlockState adjBlockState = world.getBlockState(adjPos); - if (adjBlockState.getFluidState().getType().isSame(fluid)) { - return true; - } - return adjBlockState.canOcclude() && dir != Direction.UP && adjBlockState.isFaceSturdy(world, adjPos, dir.getOpposite(), SupportType.FULL); + private boolean isFullBlockFluidOccluded(BlockAndTintGetter world, BlockPos pos, Direction dir, BlockState blockState, FluidState fluid) { + // check if this face of the fluid, assuming a full-block cull shape, is occluded by the block it's in or a neighboring block. + // it doesn't do a voxel shape comparison with the neighboring blocks since that is already done by isSideExposed + return !this.occlusionCache.shouldDrawFullBlockFluidSide(blockState, world, pos, dir, fluid, Shapes.block()); } private boolean isSideExposed(BlockAndTintGetter world, int x, int y, int z, Direction dir, float height) { @@ -87,7 +75,7 @@ private boolean isSideExposed(BlockAndTintGetter world, int x, int y, int z, Dir BlockState blockState = world.getBlockState(pos); if (blockState.canOcclude()) { - VoxelShape shape = blockState.getOcclusionShape(world, pos); + VoxelShape shape = blockState.getOcclusionShape(); // Hoist these checks to avoid allocating the shape below if (shape.isEmpty()) { @@ -109,15 +97,16 @@ public void render(LevelSlice level, BlockState blockState, FluidState fluidStat Fluid fluid = fluidState.getType(); - boolean sfUp = this.isFluidOccluded(level, posX, posY, posZ, Direction.UP, blockState, fluid); - boolean sfDown = this.isFluidOccluded(level, posX, posY, posZ, Direction.DOWN, blockState, fluid) || + boolean cullUp = this.isFullBlockFluidOccluded(level, blockPos, Direction.UP, blockState, fluidState); + boolean cullDown = this.isFullBlockFluidOccluded(level, blockPos, Direction.DOWN, blockState, fluidState) || !this.isSideExposed(level, posX, posY, posZ, Direction.DOWN, 0.8888889F); - boolean sfNorth = this.isFluidOccluded(level, posX, posY, posZ, Direction.NORTH, blockState, fluid); - boolean sfSouth = this.isFluidOccluded(level, posX, posY, posZ, Direction.SOUTH, blockState, fluid); - boolean sfWest = this.isFluidOccluded(level, posX, posY, posZ, Direction.WEST, blockState, fluid); - boolean sfEast = this.isFluidOccluded(level, posX, posY, posZ, Direction.EAST, blockState, fluid); + boolean cullNorth = this.isFullBlockFluidOccluded(level, blockPos, Direction.NORTH, blockState, fluidState); + boolean cullSouth = this.isFullBlockFluidOccluded(level, blockPos, Direction.SOUTH, blockState, fluidState); + boolean cullWest = this.isFullBlockFluidOccluded(level, blockPos, Direction.WEST, blockState, fluidState); + boolean cullEast = this.isFullBlockFluidOccluded(level, blockPos, Direction.EAST, blockState, fluidState); - if (sfUp && sfDown && sfEast && sfWest && sfNorth && sfSouth) { + // stop rendering if all faces of the fluid are occluded + if (cullUp && cullDown && cullEast && cullWest && cullNorth && cullSouth) { return; } @@ -149,7 +138,7 @@ public void render(LevelSlice level, BlockState blockState, FluidState fluidStat .move(Direction.NORTH) .move(Direction.EAST)); } - float yOffset = sfDown ? 0.0F : EPSILON; + float yOffset = cullDown ? 0.0F : EPSILON; final ModelQuadViewMutable quad = this.quad; @@ -158,7 +147,7 @@ public void render(LevelSlice level, BlockState blockState, FluidState fluidStat quad.setFlags(0); - if (!sfUp && this.isSideExposed(level, posX, posY, posZ, Direction.UP, Math.min(Math.min(northWestHeight, southWestHeight), Math.min(southEastHeight, northEastHeight)))) { + if (!cullUp && this.isSideExposed(level, posX, posY, posZ, Direction.UP, Math.min(Math.min(northWestHeight, southWestHeight), Math.min(southEastHeight, northEastHeight)))) { northWestHeight -= EPSILON; southWestHeight -= EPSILON; southEastHeight -= EPSILON; @@ -243,7 +232,7 @@ && isAlignedEquals(southEastHeight, southWestHeight) } } - if (!sfDown) { + if (!cullDown) { TextureAtlasSprite sprite = sprites[0]; float minU = sprite.getU0(); @@ -273,7 +262,7 @@ && isAlignedEquals(southEastHeight, southWestHeight) switch (dir) { case NORTH -> { - if (sfNorth) { + if (cullNorth) { continue; } c1 = northWestHeight; @@ -284,7 +273,7 @@ && isAlignedEquals(southEastHeight, southWestHeight) z2 = z1; } case SOUTH -> { - if (sfSouth) { + if (cullSouth) { continue; } c1 = southEastHeight; @@ -295,7 +284,7 @@ && isAlignedEquals(southEastHeight, southWestHeight) z2 = z1; } case WEST -> { - if (sfWest) { + if (cullWest) { continue; } c1 = southWestHeight; @@ -306,7 +295,7 @@ && isAlignedEquals(southEastHeight, southWestHeight) z2 = 0.0f; } case EAST -> { - if (sfEast) { + if (cullEast) { continue; } c1 = northEastHeight; diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/compile/tasks/ChunkBuilderMeshingTask.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/compile/tasks/ChunkBuilderMeshingTask.java index f3992c935a..a328e552c6 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/compile/tasks/ChunkBuilderMeshingTask.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/compile/tasks/ChunkBuilderMeshingTask.java @@ -3,6 +3,7 @@ import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap; import net.caffeinemc.mods.sodium.client.SodiumClientMod; import net.caffeinemc.mods.sodium.client.render.chunk.ExtendedBlockEntityType; +import net.caffeinemc.mods.sodium.client.render.chunk.DefaultChunkRenderer; import net.caffeinemc.mods.sodium.client.render.chunk.RenderSection; import net.caffeinemc.mods.sodium.client.render.chunk.compile.ChunkBuildBuffers; import net.caffeinemc.mods.sodium.client.render.chunk.compile.ChunkBuildContext; @@ -32,6 +33,8 @@ import net.minecraft.client.renderer.chunk.VisGraph; import net.minecraft.client.resources.model.BakedModel; import net.minecraft.core.BlockPos; +import net.minecraft.util.profiling.Profiler; +import net.minecraft.util.profiling.ProfilerFiller; import net.minecraft.world.level.block.RenderShape; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; @@ -57,6 +60,7 @@ public ChunkBuilderMeshingTask(RenderSection render, int buildTime, Vector3dc ab @Override public ChunkBuildOutput execute(ChunkBuildContext buildContext, CancellationToken cancellationToken) { + ProfilerFiller profiler = Profiler.get(); BuiltSectionInfo.Builder renderData = new BuiltSectionInfo.Builder(); VisGraph occluder = new VisGraph(); @@ -82,13 +86,14 @@ public ChunkBuildOutput execute(ChunkBuildContext buildContext, CancellationToke TranslucentGeometryCollector collector; if (SodiumClientMod.options().performance.getSortBehavior() != SortBehavior.OFF) { - collector = new TranslucentGeometryCollector(render.getPosition()); + collector = new TranslucentGeometryCollector(this.render.getPosition()); } else { collector = null; } BlockRenderer blockRenderer = cache.getBlockRenderer(); blockRenderer.prepare(buffers, slice, collector); + profiler.push("render blocks"); try { for (int y = minY; y < maxY; y++) { if (cancellationToken.isCancelled()) { @@ -130,7 +135,7 @@ public ChunkBuildOutput execute(ChunkBuildContext buildContext, CancellationToke } } - if (blockState.isSolidRender(slice, blockPos)) { + if (blockState.isSolidRender()) { occluder.setOpaque(blockPos); } } @@ -143,8 +148,9 @@ public ChunkBuildOutput execute(ChunkBuildContext buildContext, CancellationToke // Create a new crash report for other exceptions (e.g. thrown in getQuads) throw fillCrashInfo(CrashReport.forThrowable(ex, "Encountered exception while building chunk meshes"), slice, blockPos); } + profiler.popPush("mesh appenders"); - PlatformLevelRenderHooks.INSTANCE.runChunkMeshAppenders(renderContext.getRenderers(), type -> buffers.get(DefaultMaterials.forRenderLayer(type)).asFallbackVertexConsumer(DefaultMaterials.forRenderLayer(type), collector), + PlatformLevelRenderHooks.INSTANCE.runChunkMeshAppenders(this.renderContext.getRenderers(), type -> buffers.get(DefaultMaterials.forRenderLayer(type)).asFallbackVertexConsumer(DefaultMaterials.forRenderLayer(type), collector), slice); blockRenderer.release(); @@ -155,11 +161,18 @@ public ChunkBuildOutput execute(ChunkBuildContext buildContext, CancellationToke } Map meshes = new Reference2ReferenceOpenHashMap<>(); + var visibleSlices = DefaultChunkRenderer.getVisibleFaces( + (int) this.absoluteCameraPos.x(), (int) this.absoluteCameraPos.y(), (int) this.absoluteCameraPos.z(), + this.render.getChunkX(), this.render.getChunkY(), this.render.getChunkZ()); + profiler.popPush("meshing"); for (TerrainRenderPass pass : DefaultTerrainRenderPasses.ALL) { - // consolidate all translucent geometry into UNASSIGNED so that it's rendered - // all together if it needs to share an index buffer between the directions - BuiltSectionMeshParts mesh = buffers.createMesh(pass, pass.isTranslucent() && sortType.needsDirectionMixing); + // if the translucent geometry needs to share an index buffer between the directions, + // consolidate all translucent geometry into UNASSIGNED + boolean translucentBehavior = collector != null && pass.isTranslucent(); + boolean forceUnassigned = translucentBehavior && sortType.needsDirectionMixing; + boolean sliceReordering = !translucentBehavior || sortType.allowSliceReordering; + BuiltSectionMeshParts mesh = buffers.createMesh(pass, visibleSlices, forceUnassigned, sliceReordering); if (mesh != null) { meshes.put(pass, mesh); @@ -170,11 +183,14 @@ public ChunkBuildOutput execute(ChunkBuildContext buildContext, CancellationToke // cancellation opportunity right before translucent sorting if (cancellationToken.isCancelled()) { meshes.forEach((pass, mesh) -> mesh.getVertexData().free()); + profiler.pop(); return null; } renderData.setOcclusionData(occluder.resolve()); + profiler.popPush("translucency sorting"); + boolean reuseUploadedData = false; TranslucentData translucentData = null; if (collector != null) { @@ -185,16 +201,19 @@ public ChunkBuildOutput execute(ChunkBuildContext buildContext, CancellationToke } var output = new ChunkBuildOutput(this.render, this.submitTime, translucentData, renderData.build(), meshes); + if (collector != null) { if (reuseUploadedData) { output.markAsReusingUploadedData(); } else if (translucentData instanceof PresentTranslucentData present) { var sorter = present.getSorter(); sorter.writeIndexBuffer(this, true); - output.copyResultFrom(sorter); + output.setSorter(sorter); } } + profiler.pop(); + return output; } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/compile/tasks/ChunkBuilderSortingTask.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/compile/tasks/ChunkBuilderSortingTask.java index c4efd4ddc1..d3178ceb8e 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/compile/tasks/ChunkBuilderSortingTask.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/compile/tasks/ChunkBuilderSortingTask.java @@ -1,6 +1,8 @@ package net.caffeinemc.mods.sodium.client.render.chunk.compile.tasks; import net.caffeinemc.mods.sodium.client.render.chunk.translucent_sorting.data.Sorter; +import net.minecraft.util.profiling.Profiler; +import net.minecraft.util.profiling.ProfilerFiller; import org.joml.Vector3dc; import net.caffeinemc.mods.sodium.client.render.chunk.RenderSection; @@ -23,7 +25,13 @@ public ChunkSortOutput execute(ChunkBuildContext context, CancellationToken canc if (cancellationToken.isCancelled()) { return null; } + + ProfilerFiller profiler = Profiler.get(); + profiler.push("translucency sorting"); + this.sorter.writeIndexBuffer(this, false); + + profiler.pop(); return new ChunkSortOutput(this.render, this.submitTime, this.sorter); } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/data/BuiltSectionMeshParts.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/data/BuiltSectionMeshParts.java index ec250afb86..21a1736347 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/data/BuiltSectionMeshParts.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/data/BuiltSectionMeshParts.java @@ -1,13 +1,14 @@ package net.caffeinemc.mods.sodium.client.render.chunk.data; +import net.caffeinemc.mods.sodium.client.model.quad.properties.ModelQuadFacing; import net.caffeinemc.mods.sodium.client.util.NativeBuffer; public class BuiltSectionMeshParts { - private final int[] vertexCounts; + private final int[] vertexSegments; private final NativeBuffer buffer; public BuiltSectionMeshParts(NativeBuffer buffer, int[] vertexCounts) { - this.vertexCounts = vertexCounts; + this.vertexSegments = vertexCounts; this.buffer = buffer; } @@ -15,7 +16,17 @@ public NativeBuffer getVertexData() { return this.buffer; } - public int[] getVertexCounts() { - return this.vertexCounts; + public int[] getVertexSegments() { + return this.vertexSegments; + } + + public int[] computeVertexCounts() { + var vertexCounts = new int[ModelQuadFacing.COUNT]; + + for (int i = 0; i < this.vertexSegments.length; i += 2) { + vertexCounts[this.vertexSegments[i + 1]] = this.vertexSegments[i]; + } + + return vertexCounts; } } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/data/SectionRenderDataStorage.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/data/SectionRenderDataStorage.java index 87f9998284..99fbe9e69a 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/data/SectionRenderDataStorage.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/data/SectionRenderDataStorage.java @@ -1,12 +1,18 @@ package net.caffeinemc.mods.sodium.client.render.chunk.data; +import net.caffeinemc.mods.sodium.client.gl.arena.GlBufferArena; import net.caffeinemc.mods.sodium.client.gl.arena.GlBufferSegment; +import net.caffeinemc.mods.sodium.client.gl.arena.PendingUpload; +import net.caffeinemc.mods.sodium.client.gl.device.CommandList; import net.caffeinemc.mods.sodium.client.model.quad.properties.ModelQuadFacing; +import net.caffeinemc.mods.sodium.client.render.chunk.SharedQuadIndexBuffer; import net.caffeinemc.mods.sodium.client.render.chunk.region.RenderRegion; +import net.caffeinemc.mods.sodium.client.util.UInt32; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Arrays; +import java.util.stream.Stream; /** * The section render data storage stores the gl buffer segments of uploaded @@ -16,20 +22,24 @@ * buffer segments is stored in a natively allocated piece of memory referenced * by {@code pMeshDataArray} and accessed through * {@link SectionRenderDataUnsafe}. - * + *

* When the backing buffer (from the gl buffer arena) is resized, the storage * object is notified and then it updates the changed offsets of the buffer * segments. Since the index data's size and alignment directly corresponds to * that of the vertex data except for the vertex/index scaling of two thirds, * only an offset to the index data within the index data buffer arena is * stored. - * + *

* Index and vertex data storage can be managed separately since they may be * updated independently of each other (in both directions). */ public class SectionRenderDataStorage { private final @Nullable GlBufferSegment[] vertexAllocations; - private final @Nullable GlBufferSegment @Nullable[] elementAllocations; + private final @Nullable GlBufferSegment @Nullable [] elementAllocations; + private @Nullable GlBufferSegment sharedIndexAllocation; + private int sharedIndexCapacity = 0; + private boolean needsSharedIndexUpdate = false; + private final int[] sharedIndexUsage = new int[RenderRegion.REGION_SIZE]; private final long pMeshDataArray; @@ -45,8 +55,7 @@ public SectionRenderDataStorage(boolean storesIndices) { this.pMeshDataArray = SectionRenderDataUnsafe.allocateHeap(RenderRegion.REGION_SIZE); } - public void setVertexData(int localSectionIndex, - GlBufferSegment allocation, int[] vertexCounts) { + public void setVertexData(int localSectionIndex, GlBufferSegment allocation, int[] vertexSegments) { GlBufferSegment prev = this.vertexAllocations[localSectionIndex]; if (prev != null) { @@ -58,22 +67,25 @@ public void setVertexData(int localSectionIndex, var pMeshData = this.getDataPointer(localSectionIndex); int sliceMask = 0; - int vertexOffset = allocation.getOffset(); + long facingList = 0; - for (int facingIndex = 0; facingIndex < ModelQuadFacing.COUNT; facingIndex++) { - int vertexCount = vertexCounts[facingIndex]; + for (int i = 0; i < ModelQuadFacing.COUNT; i++) { + var segmentIndex = i << 1; - SectionRenderDataUnsafe.setVertexOffset(pMeshData, facingIndex, vertexOffset); - SectionRenderDataUnsafe.setElementCount(pMeshData, facingIndex, (vertexCount >> 2) * 6); + int facing = vertexSegments[segmentIndex + 1]; + facingList |= (long) facing << (i * 8); + + long vertexCount = UInt32.upcast(vertexSegments[segmentIndex]); + SectionRenderDataUnsafe.setVertexCount(pMeshData, i, vertexCount); if (vertexCount > 0) { - sliceMask |= 1 << facingIndex; + sliceMask |= 1 << facing; } - - vertexOffset += vertexCount; } + SectionRenderDataUnsafe.setBaseVertex(pMeshData, allocation.getOffset()); SectionRenderDataUnsafe.setSliceMask(pMeshData, sliceMask); + SectionRenderDataUnsafe.setFacingList(pMeshData, facingList); } public void setIndexData(int localSectionIndex, GlBufferSegment allocation) { @@ -91,8 +103,88 @@ public void setIndexData(int localSectionIndex, GlBufferSegment allocation) { var pMeshData = this.getDataPointer(localSectionIndex); - SectionRenderDataUnsafe.setBaseElement(pMeshData, - allocation.getOffset() | SectionRenderDataUnsafe.BASE_ELEMENT_MSB); + SectionRenderDataUnsafe.setLocalBaseElement(pMeshData, allocation.getOffset()); + } + + public void setSharedIndexUsage(int localSectionIndex, int newUsage) { + var previousUsage = this.sharedIndexUsage[localSectionIndex]; + if (previousUsage == newUsage) { + return; + } + + // mark for update if usage is down from max (may need to shrink buffer) + // or if usage increased beyond the max (need to grow buffer) + if (newUsage < previousUsage && previousUsage == this.sharedIndexCapacity || + newUsage > this.sharedIndexCapacity || + newUsage > 0 && this.sharedIndexAllocation == null) { + this.needsSharedIndexUpdate = true; + } else { + // just set the base element since no update is happening + var sharedBaseElement = this.sharedIndexAllocation.getOffset(); + var pMeshData = this.getDataPointer(localSectionIndex); + SectionRenderDataUnsafe.setSharedBaseElement(pMeshData, sharedBaseElement); + } + + this.sharedIndexUsage[localSectionIndex] = newUsage; + } + + public boolean needsSharedIndexUpdate() { + return this.needsSharedIndexUpdate; + } + + /** + * Updates the shared index data buffer to match the current usage. + * + * @param arena The buffer arena to allocate the new buffer from + * @return true if the arena resized itself + */ + public boolean updateSharedIndexData(CommandList commandList, GlBufferArena arena) { + // assumes this.needsSharedIndexUpdate is true when this is called + this.needsSharedIndexUpdate = false; + + // determine the new required capacity + int newCapacity = 0; + for (int i = 0; i < RenderRegion.REGION_SIZE; i++) { + newCapacity = Math.max(newCapacity, this.sharedIndexUsage[i]); + } + if (newCapacity == this.sharedIndexCapacity) { + return false; + } + + this.sharedIndexCapacity = newCapacity; + + // remove the existing allocation and exit if we don't need to create a new one + if (this.sharedIndexAllocation != null) { + this.sharedIndexAllocation.delete(); + this.sharedIndexAllocation = null; + } + if (this.sharedIndexCapacity == 0) { + return false; + } + + // add some base-level capacity to avoid resizing the buffer too often + if (this.sharedIndexCapacity < 128) { + this.sharedIndexCapacity += 32; + } + + // create and upload a new shared index buffer + var buffer = SharedQuadIndexBuffer.createIndexBuffer(SharedQuadIndexBuffer.IndexType.INTEGER, this.sharedIndexCapacity); + var pendingUpload = new PendingUpload(buffer); + var bufferChanged = arena.upload(commandList, Stream.of(pendingUpload)); + this.sharedIndexAllocation = pendingUpload.getResult(); + buffer.free(); + + // only write the base elements now if we're not going to do so again later because of the buffer resize + if (!bufferChanged) { + var sharedBaseElement = this.sharedIndexAllocation.getOffset(); + for (int i = 0; i < RenderRegion.REGION_SIZE; i++) { + if (this.sharedIndexUsage[i] > 0) { + SectionRenderDataUnsafe.setSharedBaseElement(this.getDataPointer(i), sharedBaseElement); + } + } + } + + return bufferChanged; } public void removeData(int localSectionIndex) { @@ -101,6 +193,8 @@ public void removeData(int localSectionIndex) { if (this.elementAllocations != null) { this.removeIndexData(localSectionIndex); } + + this.setSharedIndexUsage(localSectionIndex, 0); } public void removeVertexData(int localSectionIndex) { @@ -124,7 +218,7 @@ private void removeVertexData(int localSectionIndex, boolean retainIndexData) { SectionRenderDataUnsafe.clear(pMeshData); if (retainIndexData) { - SectionRenderDataUnsafe.setBaseElement(pMeshData, baseElement); + SectionRenderDataUnsafe.setLocalBaseElement(pMeshData, baseElement); } } @@ -157,28 +251,27 @@ private void updateMeshes(int sectionIndex) { return; } - var offset = allocation.getOffset(); var data = this.getDataPointer(sectionIndex); - - for (int facing = 0; facing < ModelQuadFacing.COUNT; facing++) { - SectionRenderDataUnsafe.setVertexOffset(data, facing, offset); - - var count = SectionRenderDataUnsafe.getElementCount(data, facing); - offset += (count / 6) * 4; // convert elements back into vertices - } + long offset = allocation.getOffset(); + SectionRenderDataUnsafe.setBaseVertex(data, offset); } public void onIndexBufferResized() { - if (this.elementAllocations == null) { - return; + long sharedBaseElement = 0; + if (this.sharedIndexAllocation != null) { + sharedBaseElement = this.sharedIndexAllocation.getOffset(); } - for (int sectionIndex = 0; sectionIndex < RenderRegion.REGION_SIZE; sectionIndex++) { - var allocation = this.elementAllocations[sectionIndex]; + for (int i = 0; i < RenderRegion.REGION_SIZE; i++) { + if (this.sharedIndexUsage[i] > 0) { + // update index sharing sections to use the new shared index buffer's offset + SectionRenderDataUnsafe.setSharedBaseElement(this.getDataPointer(i), sharedBaseElement); + } else if (this.elementAllocations != null) { + var allocation = this.elementAllocations[i]; - if (allocation != null) { - SectionRenderDataUnsafe.setBaseElement(this.getDataPointer(sectionIndex), - allocation.getOffset() | SectionRenderDataUnsafe.BASE_ELEMENT_MSB); + if (allocation != null) { + SectionRenderDataUnsafe.setLocalBaseElement(this.getDataPointer(i), allocation.getOffset()); + } } } } @@ -194,6 +287,10 @@ public void delete() { deleteAllocations(this.elementAllocations); } + if (this.sharedIndexAllocation != null) { + this.sharedIndexAllocation.delete(); + } + SectionRenderDataUnsafe.freeHeap(this.pMeshDataArray); } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/data/SectionRenderDataUnsafe.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/data/SectionRenderDataUnsafe.java index cf483e1227..0db9cfb209 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/data/SectionRenderDataUnsafe.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/data/SectionRenderDataUnsafe.java @@ -1,5 +1,6 @@ package net.caffeinemc.mods.sodium.client.render.chunk.data; +import net.caffeinemc.mods.sodium.client.util.UInt32; import org.lwjgl.system.MemoryUtil; // This code is a terrible hack to get around the fact that we are so incredibly memory bound, and that we @@ -15,20 +16,16 @@ // Please never try to write performance critical code in Java. This is what it will do to you. And you will still be // three times slower than the most naive solution in literally any other language that LLVM can compile. -// struct SectionRenderData { // 64 bytes -// base_element: u32 -// mask: u32, -// ranges: [VertexRange; 7] -// } -// -// struct VertexRange { // 8 bytes -// offset: u32, -// count: u32 +// struct SectionRenderData { // 48 bytes +// base_element: u32, +// base_vertex: u32, +// is_local_index: u8, +// facing_list: u56, +// slice_mask: u32, +// vertex_count: [u32; 7] // } public class SectionRenderDataUnsafe { - public static final int BASE_ELEMENT_MSB = 1 << 31; - /** * When the "base element" field is not specified (indicated by setting the MSB to 0), the indices for the geometry set * should be sourced from a monotonic sequence (see {@link net.caffeinemc.mods.sodium.client.render.chunk.SharedQuadIndexBuffer}). @@ -36,9 +33,11 @@ public class SectionRenderDataUnsafe { * Otherwise, indices should be sourced from the index buffer for the render region using the specified offset. */ private static final long OFFSET_BASE_ELEMENT = 0; - - private static final long OFFSET_SLICE_MASK = 4; - private static final long OFFSET_SLICE_RANGES = 8; + private static final long OFFSET_BASE_VERTEX = 4; + private static final long OFFSET_FACING_LIST = 8; + private static final long OFFSET_IS_LOCAL_INDEX = 15; + private static final long OFFSET_SLICE_MASK = 16; + private static final long OFFSET_ELEMENT_COUNTS = 20; private static final long ALIGNMENT = 64; private static final long STRIDE = 64; // cache-line friendly! :) @@ -64,6 +63,20 @@ public static long heapPointer(long ptr, int index) { return ptr + (index * STRIDE); } + public static void setLocalBaseElement(long ptr, long value /* Uint32 */) { + MemoryUtil.memPutInt(ptr + OFFSET_BASE_ELEMENT, UInt32.downcast(value)); + MemoryUtil.memPutByte(ptr + OFFSET_IS_LOCAL_INDEX, (byte) 1); + } + + public static void setSharedBaseElement(long ptr, long value /* Uint32 */) { + MemoryUtil.memPutInt(ptr + OFFSET_BASE_ELEMENT, UInt32.downcast(value)); + MemoryUtil.memPutByte(ptr + OFFSET_IS_LOCAL_INDEX, (byte) 0); + } + + public static long getBaseElement(long ptr) { + return Integer.toUnsignedLong(MemoryUtil.memGetInt(ptr + OFFSET_BASE_ELEMENT)); + } + public static void setSliceMask(long ptr, int value) { MemoryUtil.memPutInt(ptr + OFFSET_SLICE_MASK, value); } @@ -72,27 +85,31 @@ public static int getSliceMask(long ptr) { return MemoryUtil.memGetInt(ptr + OFFSET_SLICE_MASK); } - public static void setBaseElement(long ptr, int value) { - MemoryUtil.memPutInt(ptr + OFFSET_BASE_ELEMENT, value); + public static void setFacingList(long ptr, long facingList) { + MemoryUtil.memPutLong(ptr + OFFSET_FACING_LIST, facingList); + } + + public static long getFacingList(long ptr) { + return MemoryUtil.memGetLong(ptr + OFFSET_FACING_LIST); } - public static int getBaseElement(long ptr) { - return MemoryUtil.memGetInt(ptr + OFFSET_BASE_ELEMENT); + public static boolean isLocalIndex(long ptr) { + return MemoryUtil.memGetByte(ptr + OFFSET_IS_LOCAL_INDEX) != 0; } - public static void setVertexOffset(long ptr, int facing, int value) { - MemoryUtil.memPutInt(ptr + OFFSET_SLICE_RANGES + (facing * 8L) + 0L, value); + public static void setBaseVertex(long ptr, long value /* Uint32 */) { + MemoryUtil.memPutInt(ptr + OFFSET_BASE_VERTEX, UInt32.downcast(value)); } - public static int getVertexOffset(long ptr, int facing) { - return MemoryUtil.memGetInt(ptr + OFFSET_SLICE_RANGES + (facing * 8L) + 0L); + public static long /* Uint32 */ getBaseVertex(long ptr) { + return UInt32.upcast(MemoryUtil.memGetInt(ptr + OFFSET_BASE_VERTEX)); } - public static void setElementCount(long ptr, int facing, int value) { - MemoryUtil.memPutInt(ptr + OFFSET_SLICE_RANGES + (facing * 8L) + 4L, value); + public static void setVertexCount(long ptr, int index, long count /* Uint32 */) { + MemoryUtil.memPutInt(ptr + OFFSET_ELEMENT_COUNTS + (index * 4), UInt32.downcast(count)); } - public static int getElementCount(long ptr, int facing) { - return MemoryUtil.memGetInt(ptr + OFFSET_SLICE_RANGES + (facing * 8L) + 4L); + public static long /* Uint32 */ getVertexCount(long ptr, int index) { + return UInt32.upcast(MemoryUtil.memGetInt(ptr + OFFSET_ELEMENT_COUNTS + (index * 4))); } } \ No newline at end of file diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/lists/ChunkRenderList.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/lists/ChunkRenderList.java index 75e6757df8..e45638b8ad 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/lists/ChunkRenderList.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/lists/ChunkRenderList.java @@ -1,11 +1,14 @@ package net.caffeinemc.mods.sodium.client.render.chunk.lists; +import net.caffeinemc.mods.sodium.client.render.chunk.LocalSectionIndex; import net.caffeinemc.mods.sodium.client.render.chunk.RenderSection; import net.caffeinemc.mods.sodium.client.render.chunk.RenderSectionFlags; +import net.caffeinemc.mods.sodium.client.render.chunk.region.RenderRegion; +import net.caffeinemc.mods.sodium.client.util.iterator.ByteArrayIterator; import net.caffeinemc.mods.sodium.client.util.iterator.ByteIterator; import net.caffeinemc.mods.sodium.client.util.iterator.ReversibleByteArrayIterator; -import net.caffeinemc.mods.sodium.client.util.iterator.ByteArrayIterator; -import net.caffeinemc.mods.sodium.client.render.chunk.region.RenderRegion; +import net.minecraft.core.SectionPos; +import net.minecraft.util.Mth; import org.jetbrains.annotations.Nullable; public class ChunkRenderList { @@ -37,6 +40,39 @@ public void reset(int frame) { this.lastVisibleFrame = frame; } + // clamping the relative camera position to the region bounds means there can only be very few different distances + private static final int SORTING_HISTOGRAM_SIZE = RenderRegion.REGION_WIDTH + RenderRegion.REGION_HEIGHT + RenderRegion.REGION_LENGTH - 2; + + public void sortSections(SectionPos cameraPos, int[] sortItems) { + var cameraX = Mth.clamp(cameraPos.getX() - this.region.getChunkX(), 0, RenderRegion.REGION_WIDTH - 1); + var cameraY = Mth.clamp(cameraPos.getY() - this.region.getChunkY(), 0, RenderRegion.REGION_HEIGHT - 1); + var cameraZ = Mth.clamp(cameraPos.getZ() - this.region.getChunkZ(), 0, RenderRegion.REGION_LENGTH - 1); + + int[] histogram = new int[SORTING_HISTOGRAM_SIZE]; + + for (int i = 0; i < this.sectionsWithGeometryCount; i++) { + var index = this.sectionsWithGeometry[i] & 0xFF; // makes sure the byte -> int conversion is unsigned + var x = Math.abs(LocalSectionIndex.unpackX(index) - cameraX); + var y = Math.abs(LocalSectionIndex.unpackY(index) - cameraY); + var z = Math.abs(LocalSectionIndex.unpackZ(index) - cameraZ); + + var distance = x + y + z; + histogram[distance]++; + sortItems[i] = distance << 8 | index; + } + + // prefix sum to calculate indexes + for (int i = 1; i < SORTING_HISTOGRAM_SIZE; i++) { + histogram[i] += histogram[i - 1]; + } + + for (int i = 0; i < this.sectionsWithGeometryCount; i++) { + var item = sortItems[i]; + var distance = item >>> 8; + this.sectionsWithGeometry[--histogram[distance]] = (byte) item; + } + } + public void add(RenderSection render) { if (this.size >= RenderRegion.REGION_SIZE) { throw new ArrayIndexOutOfBoundsException("Render list is full"); diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/lists/SortedRenderLists.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/lists/SortedRenderLists.java index 1837e0ae9d..080530bf11 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/lists/SortedRenderLists.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/lists/SortedRenderLists.java @@ -1,9 +1,7 @@ package net.caffeinemc.mods.sodium.client.render.chunk.lists; import it.unimi.dsi.fastutil.objects.ObjectArrayList; -import net.caffeinemc.mods.sodium.client.render.chunk.RenderSection; import net.caffeinemc.mods.sodium.client.util.iterator.ReversibleObjectArrayIterator; -import net.caffeinemc.mods.sodium.client.render.chunk.region.RenderRegion; /** * Stores one render list of sections per region, sorted by the order in which @@ -27,44 +25,4 @@ public ReversibleObjectArrayIterator iterator(boolean reverse) public static SortedRenderLists empty() { return EMPTY; } - - public static class Builder { - private final ObjectArrayList lists = new ObjectArrayList<>(); - private final int frame; - - public Builder(int frame) { - this.frame = frame; - } - - public void add(RenderSection section) { - RenderRegion region = section.getRegion(); - ChunkRenderList list = region.getRenderList(); - - // Even if a section does not have render objects, we must ensure the render list is initialized and put - // into the sorted queue of lists, so that we maintain the correct order of draw calls. - if (list.getLastVisibleFrame() != this.frame) { - list.reset(this.frame); - - this.lists.add(list); - } - - // Only add the section to the render list if it actually contains render objects - if (section.getFlags() != 0) { - list.add(section); - } - } - - public SortedRenderLists build() { - var filtered = new ObjectArrayList(this.lists.size()); - - // Filter any empty render lists - for (var list : this.lists) { - if (list.size() > 0) { - filtered.add(list); - } - } - - return new SortedRenderLists(filtered); - } - } } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/lists/VisibleChunkCollector.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/lists/VisibleChunkCollector.java index 5ed657795f..89cf22cf78 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/lists/VisibleChunkCollector.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/lists/VisibleChunkCollector.java @@ -1,12 +1,17 @@ package net.caffeinemc.mods.sodium.client.render.chunk.lists; +import it.unimi.dsi.fastutil.ints.IntArrays; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import net.caffeinemc.mods.sodium.client.render.chunk.ChunkUpdateType; import net.caffeinemc.mods.sodium.client.render.chunk.RenderSection; import net.caffeinemc.mods.sodium.client.render.chunk.occlusion.OcclusionCuller; import net.caffeinemc.mods.sodium.client.render.chunk.region.RenderRegion; +import net.caffeinemc.mods.sodium.client.render.viewport.Viewport; -import java.util.*; +import java.util.ArrayDeque; +import java.util.EnumMap; +import java.util.Map; +import java.util.Queue; /** * The visible chunk collector is passed to the occlusion graph search culler to @@ -30,22 +35,22 @@ public VisibleChunkCollector(int frame) { } @Override - public void visit(RenderSection section, boolean visible) { - RenderRegion region = section.getRegion(); - ChunkRenderList renderList = region.getRenderList(); + public void visit(RenderSection section) { + // only process section (and associated render list) if it has content that needs rendering + if (section.getFlags() != 0) { + RenderRegion region = section.getRegion(); + ChunkRenderList renderList = region.getRenderList(); - // Even if a section does not have render objects, we must ensure the render list is initialized and put - // into the sorted queue of lists, so that we maintain the correct order of draw calls. - if (renderList.getLastVisibleFrame() != this.frame) { - renderList.reset(this.frame); + if (renderList.getLastVisibleFrame() != this.frame) { + renderList.reset(this.frame); - this.sortedRenderLists.add(renderList); - } + this.sortedRenderLists.add(renderList); + } - if (visible && section.getFlags() != 0) { renderList.add(section); } + // always add to rebuild lists though, because it might just not be built yet this.addToRebuildLists(section); } @@ -61,8 +66,42 @@ private void addToRebuildLists(RenderSection section) { } } - public SortedRenderLists createRenderLists() { - return new SortedRenderLists(this.sortedRenderLists); + private static int[] sortItems = new int[RenderRegion.REGION_SIZE]; + + public SortedRenderLists createRenderLists(Viewport viewport) { + // sort the regions by distance to fix rare region ordering bugs + var sectionPos = viewport.getChunkCoord(); + var cameraX = sectionPos.getX() >> RenderRegion.REGION_WIDTH_SH; + var cameraY = sectionPos.getY() >> RenderRegion.REGION_HEIGHT_SH; + var cameraZ = sectionPos.getZ() >> RenderRegion.REGION_LENGTH_SH; + var size = this.sortedRenderLists.size(); + + if (sortItems.length < size) { + sortItems = new int[size]; + } + + for (var i = 0; i < size; i++) { + var region = this.sortedRenderLists.get(i).getRegion(); + var x = Math.abs(region.getX() - cameraX); + var y = Math.abs(region.getY() - cameraY); + var z = Math.abs(region.getZ() - cameraZ); + sortItems[i] = (x + y + z) << 16 | i; + } + + IntArrays.unstableSort(sortItems, 0, size); + + var sorted = new ObjectArrayList(size); + for (var i = 0; i < size; i++) { + var key = sortItems[i]; + var renderList = this.sortedRenderLists.get(key & 0xFFFF); + sorted.add(renderList); + } + + for (var list : sorted) { + list.sortSections(sectionPos, sortItems); + } + + return new SortedRenderLists(sorted); } public Map> getRebuildLists() { diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/occlusion/OcclusionCuller.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/occlusion/OcclusionCuller.java index 3bd0376297..a6550ff8ca 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/occlusion/OcclusionCuller.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/occlusion/OcclusionCuller.java @@ -37,6 +37,8 @@ public void findVisible(Visitor visitor, while (queues.flip()) { processQueue(visitor, viewport, searchDistance, useOcclusionCulling, frame, queues.read(), queues.write()); } + + this.addNearbySections(visitor, viewport, searchDistance, frame); } private static void processQueue(Visitor visitor, @@ -50,21 +52,26 @@ private static void processQueue(Visitor visitor, RenderSection section; while ((section = readQueue.dequeue()) != null) { - boolean visible = isSectionVisible(section, viewport, searchDistance); - visitor.visit(section, visible); - - if (!visible) { + if (!isSectionVisible(section, viewport, searchDistance)) { continue; } + visitor.visit(section); + int connections; { if (useOcclusionCulling) { + var sectionVisibilityData = section.getVisibilityData(); + + // occlude paths through the section if it's being viewed at an angle where + // the other side can't possibly be seen + sectionVisibilityData &= getAngleVisibilityMask(viewport, section); + // When using occlusion culling, we can only traverse into neighbors for which there is a path of // visibility through this chunk. This is determined by taking all the incoming paths to this chunk and // creating a union of the outgoing paths from those. - connections = VisibilityEncoding.getConnections(section.getVisibilityData(), section.getIncomingDirections()); + connections = VisibilityEncoding.getConnections(sectionVisibilityData, section.getIncomingDirections()); } else { // Not using any occlusion culling, so traversing in any direction is legal. connections = GraphDirectionSet.ALL; @@ -79,6 +86,30 @@ private static void processQueue(Visitor visitor, } } + private static final long UP_DOWN_OCCLUDED = (1L << VisibilityEncoding.bit(GraphDirection.DOWN, GraphDirection.UP)) | (1L << VisibilityEncoding.bit(GraphDirection.UP, GraphDirection.DOWN)); + private static final long NORTH_SOUTH_OCCLUDED = (1L << VisibilityEncoding.bit(GraphDirection.NORTH, GraphDirection.SOUTH)) | (1L << VisibilityEncoding.bit(GraphDirection.SOUTH, GraphDirection.NORTH)); + private static final long WEST_EAST_OCCLUDED = (1L << VisibilityEncoding.bit(GraphDirection.WEST, GraphDirection.EAST)) | (1L << VisibilityEncoding.bit(GraphDirection.EAST, GraphDirection.WEST)); + + private static long getAngleVisibilityMask(Viewport viewport, RenderSection section) { + var transform = viewport.getTransform(); + var dx = Math.abs(transform.x - section.getCenterX()); + var dy = Math.abs(transform.y - section.getCenterY()); + var dz = Math.abs(transform.z - section.getCenterZ()); + + var angleOcclusionMask = 0L; + if (dx > dy || dz > dy) { + angleOcclusionMask |= UP_DOWN_OCCLUDED; + } + if (dx > dz || dy > dz) { + angleOcclusionMask |= NORTH_SOUTH_OCCLUDED; + } + if (dy > dx || dz > dx) { + angleOcclusionMask |= WEST_EAST_OCCLUDED; + } + + return ~angleOcclusionMask; + } + private static boolean isSectionVisible(RenderSection section, Viewport viewport, float maxDistance) { return isWithinRenderDistance(viewport.getTransform(), section, maxDistance) && isWithinFrustum(viewport, section); } @@ -179,13 +210,54 @@ private static int nearestToZero(int min, int max) { // The bounding box of a chunk section must be large enough to contain all possible geometry within it. Block models // can extend outside a block volume by +/- 1.0 blocks on all axis. Additionally, we make use of a small epsilon // to deal with floating point imprecision during a frustum check (see GH#2132). - private static final float CHUNK_SECTION_SIZE = 8.0f /* chunk bounds */ + 1.0f /* maximum model extent */ + 0.125f /* epsilon */; + private static final float CHUNK_SECTION_RADIUS = 8.0f /* chunk bounds */; + private static final float CHUNK_SECTION_SIZE = CHUNK_SECTION_RADIUS + 1.0f /* maximum model extent */ + 0.125f /* epsilon */; public static boolean isWithinFrustum(Viewport viewport, RenderSection section) { return viewport.isBoxVisible(section.getCenterX(), section.getCenterY(), section.getCenterZ(), CHUNK_SECTION_SIZE, CHUNK_SECTION_SIZE, CHUNK_SECTION_SIZE); } + // this bigger chunk section size is only used for frustum-testing nearby sections with large models + private static final float CHUNK_SECTION_SIZE_NEARBY = CHUNK_SECTION_RADIUS + 2.0f /* bigger model extent */ + 0.125f /* epsilon */; + + public static boolean isWithinNearbySectionFrustum(Viewport viewport, RenderSection section) { + return viewport.isBoxVisible(section.getCenterX(), section.getCenterY(), section.getCenterZ(), + CHUNK_SECTION_SIZE_NEARBY, CHUNK_SECTION_SIZE_NEARBY, CHUNK_SECTION_SIZE_NEARBY); + } + + // This method visits sections near the origin that are not in the path of the graph traversal + // but have bounding boxes that may intersect with the frustum. It does this additional check + // for all neighboring, even diagonally neighboring, sections around the origin to render them + // if their extended bounding box is visible, and they may render large models that extend + // outside the 16x16x16 base volume of the section. + private void addNearbySections(Visitor visitor, Viewport viewport, float searchDistance, int frame) { + var origin = viewport.getChunkCoord(); + var originX = origin.getX(); + var originY = origin.getY(); + var originZ = origin.getZ(); + + for (var dx = -1; dx <= 1; dx++) { + for (var dy = -1; dy <= 1; dy++) { + for (var dz = -1; dz <= 1; dz++) { + if (dx == 0 && dy == 0 && dz == 0) { + continue; + } + + var section = this.getRenderSection(originX + dx, originY + dy, originZ + dz); + + // additionally render not yet visited but visible sections + if (section != null && section.getLastVisibleFrame() != frame && isWithinNearbySectionFrustum(viewport, section)) { + // reset state on first visit, but don't enqueue + section.setLastVisibleFrame(frame); + + visitor.visit(section); + } + } + } + } + } + private void init(Visitor visitor, WriteQueue queue, Viewport viewport, @@ -195,14 +267,14 @@ private void init(Visitor visitor, { var origin = viewport.getChunkCoord(); - if (origin.getY() < this.level.getMinSection()) { + if (origin.getY() < this.level.getMinSectionY()) { // below the level this.initOutsideWorldHeight(queue, viewport, searchDistance, frame, - this.level.getMinSection(), GraphDirection.DOWN); - } else if (origin.getY() >= this.level.getMaxSection()) { + this.level.getMinSectionY(), GraphDirection.DOWN); + } else if (origin.getY() > this.level.getMaxSectionY()) { // above the level this.initOutsideWorldHeight(queue, viewport, searchDistance, frame, - this.level.getMaxSection() - 1, GraphDirection.UP); + this.level.getMaxSectionY(), GraphDirection.UP); } else { this.initWithinWorld(visitor, queue, viewport, useOcclusionCulling, frame); } @@ -219,7 +291,7 @@ private void initWithinWorld(Visitor visitor, WriteQueue queue, V section.setLastVisibleFrame(frame); section.setIncomingDirections(GraphDirectionSet.NONE); - visitor.visit(section, true); + visitor.visit(section); int outgoing; @@ -305,6 +377,6 @@ private RenderSection getRenderSection(int x, int y, int z) { } public interface Visitor { - void visit(RenderSection section, boolean visible); + void visit(RenderSection section); } } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/occlusion/VisibilityEncoding.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/occlusion/VisibilityEncoding.java index db7e9e88f7..cf184efde0 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/occlusion/VisibilityEncoding.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/occlusion/VisibilityEncoding.java @@ -21,7 +21,7 @@ public static long encode(@NotNull VisibilitySet occlusionData) { return visibilityData; } - private static int bit(int from, int to) { + public static int bit(int from, int to) { return (from * 8) + to; } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/region/RenderRegion.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/region/RenderRegion.java index df6df676d4..e4dc883669 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/region/RenderRegion.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/region/RenderRegion.java @@ -24,13 +24,13 @@ public class RenderRegion { public static final int REGION_HEIGHT = 4; public static final int REGION_LENGTH = 8; - private static final int REGION_WIDTH_M = RenderRegion.REGION_WIDTH - 1; - private static final int REGION_HEIGHT_M = RenderRegion.REGION_HEIGHT - 1; - private static final int REGION_LENGTH_M = RenderRegion.REGION_LENGTH - 1; + public static final int REGION_WIDTH_M = RenderRegion.REGION_WIDTH - 1; + public static final int REGION_HEIGHT_M = RenderRegion.REGION_HEIGHT - 1; + public static final int REGION_LENGTH_M = RenderRegion.REGION_LENGTH - 1; - protected static final int REGION_WIDTH_SH = Integer.bitCount(REGION_WIDTH_M); - protected static final int REGION_HEIGHT_SH = Integer.bitCount(REGION_HEIGHT_M); - protected static final int REGION_LENGTH_SH = Integer.bitCount(REGION_LENGTH_M); + public static final int REGION_WIDTH_SH = Integer.bitCount(REGION_WIDTH_M); + public static final int REGION_HEIGHT_SH = Integer.bitCount(REGION_HEIGHT_M); + public static final int REGION_LENGTH_SH = Integer.bitCount(REGION_LENGTH_M); public static final int REGION_SIZE = REGION_WIDTH * REGION_HEIGHT * REGION_LENGTH; @@ -64,6 +64,18 @@ public static long key(int x, int y, int z) { return SectionPos.asLong(x, y, z); } + public int getX() { + return this.x; + } + + public int getY() { + return this.y; + } + + public int getZ() { + return this.z; + } + public int getChunkX() { return this.x << REGION_WIDTH_SH; } @@ -217,9 +229,9 @@ public DeviceResources(CommandList commandList, StagingBuffer stagingBuffer) { // the magic number 756 for the initial size is arbitrary, it was made up. var initialVertices = 756; - this.geometryArena = new GlBufferArena(commandList, REGION_SIZE * initialVertices, stride, stagingBuffer); + this.geometryArena = new GlBufferArena(commandList, REGION_SIZE * initialVertices, stride, stagingBuffer, false); var initialIndices = (initialVertices / 4) * 6; - this.indexArena = new GlBufferArena(commandList, REGION_SIZE * initialIndices, Integer.BYTES, stagingBuffer); + this.indexArena = new GlBufferArena(commandList, REGION_SIZE * initialIndices, Integer.BYTES, stagingBuffer, true); } public void updateTessellation(CommandList commandList, GlTessellation tessellation) { diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/region/RenderRegionManager.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/region/RenderRegionManager.java index 71665e93a2..cdd6df0d72 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/region/RenderRegionManager.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/region/RenderRegionManager.java @@ -18,6 +18,9 @@ import net.caffeinemc.mods.sodium.client.render.chunk.terrain.DefaultTerrainRenderPasses; import net.caffeinemc.mods.sodium.client.render.chunk.terrain.TerrainRenderPass; +import net.minecraft.util.profiling.Profiler; +import net.minecraft.util.profiling.ProfilerFiller; +import net.caffeinemc.mods.sodium.client.render.chunk.translucent_sorting.data.SharedIndexSorter; import org.jetbrains.annotations.NotNull; import java.util.*; @@ -80,35 +83,51 @@ private void uploadResults(CommandList commandList, RenderRegion region, Collect if (mesh != null) { uploads.add(new PendingSectionMeshUpload(result.render, mesh, pass, - new PendingUpload(mesh.getVertexData()))); + new PendingUpload(mesh.getVertexData()))); } } } if (result instanceof ChunkSortOutput indexDataOutput && !indexDataOutput.isReusingUploadedIndexData()) { - var buffer = indexDataOutput.getIndexBuffer(); - - // when a non-present TranslucentData is used like NoData, the indexBuffer is null - if (buffer == null) { - continue; - } + var sorter = indexDataOutput.getSorter(); + if (sorter instanceof SharedIndexSorter sharedIndexSorter) { + var storage = region.createStorage(DefaultTerrainRenderPasses.TRANSLUCENT); + storage.removeIndexData(renderSectionIndex); + storage.setSharedIndexUsage(renderSectionIndex, sharedIndexSorter.quadCount()); + } else { + var storage = region.getStorage(DefaultTerrainRenderPasses.TRANSLUCENT); + if (storage != null) { + storage.removeIndexData(renderSectionIndex); + storage.setSharedIndexUsage(renderSectionIndex, 0); + } - indexUploads.add(new PendingSectionIndexBufferUpload(result.render, new PendingUpload(buffer))); + if (sorter == null) { + continue; + } + // when a non-present TranslucentData is used like NoData, the indexBuffer is null + var buffer = sorter.getIndexBuffer(); + if (buffer == null) { + continue; + } - var storage = region.getStorage(DefaultTerrainRenderPasses.TRANSLUCENT); - if (storage != null) { - storage.removeIndexData(renderSectionIndex); + indexUploads.add(new PendingSectionIndexBufferUpload(result.render, new PendingUpload(buffer))); } } } + ProfilerFiller profiler = Profiler.get(); + // If we have nothing to upload, abort! - if (uploads.isEmpty() && indexUploads.isEmpty()) { + var translucentStorage = region.getStorage(DefaultTerrainRenderPasses.TRANSLUCENT); + var needsSharedIndexUpdate = translucentStorage != null && translucentStorage.needsSharedIndexUpdate(); + if (uploads.isEmpty() && indexUploads.isEmpty() && !needsSharedIndexUpdate) { return; } var resources = region.createResources(commandList); + profiler.push("upload_vertices"); + if (!uploads.isEmpty()) { var arena = resources.getGeometryArena(); boolean bufferChanged = arena.upload(commandList, uploads.stream() @@ -124,24 +143,33 @@ private void uploadResults(CommandList commandList, RenderRegion region, Collect for (PendingSectionMeshUpload upload : uploads) { var storage = region.createStorage(upload.pass); storage.setVertexData(upload.section.getSectionIndex(), - upload.vertexUpload.getResult(), upload.meshData.getVertexCounts()); + upload.vertexUpload.getResult(), upload.meshData.getVertexSegments()); } } + profiler.popPush("upload_indices"); + var indexBufferChanged = false; + if (!indexUploads.isEmpty()) { var arena = resources.getIndexArena(); - boolean bufferChanged = arena.upload(commandList, indexUploads.stream() + indexBufferChanged = arena.upload(commandList, indexUploads.stream() .map(upload -> upload.indexBufferUpload)); - if (bufferChanged) { - region.refreshIndexedTesselation(commandList); - } - for (PendingSectionIndexBufferUpload upload : indexUploads) { var storage = region.createStorage(DefaultTerrainRenderPasses.TRANSLUCENT); storage.setIndexData(upload.section.getSectionIndex(), upload.indexBufferUpload.getResult()); } } + + if (needsSharedIndexUpdate) { + indexBufferChanged |= translucentStorage.updateSharedIndexData(commandList, resources.getIndexArena()); + } + + if (indexBufferChanged) { + region.refreshIndexedTesselation(commandList); + } + + profiler.pop(); } private Reference2ReferenceMap.FastEntrySet> createMeshUploadQueues(Collection results) { @@ -196,7 +224,6 @@ private record PendingSectionMeshUpload(RenderSection section, BuiltSectionMeshP private record PendingSectionIndexBufferUpload(RenderSection section, PendingUpload indexBufferUpload) { } - private static StagingBuffer createStagingBuffer(CommandList commandList) { if (SodiumClientMod.options().advanced.useAdvancedStagingBuffers && MappedStagingBuffer.isSupported(RenderDevice.INSTANCE)) { return new MappedStagingBuffer(commandList); diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/shader/ChunkShaderBindingPoints.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/shader/ChunkShaderBindingPoints.java index b113ad0703..163190d51f 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/shader/ChunkShaderBindingPoints.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/shader/ChunkShaderBindingPoints.java @@ -1,11 +1,10 @@ package net.caffeinemc.mods.sodium.client.render.chunk.shader; public class ChunkShaderBindingPoints { - public static final int ATTRIBUTE_POSITION_HI = 0; - public static final int ATTRIBUTE_POSITION_LO = 1; - public static final int ATTRIBUTE_COLOR = 2; - public static final int ATTRIBUTE_TEXTURE = 3; - public static final int ATTRIBUTE_LIGHT_MATERIAL_INDEX = 4; + public static final int ATTRIBUTE_POSITION = 0; + public static final int ATTRIBUTE_COLOR = 1; + public static final int ATTRIBUTE_TEXTURE = 2; + public static final int ATTRIBUTE_LIGHT_MATERIAL_INDEX = 3; public static final int FRAG_COLOR = 0; } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/shader/ChunkShaderFogComponent.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/shader/ChunkShaderFogComponent.java index 6855970186..8196d88c74 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/shader/ChunkShaderFogComponent.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/shader/ChunkShaderFogComponent.java @@ -45,11 +45,11 @@ public Smooth(ShaderBindingContext context) { @Override public void setup() { - this.uFogColor.set(RenderSystem.getShaderFogColor()); - this.uFogShape.set(RenderSystem.getShaderFogShape().getIndex()); + this.uFogColor.set(RenderSystem.getShaderFog().red(), RenderSystem.getShaderFog().green(), RenderSystem.getShaderFog().blue(), RenderSystem.getShaderFog().alpha()); + this.uFogShape.set(RenderSystem.getShaderFog().shape().getIndex()); - this.uFogStart.setFloat(RenderSystem.getShaderFogStart()); - this.uFogEnd.setFloat(RenderSystem.getShaderFogEnd()); + this.uFogStart.setFloat(RenderSystem.getShaderFog().start()); + this.uFogEnd.setFloat(RenderSystem.getShaderFog().end()); } } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/translucent_sorting/SortType.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/translucent_sorting/SortType.java index d00713259a..2980f99365 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/translucent_sorting/SortType.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/translucent_sorting/SortType.java @@ -8,17 +8,17 @@ public enum SortType { /** * The section is fully empty, no index buffer is needed. */ - EMPTY_SECTION(false), + EMPTY_SECTION(false, true), /** * The section has no translucent geometry, no index buffer is needed. */ - NO_TRANSLUCENT(false), + NO_TRANSLUCENT(false, true), /** * No sorting is required and the sort order doesn't matter. */ - NONE(false), + NONE(false, true), /** * There is only one sort order. No active sorting is required, but an initial @@ -45,8 +45,15 @@ public enum SortType { DYNAMIC(true); public final boolean needsDirectionMixing; + public final boolean allowSliceReordering; SortType(boolean needsDirectionMixing) { this.needsDirectionMixing = needsDirectionMixing; + this.allowSliceReordering = false; + } + + SortType(boolean needsDirectionMixing, boolean allowSliceReordering) { + this.needsDirectionMixing = needsDirectionMixing; + this.allowSliceReordering = allowSliceReordering; } } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/translucent_sorting/TQuad.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/translucent_sorting/TQuad.java index bdf230a6ac..8d0ae60c73 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/translucent_sorting/TQuad.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/translucent_sorting/TQuad.java @@ -25,37 +25,42 @@ public class TQuad { private ModelQuadFacing facing; private final float[] extents; + private float[] vertexPositions; private final int packedNormal; - private float dotProduct; + private final float accurateDotProduct; + private float quantizedDotProduct; private Vector3fc center; // null on aligned quads private Vector3fc quantizedNormal; + private Vector3fc accurateNormal; - private TQuad(ModelQuadFacing facing, float[] extents, Vector3fc center, int packedNormal) { + private TQuad(ModelQuadFacing facing, float[] extents, float[] vertexPositions, Vector3fc center, int packedNormal) { this.facing = facing; this.extents = extents; + this.vertexPositions = vertexPositions; this.center = center; this.packedNormal = packedNormal; if (this.facing.isAligned()) { - this.dotProduct = getAlignedDotProduct(this.facing, this.extents); + this.accurateDotProduct = getAlignedDotProduct(this.facing, this.extents); } else { float normX = NormI8.unpackX(this.packedNormal); float normY = NormI8.unpackY(this.packedNormal); float normZ = NormI8.unpackZ(this.packedNormal); - this.dotProduct = this.getCenter().dot(normX, normY, normZ); + this.accurateDotProduct = this.getCenter().dot(normX, normY, normZ); } + this.quantizedDotProduct = this.accurateDotProduct; } private static float getAlignedDotProduct(ModelQuadFacing facing, float[] extents) { return extents[facing.ordinal()] * facing.getSign(); } - static TQuad fromAligned(ModelQuadFacing facing, float[] extents, Vector3fc center) { - return new TQuad(facing, extents, center, ModelQuadFacing.PACKED_ALIGNED_NORMALS[facing.ordinal()]); + static TQuad fromAligned(ModelQuadFacing facing, float[] extents, float[] vertexPositions, Vector3fc center) { + return new TQuad(facing, extents, vertexPositions, center, ModelQuadFacing.PACKED_ALIGNED_NORMALS[facing.ordinal()]); } - static TQuad fromUnaligned(ModelQuadFacing facing, float[] extents, Vector3fc center, int packedNormal) { - return new TQuad(facing, extents, center, packedNormal); + static TQuad fromUnaligned(ModelQuadFacing facing, float[] extents, float[] vertexPositions, Vector3fc center, int packedNormal) { + return new TQuad(facing, extents, vertexPositions, center, packedNormal); } public ModelQuadFacing getFacing() { @@ -73,9 +78,9 @@ public ModelQuadFacing useQuantizedFacing() { this.getQuantizedNormal(); this.facing = ModelQuadFacing.fromNormal(this.quantizedNormal.x(), this.quantizedNormal.y(), this.quantizedNormal.z()); if (this.facing.isAligned()) { - this.dotProduct = getAlignedDotProduct(this.facing, this.extents); + this.quantizedDotProduct = getAlignedDotProduct(this.facing, this.extents); } else { - this.dotProduct = this.getCenter().dot(this.quantizedNormal); + this.quantizedDotProduct = this.getCenter().dot(this.quantizedNormal); } } @@ -86,6 +91,31 @@ public float[] getExtents() { return this.extents; } + public float[] getVertexPositions() { + // calculate vertex positions from extents if there's no cached value + // (we don't want to be preemptively collecting vertex positions for all aligned quads) + if (this.vertexPositions == null) { + this.vertexPositions = new float[12]; + + var facingAxis = this.facing.getAxis(); + var xRange = facingAxis == 0 ? 0 : 3; + var yRange = facingAxis == 1 ? 0 : 3; + var zRange = facingAxis == 2 ? 0 : 3; + + var itemIndex = 0; + for (int x = 0; x <= xRange; x += 3) { + for (int y = 0; y <= yRange; y += 3) { + for (int z = 0; z <= zRange; z += 3) { + this.vertexPositions[itemIndex++] = this.extents[x]; + this.vertexPositions[itemIndex++] = this.extents[y + 1]; + this.vertexPositions[itemIndex++] = this.extents[z + 2]; + } + } + } + } + return this.vertexPositions; + } + public Vector3fc getCenter() { // calculate aligned quad center on demand if (this.center == null) { @@ -97,8 +127,12 @@ public Vector3fc getCenter() { return this.center; } - public float getDotProduct() { - return this.dotProduct; + public float getAccurateDotProduct() { + return this.accurateDotProduct; + } + + public float getQuantizedDotProduct() { + return this.quantizedDotProduct; } public int getPackedNormal() { @@ -116,6 +150,20 @@ public Vector3fc getQuantizedNormal() { return this.quantizedNormal; } + public Vector3fc getAccurateNormal() { + if (this.facing.isAligned()) { + return this.facing.getAlignedNormal(); + } else { + if (this.accurateNormal == null) { + this.accurateNormal = new Vector3f( + NormI8.unpackX(this.packedNormal), + NormI8.unpackY(this.packedNormal), + NormI8.unpackZ(this.packedNormal)); + } + return this.accurateNormal; + } + } + private void computeQuantizedNormal() { float normX = NormI8.unpackX(this.packedNormal); float normY = NormI8.unpackY(this.packedNormal); @@ -150,7 +198,7 @@ int getQuadHash() { } else { result = 31 * result + this.packedNormal; } - result = 31 * result + Float.hashCode(this.dotProduct); + result = 31 * result + Float.hashCode(this.quantizedDotProduct); return result; } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/translucent_sorting/TranslucentGeometryCollector.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/translucent_sorting/TranslucentGeometryCollector.java index 6edd1314e7..8ba267a3e6 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/translucent_sorting/TranslucentGeometryCollector.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/translucent_sorting/TranslucentGeometryCollector.java @@ -116,7 +116,7 @@ public void appendQuad(int packedNormal, ChunkVertexEncoder.Vertex[] vertices, M float lastX = vertices[3].x; float lastY = vertices[3].y; float lastZ = vertices[3].z; - int uniqueQuads = 0; + int uniqueVertexes = 0; float posXExtent = Float.NEGATIVE_INFINITY; float posYExtent = Float.NEGATIVE_INFINITY; @@ -141,7 +141,7 @@ public void appendQuad(int packedNormal, ChunkVertexEncoder.Vertex[] vertices, M xSum += x; ySum += y; zSum += z; - uniqueQuads++; + uniqueVertexes++; } if (i != 3) { lastX = x; @@ -185,13 +185,38 @@ public void appendQuad(int packedNormal, ChunkVertexEncoder.Vertex[] vertices, M } Vector3fc center = null; - if (!facing.isAligned() || uniqueQuads != 4) { - var centerX = xSum / uniqueQuads; - var centerY = ySum / uniqueQuads; - var centerZ = zSum / uniqueQuads; + if (!facing.isAligned() || uniqueVertexes != 4) { + var centerX = xSum / uniqueVertexes; + var centerY = ySum / uniqueVertexes; + var centerZ = zSum / uniqueVertexes; center = new Vector3f(centerX, centerY, centerZ); } + // check if we need to store vertex positions for this quad, only necessary if it's unaligned or rotated (yet aligned) + var needsVertexPositions = uniqueVertexes != 4 || !facing.isAligned(); + if (!needsVertexPositions) { + for (int i = 0; i < 4; i++) { + var vertex = vertices[i]; + if (vertex.x != posYExtent && vertex.x != negYExtent || + vertex.y != posZExtent && vertex.y != negZExtent || + vertex.z != posXExtent && vertex.z != negXExtent) { + needsVertexPositions = true; + break; + } + } + } + + float[] vertexPositions = null; + if (needsVertexPositions) { + vertexPositions = new float[12]; + for (int i = 0, itemIndex = 0; i < 4; i++) { + var vertex = vertices[i]; + vertexPositions[itemIndex++] = vertex.x; + vertexPositions[itemIndex++] = vertex.y; + vertexPositions[itemIndex++] = vertex.z; + } + } + if (facing.isAligned()) { // only update global extents if there are no unaligned quads since this is only // used for the convex box test which doesn't work with unaligned quads anyway @@ -204,11 +229,11 @@ public void appendQuad(int packedNormal, ChunkVertexEncoder.Vertex[] vertices, M this.extents[5] = Math.min(this.extents[5], negZExtent); } - var quad = TQuad.fromAligned(facing, extents, center); + var quad = TQuad.fromAligned(facing, extents, vertexPositions, center); quadList.add(quad); var extreme = this.alignedExtremes[direction]; - var distance = quad.getDotProduct(); + var distance = quad.getAccurateDotProduct(); // check if this is a new dot product for this distance var existingExtreme = this.alignedExtremes[direction]; @@ -225,11 +250,11 @@ public void appendQuad(int packedNormal, ChunkVertexEncoder.Vertex[] vertices, M } else { this.hasUnaligned = true; - var quad = TQuad.fromUnaligned(facing, extents, center, packedNormal); + var quad = TQuad.fromUnaligned(facing, extents, vertexPositions, center, packedNormal); quadList.add(quad); // update the two unaligned normals that are tracked - var distance = quad.getDotProduct(); + var distance = quad.getAccurateDotProduct(); if (packedNormal == this.unalignedANormal) { if (Float.isNaN(this.unalignedADistance1)) { this.unalignedADistance1 = distance; @@ -536,7 +561,7 @@ public TranslucentData getTranslucentData( return NoData.forNoTranslucent(this.sectionPos); } - var vertexCounts = translucentMesh.getVertexCounts(); + var vertexCounts = translucentMesh.computeVertexCounts(); // re-use the original translucent data if it's the same. This reduces the // amount of generated and uploaded index data when sections are rebuilt without diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/translucent_sorting/bsp_tree/BSPNode.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/translucent_sorting/bsp_tree/BSPNode.java index 20a48780d1..6362cbc07f 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/translucent_sorting/bsp_tree/BSPNode.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/translucent_sorting/bsp_tree/BSPNode.java @@ -65,7 +65,7 @@ private static boolean doubleLeafPossible(TQuad quadA, TQuad quadB) { // opposite normal (distance irrelevant) if (NormI8.isOpposite(packedNormalA, packedNormalB) // same normal and same distance - || packedNormalA == packedNormalB && quadA.getDotProduct() == quadB.getDotProduct()) { + || packedNormalA == packedNormalB && quadA.getAccurateDotProduct() == quadB.getAccurateDotProduct()) { return true; } } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/translucent_sorting/bsp_tree/InnerPartitionBSPNode.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/translucent_sorting/bsp_tree/InnerPartitionBSPNode.java index f56c9a501d..3ee6572093 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/translucent_sorting/bsp_tree/InnerPartitionBSPNode.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/translucent_sorting/bsp_tree/InnerPartitionBSPNode.java @@ -540,7 +540,7 @@ static private BSPNode buildSNRLeafNodeFromQuads(BSPWorkspace workspace, IntArra for (int i = 0; i < indexes.size(); i++) { var quadIndex = indexes.getInt(i); - keys[i] = MathUtil.floatToComparableInt(workspace.quads[quadIndex].getDotProduct()); + keys[i] = MathUtil.floatToComparableInt(workspace.quads[quadIndex].getAccurateDotProduct()); } quadIndexes = RadixSort.sort(keys); @@ -553,7 +553,7 @@ static private BSPNode buildSNRLeafNodeFromQuads(BSPWorkspace workspace, IntArra for (int i = 0; i < indexes.size(); i++) { var quadIndex = indexes.getInt(i); - int dotProductComponent = MathUtil.floatToComparableInt(workspace.quads[quadIndex].getDotProduct()); + int dotProductComponent = MathUtil.floatToComparableInt(workspace.quads[quadIndex].getAccurateDotProduct()); sortData[i] = (long) dotProductComponent << 32 | quadIndex; } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/translucent_sorting/data/AnyOrderData.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/translucent_sorting/data/AnyOrderData.java index 34f923ff02..a8fff490e0 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/translucent_sorting/data/AnyOrderData.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/translucent_sorting/data/AnyOrderData.java @@ -46,21 +46,7 @@ public Sorter getSorter() { public static AnyOrderData fromMesh(int[] vertexCounts, TQuad[] quads, SectionPos sectionPos) { var anyOrderData = new AnyOrderData(sectionPos, vertexCounts, quads.length); - var sorter = new StaticSorter(quads.length); - anyOrderData.sorterOnce = sorter; - var indexBuffer = sorter.getIntBuffer(); - - for (var vertexCount : vertexCounts) { - if (vertexCount <= 0) { - continue; - } - - int count = TranslucentData.vertexCountToQuadCount(vertexCount); - for (int i = 0; i < count; i++) { - TranslucentData.writeQuadVertexIndexes(indexBuffer, i); - } - } - + anyOrderData.sorterOnce = new SharedIndexSorter(quads.length); return anyOrderData; } } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/translucent_sorting/data/DynamicSorter.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/translucent_sorting/data/DynamicSorter.java index 87539c346d..d184fb4ad3 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/translucent_sorting/data/DynamicSorter.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/translucent_sorting/data/DynamicSorter.java @@ -1,6 +1,6 @@ package net.caffeinemc.mods.sodium.client.render.chunk.translucent_sorting.data; -abstract class DynamicSorter extends Sorter { +abstract class DynamicSorter extends PresentSorter { private final int quadCount; DynamicSorter(int quadCount) { diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/translucent_sorting/data/DynamicTopoData.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/translucent_sorting/data/DynamicTopoData.java index 7b5fc1e491..200ec74a93 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/translucent_sorting/data/DynamicTopoData.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/translucent_sorting/data/DynamicTopoData.java @@ -1,7 +1,6 @@ package net.caffeinemc.mods.sodium.client.render.chunk.translucent_sorting.data; import it.unimi.dsi.fastutil.objects.Object2ReferenceOpenHashMap; -import net.caffeinemc.mods.sodium.client.render.chunk.data.BuiltSectionMeshParts; import net.caffeinemc.mods.sodium.client.render.chunk.translucent_sorting.TQuad; import net.caffeinemc.mods.sodium.client.render.chunk.translucent_sorting.trigger.GeometryPlanes; import net.caffeinemc.mods.sodium.client.util.sorting.RadixSort; diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/translucent_sorting/data/PresentSorter.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/translucent_sorting/data/PresentSorter.java new file mode 100644 index 0000000000..81358a07a4 --- /dev/null +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/translucent_sorting/data/PresentSorter.java @@ -0,0 +1,23 @@ +package net.caffeinemc.mods.sodium.client.render.chunk.translucent_sorting.data; + +import net.caffeinemc.mods.sodium.client.util.NativeBuffer; + +public abstract class PresentSorter implements Sorter { + private NativeBuffer indexBuffer; + + @Override + public NativeBuffer getIndexBuffer() { + return this.indexBuffer; + } + + void initBufferWithQuadLength(int quadCount) { + this.indexBuffer = new NativeBuffer(TranslucentData.quadCountToIndexBytes(quadCount)); + } + + @Override + public void destroy() { + if (this.indexBuffer != null) { + this.indexBuffer.free(); + } + } +} diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/translucent_sorting/data/SharedIndexSorter.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/translucent_sorting/data/SharedIndexSorter.java new file mode 100644 index 0000000000..9dc709a761 --- /dev/null +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/translucent_sorting/data/SharedIndexSorter.java @@ -0,0 +1,27 @@ +package net.caffeinemc.mods.sodium.client.render.chunk.translucent_sorting.data; + +import net.caffeinemc.mods.sodium.client.util.NativeBuffer; + +import java.nio.IntBuffer; + +public record SharedIndexSorter(int quadCount) implements Sorter { + @Override + public NativeBuffer getIndexBuffer() { + return null; + } + + @Override + public IntBuffer getIntBuffer() { + return null; + } + + @Override + public void writeIndexBuffer(CombinedCameraPos cameraPos, boolean initial) { + // no-op + } + + @Override + public void destroy() { + // no-op + } +} diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/translucent_sorting/data/SortData.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/translucent_sorting/data/SortData.java deleted file mode 100644 index c543a82335..0000000000 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/translucent_sorting/data/SortData.java +++ /dev/null @@ -1,5 +0,0 @@ -package net.caffeinemc.mods.sodium.client.render.chunk.translucent_sorting.data; - -public interface SortData extends PresentSortData { - boolean isReusingUploadedIndexData(); -} diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/translucent_sorting/data/Sorter.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/translucent_sorting/data/Sorter.java index 1103773793..545e58a2b9 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/translucent_sorting/data/Sorter.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/translucent_sorting/data/Sorter.java @@ -1,18 +1,7 @@ package net.caffeinemc.mods.sodium.client.render.chunk.translucent_sorting.data; -import net.caffeinemc.mods.sodium.client.util.NativeBuffer; +public interface Sorter extends PresentSortData { + void writeIndexBuffer(CombinedCameraPos cameraPos, boolean initial); -public abstract class Sorter implements PresentSortData { - private NativeBuffer indexBuffer; - - public abstract void writeIndexBuffer(CombinedCameraPos cameraPos, boolean initial); - - @Override - public NativeBuffer getIndexBuffer() { - return this.indexBuffer; - } - - void initBufferWithQuadLength(int quadCount) { - this.indexBuffer = new NativeBuffer(TranslucentData.quadCountToIndexBytes(quadCount)); - } + void destroy(); } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/translucent_sorting/data/StaticNormalRelativeData.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/translucent_sorting/data/StaticNormalRelativeData.java index e0f253697f..888ccd2034 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/translucent_sorting/data/StaticNormalRelativeData.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/translucent_sorting/data/StaticNormalRelativeData.java @@ -1,6 +1,5 @@ package net.caffeinemc.mods.sodium.client.render.chunk.translucent_sorting.data; -import net.caffeinemc.mods.sodium.client.render.chunk.data.BuiltSectionMeshParts; import net.caffeinemc.mods.sodium.client.render.chunk.translucent_sorting.SortType; import net.caffeinemc.mods.sodium.client.render.chunk.translucent_sorting.TQuad; import net.caffeinemc.mods.sodium.client.util.MathUtil; @@ -50,7 +49,7 @@ private static StaticNormalRelativeData fromDoubleUnaligned(int[] vertexCounts, final var keys = new int[quads.length]; for (int q = 0; q < quads.length; q++) { - keys[q] = MathUtil.floatToComparableInt(quads[q].getDotProduct()); + keys[q] = MathUtil.floatToComparableInt(quads[q].getAccurateDotProduct()); } var indices = RadixSort.sort(keys); @@ -62,7 +61,7 @@ private static StaticNormalRelativeData fromDoubleUnaligned(int[] vertexCounts, final var sortData = new long[quads.length]; for (int q = 0; q < quads.length; q++) { - int dotProductComponent = MathUtil.floatToComparableInt(quads[q].getDotProduct()); + int dotProductComponent = MathUtil.floatToComparableInt(quads[q].getAccurateDotProduct()); sortData[q] = (long) dotProductComponent << 32 | q; } @@ -116,7 +115,7 @@ private static StaticNormalRelativeData fromMixed(int[] vertexCounts, final var keys = new int[count]; for (int q = 0; q < count; q++) { - keys[q] = MathUtil.floatToComparableInt(quads[quadIndex++].getDotProduct()); + keys[q] = MathUtil.floatToComparableInt(quads[quadIndex++].getAccurateDotProduct()); } var indices = RadixSort.sort(keys); @@ -127,7 +126,7 @@ private static StaticNormalRelativeData fromMixed(int[] vertexCounts, } else { for (int i = 0; i < count; i++) { var quad = quads[quadIndex++]; - int dotProductComponent = MathUtil.floatToComparableInt(quad.getDotProduct()); + int dotProductComponent = MathUtil.floatToComparableInt(quad.getAccurateDotProduct()); sortData[i] = (long) dotProductComponent << 32 | i; } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/translucent_sorting/data/StaticSorter.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/translucent_sorting/data/StaticSorter.java index fe6bc4d133..cbc09e19ed 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/translucent_sorting/data/StaticSorter.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/translucent_sorting/data/StaticSorter.java @@ -1,6 +1,6 @@ package net.caffeinemc.mods.sodium.client.render.chunk.translucent_sorting.data; -class StaticSorter extends Sorter { +class StaticSorter extends PresentSorter { StaticSorter(int quadCount) { this.initBufferWithQuadLength(quadCount); } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/translucent_sorting/data/TopoGraphSorting.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/translucent_sorting/data/TopoGraphSorting.java index d45af2c756..eb8e9b3dea 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/translucent_sorting/data/TopoGraphSorting.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/translucent_sorting/data/TopoGraphSorting.java @@ -19,6 +19,8 @@ * significantly more robust topo sorting. */ public class TopoGraphSorting { + private static final float HALF_SPACE_EPSILON = 0.001f; + private TopoGraphSorting() { } @@ -35,8 +37,36 @@ private static boolean pointOutsideHalfSpace(float planeDistance, Vector3fc plan return planeNormal.dot(point) > planeDistance; } - private static boolean pointInsideHalfSpace(float planeDistance, Vector3fc planeNormal, Vector3fc point) { - return planeNormal.dot(point) < planeDistance; + /** + * Test if the given point is within the half space defined by the plane anchor and the plane normal. The normal points away from the space considered to be inside. + *

+ * A small epsilon is added in the test to account for floating point errors, making it harder for a point on the edge to be considered inside. + * + * @param planeDistance dot product of the plane + * @param planeNormal the normal of the plane + * @param x x coordinate of the point + * @param y y coordinate of the point + * @param z z coordinate of the point + * @return true if the point is inside the half space + */ + private static boolean pointInsideHalfSpaceEpsilon(float planeDistance, Vector3fc planeNormal, float x, float y, float z) { + return planeNormal.dot(x, y, z) + HALF_SPACE_EPSILON < planeDistance; + } + + /** + * Test if the given point is outside the half space defined by the plane anchor and the plane normal. The normal points away from the space considered to be inside. + *

+ * A small epsilon is subtracted in the test to account for floating point errors, making it harder for a point on the edge to be considered outside. + * + * @param planeDistance dot product of the plane + * @param planeNormal the normal of the plane + * @param x x coordinate of the point + * @param y y coordinate of the point + * @param z z coordinate of the point + * @return true if the point is inside the half space + */ + private static boolean pointOutsideHalfSpaceEpsilon(float planeDistance, Vector3fc planeNormal, float x, float y, float z) { + return planeNormal.dot(x, y, z) - HALF_SPACE_EPSILON > planeDistance; } public static boolean orthogonalQuadVisibleThrough(TQuad quadA, TQuad quadB) { @@ -127,12 +157,9 @@ private static boolean visibilityWithSeparator(TQuad quadA, TQuad quadB, * Checks if one quad is visible through the other quad. This accepts arbitrary * quads, even unaligned ones. * - * @param quad the quad through which the other quad is being - * tested + * @param quad the quad through which the other quad is being tested * @param other the quad being tested - * @param distancesByNormal a map of normals to sorted arrays of face plane - * distances for disproving that the quads are visible - * through each other, null to disable + * @param distancesByNormal a map of normals to sorted arrays of face plane distances for disproving that the quads are visible through each other, null to disable * @return true if the other quad is visible through the first quad */ private static boolean quadVisibleThrough(TQuad quad, TQuad other, @@ -141,11 +168,12 @@ private static boolean quadVisibleThrough(TQuad quad, TQuad other, return false; } - // aligned quads - var quadFacing = quad.useQuantizedFacing(); - var otherFacing = other.useQuantizedFacing(); - boolean result; + var quadFacing = quad.getFacing(); + var otherFacing = other.getFacing(); + boolean result = false; if (quadFacing != ModelQuadFacing.UNASSIGNED && otherFacing != ModelQuadFacing.UNASSIGNED) { + // aligned quads + // opposites never see each other if (quadFacing.getOpposite() == otherFacing) { return false; @@ -162,15 +190,45 @@ private static boolean quadVisibleThrough(TQuad quad, TQuad other, } } else { // at least one unaligned quad - // this is an approximation since our quads don't store all their vertices. - // check that other center is within the half space of quad and that quad isn't - // in the half space of other - result = pointInsideHalfSpace(quad.getDotProduct(), quad.getQuantizedNormal(), other.getCenter()) - && !pointInsideHalfSpace(other.getDotProduct(), other.getQuantizedNormal(), quad.getCenter()); + + var quadDot = quad.getAccurateDotProduct(); + var quadNormal = quad.getAccurateNormal(); + var otherVertexPositions = other.getVertexPositions(); + + // at least one of the other quad's vertexes must be inside the half space of the first quad + var otherInsideQuad = false; + for (int i = 0, itemIndex = 0; i < 4; i++) { + if (pointInsideHalfSpaceEpsilon(quadDot, quadNormal, + otherVertexPositions[itemIndex++], + otherVertexPositions[itemIndex++], + otherVertexPositions[itemIndex++])) { + otherInsideQuad = true; + break; + } + } + if (otherInsideQuad) { + var otherDot = other.getAccurateDotProduct(); + var otherNormal = other.getAccurateNormal(); + var quadVertexPositions = quad.getVertexPositions(); + + // not all the quad's vertexes must be inside the half space of the other quad + // i.e. there must be at least one vertex outside the other quad + var quadNotFullyInsideOther = false; + for (int i = 0, itemIndex = 0; i < 4; i++) { + if (pointOutsideHalfSpaceEpsilon(otherDot, otherNormal, + quadVertexPositions[itemIndex++], + quadVertexPositions[itemIndex++], + quadVertexPositions[itemIndex++])) { + quadNotFullyInsideOther = true; + break; + } + } + + result = quadNotFullyInsideOther; + } } - // if enabled and necessary, try to disprove this see-through relationship with - // a separator plane + // if enabled and necessary, try to disprove this see-through relationship with a separator plane if (result && distancesByNormal != null) { return visibilityWithSeparator(quad, other, distancesByNormal, cameraPos); } @@ -209,10 +267,7 @@ public static boolean topoGraphSort( for (int i = 0; i < allQuads.length; i++) { TQuad quad = allQuads[i]; - // NOTE: This approximation may introduce wrong sorting if the real and the - // quantized normal aren't the same. A quad may be ignored with the quantized - // normal, but it's actually visible in camera. - if (pointOutsideHalfSpace(quad.getDotProduct(), quad.getQuantizedNormal(), cameraPos)) { + if (pointOutsideHalfSpace(quad.getAccurateDotProduct(), quad.getAccurateNormal(), cameraPos)) { activeToRealIndex[quadCount] = i; quads[quadCount] = quad; quadCount++; diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/translucent_sorting/trigger/GeometryPlanes.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/translucent_sorting/trigger/GeometryPlanes.java index 8f75c2db7d..16acdf0230 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/translucent_sorting/trigger/GeometryPlanes.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/translucent_sorting/trigger/GeometryPlanes.java @@ -92,9 +92,9 @@ public void addUnalignedPlane(SectionPos sectionPos, Vector3fc normal, float dis public void addQuadPlane(SectionPos sectionPos, TQuad quad) { var facing = quad.useQuantizedFacing(); if (facing.isAligned()) { - this.addAlignedPlane(sectionPos, facing.ordinal(), quad.getDotProduct()); + this.addAlignedPlane(sectionPos, facing.ordinal(), quad.getQuantizedDotProduct()); } else { - this.addUnalignedPlane(sectionPos, quad.getQuantizedNormal(), quad.getDotProduct()); + this.addUnalignedPlane(sectionPos, quad.getQuantizedNormal(), quad.getQuantizedDotProduct()); } } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/vertex/builder/ChunkMeshBufferBuilder.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/vertex/builder/ChunkMeshBufferBuilder.java index 574701eda2..0b9771d600 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/vertex/builder/ChunkMeshBufferBuilder.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/vertex/builder/ChunkMeshBufferBuilder.java @@ -3,6 +3,7 @@ import net.caffeinemc.mods.sodium.client.render.chunk.terrain.material.Material; import net.caffeinemc.mods.sodium.client.render.chunk.vertex.format.ChunkVertexEncoder; import net.caffeinemc.mods.sodium.client.render.chunk.vertex.format.ChunkVertexType; +import org.apache.commons.lang3.Validate; import org.lwjgl.system.MemoryUtil; import java.nio.ByteBuffer; @@ -13,8 +14,9 @@ public class ChunkMeshBufferBuilder { private final int initialCapacity; private ByteBuffer buffer; - private int count; - private int capacity; + private int vertexCount; + private int vertexCapacity; + private int sectionIndex; public ChunkMeshBufferBuilder(ChunkVertexType vertexType, int initialCapacity) { @@ -23,7 +25,7 @@ public ChunkMeshBufferBuilder(ChunkVertexType vertexType, int initialCapacity) { this.buffer = null; - this.capacity = initialCapacity; + this.vertexCapacity = initialCapacity; this.initialCapacity = initialCapacity; } @@ -32,36 +34,40 @@ public void push(ChunkVertexEncoder.Vertex[] vertices, Material material) { } public void push(ChunkVertexEncoder.Vertex[] vertices, int materialBits) { - var vertexCount = vertices.length; - - if (this.count + vertexCount >= this.capacity) { - this.grow(this.stride * vertexCount); + if (vertices.length != 4) { + throw new IllegalArgumentException("Only quad primitives (with 4 vertices) can be pushed"); } - this.encoder.write(MemoryUtil.memAddress(this.buffer, this.count * this.stride), - materialBits, vertices, this.sectionIndex); + this.ensureCapacity(4); - this.count += vertexCount; + this.encoder.write(MemoryUtil.memAddress(this.buffer, this.vertexCount * this.stride), + materialBits, vertices, this.sectionIndex); + this.vertexCount += 4; } - private void grow(int len) { - // The new capacity will at least as large as the write it needs to service - int cap = Math.max(this.capacity * 2, this.capacity + len); + private void ensureCapacity(int vertexCount) { + if (this.vertexCount + vertexCount >= this.vertexCapacity) { + this.grow(vertexCount); + } + } - // Update the buffer and capacity now - this.setBufferSize(cap * this.stride); + private void grow(int vertexCount) { + this.reallocate( + // The new capacity will at least twice as large + Math.max(this.vertexCapacity * 2, this.vertexCapacity + vertexCount) + ); } - private void setBufferSize(int capacity) { - this.buffer = MemoryUtil.memRealloc(this.buffer, capacity * this.stride); - this.capacity = capacity; + private void reallocate(int vertexCount) { + this.buffer = MemoryUtil.memRealloc(this.buffer, vertexCount * this.stride); + this.vertexCapacity = vertexCount; } public void start(int sectionIndex) { - this.count = 0; + this.vertexCount = 0; this.sectionIndex = sectionIndex; - this.setBufferSize(this.initialCapacity); + this.reallocate(this.initialCapacity); } public void destroy() { @@ -73,7 +79,7 @@ public void destroy() { } public boolean isEmpty() { - return this.count == 0; + return this.vertexCount == 0; } public ByteBuffer slice() { @@ -81,10 +87,10 @@ public ByteBuffer slice() { throw new IllegalStateException("No vertex data in buffer"); } - return MemoryUtil.memSlice(this.buffer, 0, this.stride * this.count); + return MemoryUtil.memSlice(this.buffer, 0, this.stride * this.vertexCount); } public int count() { - return this.count; + return this.vertexCount; } } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/vertex/format/impl/CompactChunkVertex.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/vertex/format/impl/CompactChunkVertex.java index 627391c3e1..44c73a8719 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/vertex/format/impl/CompactChunkVertex.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/vertex/format/impl/CompactChunkVertex.java @@ -1,10 +1,10 @@ package net.caffeinemc.mods.sodium.client.render.chunk.vertex.format.impl; +import net.caffeinemc.mods.sodium.api.util.ColorARGB; import net.caffeinemc.mods.sodium.client.gl.attribute.GlVertexFormat; import net.caffeinemc.mods.sodium.client.render.chunk.shader.ChunkShaderBindingPoints; import net.caffeinemc.mods.sodium.client.render.chunk.vertex.format.ChunkVertexEncoder; import net.caffeinemc.mods.sodium.client.render.chunk.vertex.format.ChunkVertexType; -import net.caffeinemc.mods.sodium.client.render.frapi.helper.ColorHelper; import net.minecraft.util.Mth; import org.lwjgl.system.MemoryUtil; @@ -12,8 +12,7 @@ public class CompactChunkVertex implements ChunkVertexType { public static final int STRIDE = 20; public static final GlVertexFormat VERTEX_FORMAT = GlVertexFormat.builder(STRIDE) - .addElement(DefaultChunkMeshAttributes.POSITION_HI, ChunkShaderBindingPoints.ATTRIBUTE_POSITION_HI, 0) - .addElement(DefaultChunkMeshAttributes.POSITION_LO, ChunkShaderBindingPoints.ATTRIBUTE_POSITION_LO, 4) + .addElement(DefaultChunkMeshAttributes.POSITION, ChunkShaderBindingPoints.ATTRIBUTE_POSITION, 0) .addElement(DefaultChunkMeshAttributes.COLOR, ChunkShaderBindingPoints.ATTRIBUTE_COLOR, 8) .addElement(DefaultChunkMeshAttributes.TEXTURE, ChunkShaderBindingPoints.ATTRIBUTE_TEXTURE, 12) .addElement(DefaultChunkMeshAttributes.LIGHT_MATERIAL_INDEX, ChunkShaderBindingPoints.ATTRIBUTE_LIGHT_MATERIAL_INDEX, 16) @@ -59,7 +58,7 @@ public ChunkVertexEncoder getEncoder() { MemoryUtil.memPutInt(ptr + 0L, packPositionHi(x, y, z)); MemoryUtil.memPutInt(ptr + 4L, packPositionLo(x, y, z)); - MemoryUtil.memPutInt(ptr + 8L, ColorHelper.multiplyRGB(vertex.color, vertex.ao)); + MemoryUtil.memPutInt(ptr + 8L, ColorARGB.mulRGB(vertex.color, vertex.ao)); MemoryUtil.memPutInt(ptr + 12L, packTexture(u, v)); MemoryUtil.memPutInt(ptr + 16L, packLightAndData(light, materialBits, section)); diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/vertex/format/impl/DefaultChunkMeshAttributes.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/vertex/format/impl/DefaultChunkMeshAttributes.java index 5da6117115..04170bc924 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/vertex/format/impl/DefaultChunkMeshAttributes.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/vertex/format/impl/DefaultChunkMeshAttributes.java @@ -4,8 +4,7 @@ import net.caffeinemc.mods.sodium.client.render.vertex.VertexFormatAttribute; public class DefaultChunkMeshAttributes { - public static final VertexFormatAttribute POSITION_HI = new VertexFormatAttribute("POSITION_HI", GlVertexAttributeFormat.UNSIGNED_INT, 1, false, true); - public static final VertexFormatAttribute POSITION_LO = new VertexFormatAttribute("POSITION_LO", GlVertexAttributeFormat.UNSIGNED_INT, 1, false, true); + public static final VertexFormatAttribute POSITION = new VertexFormatAttribute("POSITION", GlVertexAttributeFormat.UNSIGNED_INT, 2, false, true); public static final VertexFormatAttribute COLOR = new VertexFormatAttribute("COLOR", GlVertexAttributeFormat.UNSIGNED_BYTE, 4, true, false); public static final VertexFormatAttribute TEXTURE = new VertexFormatAttribute("TEXTURE", GlVertexAttributeFormat.UNSIGNED_SHORT, 2, false, true); public static final VertexFormatAttribute LIGHT_MATERIAL_INDEX = new VertexFormatAttribute("LIGHT_MATERIAL_INDEX", GlVertexAttributeFormat.UNSIGNED_BYTE, 4, false, true); diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/frapi/SodiumRenderer.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/frapi/SodiumRenderer.java index 7b39a52063..1c6097574b 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/frapi/SodiumRenderer.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/frapi/SodiumRenderer.java @@ -18,11 +18,11 @@ import net.caffeinemc.mods.sodium.client.render.frapi.material.MaterialFinderImpl; import net.caffeinemc.mods.sodium.client.render.frapi.material.RenderMaterialImpl; -import net.caffeinemc.mods.sodium.client.render.frapi.mesh.MeshBuilderImpl; +import net.caffeinemc.mods.sodium.client.render.frapi.mesh.MutableMeshImpl; import net.fabricmc.fabric.api.renderer.v1.Renderer; import net.fabricmc.fabric.api.renderer.v1.material.MaterialFinder; import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial; -import net.fabricmc.fabric.api.renderer.v1.mesh.MeshBuilder; +import net.fabricmc.fabric.api.renderer.v1.mesh.MutableMesh; import net.minecraft.resources.ResourceLocation; import java.util.HashMap; @@ -36,7 +36,7 @@ public class SodiumRenderer implements Renderer { public static final RenderMaterial STANDARD_MATERIAL = INSTANCE.materialFinder().find(); static { - INSTANCE.registerMaterial(RenderMaterial.MATERIAL_STANDARD, STANDARD_MATERIAL); + INSTANCE.registerMaterial(RenderMaterial.STANDARD_ID, STANDARD_MATERIAL); } private final HashMap materialMap = new HashMap<>(); @@ -44,8 +44,8 @@ public class SodiumRenderer implements Renderer { private SodiumRenderer() { } @Override - public MeshBuilder meshBuilder() { - return new MeshBuilderImpl(); + public MutableMesh mutableMesh() { + return new MutableMeshImpl(); } @Override diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/frapi/helper/ColorHelper.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/frapi/helper/ColorHelper.java index 9961263598..2acf626078 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/frapi/helper/ColorHelper.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/frapi/helper/ColorHelper.java @@ -16,7 +16,8 @@ package net.caffeinemc.mods.sodium.client.render.frapi.helper; -import java.nio.ByteOrder; +import net.caffeinemc.mods.sodium.api.util.ColorABGR; +import net.caffeinemc.mods.sodium.api.util.ColorARGB; /** * Static routines of general utility for renderer implementations. @@ -24,44 +25,9 @@ * designed to be usable without the default renderer. */ public abstract class ColorHelper { - private ColorHelper() { } - - private static final boolean BIG_ENDIAN = ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN; - - /** Component-wise multiply. Components need to be in same order in both inputs! */ - public static int multiplyColor(int color1, int color2) { - if (color1 == -1) { - return color2; - } else if (color2 == -1) { - return color1; - } - - final int alpha = ((color1 >>> 24) & 0xFF) * ((color2 >>> 24) & 0xFF) / 0xFF; - final int red = ((color1 >>> 16) & 0xFF) * ((color2 >>> 16) & 0xFF) / 0xFF; - final int green = ((color1 >>> 8) & 0xFF) * ((color2 >>> 8) & 0xFF) / 0xFF; - final int blue = (color1 & 0xFF) * (color2 & 0xFF) / 0xFF; - - return (alpha << 24) | (red << 16) | (green << 8) | blue; - } - - /** Multiplies three lowest components by shade. High byte (usually alpha) unchanged. */ - public static int multiplyRGB(int color, float shade) { - final int alpha = ((color >>> 24) & 0xFF); - final int red = (int) (((color >>> 16) & 0xFF) * shade); - final int green = (int) (((color >>> 8) & 0xFF) * shade); - final int blue = (int) ((color & 0xFF) * shade); - - return (alpha << 24) | (red << 16) | (green << 8) | blue; - } - - /** - * Component-wise max. - */ public static int maxBrightness(int b0, int b1) { - if (b0 == 0) return b1; - if (b1 == 0) return b0; - - return Math.max(b0 & 0xFFFF, b1 & 0xFFFF) | Math.max(b0 & 0xFFFF0000, b1 & 0xFFFF0000); + return Math.max(b0 & 0x0000FFFF, b1 & 0x0000FFFF) | + Math.max(b0 & 0xFFFF0000, b1 & 0xFFFF0000); } /* @@ -81,36 +47,16 @@ Vanilla color format (big endian): RGBA (0xRRGGBBAA) */ /** - * Converts from ARGB color to ABGR color if little endian or RGBA color if big endian. + * Converts from ARGB color to ABGR color. The result will be in the platform's native byte order. */ public static int toVanillaColor(int color) { - if (color == -1) { - return -1; - } - - if (BIG_ENDIAN) { - // ARGB to RGBA - return ((color & 0x00FFFFFF) << 8) | ((color & 0xFF000000) >>> 24); - } else { - // ARGB to ABGR - return (color & 0xFF00FF00) | ((color & 0x00FF0000) >>> 16) | ((color & 0x000000FF) << 16); - } + return ColorABGR.toNativeByteOrder(ColorARGB.toABGR(color)); } /** - * Converts to ARGB color from ABGR color if little endian or RGBA color if big endian. + * Converts from ABGR color to ARGB color. The input should be in the platform's native byte order. */ public static int fromVanillaColor(int color) { - if (color == -1) { - return -1; - } - - if (BIG_ENDIAN) { - // RGBA to ARGB - return ((color & 0xFFFFFF00) >>> 8) | ((color & 0x000000FF) << 24); - } else { - // ABGR to ARGB - return (color & 0xFF00FF00) | ((color & 0x00FF0000) >>> 16) | ((color & 0x000000FF) << 16); - } + return ColorARGB.fromABGR(ColorABGR.fromNativeByteOrder(color)); } } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/frapi/helper/GeometryHelper.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/frapi/helper/GeometryHelper.java index da25fe4eda..e1e0ef1f97 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/frapi/helper/GeometryHelper.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/frapi/helper/GeometryHelper.java @@ -21,6 +21,7 @@ import net.minecraft.core.Direction; import net.minecraft.util.Mth; import org.joml.Vector3f; +import org.joml.Vector3fc; /** * Static routines of general utility for renderer implementations. @@ -49,7 +50,7 @@ public static boolean isQuadParallelToFace(Direction face, QuadView quad) { *

Derived from the quad face normal and expects convex quads with all points co-planar. */ public static Direction lightFace(QuadView quad) { - final Vector3f normal = quad.faceNormal(); + final Vector3fc normal = quad.faceNormal(); return switch (GeometryHelper.longestAxis(normal)) { case X -> normal.x() > 0 ? Direction.EAST : Direction.WEST; case Y -> normal.y() > 0 ? Direction.UP : Direction.DOWN; @@ -63,7 +64,7 @@ public static Direction lightFace(QuadView quad) { /** * @see #longestAxis(float, float, float) */ - public static Direction.Axis longestAxis(Vector3f vec) { + public static Direction.Axis longestAxis(Vector3fc vec) { return longestAxis(vec.x(), vec.y(), vec.z()); } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/frapi/helper/NormalHelper.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/frapi/helper/NormalHelper.java index f2fd5a5486..cd287099f1 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/frapi/helper/NormalHelper.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/frapi/helper/NormalHelper.java @@ -42,7 +42,7 @@ public static void computeFaceNormal(@NotNull Vector3f saveTo, QuadView q) { final Direction nominalFace = q.nominalFace(); if (nominalFace != null && GeometryHelper.isQuadParallelToFace(nominalFace, q)) { - Vec3i vec = nominalFace.getNormal(); + Vec3i vec = nominalFace.getUnitVec3i(); saveTo.set(vec.getX(), vec.getY(), vec.getZ()); return; } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/frapi/material/MaterialFinderImpl.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/frapi/material/MaterialFinderImpl.java index 44a5b97f83..105cd02a1c 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/frapi/material/MaterialFinderImpl.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/frapi/material/MaterialFinderImpl.java @@ -27,7 +27,6 @@ public class MaterialFinderImpl extends MaterialViewImpl implements MaterialFind static { MaterialFinderImpl finder = new MaterialFinderImpl(); finder.ambientOcclusion(TriState.DEFAULT); - finder.glint(TriState.DEFAULT); finder.shadeMode(ShadeMode.ENHANCED); defaultBits = finder.bits; @@ -48,12 +47,6 @@ public MaterialFinder blendMode(BlendMode blendMode) { return this; } - @Override - public MaterialFinder disableColorIndex(boolean disable) { - bits = disable ? (bits | COLOR_DISABLE_FLAG) : (bits & ~COLOR_DISABLE_FLAG); - return this; - } - @Override public MaterialFinder emissive(boolean isEmissive) { bits = isEmissive ? (bits | EMISSIVE_FLAG) : (bits & ~EMISSIVE_FLAG); @@ -75,10 +68,10 @@ public MaterialFinder ambientOcclusion(TriState mode) { } @Override - public MaterialFinder glint(TriState mode) { - Objects.requireNonNull(mode, "glint TriState may not be null"); + public MaterialFinder glintMode(GlintMode mode) { + Objects.requireNonNull(mode, "GlintMode may not be null"); - bits = (bits & ~GLINT_MASK) | (mode.ordinal() << GLINT_BIT_OFFSET); + bits = (bits & ~GLINT_MODE_MASK) | (mode.ordinal() << GLINT_MODE_BIT_OFFSET); return this; } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/frapi/material/MaterialViewImpl.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/frapi/material/MaterialViewImpl.java index e6a4ea1e2e..9bd55d47e2 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/frapi/material/MaterialViewImpl.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/frapi/material/MaterialViewImpl.java @@ -17,6 +17,7 @@ package net.caffeinemc.mods.sodium.client.render.frapi.material; import net.fabricmc.fabric.api.renderer.v1.material.BlendMode; +import net.fabricmc.fabric.api.renderer.v1.material.GlintMode; import net.fabricmc.fabric.api.renderer.v1.material.MaterialView; import net.fabricmc.fabric.api.renderer.v1.material.ShadeMode; import net.fabricmc.fabric.api.util.TriState; @@ -33,32 +34,32 @@ public class MaterialViewImpl implements MaterialView { private static final int BLEND_MODE_COUNT = BLEND_MODES.length; private static final TriState[] TRI_STATES = TriState.values(); private static final int TRI_STATE_COUNT = TRI_STATES.length; + private static final GlintMode[] GLINT_MODES = GlintMode.values(); + private static final int GLINT_MODE_COUNT = GLINT_MODES.length; private static final ShadeMode[] SHADE_MODES = ShadeMode.values(); private static final int SHADE_MODE_COUNT = SHADE_MODES.length; protected static final int BLEND_MODE_BIT_LENGTH = Mth.ceillog2(BLEND_MODE_COUNT); - protected static final int COLOR_DISABLE_BIT_LENGTH = 1; protected static final int EMISSIVE_BIT_LENGTH = 1; protected static final int DIFFUSE_BIT_LENGTH = 1; protected static final int AO_BIT_LENGTH = Mth.ceillog2(TRI_STATE_COUNT); - protected static final int GLINT_BIT_LENGTH = Mth.ceillog2(TRI_STATE_COUNT); + protected static final int GLINT_MODE_BIT_LENGTH = Mth.ceillog2(GLINT_MODE_COUNT); protected static final int SHADE_MODE_BIT_LENGTH = Mth.ceillog2(SHADE_MODE_COUNT); protected static final int BLEND_MODE_BIT_OFFSET = 0; - protected static final int COLOR_DISABLE_BIT_OFFSET = BLEND_MODE_BIT_OFFSET + BLEND_MODE_BIT_LENGTH; - protected static final int EMISSIVE_BIT_OFFSET = COLOR_DISABLE_BIT_OFFSET + COLOR_DISABLE_BIT_LENGTH; + protected static final int EMISSIVE_BIT_OFFSET = BLEND_MODE_BIT_OFFSET + BLEND_MODE_BIT_LENGTH; protected static final int DIFFUSE_BIT_OFFSET = EMISSIVE_BIT_OFFSET + EMISSIVE_BIT_LENGTH; protected static final int AO_BIT_OFFSET = DIFFUSE_BIT_OFFSET + DIFFUSE_BIT_LENGTH; protected static final int GLINT_BIT_OFFSET = AO_BIT_OFFSET + AO_BIT_LENGTH; - protected static final int SHADE_MODE_BIT_OFFSET = GLINT_BIT_OFFSET + GLINT_BIT_LENGTH; + protected static final int GLINT_MODE_BIT_OFFSET = AO_BIT_OFFSET + AO_BIT_LENGTH; + protected static final int SHADE_MODE_BIT_OFFSET = GLINT_MODE_BIT_OFFSET + GLINT_MODE_BIT_LENGTH; protected static final int TOTAL_BIT_LENGTH = SHADE_MODE_BIT_OFFSET + SHADE_MODE_BIT_LENGTH; protected static final int BLEND_MODE_MASK = bitMask(BLEND_MODE_BIT_LENGTH, BLEND_MODE_BIT_OFFSET); - protected static final int COLOR_DISABLE_FLAG = bitMask(COLOR_DISABLE_BIT_LENGTH, COLOR_DISABLE_BIT_OFFSET); protected static final int EMISSIVE_FLAG = bitMask(EMISSIVE_BIT_LENGTH, EMISSIVE_BIT_OFFSET); protected static final int DIFFUSE_FLAG = bitMask(DIFFUSE_BIT_LENGTH, DIFFUSE_BIT_OFFSET); protected static final int AO_MASK = bitMask(AO_BIT_LENGTH, AO_BIT_OFFSET); - protected static final int GLINT_MASK = bitMask(GLINT_BIT_LENGTH, GLINT_BIT_OFFSET); + protected static final int GLINT_MODE_MASK = bitMask(GLINT_MODE_BIT_LENGTH, GLINT_MODE_BIT_OFFSET); protected static final int SHADE_MODE_MASK = bitMask(SHADE_MODE_BIT_LENGTH, SHADE_MODE_BIT_OFFSET); protected static int bitMask(int bitLength, int bitOffset) { @@ -68,12 +69,12 @@ protected static int bitMask(int bitLength, int bitOffset) { protected static boolean areBitsValid(int bits) { int blendMode = (bits & BLEND_MODE_MASK) >>> BLEND_MODE_BIT_OFFSET; int ao = (bits & AO_MASK) >>> AO_BIT_OFFSET; - int glint = (bits & GLINT_MASK) >>> GLINT_BIT_OFFSET; + int glintMode = (bits & GLINT_MODE_MASK) >>> GLINT_MODE_BIT_OFFSET; int shadeMode = (bits & SHADE_MODE_MASK) >>> SHADE_MODE_BIT_OFFSET; return blendMode < BLEND_MODE_COUNT && ao < TRI_STATE_COUNT - && glint < TRI_STATE_COUNT + && glintMode < GLINT_MODE_COUNT && shadeMode < SHADE_MODE_COUNT; } @@ -88,11 +89,6 @@ public BlendMode blendMode() { return BLEND_MODES[(bits & BLEND_MODE_MASK) >>> BLEND_MODE_BIT_OFFSET]; } - @Override - public boolean disableColorIndex() { - return (bits & COLOR_DISABLE_FLAG) != 0; - } - @Override public boolean emissive() { return (bits & EMISSIVE_FLAG) != 0; @@ -109,8 +105,8 @@ public TriState ambientOcclusion() { } @Override - public TriState glint() { - return TRI_STATES[(bits & GLINT_MASK) >>> GLINT_BIT_OFFSET]; + public GlintMode glintMode() { + return GLINT_MODES[(bits & GLINT_MODE_MASK) >>> GLINT_MODE_BIT_OFFSET]; } @Override diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/frapi/material/RenderMaterialImpl.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/frapi/material/RenderMaterialImpl.java index 760f8b4dac..91a202828a 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/frapi/material/RenderMaterialImpl.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/frapi/material/RenderMaterialImpl.java @@ -17,6 +17,7 @@ package net.caffeinemc.mods.sodium.client.render.frapi.material; import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial; +import net.fabricmc.fabric.api.util.TriState; public class RenderMaterialImpl extends MaterialViewImpl implements RenderMaterial { public static final int VALUE_COUNT = 1 << TOTAL_BIT_LENGTH; @@ -49,4 +50,12 @@ public static RenderMaterialImpl setDisableDiffuse(RenderMaterialImpl material, return material; } + + public static RenderMaterialImpl setAmbientOcclusion(RenderMaterialImpl material, TriState mode) { + if (material.ambientOcclusion() != mode) { + return byIndex((material.bits & ~AO_MASK) | (mode.ordinal() << AO_BIT_OFFSET)); + } + + return material; + } } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/frapi/mesh/EncodingFormat.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/frapi/mesh/EncodingFormat.java index 583867ec87..3f40457ffe 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/frapi/mesh/EncodingFormat.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/frapi/mesh/EncodingFormat.java @@ -21,6 +21,7 @@ import com.mojang.blaze3d.vertex.VertexFormat; import net.caffeinemc.mods.sodium.client.model.quad.properties.ModelQuadFacing; import net.caffeinemc.mods.sodium.client.model.quad.properties.ModelQuadFlags; +import net.caffeinemc.mods.sodium.client.render.frapi.SodiumRenderer; import net.caffeinemc.mods.sodium.client.render.frapi.material.RenderMaterialImpl; import net.fabricmc.fabric.api.renderer.v1.mesh.QuadView; import net.fabricmc.fabric.api.renderer.v1.model.ModelHelper; @@ -37,7 +38,7 @@ private EncodingFormat() { } static final int HEADER_BITS = 0; static final int HEADER_FACE_NORMAL = 1; - static final int HEADER_COLOR_INDEX = 2; + static final int HEADER_TINT_INDEX = 2; static final int HEADER_TAG = 3; public static final int HEADER_STRIDE = 4; diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/frapi/mesh/MeshBuilderImpl.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/frapi/mesh/MeshBuilderImpl.java deleted file mode 100644 index 34e69c8b1f..0000000000 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/frapi/mesh/MeshBuilderImpl.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (c) 2016, 2017, 2018, 2019 FabricMC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package net.caffeinemc.mods.sodium.client.render.frapi.mesh; - -import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh; -import net.fabricmc.fabric.api.renderer.v1.mesh.MeshBuilder; -import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter; - -/** - * Our implementation of {@link MeshBuilder}, used for static mesh creation and baking. - * Not much to it - mainly it just needs to grow the int[] array as quads are appended - * and maintain/provide a properly-configured {@link net.fabricmc.fabric.api.renderer.v1.mesh.MutableQuadView} instance. - * All the encoding and other work is handled in the quad base classes. - * The one interesting bit is in {@link Maker#emitDirectly()}. - */ -public class MeshBuilderImpl implements MeshBuilder { - private int[] data = new int[256]; - private int index = 0; - private int limit = data.length; - private final Maker maker = new Maker(); - - public MeshBuilderImpl() { - ensureCapacity(EncodingFormat.TOTAL_STRIDE); - maker.data = data; - maker.baseIndex = index; - maker.clear(); - } - - protected void ensureCapacity(int stride) { - if (stride > limit - index) { - limit *= 2; - final int[] bigger = new int[limit]; - System.arraycopy(data, 0, bigger, 0, index); - data = bigger; - maker.data = data; - } - } - - @Override - public QuadEmitter getEmitter() { - maker.clear(); - return maker; - } - - @Override - public Mesh build() { - final int[] packed = new int[index]; - System.arraycopy(data, 0, packed, 0, index); - index = 0; - maker.baseIndex = index; - maker.clear(); - return new MeshImpl(packed); - } - - /** - * Our base classes are used differently so we define final - * encoding steps in subtypes. This will be a static mesh used - * at render time so we want to capture all geometry now and - * apply non-location-dependent lighting. - */ - private class Maker extends MutableQuadViewImpl { - @Override - public void emitDirectly() { - computeGeometry(); - index += EncodingFormat.TOTAL_STRIDE; - ensureCapacity(EncodingFormat.TOTAL_STRIDE); - baseIndex = index; - } - } -} diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/frapi/mesh/MeshImpl.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/frapi/mesh/MeshImpl.java index 60fa624c90..051d27a709 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/frapi/mesh/MeshImpl.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/frapi/mesh/MeshImpl.java @@ -16,9 +16,11 @@ package net.caffeinemc.mods.sodium.client.render.frapi.mesh; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh; import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter; import net.fabricmc.fabric.api.renderer.v1.mesh.QuadView; +import org.jetbrains.annotations.Range; import java.util.function.Consumer; @@ -28,17 +30,38 @@ */ public class MeshImpl implements Mesh { /** Used to satisfy external calls to {@link #forEach(Consumer)}. */ - private final ThreadLocal cursorPool = ThreadLocal.withInitial(QuadViewImpl::new); + private static final ThreadLocal> CURSOR_POOLS = ThreadLocal.withInitial(ObjectArrayList::new); - final int[] data; + int[] data; + int limit; MeshImpl(int[] data) { this.data = data; + limit = data.length; } + MeshImpl() {} + @Override - public void forEach(Consumer consumer) { - forEach(consumer, cursorPool.get()); + @Range(from = 0, to = Integer.MAX_VALUE) + public int size() { + return limit / EncodingFormat.TOTAL_STRIDE; + } + + @Override + public void forEach(Consumer action) { + ObjectArrayList pool = CURSOR_POOLS.get(); + QuadViewImpl cursor; + + if (pool.isEmpty()) { + cursor = new QuadViewImpl(); + } else { + cursor = pool.pop(); + } + + forEach(action, cursor); + + pool.push(cursor); } /** @@ -46,30 +69,34 @@ public void forEach(Consumer consumer) { * to avoid the performance hit of a thread-local lookup. * Also means renderer can hold final references to quad buffers. */ - void forEach(Consumer consumer, QuadViewImpl cursor) { - final int limit = data.length; + void forEach(Consumer action, C cursor) { + final int limit = this.limit; int index = 0; cursor.data = this.data; while (index < limit) { cursor.baseIndex = index; cursor.load(); - consumer.accept(cursor); + action.accept(cursor); index += EncodingFormat.TOTAL_STRIDE; } + + cursor.data = null; } + // TODO: This could be optimized by checking if the emitter is that of a MutableMeshImpl and if + // it has no transforms, in which case the entire data array can be copied in bulk. @Override public void outputTo(QuadEmitter emitter) { MutableQuadViewImpl e = (MutableQuadViewImpl) emitter; final int[] data = this.data; - final int limit = data.length; + final int limit = this.limit; int index = 0; while (index < limit) { System.arraycopy(data, index, e.data, e.baseIndex, EncodingFormat.TOTAL_STRIDE); e.load(); - e.emitDirectly(); + e.transformAndEmit(); index += EncodingFormat.TOTAL_STRIDE; } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/frapi/mesh/MutableMeshImpl.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/frapi/mesh/MutableMeshImpl.java new file mode 100644 index 0000000000..1f58afcb7d --- /dev/null +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/frapi/mesh/MutableMeshImpl.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.caffeinemc.mods.sodium.client.render.frapi.mesh; + +import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh; +import net.fabricmc.fabric.api.renderer.v1.mesh.MutableMesh; +import net.fabricmc.fabric.api.renderer.v1.mesh.MutableQuadView; +import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter; + +import java.util.function.Consumer; + +/** + * Our implementation of {@link MutableMesh}, used for static mesh creation and baking. + * Not much to it - mainly it just needs to grow the int[] array as quads are appended + * and maintain/provide a properly-configured {@link net.fabricmc.fabric.api.renderer.v1.mesh.MutableQuadView} instance. + * All the encoding and other work is handled in the quad base classes. + * The one interesting bit is in {@link MutableQuadViewImpl#emitDirectly()}. + */ +public class MutableMeshImpl extends MeshImpl implements MutableMesh { + private final MutableQuadViewImpl emitter = new MutableQuadViewImpl() { + @Override + protected void emitDirectly() { + // Necessary because the validity of geometry is not encoded; reading mesh data always + // uses QuadViewImpl#load(), which assumes valid geometry. Built immutable meshes + // should also have valid geometry for better performance. + computeGeometry(); + limit += EncodingFormat.TOTAL_STRIDE; + ensureCapacity(EncodingFormat.TOTAL_STRIDE); + baseIndex = limit; + } +}; + + public MutableMeshImpl() { + data = new int[8 * EncodingFormat.TOTAL_STRIDE]; + limit = 0; + + ensureCapacity(EncodingFormat.TOTAL_STRIDE); + emitter.data = data; + emitter.baseIndex = limit; + emitter.clear(); + } + + private void ensureCapacity(int stride) { + if (stride > data.length - limit) { + final int[] bigger = new int[data.length * 2]; + System.arraycopy(data, 0, bigger, 0, limit); + data = bigger; + emitter.data = data; + } + } + + @Override + public QuadEmitter emitter() { + emitter.clear(); + return emitter; + } + + @Override + public void forEachMutable(Consumer action) { + // emitDirectly will not be called by forEach, so just reuse the main emitter. + forEach(action, emitter); + emitter.data = data; + emitter.baseIndex = limit; + } + + @Override + public Mesh immutableCopy() { + final int[] packed = new int[limit]; + System.arraycopy(data, 0, packed, 0, limit); + return new MeshImpl(packed); + } + + @Override + public void clear() { + limit = 0; + emitter.baseIndex = limit; + emitter.clear(); + } +} diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/frapi/mesh/MutableQuadViewImpl.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/frapi/mesh/MutableQuadViewImpl.java index 3ab4bc208f..fcde4314fa 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/frapi/mesh/MutableQuadViewImpl.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/frapi/mesh/MutableQuadViewImpl.java @@ -16,6 +16,7 @@ package net.caffeinemc.mods.sodium.client.render.frapi.mesh; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; import net.caffeinemc.mods.sodium.api.util.NormI8; import net.caffeinemc.mods.sodium.client.model.quad.BakedQuadView; import net.caffeinemc.mods.sodium.client.render.frapi.SodiumRenderer; @@ -24,8 +25,11 @@ import net.caffeinemc.mods.sodium.client.render.frapi.material.RenderMaterialImpl; import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial; import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter; +import net.fabricmc.fabric.api.renderer.v1.mesh.QuadTransform; import net.fabricmc.fabric.api.renderer.v1.mesh.QuadView; import net.fabricmc.fabric.api.renderer.v1.model.SpriteFinder; +import net.minecraft.client.renderer.LightTexture; +import net.fabricmc.fabric.api.util.TriState; import net.minecraft.client.renderer.block.model.BakedQuad; import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.core.Direction; @@ -47,6 +51,42 @@ public abstract class MutableQuadViewImpl extends QuadViewImpl implements QuadEm @Nullable private TextureAtlasSprite cachedSprite; + protected static final QuadTransform NO_TRANSFORM = q -> true; + + protected QuadTransform activeTransform = NO_TRANSFORM; + private final ObjectArrayList transformStack = new ObjectArrayList<>(); + private final QuadTransform stackTransform = q -> { + int i = transformStack.size() - 1; + + while (i >= 0) { + if (!transformStack.get(i--).transform(q)) { + return false; + } + } + + return true; + }; + + /** Used for quick clearing of quad buffers. Implicitly has invalid geometry. */ + static final int[] DEFAULT = EMPTY.clone(); + + static { + MutableQuadViewImpl quad = new MutableQuadViewImpl() { + @Override + protected void emitDirectly() { + // This quad won't be emitted. It's only used to configure the default quad data. + } + }; + + // Start with all zeroes + quad.data = DEFAULT; + // Apply non-zero defaults + quad.color(-1, -1, -1, -1); + quad.cullFace(null); + quad.material(SodiumRenderer.STANDARD_MATERIAL); + quad.tintIndex(-1); + } + @Nullable public TextureAtlasSprite cachedSprite() { return cachedSprite; @@ -67,14 +107,9 @@ public TextureAtlasSprite sprite(SpriteFinder finder) { } public void clear() { - System.arraycopy(EMPTY, 0, data, baseIndex, EncodingFormat.TOTAL_STRIDE); + System.arraycopy(DEFAULT, 0, data, baseIndex, EncodingFormat.TOTAL_STRIDE); isGeometryInvalid = true; nominalFace = null; - normalFlags(0); - tag(0); - colorIndex(-1); - cullFace(null); - material(SodiumRenderer.STANDARD_MATERIAL); cachedSprite(null); } @@ -133,6 +168,32 @@ public MutableQuadViewImpl normal(int vertexIndex, float x, float y, float z) { return this; } + @Override + public void pushTransform(QuadTransform transform) { + if (transform == null) { + throw new NullPointerException("QuadTransform cannot be null!"); + } + + transformStack.push(transform); + + if (transformStack.size() == 1) { + activeTransform = transform; + } else if (transformStack.size() == 2) { + activeTransform = stackTransform; + } + } + + @Override + public void popTransform() { + transformStack.pop(); + + if (transformStack.isEmpty()) { + activeTransform = NO_TRANSFORM; + } else if (transformStack.size() == 1) { + activeTransform = transformStack.getFirst(); + } + } + /** * Internal helper method. Copies face normals to vertex normals lacking one. */ @@ -167,17 +228,13 @@ public final MutableQuadViewImpl nominalFace(@Nullable Direction face) { @Override public final MutableQuadViewImpl material(RenderMaterial material) { - if (material == null) { - material = SodiumRenderer.STANDARD_MATERIAL; - } - data[baseIndex + HEADER_BITS] = EncodingFormat.material(data[baseIndex + HEADER_BITS], (RenderMaterialImpl) material); return this; } @Override - public final MutableQuadViewImpl colorIndex(int colorIndex) { - data[baseIndex + HEADER_COLOR_INDEX] = colorIndex; + public final MutableQuadViewImpl tintIndex(int tintIndex) { + data[baseIndex + HEADER_TINT_INDEX] = tintIndex; return this; } @@ -190,12 +247,15 @@ public final MutableQuadViewImpl tag(int tag) { @Override public MutableQuadViewImpl copyFrom(QuadView quad) { final QuadViewImpl q = (QuadViewImpl) quad; - q.computeGeometry(); System.arraycopy(q.data, q.baseIndex, data, baseIndex, EncodingFormat.TOTAL_STRIDE); - faceNormal.set(q.faceNormal); nominalFace = q.nominalFace; - isGeometryInvalid = false; + + isGeometryInvalid = q.isGeometryInvalid; + + if (!isGeometryInvalid) { + faceNormal.set(q.faceNormal); + } if (quad instanceof MutableQuadViewImpl mutableQuad) { cachedSprite(mutableQuad.cachedSprite()); @@ -235,13 +295,17 @@ public final MutableQuadViewImpl fromVanilla(BakedQuad quad, RenderMaterial mate fromVanillaInternal(quad.getVertices(), 0); data[baseIndex + HEADER_BITS] = EncodingFormat.cullFace(0, cullFace); nominalFace(quad.getDirection()); - colorIndex(quad.getTintIndex()); + tintIndex(quad.getTintIndex()); // TODO: Is this the same as hasShade? if (!((BakedQuadView) quad).hasShade()) { material = RenderMaterialImpl.setDisableDiffuse((RenderMaterialImpl) material, true); } + if (material.ambientOcclusion().orElse(true) && !((BakedQuadView) quad).hasAO()) { + material = RenderMaterialImpl.setAmbientOcclusion((RenderMaterialImpl) material, TriState.FALSE); + } + material(material); tag(0); @@ -254,6 +318,14 @@ public final MutableQuadViewImpl fromVanilla(BakedQuad quad, RenderMaterial mate data[baseIndex + HEADER_BITS] = EncodingFormat.geometryFlags(headerBits, bakedView.getFlags()); isGeometryInvalid = false; + int lightEmission = quad.getLightEmission(); + + if (lightEmission > 0) { + for (int i = 0; i < 4; i++) { + lightmap(i, LightTexture.lightCoordsWithEmission(lightmap(i), lightEmission)); + } + } + cachedSprite(quad.getSprite()); return this; } @@ -262,11 +334,20 @@ public final MutableQuadViewImpl fromVanilla(BakedQuad quad, RenderMaterial mate * Emit the quad without clearing the underlying data. * Geometry is not guaranteed to be valid when called, but can be computed by calling {@link #computeGeometry()}. */ - public abstract void emitDirectly(); + protected abstract void emitDirectly(); + + /** + * Apply transforms and then if transforms return true, emit the quad without clearing the underlying data. + */ + public final void transformAndEmit() { + if (activeTransform.transform(this)) { + emitDirectly(); + } + } @Override public final MutableQuadViewImpl emit() { - emitDirectly(); + transformAndEmit(); clear(); return this; } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/frapi/mesh/QuadViewImpl.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/frapi/mesh/QuadViewImpl.java index fce995a97b..4610108382 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/frapi/mesh/QuadViewImpl.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/frapi/mesh/QuadViewImpl.java @@ -256,8 +256,8 @@ public final RenderMaterialImpl material() { } @Override - public final int colorIndex() { - return data[baseIndex + HEADER_COLOR_INDEX]; + public final int tintIndex() { + return data[baseIndex + HEADER_TINT_INDEX]; } @Override @@ -328,8 +328,8 @@ public int getLight(int idx) { } @Override - public int getColorIndex() { - return material().disableColorIndex() ? -1 : colorIndex(); + public int getTintIndex() { + return tintIndex(); } @Override @@ -342,6 +342,11 @@ public Direction getLightFace() { return lightFace(); } + @Override + public int getMaxLightQuad(int idx) { + return lightmap(idx); + } + @Override public int getFlags() { return geometryFlags(); diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/frapi/render/AbstractBlockRenderContext.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/frapi/render/AbstractBlockRenderContext.java index 828e2663ae..84e20f5f0b 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/frapi/render/AbstractBlockRenderContext.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/frapi/render/AbstractBlockRenderContext.java @@ -33,6 +33,7 @@ import org.jetbrains.annotations.Nullable; import java.util.List; +import java.util.function.Predicate; import java.util.function.Supplier; /** @@ -58,12 +59,17 @@ public abstract class AbstractBlockRenderContext extends AbstractRenderContext { STANDARD_MATERIALS[i] = SodiumRenderer.INSTANCE.materialFinder().ambientOcclusion(state).find(); } } - private final MutableQuadViewImpl editorQuad = new MutableQuadViewImpl() { + + public class BlockEmitter extends MutableQuadViewImpl { { data = new int[EncodingFormat.TOTAL_STRIDE]; clear(); } + public void bufferDefaultModel(BakedModel model, BlockState state, Predicate cullTest) { + AbstractBlockRenderContext.this.bufferDefaultModel(model, state, cullTest); + } + @Override public void emitDirectly() { if (type == null) { @@ -71,10 +77,11 @@ public void emitDirectly() { } renderQuad(this); } - }; + } + + - @Deprecated - private final BakedModelConsumerImpl vanillaModelConsumer = new BakedModelConsumerImpl(); + private final MutableQuadViewImpl editorQuad = new BlockEmitter(); /** * The world which the block is being rendered in. @@ -131,7 +138,6 @@ public QuadEmitter getEmitter() { return this.editorQuad; } - @Override public boolean isFaceCulled(@Nullable Direction face) { if (face == null || !this.enableCulling) { return false; @@ -153,26 +159,10 @@ public boolean isFaceCulled(@Nullable Direction face) { } } - @Override - public ItemDisplayContext itemTransformationMode() { - throw new UnsupportedOperationException("itemTransformationMode can only be called on an item render context."); - } - - @SuppressWarnings("removal") - @Deprecated - @Override - public BakedModelConsumer bakedModelConsumer() { - return this.vanillaModelConsumer; - } - /** * Pipeline entrypoint - handles transform and culling checks. */ private void renderQuad(MutableQuadViewImpl quad) { - if (!this.transform(quad)) { - return; - } - if (this.isFaceCulled(quad.cullFace())) { return; } @@ -217,61 +207,44 @@ protected void shadeQuad(MutableQuadViewImpl quad, LightMode lightMode, boolean } /* Handling of vanilla models - this is the hot path for non-modded models */ - public void bufferDefaultModel(BakedModel model, @Nullable BlockState state) { + public void bufferDefaultModel(BakedModel model, @Nullable BlockState state, Predicate cullTest) { MutableQuadViewImpl editorQuad = this.editorQuad; // If there is no transform, we can check the culling face once for all the quads, // and we don't need to check for transforms per-quad. - boolean noTransform = !this.hasTransform(); for (int i = 0; i <= ModelHelper.NULL_FACE_ID; i++) { final Direction cullFace = ModelHelper.faceFromIndex(i); + if (cullTest.test(cullFace)) { + continue; + } + RandomSource random = this.randomSupplier.get(); AmbientOcclusionMode ao = PlatformBlockAccess.getInstance().usesAmbientOcclusion(model, state, modelData, type, slice, pos); - if (noTransform) { - if (!this.isFaceCulled(cullFace)) { - final List quads = PlatformModelAccess.getInstance().getQuads(level, pos, model, state, cullFace, random, type, modelData); - final int count = quads.size(); - - for (int j = 0; j < count; j++) { - final BakedQuad q = quads.get(j); - editorQuad.fromVanilla(q, (type == RenderType.tripwire() || type == RenderType.translucent()) ? TRANSLUCENT_MATERIAL : STANDARD_MATERIALS[ao.ordinal()], cullFace); - // Call processQuad instead of emit for efficiency - // (avoid unnecessarily clearing data, trying to apply transforms, and performing cull check again) - - this.processQuad(editorQuad); - } - } - } else { - final List quads = PlatformModelAccess.getInstance().getQuads(level, pos, model, state, cullFace, random, type, modelData); - final int count = quads.size(); - - for (int j = 0; j < count; j++) { - final BakedQuad q = quads.get(j); - editorQuad.fromVanilla(q, (type == RenderType.tripwire() || type == RenderType.translucent()) ? TRANSLUCENT_MATERIAL : STANDARD_MATERIALS[ao.ordinal()], cullFace); - // Call renderQuad instead of emit for efficiency - // (avoid unnecessarily clearing data) - this.renderQuad(editorQuad); - } + + final List quads = PlatformModelAccess.getInstance().getQuads(level, pos, model, state, cullFace, random, type, modelData); + final int count = quads.size(); + + for (int j = 0; j < count; j++) { + final BakedQuad q = quads.get(j); + editorQuad.fromVanilla(q, (type == RenderType.tripwire() || type == RenderType.translucent()) ? TRANSLUCENT_MATERIAL : STANDARD_MATERIALS[ao.ordinal()], cullFace); + // Call processQuad instead of emit for efficiency + // (avoid unnecessarily clearing data, trying to apply transforms, and performing cull check again) + + this.processQuad(editorQuad); } } editorQuad.clear(); } - @SuppressWarnings("removal") - @Deprecated - private class BakedModelConsumerImpl implements BakedModelConsumer { - @Override - public void accept(BakedModel model) { - accept(model, AbstractBlockRenderContext.this.state); - } + public SodiumModelData getModelData() { + return modelData; + } - @Override - public void accept(BakedModel model, @Nullable BlockState state) { - AbstractBlockRenderContext.this.bufferDefaultModel(model, state); - } + public RenderType getRenderType() { + return type; } } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/frapi/render/AbstractRenderContext.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/frapi/render/AbstractRenderContext.java index 6074c497cf..0f033a2c25 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/frapi/render/AbstractRenderContext.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/frapi/render/AbstractRenderContext.java @@ -19,69 +19,10 @@ import it.unimi.dsi.fastutil.objects.ObjectArrayList; import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh; import net.fabricmc.fabric.api.renderer.v1.mesh.MutableQuadView; -import net.fabricmc.fabric.api.renderer.v1.render.RenderContext; +import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter; import java.util.function.Consumer; -public abstract class AbstractRenderContext implements RenderContext { - private static final QuadTransform NO_TRANSFORM = q -> true; - - private QuadTransform activeTransform = NO_TRANSFORM; - private final ObjectArrayList transformStack = new ObjectArrayList<>(); - private final QuadTransform stackTransform = q -> { - int i = transformStack.size() - 1; - - while (i >= 0) { - if (!transformStack.get(i--).transform(q)) { - return false; - } - } - - return true; - }; - - @Deprecated - private final Consumer meshConsumer = mesh -> mesh.outputTo(getEmitter()); - - protected final boolean transform(MutableQuadView q) { - return activeTransform.transform(q); - } - - @Override - public boolean hasTransform() { - return activeTransform != NO_TRANSFORM; - } - - @Override - public void pushTransform(QuadTransform transform) { - if (transform == null) { - throw new NullPointerException("Renderer received null QuadTransform."); - } - - transformStack.push(transform); - - if (transformStack.size() == 1) { - activeTransform = transform; - } else if (transformStack.size() == 2) { - activeTransform = stackTransform; - } - } - - @Override - public void popTransform() { - transformStack.pop(); - - if (transformStack.isEmpty()) { - activeTransform = NO_TRANSFORM; - } else if (transformStack.size() == 1) { - activeTransform = transformStack.get(0); - } - } - - // Overridden to prevent allocating a lambda every time this method is called. - @Deprecated - @Override - public Consumer meshConsumer() { - return meshConsumer; - } +public abstract class AbstractRenderContext { + abstract QuadEmitter getEmitter(); } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/frapi/render/ItemRenderContext.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/frapi/render/ItemRenderContext.java index 62dfae8cad..d08d1d414f 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/frapi/render/ItemRenderContext.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/frapi/render/ItemRenderContext.java @@ -19,6 +19,7 @@ import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.VertexConsumer; import com.mojang.math.MatrixUtil; +import net.caffeinemc.mods.sodium.api.util.ColorMixer; import net.caffeinemc.mods.sodium.client.render.frapi.helper.ColorHelper; import net.caffeinemc.mods.sodium.client.render.frapi.mesh.EncodingFormat; import net.caffeinemc.mods.sodium.client.render.frapi.mesh.MutableQuadViewImpl; @@ -26,28 +27,24 @@ import net.caffeinemc.mods.sodium.client.render.texture.SpriteUtil; import net.caffeinemc.mods.sodium.mixin.features.render.frapi.ItemRendererAccessor; import net.fabricmc.fabric.api.renderer.v1.material.BlendMode; +import net.fabricmc.fabric.api.renderer.v1.material.GlintMode; import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial; import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter; import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel; -import net.fabricmc.fabric.api.util.TriState; import net.fabricmc.fabric.impl.renderer.VanillaModelEncoder; -import net.minecraft.client.Minecraft; -import net.minecraft.client.color.item.ItemColors; import net.minecraft.client.renderer.*; import net.minecraft.client.renderer.entity.ItemRenderer; +import net.minecraft.client.renderer.item.ItemStackRenderState; import net.minecraft.client.resources.model.BakedModel; -import net.minecraft.core.Direction; import net.minecraft.util.RandomSource; -import net.minecraft.world.item.BlockItem; -import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemDisplayContext; -import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.levelgen.SingleThreadedRandomSource; import org.jetbrains.annotations.Nullable; import org.joml.Matrix3f; import org.joml.Matrix4f; +import java.util.Arrays; import java.util.function.Supplier; /** @@ -56,32 +53,43 @@ public class ItemRenderContext extends AbstractRenderContext { /** Value vanilla uses for item rendering. The only sensible choice, of course. */ private static final long ITEM_RANDOM_SEED = 42L; + private static final int GLINT_COUNT = ItemStackRenderState.FoilType.values().length; - private final MutableQuadViewImpl editorQuad = new MutableQuadViewImpl() { + public class ItemEmitter extends MutableQuadViewImpl { { data = new int[EncodingFormat.TOTAL_STRIDE]; clear(); } + public void bufferDefaultModel(BakedModel model) { + ItemRenderContext.this.bufferDefaultModel(this, model, null); + } + + @Override public void emitDirectly() { renderQuad(this); } - }; - @Deprecated - private final BakedModelConsumerImpl vanillaModelConsumer = new BakedModelConsumerImpl(); + public boolean hasTransforms() { + return activeTransform != NO_TRANSFORM; + } + } + + private final MutableQuadViewImpl editorQuad = new ItemEmitter(); - private final ItemColors colorMap; private final VanillaModelBufferer vanillaBufferer; + public ItemRenderContext(VanillaModelBufferer vanillaBufferer) { + this.vanillaBufferer = vanillaBufferer; + } + private final RandomSource random = new SingleThreadedRandomSource(ITEM_RANDOM_SEED); private final Supplier randomSupplier = () -> { random.setSeed(ITEM_RANDOM_SEED); return random; }; - private ItemStack itemStack; private ItemDisplayContext transformMode; private PoseStack poseStack; private Matrix4f matPosition; @@ -90,23 +98,13 @@ public void emitDirectly() { private MultiBufferSource bufferSource; private int lightmap; private int overlay; + private int[] colors; - private boolean isDefaultTranslucent; - private boolean isTranslucentDirect; - private boolean isDefaultGlint; - private boolean isGlintDynamicDisplay; + private RenderType defaultLayer; + private ItemStackRenderState.FoilType defaultGlint; - private PoseStack.Pose dynamicDisplayGlintEntry; - private VertexConsumer translucentVertexConsumer; - private VertexConsumer cutoutVertexConsumer; - private VertexConsumer translucentGlintVertexConsumer; - private VertexConsumer cutoutGlintVertexConsumer; - private VertexConsumer defaultVertexConsumer; - - public ItemRenderContext(ItemColors colorMap, VanillaModelBufferer vanillaBufferer) { - this.colorMap = colorMap; - this.vanillaBufferer = vanillaBufferer; - } + private PoseStack.Pose specialGlintEntry; + private final VertexConsumer[] vertexConsumerCache = new VertexConsumer[3 * GLINT_COUNT]; @Override public QuadEmitter getEmitter() { @@ -114,25 +112,7 @@ public QuadEmitter getEmitter() { return editorQuad; } - @Override - public boolean isFaceCulled(@Nullable Direction face) { - throw new UnsupportedOperationException("isFaceCulled can only be called on a block render context."); - } - - @Override - public ItemDisplayContext itemTransformationMode() { - return transformMode; - } - - @SuppressWarnings("removal") - @Deprecated - @Override - public BakedModelConsumer bakedModelConsumer() { - return vanillaModelConsumer; - } - - public void renderModel(ItemStack itemStack, ItemDisplayContext transformMode, boolean invert, PoseStack poseStack, MultiBufferSource bufferSource, int lightmap, int overlay, BakedModel model) { - this.itemStack = itemStack; + public void renderModel(ItemDisplayContext transformMode, PoseStack poseStack, MultiBufferSource bufferSource, int lightmap, int overlay, BakedModel model, int[] colors, RenderType layer, ItemStackRenderState.FoilType glint) { this.transformMode = transformMode; this.poseStack = poseStack; matPosition = poseStack.last().pose(); @@ -141,68 +121,39 @@ public void renderModel(ItemStack itemStack, ItemDisplayContext transformMode, b this.bufferSource = bufferSource; this.lightmap = lightmap; this.overlay = overlay; - computeOutputInfo(); + this.colors = colors; - ((FabricBakedModel) model).emitItemQuads(itemStack, randomSupplier, this); + defaultLayer = layer; + defaultGlint = glint; + + ((FabricBakedModel) model).emitItemQuads(getEmitter(), randomSupplier); - this.itemStack = null; this.poseStack = null; this.bufferSource = null; + this.colors = null; - dynamicDisplayGlintEntry = null; - translucentVertexConsumer = null; - cutoutVertexConsumer = null; - translucentGlintVertexConsumer = null; - cutoutGlintVertexConsumer = null; - defaultVertexConsumer = null; - } - - private void computeOutputInfo() { - isDefaultTranslucent = true; - isTranslucentDirect = true; - - Item item = itemStack.getItem(); - - if (item instanceof BlockItem blockItem) { - BlockState state = blockItem.getBlock().defaultBlockState(); - RenderType renderType = ItemBlockRenderTypes.getChunkRenderType(state); - - if (renderType != RenderType.translucent()) { - isDefaultTranslucent = false; - } - - if (transformMode != ItemDisplayContext.GUI && !transformMode.firstPerson()) { - isTranslucentDirect = false; - } - } - - isDefaultGlint = itemStack.hasFoil(); - isGlintDynamicDisplay = ItemRendererAccessor.sodium$hasAnimatedTexture(itemStack); - - defaultVertexConsumer = getVertexConsumer(BlendMode.DEFAULT, TriState.DEFAULT); + this.specialGlintEntry = null; + Arrays.fill(vertexConsumerCache, null); } private void renderQuad(MutableQuadViewImpl quad) { - if (!transform(quad)) { - return; - } - final RenderMaterial mat = quad.material(); - final int colorIndex = mat.disableColorIndex() ? -1 : quad.colorIndex(); final boolean emissive = mat.emissive(); - final VertexConsumer vertexConsumer = getVertexConsumer(mat.blendMode(), mat.glint()); + final VertexConsumer vertexConsumer = getVertexConsumer(mat.blendMode(), mat.glintMode()); - colorizeQuad(quad, colorIndex); + tintQuad(quad); shadeQuad(quad, emissive); bufferQuad(quad, vertexConsumer); } - private void colorizeQuad(MutableQuadViewImpl quad, int colorIndex) { - if (colorIndex != -1) { - final int itemColor = colorMap.getColor(itemStack, colorIndex); + private void tintQuad(MutableQuadViewImpl quad) { + final int tintIndex = quad.tintIndex(); + + if (tintIndex != -1 && tintIndex < colors.length) { + final int color = colors[tintIndex]; for (int i = 0; i < 4; i++) { - quad.color(i, ColorHelper.multiplyColor(itemColor, quad.color(i))); + quad.color(i, ColorMixer.mulComponentWise(color, quad.color(i))); } } } @@ -231,114 +182,86 @@ private void bufferQuad(MutableQuadViewImpl quad, VertexConsumer vertexConsumer) * in {@code RenderLayers.getEntityBlockLayer}. Layers other than * translucent are mapped to cutout. */ - private VertexConsumer getVertexConsumer(BlendMode blendMode, TriState glintMode) { - boolean translucent; - boolean glint; + private VertexConsumer getVertexConsumer(BlendMode blendMode, GlintMode glintMode) { + RenderType type; + ItemStackRenderState.FoilType glint; if (blendMode == BlendMode.DEFAULT) { - translucent = isDefaultTranslucent; + type = defaultLayer; } else { - translucent = blendMode == BlendMode.TRANSLUCENT; + type = blendMode == BlendMode.TRANSLUCENT ? Sheets.translucentItemSheet() : Sheets.cutoutBlockSheet(); } - if (glintMode == TriState.DEFAULT) { - glint = isDefaultGlint; + if (glintMode == GlintMode.DEFAULT) { + glint = defaultGlint; } else { - glint = glintMode == TriState.TRUE; + glint = glintMode.glint; } - if (translucent) { - if (glint) { - if (translucentGlintVertexConsumer == null) { - translucentGlintVertexConsumer = createTranslucentVertexConsumer(true); - } - - return translucentGlintVertexConsumer; - } else { - if (translucentVertexConsumer == null) { - translucentVertexConsumer = createTranslucentVertexConsumer(false); - } + int cacheIndex; - return translucentVertexConsumer; - } + if (type == Sheets.translucentItemSheet()) { + cacheIndex = 0; + } else if (type == Sheets.cutoutBlockSheet()) { + cacheIndex = GLINT_COUNT; } else { - if (glint) { - if (cutoutGlintVertexConsumer == null) { - cutoutGlintVertexConsumer = createCutoutVertexConsumer(true); - } - - return cutoutGlintVertexConsumer; - } else { - if (cutoutVertexConsumer == null) { - cutoutVertexConsumer = createCutoutVertexConsumer(false); - } - - return cutoutVertexConsumer; - } - } - } - - private VertexConsumer createTranslucentVertexConsumer(boolean glint) { - if (glint && isGlintDynamicDisplay) { - return createDynamicDisplayGlintVertexConsumer(Minecraft.useShaderTransparency() && !isTranslucentDirect ? Sheets.translucentItemSheet() : Sheets.translucentCullBlockSheet()); + cacheIndex = 2 * GLINT_COUNT; } - if (isTranslucentDirect) { - return ItemRenderer.getFoilBufferDirect(bufferSource, Sheets.translucentCullBlockSheet(), true, glint); - } else if (Minecraft.useShaderTransparency()) { - return ItemRenderer.getFoilBuffer(bufferSource, Sheets.translucentItemSheet(), true, glint); - } else { - return ItemRenderer.getFoilBuffer(bufferSource, Sheets.translucentItemSheet(), true, glint); - } - } + cacheIndex += glint.ordinal(); + VertexConsumer vertexConsumer = vertexConsumerCache[cacheIndex]; - private VertexConsumer createCutoutVertexConsumer(boolean glint) { - if (glint && isGlintDynamicDisplay) { - return createDynamicDisplayGlintVertexConsumer(Sheets.cutoutBlockSheet()); + if (vertexConsumer == null) { + vertexConsumer = createVertexConsumer(type, glint); + vertexConsumerCache[cacheIndex] = vertexConsumer; } - return ItemRenderer.getFoilBufferDirect(bufferSource, Sheets.cutoutBlockSheet(), true, glint); + return vertexConsumer; } - private VertexConsumer createDynamicDisplayGlintVertexConsumer(RenderType type) { - if (dynamicDisplayGlintEntry == null) { - dynamicDisplayGlintEntry = poseStack.last().copy(); + private VertexConsumer createVertexConsumer(RenderType type, ItemStackRenderState.FoilType glint) { + if (glint == ItemStackRenderState.FoilType.SPECIAL) { + if (specialGlintEntry == null) { + specialGlintEntry = poseStack.last().copy(); - if (transformMode == ItemDisplayContext.GUI) { - MatrixUtil.mulComponentWise(dynamicDisplayGlintEntry.pose(), 0.5F); - } else if (transformMode.firstPerson()) { - MatrixUtil.mulComponentWise(dynamicDisplayGlintEntry.pose(), 0.75F); + if (transformMode == ItemDisplayContext.GUI) { + MatrixUtil.mulComponentWise(specialGlintEntry.pose(), 0.5F); + } else if (transformMode.firstPerson()) { + MatrixUtil.mulComponentWise(specialGlintEntry.pose(), 0.75F); + } } + + return ItemRendererAccessor.sodium$getCompassFoilBuffer(bufferSource, type, specialGlintEntry); } - return ItemRenderer.getCompassFoilBuffer(bufferSource, type, dynamicDisplayGlintEntry); + return ItemRenderer.getFoilBuffer(bufferSource, type, true, glint != ItemStackRenderState.FoilType.NONE); } - public void bufferDefaultModel(BakedModel model, @Nullable BlockState state) { - if (hasTransform() || vanillaBufferer == null) { - VanillaModelEncoder.emitItemQuads(model, state, randomSupplier, ItemRenderContext.this); + public void bufferDefaultModel(QuadEmitter quadEmitter, BakedModel model, @Nullable BlockState state) { + if (vanillaBufferer == null) { + VanillaModelEncoder.emitItemQuads(quadEmitter, model, null, randomSupplier); } else { - vanillaBufferer.accept(model, itemStack, lightmap, overlay, poseStack, defaultVertexConsumer); - } - } + VertexConsumer vertexConsumer; + if (defaultGlint == ItemStackRenderState.FoilType.SPECIAL) { + PoseStack.Pose pose = poseStack.last().copy(); + if (transformMode == ItemDisplayContext.GUI) { + MatrixUtil.mulComponentWise(pose.pose(), 0.5F); + } else if (transformMode.firstPerson()) { + MatrixUtil.mulComponentWise(pose.pose(), 0.75F); + } - @SuppressWarnings("removal") - @Deprecated - private class BakedModelConsumerImpl implements BakedModelConsumer { - @Override - public void accept(BakedModel model) { - accept(model, null); - } + vertexConsumer = ItemRendererAccessor.sodium$getCompassFoilBuffer(bufferSource, defaultLayer, pose); + } else { + vertexConsumer = ItemRenderer.getFoilBuffer(bufferSource, defaultLayer, true, defaultGlint != ItemStackRenderState.FoilType.NONE); + } - @Override - public void accept(BakedModel model, @Nullable BlockState state) { - bufferDefaultModel(model, state); + vanillaBufferer.accept(model, colors, lightmap, overlay, poseStack, vertexConsumer); } } /** used to accept a method reference from the ItemRenderer. */ @FunctionalInterface public interface VanillaModelBufferer { - void accept(BakedModel model, ItemStack stack, int color, int overlay, PoseStack matrixStack, VertexConsumer buffer); + void accept(BakedModel model, int[] colirs, int color, int overlay, PoseStack matrixStack, VertexConsumer buffer); } } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/frapi/render/NonTerrainBlockRenderContext.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/frapi/render/NonTerrainBlockRenderContext.java index 130d240b2e..7c0a26cf11 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/frapi/render/NonTerrainBlockRenderContext.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/frapi/render/NonTerrainBlockRenderContext.java @@ -18,10 +18,11 @@ import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.VertexConsumer; +import net.caffeinemc.mods.sodium.api.util.ColorARGB; +import net.caffeinemc.mods.sodium.api.util.ColorMixer; import net.caffeinemc.mods.sodium.client.model.light.LightMode; import net.caffeinemc.mods.sodium.client.model.light.LightPipelineProvider; import net.caffeinemc.mods.sodium.client.model.light.data.SingleBlockLightDataCache; -import net.caffeinemc.mods.sodium.client.render.frapi.helper.ColorHelper; import net.caffeinemc.mods.sodium.client.render.frapi.mesh.MutableQuadViewImpl; import net.caffeinemc.mods.sodium.client.render.texture.SpriteFinderCache; import net.caffeinemc.mods.sodium.client.render.texture.SpriteUtil; @@ -75,7 +76,7 @@ public void renderModel(BlockAndTintGetter blockView, BakedModel model, BlockSta this.prepareCulling(cull); this.prepareAoInfo(model.useAmbientOcclusion()); - ((FabricBakedModel) model).emitBlockQuads(blockView, state, pos, this.randomSupplier, this); + ((FabricBakedModel) model).emitBlockQuads(getEmitter(), blockView, state, pos, this.randomSupplier, this::isFaceCulled); this.level = null; this.type = null; @@ -88,7 +89,6 @@ public void renderModel(BlockAndTintGetter blockView, BakedModel model, BlockSta @Override protected void processQuad(MutableQuadViewImpl quad) { final RenderMaterial mat = quad.material(); - final int colorIndex = mat.disableColorIndex() ? -1 : quad.colorIndex(); final TriState aoMode = mat.ambientOcclusion(); final ShadeMode shadeMode = mat.shadeMode(); final LightMode lightMode; @@ -99,17 +99,17 @@ protected void processQuad(MutableQuadViewImpl quad) { } final boolean emissive = mat.emissive(); - colorizeQuad(quad, colorIndex); + tintQuad(quad); shadeQuad(quad, lightMode, emissive, shadeMode); bufferQuad(quad); } - private void colorizeQuad(MutableQuadViewImpl quad, int colorIndex) { - if (colorIndex != -1) { - final int blockColor = 0xFF000000 | this.colorMap.getColor(this.state, this.level, this.pos, colorIndex); + private void tintQuad(MutableQuadViewImpl quad) { + if (quad.tintIndex() != -1) { + final int blockColor = 0xFF000000 | this.colorMap.getColor(this.state, this.level, this.pos, quad.tintIndex()); for (int i = 0; i < 4; i++) { - quad.color(i, ColorHelper.multiplyColor(blockColor, quad.color(i))); + quad.color(i, ColorMixer.mulComponentWise(blockColor, quad.color(i))); } } } @@ -121,7 +121,7 @@ protected void shadeQuad(MutableQuadViewImpl quad, LightMode lightMode, boolean float[] brightnesses = this.quadLightData.br; for (int i = 0; i < 4; i++) { - quad.color(i, ColorHelper.multiplyRGB(quad.color(i), brightnesses[i])); + quad.color(i, ColorARGB.mulRGB(quad.color(i), brightnesses[i])); } } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/immediate/CloudRenderer.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/immediate/CloudRenderer.java index e0e6cf6088..16cf9cf093 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/immediate/CloudRenderer.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/immediate/CloudRenderer.java @@ -1,184 +1,237 @@ package net.caffeinemc.mods.sodium.client.render.immediate; +import com.mojang.blaze3d.buffers.BufferUsage; +import com.mojang.blaze3d.pipeline.RenderTarget; import com.mojang.blaze3d.platform.GlStateManager; import com.mojang.blaze3d.platform.NativeImage; import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.*; -import net.caffeinemc.mods.sodium.api.vertex.format.common.ColorVertex; -import net.caffeinemc.mods.sodium.api.vertex.buffer.VertexBufferWriter; import net.caffeinemc.mods.sodium.api.util.ColorABGR; import net.caffeinemc.mods.sodium.api.util.ColorARGB; -import net.caffeinemc.mods.sodium.api.util.ColorMixer; +import net.caffeinemc.mods.sodium.api.util.ColorU8; +import net.caffeinemc.mods.sodium.api.vertex.buffer.VertexBufferWriter; +import net.caffeinemc.mods.sodium.api.vertex.format.common.ColorVertex; import net.minecraft.client.Camera; import net.minecraft.client.CloudStatus; import net.minecraft.client.Minecraft; import net.minecraft.client.multiplayer.ClientLevel; -import net.minecraft.client.renderer.FogRenderer; -import net.minecraft.client.renderer.ShaderInstance; +import net.minecraft.client.renderer.*; import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.packs.resources.Resource; -import net.minecraft.server.packs.resources.ResourceManager; import net.minecraft.server.packs.resources.ResourceProvider; +import net.minecraft.util.ARGB; import net.minecraft.util.Mth; import net.minecraft.world.phys.Vec3; +import org.apache.commons.lang3.Validate; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.joml.Matrix4f; -import org.joml.Vector3f; +import org.joml.Vector4f; import org.lwjgl.opengl.GL32C; import org.lwjgl.system.MemoryStack; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -import java.io.IOException; -import java.io.InputStream; import java.util.Objects; public class CloudRenderer { - private static final ResourceLocation CLOUDS_TEXTURE_ID = ResourceLocation.withDefaultNamespace("textures/environment/clouds.png"); - - private CloudTextureData textureData; - private ShaderInstance shaderProgram; - - private @Nullable CloudRenderer.CloudGeometry cachedGeometry; + private static final Logger LOGGER = LoggerFactory.getLogger("Sodium-CloudRenderer"); + + private static final ShaderProgram CLOUDS_SHADER = new ShaderProgram( + ResourceLocation.fromNamespaceAndPath("sodium", "clouds"), + DefaultVertexFormat.POSITION_COLOR, + ShaderDefines.builder() + .build() + ); + + private static final ResourceLocation CLOUDS_TEXTURE_ID = + ResourceLocation.withDefaultNamespace("textures/environment/clouds.png"); + + private static final float CLOUD_HEIGHT = 4.0f; // The height of the cloud cells + private static final float CLOUD_WIDTH = 12.0f; // The width/length of cloud cells + + // Bitmasks for each cloud face + private static final int FACE_MASK_NEG_Y = 1 << 0; + private static final int FACE_MASK_POS_Y = 1 << 1; + private static final int FACE_MASK_NEG_X = 1 << 2; + private static final int FACE_MASK_POS_X = 1 << 3; + private static final int FACE_MASK_NEG_Z = 1 << 4; + private static final int FACE_MASK_POS_Z = 1 << 5; + + // The brightness of each fac + // The final color of each vertex is: vec4((texel.rgb * brightness), texel.a * 0.8) + private static final int BRIGHTNESS_POS_Y = ColorU8.normalizedFloatToByte(1.0F); // used for +Y + private static final int BRIGHTNESS_NEG_Y = ColorU8.normalizedFloatToByte(0.7F); // used for -Y + private static final int BRIGHTNESS_X_AXIS = ColorU8.normalizedFloatToByte(0.9F); // used for -X and +X + private static final int BRIGHTNESS_Z_AXIS = ColorU8.normalizedFloatToByte(0.8F); // used for -Z and +Z + + private @Nullable CloudTextureData textureData; + private @Nullable CloudGeometry builtGeometry; public CloudRenderer(ResourceProvider resourceProvider) { - this.reloadTextures(resourceProvider); + this.reload(resourceProvider); } public void render(Camera camera, ClientLevel level, Matrix4f projectionMatrix, - PoseStack poseStack, + Matrix4f modelView, float ticks, - float tickDelta) + float tickDelta, + int color) { - float cloudHeight = level.effects().getCloudHeight(); + float height = level.effects() + .getCloudHeight() + 0.33f; // arithmetic against NaN always produces NaN // Vanilla uses NaN height as a way to disable cloud rendering - if (Float.isNaN(cloudHeight)) { + if (Float.isNaN(height)) { return; } - // Skip rendering clouds if texture is completely blank - if (this.textureData.isBlank) { + // Skip rendering clouds if the texture data isn't available + // This can happen if the texture failed to load, or if the texture is completely empty + if (this.textureData == null) { return; } - Vec3 pos = camera.getPosition(); + Vec3 cameraPos = camera.getPosition(); + int renderDistance = getCloudRenderDistance(); + var renderMode = Minecraft.getInstance().options.getCloudsType(); + + // Translation of the clouds texture in world-space + double worldX = (cameraPos.x + ((ticks + tickDelta) * 0.03D)); + double worldZ = (cameraPos.z + 3.96D); + + double textureWidth = this.textureData.width * CLOUD_WIDTH; + double textureHeight = this.textureData.height * CLOUD_WIDTH; + worldX -= Mth.floor(worldX / textureWidth) * textureWidth; + worldZ -= Mth.floor(worldZ / textureHeight) * textureHeight; - double cloudTime = (ticks + tickDelta) * 0.03F; - double cloudCenterX = (pos.x() + cloudTime); - double cloudCenterZ = (pos.z()) + 0.33D; + // The coordinates of the cloud cell which the camera is within + int cellX = Mth.floor(worldX / CLOUD_WIDTH); + int cellZ = Mth.floor(worldZ / CLOUD_WIDTH); - int cloudDistance = getCloudRenderDistance(); + // The orientation of the camera relative to the clouds + // This is used to cull back-facing geometry + ViewOrientation orientation; - int centerCellX = (int) (Math.floor(cloudCenterX / 12.0)); - int centerCellZ = (int) (Math.floor(cloudCenterZ / 12.0)); + if (renderMode == CloudStatus.FANCY) { + orientation = ViewOrientation.getOrientation(cameraPos, height, height + CLOUD_HEIGHT); + } else { + // When fast clouds are used, there is no orientation of faces, since culling is disabled. + // To avoid unnecessary rebuilds, simply mark a null (undefined) orientation. + orientation = null; + } - // -1 if below clouds, +1 if above clouds - var cloudType = Minecraft.getInstance().options.getCloudsType(); - int orientation = cloudType == CloudStatus.FANCY ? (int) Math.signum(pos.y() - cloudHeight) : 0; - var parameters = new CloudGeometryParameters(centerCellX, centerCellZ, cloudDistance, orientation, cloudType); + var parameters = new CloudGeometryParameters(cellX, cellZ, renderDistance, orientation, renderMode); - CloudGeometry geometry = this.cachedGeometry; + CloudGeometry geometry = this.builtGeometry; + // Re-generate the cached cloud geometry if necessary if (geometry == null || !Objects.equals(geometry.params(), parameters)) { - this.cachedGeometry = (geometry = rebuildGeometry(geometry, parameters, this.textureData)); + this.builtGeometry = (geometry = rebuildGeometry(geometry, parameters, this.textureData)); } VertexBuffer vertexBuffer = geometry.vertexBuffer(); + + // The vertex buffer can be empty when there are no clouds to render if (vertexBuffer == null) { return; } - final float translateX = (float) (cloudCenterX - (centerCellX * 12)); - final float translateZ = (float) (cloudCenterZ - (centerCellZ * 12)); - - poseStack.pushPose(); + // Apply world->view transform + final float viewPosX = (float) (worldX - (cellX * CLOUD_WIDTH)); + final float viewPosY = (float) cameraPos.y() - height; + final float viewPosZ = (float) (worldZ - (cellZ * CLOUD_WIDTH)); - var poseEntry = poseStack.last(); + Matrix4f modelViewMatrix = new Matrix4f(modelView); + modelViewMatrix.translate(-viewPosX, -viewPosY, -viewPosZ); - Matrix4f modelViewMatrix = poseEntry.pose(); - modelViewMatrix.translate(-translateX, cloudHeight - (float) pos.y() + 0.33F, -translateZ); + // State setup + final var prevFogParameters = copyShaderFogParameters(RenderSystem.getShaderFog()); + final var flat = geometry.params() + .renderMode() == CloudStatus.FAST; - final var prevShaderFogShape = RenderSystem.getShaderFogShape(); - final var prevShaderFogEnd = RenderSystem.getShaderFogEnd(); - final var prevShaderFogStart = RenderSystem.getShaderFogStart(); + FogParameters fogParameters = FogRenderer.setupFog(camera, FogRenderer.FogMode.FOG_TERRAIN, new Vector4f(prevFogParameters.red(), prevFogParameters.green(), prevFogParameters.blue(), prevFogParameters.alpha()), renderDistance * 8, shouldUseWorldFog(level, cameraPos), tickDelta); - FogRenderer.setupFog(camera, FogRenderer.FogMode.FOG_TERRAIN, cloudDistance * 8, shouldUseWorldFog(level, pos), tickDelta); + RenderSystem.setShaderColor(ARGB.redFloat(color), ARGB.greenFloat(color), ARGB.blueFloat(color), 0.8F); + RenderSystem.setShaderFog(fogParameters); - boolean fastClouds = geometry.params().renderMode() == CloudStatus.FAST; - boolean fabulous = Minecraft.useShaderTransparency(); + RenderTarget renderTarget = Minecraft.getInstance().levelRenderer.getCloudsTarget(); - if (fastClouds) { - RenderSystem.disableCull(); + if (renderTarget != null) { + renderTarget.bindWrite(false); + } else { + Minecraft.getInstance() + .getMainRenderTarget() + .bindWrite(false); } - if (fabulous) { - Minecraft.getInstance().levelRenderer.getCloudsTarget().bindWrite(false); - } + RenderSystem.enableBlend(); + RenderSystem.blendFuncSeparate(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA); - Vec3 colorModulator = level.getCloudColor(tickDelta); - RenderSystem.setShaderColor((float) colorModulator.x, (float) colorModulator.y, (float) colorModulator.z, 0.8f); + RenderSystem.setShader(CLOUDS_SHADER); - vertexBuffer.bind(); + if (flat) { + RenderSystem.disableCull(); + } - RenderSystem.enableBlend(); RenderSystem.enableDepthTest(); - RenderSystem.blendFuncSeparate(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, - GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA); RenderSystem.depthFunc(GL32C.GL_LESS); - vertexBuffer.drawWithShader(modelViewMatrix, projectionMatrix, this.shaderProgram); + // Draw + vertexBuffer.bind(); + vertexBuffer.drawWithShader(modelViewMatrix, projectionMatrix, RenderSystem.getShader()); + VertexBuffer.unbind(); + // State teardown RenderSystem.depthFunc(GL32C.GL_LEQUAL); - RenderSystem.disableBlend(); + RenderSystem.disableDepthTest(); - VertexBuffer.unbind(); - - if (fastClouds) { + if (flat) { RenderSystem.enableCull(); } - if (fabulous) { - Minecraft.getInstance().getMainRenderTarget().bindWrite(false); + RenderSystem.defaultBlendFunc(); + RenderSystem.disableBlend(); + + if (renderTarget != null) { + Minecraft.getInstance() + .getMainRenderTarget() + .bindWrite(false); } + RenderSystem.setShaderFog(prevFogParameters); RenderSystem.setShaderColor(1.0f, 1.0f, 1.0f, 1.0f); - - RenderSystem.setShaderFogShape(prevShaderFogShape); - RenderSystem.setShaderFogEnd(prevShaderFogEnd); - RenderSystem.setShaderFogStart(prevShaderFogStart); - - poseStack.popPose(); } private static @NotNull CloudGeometry rebuildGeometry(@Nullable CloudGeometry existingGeometry, CloudGeometryParameters parameters, CloudTextureData textureData) { - BufferBuilder bufferBuilder = Tesselator.getInstance().begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR); + BufferBuilder bufferBuilder = Tesselator.getInstance() + .begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR); var writer = VertexBufferWriter.of(bufferBuilder); - var originCellX = parameters.originX(); - var originCellZ = parameters.originZ(); - - var orientation = parameters.orientation(); + final var radius = parameters.radius(); + final var orientation = parameters.orientation(); + final var flat = parameters.renderMode() == CloudStatus.FAST; - var radius = parameters.radius(); - var useFastGraphics = parameters.renderMode() == CloudStatus.FAST; + final var slice = textureData.slice(parameters.originX(), parameters.originZ(), radius); - addCellGeometryToBuffer(writer, textureData, originCellX, originCellZ, 0, 0, orientation, useFastGraphics); + // Iterate from the center coordinates (0, 0) outwards + // Since the geometry will be in sorted order, this avoids needing a depth pre-pass + addCellGeometryToBuffer(writer, slice, 0, 0, orientation, flat); for (int layer = 1; layer <= radius; layer++) { for (int z = -layer; z < layer; z++) { int x = Math.abs(z) - layer; - addCellGeometryToBuffer(writer, textureData, originCellX, originCellZ, x, z, orientation, useFastGraphics); + addCellGeometryToBuffer(writer, slice, x, z, orientation, flat); } for (int z = layer; z > -layer; z--) { int x = layer - Math.abs(z); - addCellGeometryToBuffer(writer, textureData, originCellX, originCellZ, x, z, orientation, useFastGraphics); + addCellGeometryToBuffer(writer, slice, x, z, orientation, flat); } } @@ -187,38 +240,43 @@ public void render(Camera camera, for (int z = -radius; z <= -l; z++) { int x = -z - layer; - addCellGeometryToBuffer(writer, textureData, originCellX, originCellZ, x, z, orientation, useFastGraphics); + addCellGeometryToBuffer(writer, slice, x, z, orientation, flat); } for (int z = l; z <= radius; z++) { int x = z - layer; - addCellGeometryToBuffer(writer, textureData, originCellX, originCellZ, x, z, orientation, useFastGraphics); + addCellGeometryToBuffer(writer, slice, x, z, orientation, flat); } for (int z = radius; z >= l; z--) { int x = layer - z; - addCellGeometryToBuffer(writer, textureData, originCellX, originCellZ, x, z, orientation, useFastGraphics); + addCellGeometryToBuffer(writer, slice, x, z, orientation, flat); } for (int z = -l; z >= -radius; z--) { int x = layer + z; - addCellGeometryToBuffer(writer, textureData, originCellX, originCellZ, x, z, orientation, useFastGraphics); + addCellGeometryToBuffer(writer, slice, x, z, orientation, flat); } } - MeshData builtBuffer = bufferBuilder.build(); + @Nullable MeshData meshData = bufferBuilder.build(); + @Nullable VertexBuffer vertexBuffer = null; - VertexBuffer vertexBuffer = null; + if (existingGeometry != null) { + vertexBuffer = existingGeometry.vertexBuffer(); + } - if (builtBuffer != null) { - if (existingGeometry != null) { - vertexBuffer = existingGeometry.vertexBuffer(); - } + if (meshData != null) { if (vertexBuffer == null) { - vertexBuffer = new VertexBuffer(VertexBuffer.Usage.DYNAMIC); + vertexBuffer = new VertexBuffer(BufferUsage.DYNAMIC_WRITE); } - uploadToVertexBuffer(vertexBuffer, builtBuffer); + uploadToVertexBuffer(vertexBuffer, meshData); + } else { + if (vertexBuffer != null) { + vertexBuffer.close(); + vertexBuffer = null; + } } Tesselator.getInstance().clear(); @@ -227,172 +285,235 @@ public void render(Camera camera, } private static void addCellGeometryToBuffer(VertexBufferWriter writer, - CloudTextureData textureData, - int originX, - int originZ, - int offsetX, - int offsetZ, - int orientation, - boolean useFastGraphics) { - int cellX = originX + offsetX; - int cellZ = originZ + offsetZ; - - int cellIndex = textureData.getCellIndexWrapping(cellX, cellZ); - int cellFaces = textureData.getCellFaces(cellIndex) & getVisibleFaces(offsetX, offsetZ, orientation); - - if (cellFaces == 0) { + CloudTextureData.Slice textureData, + int x, + int z, + @Nullable CloudRenderer.ViewOrientation orientation, + boolean flat) + { + int index = textureData.getCellIndex(x, z); + int faces = textureData.getCellFaces(index) & getVisibleFaces(x, z, orientation); + + if (faces == 0) { return; } - int cellColor = textureData.getCellColor(cellIndex); + int color = textureData.getCellColor(index); - if (ColorABGR.unpackAlpha(cellColor) == 0) { + if (isTransparent(color)) { return; } - float x = offsetX * 12; - float z = offsetZ * 12; - - if (useFastGraphics) { - emitCellGeometry2D(writer, cellFaces, cellColor, x, z); + if (flat) { + emitCellGeometryFlat(writer, color, x, z); } else { - emitCellGeometry3D(writer, cellFaces, cellColor, x, z, false); + emitCellGeometryExterior(writer, faces, color, x, z); - int distance = Math.abs(offsetX) + Math.abs(offsetZ); - - if (distance <= 1) { - emitCellGeometry3D(writer, CloudFaceSet.all(), cellColor, x, z, true); + if (taxicabDistance(x, z) <= 1) { + emitCellGeometryInterior(writer, color, x, z); } } } - private static int getVisibleFaces(int x, int z, int orientation) { - int faces = CloudFaceSet.all(); + private static int getVisibleFaces(int x, int z, ViewOrientation orientation) { + int faces = 0; - if (x > 0) { - faces = CloudFaceSet.remove(faces, CloudFace.POS_X); + if (x <= 0) { + faces |= FACE_MASK_POS_X; } - if (z > 0) { - faces = CloudFaceSet.remove(faces, CloudFace.POS_Z); + if (z <= 0) { + faces |= FACE_MASK_POS_Z; } - if (x < 0) { - faces = CloudFaceSet.remove(faces, CloudFace.NEG_X); + if (x >= 0) { + faces |= FACE_MASK_NEG_X; } - if (z < 0) { - faces = CloudFaceSet.remove(faces, CloudFace.NEG_Z); + if (z >= 0) { + faces |= FACE_MASK_NEG_Z; } - if (orientation < 0) { - faces = CloudFaceSet.remove(faces, CloudFace.POS_Y); + if (orientation != ViewOrientation.BELOW_CLOUDS) { + faces |= FACE_MASK_POS_Y; } - if (orientation > 0) { - faces = CloudFaceSet.remove(faces, CloudFace.NEG_Y); + if (orientation != ViewOrientation.ABOVE_CLOUDS) { + faces |= FACE_MASK_NEG_Y; } return faces; } - private static final Vector3f[][] VERTICES = new Vector3f[CloudFace.COUNT][]; - - static { - VERTICES[CloudFace.NEG_Y.ordinal()] = new Vector3f[] { - new Vector3f(12.0f, 0.0f, 12.0f), - new Vector3f( 0.0f, 0.0f, 12.0f), - new Vector3f( 0.0f, 0.0f, 0.0f), - new Vector3f(12.0f, 0.0f, 0.0f) - }; - - VERTICES[CloudFace.POS_Y.ordinal()] = new Vector3f[] { - new Vector3f( 0.0f, 4.0f, 12.0f), - new Vector3f(12.0f, 4.0f, 12.0f), - new Vector3f(12.0f, 4.0f, 0.0f), - new Vector3f( 0.0f, 4.0f, 0.0f) - }; - - VERTICES[CloudFace.NEG_X.ordinal()] = new Vector3f[] { - new Vector3f( 0.0f, 0.0f, 12.0f), - new Vector3f( 0.0f, 4.0f, 12.0f), - new Vector3f( 0.0f, 4.0f, 0.0f), - new Vector3f( 0.0f, 0.0f, 0.0f) - }; - - VERTICES[CloudFace.POS_X.ordinal()] = new Vector3f[] { - new Vector3f(12.0f, 4.0f, 12.0f), - new Vector3f(12.0f, 0.0f, 12.0f), - new Vector3f(12.0f, 0.0f, 0.0f), - new Vector3f(12.0f, 4.0f, 0.0f) - }; - - VERTICES[CloudFace.NEG_Z.ordinal()] = new Vector3f[] { - new Vector3f(12.0f, 4.0f, 0.0f), - new Vector3f(12.0f, 0.0f, 0.0f), - new Vector3f( 0.0f, 0.0f, 0.0f), - new Vector3f( 0.0f, 4.0f, 0.0f) - }; - - VERTICES[CloudFace.POS_Z.ordinal()] = new Vector3f[] { - new Vector3f(12.0f, 0.0f, 12.0f), - new Vector3f(12.0f, 4.0f, 12.0f), - new Vector3f( 0.0f, 4.0f, 12.0f), - new Vector3f( 0.0f, 0.0f, 12.0f) - }; - } - - private static void emitCellGeometry2D(VertexBufferWriter writer, int faces, int color, float x, float z) { + private static void emitCellGeometryFlat(VertexBufferWriter writer, int texel, int x, int z) { try (MemoryStack stack = MemoryStack.stackPush()) { - final long buffer = stack.nmalloc(4 * ColorVertex.STRIDE); - - long ptr = buffer; - int count = 0; + final long vertexBuffer = stack.nmalloc(4 * ColorVertex.STRIDE); + long ptr = vertexBuffer; - int mixedColor = ColorMixer.mul(color, CloudFace.POS_Y.getColor()); + final float x0 = (x * CLOUD_WIDTH); + final float x1 = x0 + CLOUD_WIDTH; + final float z0 = (z * CLOUD_WIDTH); + final float z1 = z0 + CLOUD_WIDTH; - ptr = writeVertex(ptr, x + 12.0f, 0.0f, z + 12.0f, mixedColor); - ptr = writeVertex(ptr, x + 0.0f, 0.0f, z + 12.0f, mixedColor); - ptr = writeVertex(ptr, x + 0.0f, 0.0f, z + 0.0f, mixedColor); - ptr = writeVertex(ptr, x + 12.0f, 0.0f, z + 0.0f, mixedColor); - - count += 4; + { + final int color = ColorABGR.mulRGB(texel, BRIGHTNESS_POS_Y); + ptr = writeVertex(ptr, x1, 0.0f, z1, color); + ptr = writeVertex(ptr, x0, 0.0f, z1, color); + ptr = writeVertex(ptr, x0, 0.0f, z0, color); + ptr = writeVertex(ptr, x1, 0.0f, z0, color); + } - writer.push(stack, buffer, count, ColorVertex.FORMAT); + writer.push(stack, vertexBuffer, 4, ColorVertex.FORMAT); } } - private static void emitCellGeometry3D(VertexBufferWriter writer, int visibleFaces, int baseColor, float posX, float posZ, boolean interior) { + private static void emitCellGeometryExterior(VertexBufferWriter writer, int cellFaces, int cellColor, int cellX, int cellZ) { try (MemoryStack stack = MemoryStack.stackPush()) { - final long buffer = stack.nmalloc(6 * 4 * ColorVertex.STRIDE); + final long vertexBuffer = stack.nmalloc(6 * 4 * ColorVertex.STRIDE); + int vertexCount = 0; + + long ptr = vertexBuffer; + + final float x0 = cellX * CLOUD_WIDTH; + final float y0 = 0.0f; + final float z0 = cellZ * CLOUD_WIDTH; + + final float x1 = x0 + CLOUD_WIDTH; + final float y1 = y0 + CLOUD_HEIGHT; + final float z1 = z0 + CLOUD_WIDTH; + + // -Y + if ((cellFaces & FACE_MASK_NEG_Y) != 0) { + final int vertexColor = ColorABGR.mulRGB(cellColor, BRIGHTNESS_NEG_Y); + ptr = writeVertex(ptr, x1, y0, z1, vertexColor); + ptr = writeVertex(ptr, x0, y0, z1, vertexColor); + ptr = writeVertex(ptr, x0, y0, z0, vertexColor); + ptr = writeVertex(ptr, x1, y0, z0, vertexColor); + vertexCount += 4; + } + + // +Y + if ((cellFaces & FACE_MASK_POS_Y) != 0) { + final int vertexColor = ColorABGR.mulRGB(cellColor, BRIGHTNESS_POS_Y); + ptr = writeVertex(ptr, x0, y1, z1, vertexColor); + ptr = writeVertex(ptr, x1, y1, z1, vertexColor); + ptr = writeVertex(ptr, x1, y1, z0, vertexColor); + ptr = writeVertex(ptr, x0, y1, z0, vertexColor); + vertexCount += 4; + } - long ptr = buffer; - int count = 0; + if ((cellFaces & (FACE_MASK_NEG_X | FACE_MASK_POS_X)) != 0) { + final int vertexColor = ColorABGR.mulRGB(cellColor, BRIGHTNESS_X_AXIS); - for (var face : CloudFace.VALUES) { - if (!CloudFaceSet.contains(visibleFaces, face)) { - continue; + // -X + if ((cellFaces & FACE_MASK_NEG_X) != 0) { + ptr = writeVertex(ptr, x0, y0, z1, vertexColor); + ptr = writeVertex(ptr, x0, y1, z1, vertexColor); + ptr = writeVertex(ptr, x0, y1, z0, vertexColor); + ptr = writeVertex(ptr, x0, y0, z0, vertexColor); + vertexCount += 4; } - final var vertices = VERTICES[face.ordinal()]; - final int color = ColorMixer.mul(baseColor, face.getColor()); + // +X + if ((cellFaces & FACE_MASK_POS_X) != 0) { + ptr = writeVertex(ptr, x1, y1, z1, vertexColor); + ptr = writeVertex(ptr, x1, y0, z1, vertexColor); + ptr = writeVertex(ptr, x1, y0, z0, vertexColor); + ptr = writeVertex(ptr, x1, y1, z0, vertexColor); + vertexCount += 4; + } + } - for (int vertexIndex = 0; vertexIndex < 4; vertexIndex++) { - Vector3f vertex = vertices[interior ? 3 - vertexIndex : vertexIndex]; + if ((cellFaces & (FACE_MASK_NEG_Z | FACE_MASK_POS_Z)) != 0) { + final int vertexColor = ColorABGR.mulRGB(cellColor, BRIGHTNESS_Z_AXIS); - final float x = vertex.x + posX; - final float y = vertex.y; - final float z = vertex.z + posZ; + // -Z + if ((cellFaces & FACE_MASK_NEG_Z) != 0) { + ptr = writeVertex(ptr, x1, y1, z0, vertexColor); + ptr = writeVertex(ptr, x1, y0, z0, vertexColor); + ptr = writeVertex(ptr, x0, y0, z0, vertexColor); + ptr = writeVertex(ptr, x0, y1, z0, vertexColor); + vertexCount += 4; + } - ptr = writeVertex(ptr, x, y, z, color); + // +Z + if ((cellFaces & FACE_MASK_POS_Z) != 0) { + ptr = writeVertex(ptr, x1, y0, z1, vertexColor); + ptr = writeVertex(ptr, x1, y1, z1, vertexColor); + ptr = writeVertex(ptr, x0, y1, z1, vertexColor); + ptr = writeVertex(ptr, x0, y0, z1, vertexColor); + vertexCount += 4; } + } + + writer.push(stack, vertexBuffer, vertexCount, ColorVertex.FORMAT); + } + } + + private static void emitCellGeometryInterior(VertexBufferWriter writer, int baseColor, int cellX, int cellZ) { + try (MemoryStack stack = MemoryStack.stackPush()) { + final long vertexBuffer = stack.nmalloc(6 * 4 * ColorVertex.STRIDE); + long ptr = vertexBuffer; + + final float x0 = cellX * CLOUD_WIDTH; + final float y0 = 0.0f; + final float z0 = cellZ * CLOUD_WIDTH; + + final float x1 = x0 + CLOUD_WIDTH; + final float y1 = y0 + CLOUD_HEIGHT; + final float z1 = z0 + CLOUD_WIDTH; + + { + // -Y + final int vertexColor = ColorABGR.mulRGB(baseColor, BRIGHTNESS_NEG_Y); + ptr = writeVertex(ptr, x1, y0, z0, vertexColor); + ptr = writeVertex(ptr, x0, y0, z0, vertexColor); + ptr = writeVertex(ptr, x0, y0, z1, vertexColor); + ptr = writeVertex(ptr, x1, y0, z1, vertexColor); + } + + { + // +Y + final int vertexColor = ColorABGR.mulRGB(baseColor, BRIGHTNESS_POS_Y); + ptr = writeVertex(ptr, x0, y1, z0, vertexColor); + ptr = writeVertex(ptr, x1, y1, z0, vertexColor); + ptr = writeVertex(ptr, x1, y1, z1, vertexColor); + ptr = writeVertex(ptr, x0, y1, z1, vertexColor); + } - count += 4; + { + final int vertexColor = ColorABGR.mulRGB(baseColor, BRIGHTNESS_X_AXIS); + + // -X + ptr = writeVertex(ptr, x0, y0, z0, vertexColor); + ptr = writeVertex(ptr, x0, y1, z0, vertexColor); + ptr = writeVertex(ptr, x0, y1, z1, vertexColor); + ptr = writeVertex(ptr, x0, y0, z1, vertexColor); + + // +X + ptr = writeVertex(ptr, x1, y1, z0, vertexColor); + ptr = writeVertex(ptr, x1, y0, z0, vertexColor); + ptr = writeVertex(ptr, x1, y0, z1, vertexColor); + ptr = writeVertex(ptr, x1, y1, z1, vertexColor); } - if (count > 0) { - writer.push(stack, buffer, count, ColorVertex.FORMAT); + { + final int vertexColor = ColorABGR.mulRGB(baseColor, BRIGHTNESS_Z_AXIS); + + // -Z + ptr = writeVertex(ptr, x0, y1, z0, vertexColor); + ptr = writeVertex(ptr, x0, y0, z0, vertexColor); + ptr = writeVertex(ptr, x1, y0, z0, vertexColor); + ptr = writeVertex(ptr, x1, y1, z0, vertexColor); + + // +Z + ptr = writeVertex(ptr, x0, y0, z1, vertexColor); + ptr = writeVertex(ptr, x0, y1, z1, vertexColor); + ptr = writeVertex(ptr, x1, y1, z1, vertexColor); + ptr = writeVertex(ptr, x1, y0, z1, vertexColor); } + + writer.push(stack, vertexBuffer, 6 * 4, ColorVertex.FORMAT); } } @@ -408,43 +529,36 @@ private static void uploadToVertexBuffer(VertexBuffer vertexBuffer, MeshData bui VertexBuffer.unbind(); } - public void reloadTextures(ResourceProvider resourceProvider) { + public void reload(ResourceProvider resourceProvider) { this.destroy(); - - this.textureData = loadTextureData(); - - try { - this.shaderProgram = new ShaderInstance(resourceProvider, "clouds", DefaultVertexFormat.POSITION_COLOR); - } catch (IOException e) { - throw new RuntimeException(e); - } + this.textureData = loadTextureData(resourceProvider); } public void destroy() { - if (this.shaderProgram != null) { - this.shaderProgram.close(); - this.shaderProgram = null; - } + if (this.builtGeometry != null) { + var vertexBuffer = this.builtGeometry.vertexBuffer(); - if (this.cachedGeometry != null) { - var vertexBuffer = this.cachedGeometry.vertexBuffer(); - vertexBuffer.close(); + if (vertexBuffer != null) { + vertexBuffer.close(); + } - this.cachedGeometry = null; + this.builtGeometry = null; } } - private static CloudTextureData loadTextureData() { - ResourceManager resourceManager = Minecraft.getInstance().getResourceManager(); - Resource resource = resourceManager.getResource(CLOUDS_TEXTURE_ID) - .orElseThrow(); + private static @Nullable CloudTextureData loadTextureData(ResourceProvider resourceProvider) { + var resource = resourceProvider.getResource(CloudRenderer.CLOUDS_TEXTURE_ID) + .orElseThrow(); // always provided by default resource pack - try (InputStream inputStream = resource.open()){ - try (NativeImage nativeImage = NativeImage.read(inputStream)) { - return new CloudTextureData(nativeImage); - } - } catch (IOException ex) { - throw new RuntimeException("Failed to load texture data", ex); + try (var inputStream = resource.open(); + var nativeImage = NativeImage.read(inputStream)) + { + return CloudTextureData.load(nativeImage); + } + catch (Throwable t) { + LOGGER.error("Failed to load texture '{}'. The rendering of clouds in the skybox will be disabled. " + + "This may be caused by an incompatible resource pack.", CloudRenderer.CLOUDS_TEXTURE_ID, t); + return null; } } @@ -457,96 +571,107 @@ private static int getCloudRenderDistance() { return Math.max(32, (Minecraft.getInstance().options.getEffectiveRenderDistance() * 2) + 9); } - private enum CloudFace { - NEG_Y(ColorABGR.pack(0.7F, 0.7F, 0.7F, 1.0f)), - POS_Y(ColorABGR.pack(1.0f, 1.0f, 1.0f, 1.0f)), - NEG_X(ColorABGR.pack(0.9F, 0.9F, 0.9F, 1.0f)), - POS_X(ColorABGR.pack(0.9F, 0.9F, 0.9F, 1.0f)), - NEG_Z(ColorABGR.pack(0.8F, 0.8F, 0.8F, 1.0f)), - POS_Z(ColorABGR.pack(0.8F, 0.8F, 0.8F, 1.0f)); - - public static final CloudFace[] VALUES = CloudFace.values(); - public static final int COUNT = VALUES.length; - - private final int color; - - CloudFace(int color) { - this.color = color; - } - - public int getColor() { - return this.color; - } + private static boolean isTransparent(int argb) { + return ColorARGB.unpackAlpha(argb) < 10; } - private static class CloudFaceSet { - public static int empty() { - return 0; - } - - public static boolean contains(int set, CloudFace face) { - return (set & (1 << face.ordinal())) != 0; - } - - public static int add(int set, CloudFace face) { - return set | (1 << face.ordinal()); - } - - public static int remove(int set, CloudFace face) { - return set & ~(1 << face.ordinal()); - } - - public static int all() { - return (1 << CloudFace.COUNT) - 1; - } + private static int taxicabDistance(int x, int z) { + return Math.abs(x) + Math.abs(z); } - private static boolean isTransparentCell(int color) { - return ColorARGB.unpackAlpha(color) <= 1; + private static FogParameters copyShaderFogParameters(FogParameters shaderFog) { + return new FogParameters( + shaderFog.start(), + shaderFog.end(), + shaderFog.shape(), + shaderFog.red(), + shaderFog.green(), + shaderFog.blue(), + shaderFog.alpha()); } private static class CloudTextureData { private final byte[] faces; private final int[] colors; - private boolean isBlank; private final int width, height; - public CloudTextureData(NativeImage texture) { - int width = texture.getWidth(); - int height = texture.getHeight(); - + private CloudTextureData(int width, int height) { this.faces = new byte[width * height]; this.colors = new int[width * height]; - this.isBlank = true; this.width = width; this.height = height; + } + + public Slice slice(int originX, int originY, int radius) { + final var src = this; + final var dst = new CloudTextureData.Slice(radius); + + for (int dstY = 0; dstY < dst.height; dstY++) { + int srcX = Math.floorMod(originX - radius, this.width); + int srcY = Math.floorMod(originY - radius + dstY, this.height); - this.loadTextureData(texture, width, height); + int dstX = 0; + + while (dstX < dst.width) { + final int length = Math.min(src.width - srcX, dst.width - dstX); + + final int srcPos = getCellIndex(srcX, srcY, src.width); + final int dstPos = getCellIndex(dstX, dstY, dst.width); + + System.arraycopy(this.faces, srcPos, dst.faces, dstPos, length); + System.arraycopy(this.colors, srcPos, dst.colors, dstPos, length); + + srcX = 0; + dstX += length; + } + } + + return dst; + } + + public static @Nullable CloudTextureData load(NativeImage image) { + final int width = image.getWidth(); + final int height = image.getHeight(); + + var data = new CloudTextureData(width, height); + + if (!data.loadTextureData(image, width, height)) { + return null; // The texture is empty, so it isn't necessary to render it + } + + return data; } - private void loadTextureData(NativeImage texture, int width, int height) { + private boolean loadTextureData(NativeImage texture, int width, int height) { + Validate.isTrue(this.width == width); + Validate.isTrue(this.height == height); + + boolean containsData = false; + for (int x = 0; x < width; x++) { for (int z = 0; z < height; z++) { - int index = this.getCellIndex(x, z); - int color = texture.getPixelRGBA(x, z); + int color = texture.getPixel(x, z); + if (isTransparent(color)) { + continue; + } + + int index = getCellIndex(x, z, width); this.colors[index] = color; + this.faces[index] = (byte) getOpenFaces(texture, color, x, z); - if (!isTransparentCell(color)) { - this.faces[index] = (byte) getOpenFaces(texture, color, x, z); - this.isBlank = false; - } + containsData = true; } } + + return containsData; } private static int getOpenFaces(NativeImage image, int color, int x, int z) { // Since the cloud texture is only 2D, nothing can hide the top or bottom faces - int faces = CloudFaceSet.empty(); - faces = CloudFaceSet.add(faces, CloudFace.NEG_Y); - faces = CloudFaceSet.add(faces, CloudFace.POS_Y); + int faces = FACE_MASK_NEG_Y | FACE_MASK_POS_Y; // Generate faces where the neighbor cell is a different color // Do not generate duplicate faces between two cells @@ -555,7 +680,7 @@ private static int getOpenFaces(NativeImage image, int color, int x, int z) { int neighbor = getNeighborTexel(image, x - 1, z); if (color != neighbor) { - faces = CloudFaceSet.add(faces, CloudFace.NEG_X); + faces |= FACE_MASK_NEG_X; } } @@ -564,7 +689,7 @@ private static int getOpenFaces(NativeImage image, int color, int x, int z) { int neighbor = getNeighborTexel(image, x + 1, z); if (color != neighbor) { - faces = CloudFaceSet.add(faces, CloudFace.POS_X); + faces |= FACE_MASK_POS_X; } } @@ -573,7 +698,7 @@ private static int getOpenFaces(NativeImage image, int color, int x, int z) { int neighbor = getNeighborTexel(image, x, z - 1); if (color != neighbor) { - faces = CloudFaceSet.add(faces, CloudFace.NEG_Z); + faces |= FACE_MASK_NEG_Z; } } @@ -582,7 +707,7 @@ private static int getOpenFaces(NativeImage image, int color, int x, int z) { int neighbor = getNeighborTexel(image, x, z + 1); if (color != neighbor) { - faces = CloudFaceSet.add(faces, CloudFace.POS_Z); + faces |= FACE_MASK_POS_Z; } } @@ -593,7 +718,7 @@ private static int getNeighborTexel(NativeImage image, int x, int z) { x = wrapTexelCoord(x, 0, image.getWidth() - 1); z = wrapTexelCoord(z, 0, image.getHeight() - 1); - return image.getPixelRGBA(x, z); + return image.getPixel(x, z); } private static int wrapTexelCoord(int coord, int min, int max) { @@ -608,31 +733,59 @@ private static int wrapTexelCoord(int coord, int min, int max) { return coord; } - public int getCellFaces(int index) { - return this.faces[index]; + private static int getCellIndex(int x, int z, int pitch) { + return (z * pitch) + x; } - public int getCellColor(int index) { - return this.colors[index]; - } + public static class Slice { + private final int width, height; + private final int radius; + private final byte[] faces; + private final int[] colors; - private int getCellIndexWrapping(int x, int z) { - return this.getCellIndex( - Math.floorMod(x, this.width), - Math.floorMod(z, this.height) - ); - } + public Slice(int radius) { + this.width = 1 + (radius * 2); + this.height = 1 + (radius * 2); + this.radius = radius; + this.faces = new byte[this.width * this.height]; + this.colors = new int[this.width * this.height]; + } + + public int getCellIndex(int x, int z) { + return CloudTextureData.getCellIndex(x + this.radius, z + this.radius, this.width); + } - private int getCellIndex(int x, int z) { - return (x * this.width) + z; + public int getCellFaces(int index) { + return Byte.toUnsignedInt(this.faces[index]); + } + + public int getCellColor(int index) { + return this.colors[index]; + } } } - public record CloudGeometry(VertexBuffer vertexBuffer, CloudGeometryParameters params) { + public record CloudGeometry(@Nullable VertexBuffer vertexBuffer, CloudGeometryParameters params) { } - public record CloudGeometryParameters(int originX, int originZ, int radius, int orientation, CloudStatus renderMode) { + public record CloudGeometryParameters(int originX, int originZ, int radius, @Nullable ViewOrientation orientation, CloudStatus renderMode) { } + + private enum ViewOrientation { + BELOW_CLOUDS, // Top faces should *not* be rendered + INSIDE_CLOUDS, // All faces *must* be rendered + ABOVE_CLOUDS; // Bottom faces should *not* be rendered + + public static @NotNull ViewOrientation getOrientation(Vec3 camera, float minY, float maxY) { + if (camera.y() <= minY + 0.125f /* epsilon */) { + return ViewOrientation.BELOW_CLOUDS; + } else if (camera.y() >= maxY - 0.125f /* epsilon */) { + return ViewOrientation.ABOVE_CLOUDS; + } else { + return ViewOrientation.INSIDE_CLOUDS; + } + } + } } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/immediate/model/BakedModelEncoder.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/immediate/model/BakedModelEncoder.java index 1de8965c12..854141ec5f 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/immediate/model/BakedModelEncoder.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/immediate/model/BakedModelEncoder.java @@ -1,6 +1,7 @@ package net.caffeinemc.mods.sodium.client.render.immediate.model; import com.mojang.blaze3d.vertex.PoseStack; +import net.caffeinemc.mods.sodium.api.util.ColorMixer; import net.caffeinemc.mods.sodium.client.model.quad.ModelQuadView; import net.caffeinemc.mods.sodium.api.math.MatrixHelper; import net.caffeinemc.mods.sodium.api.util.ColorABGR; @@ -23,7 +24,7 @@ private static int mergeLighting(int stored, int calculated) { private static final boolean MULTIPLY_ALPHA = PlatformRuntimeInformation.getInstance().usesAlphaMultiplication(); - public static void writeQuadVertices(VertexBufferWriter writer, PoseStack.Pose matrices, ModelQuadView quad, int color, int light, int overlay) { + public static void writeQuadVertices(VertexBufferWriter writer, PoseStack.Pose matrices, ModelQuadView quad, int color, int light, int overlay, boolean colorize) { Matrix3f matNormal = matrices.normal(); Matrix4f matPosition = matrices.pose(); @@ -37,7 +38,13 @@ public static void writeQuadVertices(VertexBufferWriter writer, PoseStack.Pose m float y = quad.getY(i); float z = quad.getZ(i); - int newLight = mergeLighting(quad.getLight(i), light); + int newLight = mergeLighting(quad.getMaxLightQuad(i), light); + + int newColor = color; + + if (colorize) { + newColor = ColorMixer.mulComponentWise(newColor, quad.getColor(i)); + } // The packed transformed normal vector int normal = MatrixHelper.transformNormal(matNormal, matrices.trustedNormals, quad.getAccurateNormal(i)); @@ -47,7 +54,7 @@ public static void writeQuadVertices(VertexBufferWriter writer, PoseStack.Pose m float yt = MatrixHelper.transformPositionY(matPosition, x, y, z); float zt = MatrixHelper.transformPositionZ(matPosition, x, y, z); - EntityVertex.write(ptr, xt, yt, zt, color, quad.getTexU(i), quad.getTexV(i), overlay, newLight, normal); + EntityVertex.write(ptr, xt, yt, zt, newColor, quad.getTexU(i), quad.getTexV(i), overlay, newLight, normal); ptr += EntityVertex.STRIDE; } @@ -116,4 +123,8 @@ public static void writeQuadVertices(VertexBufferWriter writer, PoseStack.Pose m writer.push(stack, buffer, 4, EntityVertex.FORMAT); } } + + public static boolean shouldMultiplyAlpha() { + return MULTIPLY_ALPHA; + } } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/immediate/model/EntityRenderer.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/immediate/model/EntityRenderer.java index 36ee747940..fd7743dac3 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/immediate/model/EntityRenderer.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/immediate/model/EntityRenderer.java @@ -10,22 +10,14 @@ import org.joml.Vector2f; import org.joml.Vector3f; import org.lwjgl.system.MemoryStack; -import org.lwjgl.system.MemoryUtil; -public class EntityRenderer { +import static net.caffeinemc.mods.sodium.client.render.immediate.model.ModelCuboid.*; +public class EntityRenderer { private static final int NUM_CUBE_VERTICES = 8; private static final int NUM_CUBE_FACES = 6; private static final int NUM_FACE_VERTICES = 4; - private static final int - FACE_NEG_Y = 0, // DOWN - FACE_POS_Y = 1, // UP - FACE_NEG_Z = 2, // NORTH - FACE_POS_Z = 3, // SOUTH - FACE_NEG_X = 4, // WEST - FACE_POS_X = 5; // EAST - private static final int VERTEX_X1_Y1_Z1 = 0, VERTEX_X2_Y1_Z1 = 1, @@ -38,17 +30,10 @@ public class EntityRenderer { private static final Matrix3f lastMatrix = new Matrix3f(); - private static final long SCRATCH_BUFFER = MemoryUtil.nmemAlignedAlloc(64, NUM_CUBE_FACES * NUM_FACE_VERTICES * EntityVertex.STRIDE); + private static final int VERTEX_BUFFER_BYTES = NUM_CUBE_FACES * NUM_FACE_VERTICES * EntityVertex.STRIDE; private static final Vector3f[] CUBE_CORNERS = new Vector3f[NUM_CUBE_VERTICES]; - private static final int[][] CUBE_VERTICES = new int[][] { - { VERTEX_X2_Y1_Z2, VERTEX_X1_Y1_Z2, VERTEX_X1_Y1_Z1, VERTEX_X2_Y1_Z1 }, - { VERTEX_X2_Y2_Z1, VERTEX_X1_Y2_Z1, VERTEX_X1_Y2_Z2, VERTEX_X2_Y2_Z2 }, - { VERTEX_X2_Y1_Z1, VERTEX_X1_Y1_Z1, VERTEX_X1_Y2_Z1, VERTEX_X2_Y2_Z1 }, - { VERTEX_X1_Y1_Z2, VERTEX_X2_Y1_Z2, VERTEX_X2_Y2_Z2, VERTEX_X1_Y2_Z2 }, - { VERTEX_X2_Y1_Z2, VERTEX_X2_Y1_Z1, VERTEX_X2_Y2_Z1, VERTEX_X2_Y2_Z2 }, - { VERTEX_X1_Y1_Z1, VERTEX_X1_Y1_Z2, VERTEX_X1_Y2_Z2, VERTEX_X1_Y2_Z1 }, - }; + private static final int[][] CUBE_VERTICES = new int[NUM_CUBE_FACES][]; private static final Vector3f[][] VERTEX_POSITIONS = new Vector3f[NUM_CUBE_FACES][NUM_FACE_VERTICES]; private static final Vector3f[][] VERTEX_POSITIONS_MIRRORED = new Vector3f[NUM_CUBE_FACES][NUM_FACE_VERTICES]; @@ -60,6 +45,13 @@ public class EntityRenderer { private static final int[] CUBE_NORMALS_MIRRORED = new int[NUM_CUBE_FACES]; static { + CUBE_VERTICES[FACE_NEG_Y] = new int[] { VERTEX_X2_Y1_Z2, VERTEX_X1_Y1_Z2, VERTEX_X1_Y1_Z1, VERTEX_X2_Y1_Z1 }; + CUBE_VERTICES[FACE_POS_Y] = new int[] { VERTEX_X2_Y2_Z1, VERTEX_X1_Y2_Z1, VERTEX_X1_Y2_Z2, VERTEX_X2_Y2_Z2 }; + CUBE_VERTICES[FACE_NEG_Z] = new int[] { VERTEX_X2_Y1_Z1, VERTEX_X1_Y1_Z1, VERTEX_X1_Y2_Z1, VERTEX_X2_Y2_Z1 }; + CUBE_VERTICES[FACE_POS_Z] = new int[] { VERTEX_X1_Y1_Z2, VERTEX_X2_Y1_Z2, VERTEX_X2_Y2_Z2, VERTEX_X1_Y2_Z2 }; + CUBE_VERTICES[FACE_NEG_X] = new int[] { VERTEX_X2_Y1_Z2, VERTEX_X2_Y1_Z1, VERTEX_X2_Y2_Z1, VERTEX_X2_Y2_Z2 }; + CUBE_VERTICES[FACE_POS_X] = new int[] { VERTEX_X1_Y1_Z1, VERTEX_X1_Y1_Z2, VERTEX_X1_Y2_Z2, VERTEX_X1_Y2_Z1 }; + for (int cornerIndex = 0; cornerIndex < NUM_CUBE_VERTICES; cornerIndex++) { CUBE_CORNERS[cornerIndex] = new Vector3f(); } @@ -81,24 +73,26 @@ public class EntityRenderer { public static void renderCuboid(PoseStack.Pose matrices, VertexBufferWriter writer, ModelCuboid cuboid, int light, int overlay, int color) { prepareNormalsIfChanged(matrices); - prepareVertices(matrices, cuboid); - var vertexCount = emitQuads(cuboid, color, overlay, light); - try (MemoryStack stack = MemoryStack.stackPush()) { - writer.push(stack, SCRATCH_BUFFER, vertexCount, EntityVertex.FORMAT); + final var vertexBuffer = stack.nmalloc(16, VERTEX_BUFFER_BYTES); + final var vertexCount = emitQuads(vertexBuffer, cuboid, color, overlay, light); + + if (vertexCount > 0) { + writer.push(stack, vertexBuffer, vertexCount, EntityVertex.FORMAT); + } } } - private static int emitQuads(ModelCuboid cuboid, int color, int overlay, int light) { + private static int emitQuads(final long buffer, ModelCuboid cuboid, int color, int overlay, int light) { final var positions = cuboid.mirror ? VERTEX_POSITIONS_MIRRORED : VERTEX_POSITIONS; final var textures = cuboid.mirror ? VERTEX_TEXTURES_MIRRORED : VERTEX_TEXTURES; final var normals = cuboid.mirror ? CUBE_NORMALS_MIRRORED : CUBE_NORMALS; var vertexCount = 0; - long ptr = SCRATCH_BUFFER; + long ptr = buffer; for (int quadIndex = 0; quadIndex < NUM_CUBE_FACES; quadIndex++) { if (!cuboid.shouldDrawFace(quadIndex)) { diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/immediate/model/ModelCuboid.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/immediate/model/ModelCuboid.java index 57a3dc83f0..4a13b857ca 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/immediate/model/ModelCuboid.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/immediate/model/ModelCuboid.java @@ -2,15 +2,26 @@ import java.util.Set; import net.minecraft.core.Direction; +import org.jetbrains.annotations.NotNull; public class ModelCuboid { + // The ordering needs to be the same as Minecraft, otherwise some core shader replacements + // will be unable to identify the facing. + public static final int + FACE_NEG_Y = 0, // DOWN + FACE_POS_Y = 1, // UP + FACE_NEG_X = 2, // WEST + FACE_NEG_Z = 3, // NORTH + FACE_POS_X = 4, // EAST + FACE_POS_Z = 5; // SOUTH + public final float x1, y1, z1; public final float x2, y2, z2; public final float u0, u1, u2, u3, u4, u5; public final float v0, v1, v2; - private final int faces; + private final int cullBitmask; public final boolean mirror; @@ -63,16 +74,27 @@ public ModelCuboid(int u, int v, this.mirror = mirror; - int faces = 0; + int cullBitmask = 0; - for (var dir : renderDirections) { - faces |= 1 << dir.ordinal(); + for (var direction : renderDirections) { + cullBitmask |= 1 << getFaceIndex(direction); } - this.faces = faces; + this.cullBitmask = cullBitmask; + } + + public boolean shouldDrawFace(int faceIndex) { + return (this.cullBitmask & (1 << faceIndex)) != 0; } - public boolean shouldDrawFace(int quadIndex) { - return (this.faces & (1 << quadIndex)) != 0; + public static int getFaceIndex(@NotNull Direction dir) { + return switch (dir) { + case DOWN -> FACE_NEG_Y; + case UP -> FACE_POS_Y; + case NORTH -> FACE_NEG_Z; + case SOUTH -> FACE_POS_Z; + case WEST -> FACE_NEG_X; + case EAST -> FACE_POS_X; + }; } } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/services/PlatformBlockAccess.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/services/PlatformBlockAccess.java index 7679b20dc3..a124dcabb7 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/services/PlatformBlockAccess.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/services/PlatformBlockAccess.java @@ -85,4 +85,13 @@ static PlatformBlockAccess getInstance() { * @return Whether this block entity should activate the outline shader. */ boolean shouldBlockEntityGlow(BlockEntity blockEntity, LocalPlayer player); + + /** + * Determines if a fluid adjacent to the block on the given side should not be rendered. + * + * @param adjDirection the face of this block that the fluid is adjacent to + * @param fluid the fluid that is touching that face + * @return if this block should cause the fluid's face to not render + */ + boolean shouldOccludeFluid(Direction adjDirection, BlockState adjBlockState, FluidState fluid); } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/util/UInt32.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/util/UInt32.java new file mode 100644 index 0000000000..9e04adbac1 --- /dev/null +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/util/UInt32.java @@ -0,0 +1,22 @@ +package net.caffeinemc.mods.sodium.client.util; + +public class UInt32 { + public static long upcast(int x) { + return Integer.toUnsignedLong(x); + } + + public static int downcast(long x) { + if (x < 0) { + throw new IllegalArgumentException("x < 0"); + } else if (x >= (1L << 32)) { + throw new IllegalArgumentException("x >= (1 << 32)"); + } + + return (int) x; + } + + // Note: This is unsafe when (x) exceeds the maximum range of a UInt32. + public static int uncheckedDowncast(long x) { + return (int) x; + } +} diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/util/WeightedRandomListExtension.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/util/WeightedRandomListExtension.java new file mode 100644 index 0000000000..3476471c8b --- /dev/null +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/util/WeightedRandomListExtension.java @@ -0,0 +1,7 @@ +package net.caffeinemc.mods.sodium.client.util; + +import net.minecraft.util.RandomSource; + +public interface WeightedRandomListExtension { + T sodium$getQuick(RandomSource random); +} diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/util/collections/BitArray.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/util/collections/BitArray.java index 63a47a933a..2fb9d6252a 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/util/collections/BitArray.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/util/collections/BitArray.java @@ -3,7 +3,7 @@ import java.util.Arrays; /** - * Originally authored here: https://github.com/CaffeineMC/sodium-fabric/blob/ddfb9f21a54bfb30aa876678204371e94d8001db/src/main/java/net/caffeinemc/sodium/util/collections/BitArray.java + * Originally authored here: https://github.com/CaffeineMC/sodium/blob/ddfb9f21a54bfb30aa876678204371e94d8001db/src/main/java/net/caffeinemc/sodium/util/collections/BitArray.java * @author burgerindividual */ public class BitArray { @@ -18,7 +18,7 @@ public class BitArray { /** * Returns {@param num} aligned to the next multiple of {@param alignment}. * - * Taken from https://github.com/CaffeineMC/sodium-fabric/blob/1.19.x/next/components/gfx-utils/src/main/java/net/caffeinemc/gfx/util/misc/MathUtil.java + * Taken from https://github.com/CaffeineMC/sodium/blob/1.19.x/next/components/gfx-utils/src/main/java/net/caffeinemc/gfx/util/misc/MathUtil.java * * @param num The number that will be rounded if needed * @param alignment The multiple that the output will be rounded to (must be a diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/util/color/BoxBlur.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/util/color/BoxBlur.java index f3b2d70640..e0ac2586fe 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/util/color/BoxBlur.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/util/color/BoxBlur.java @@ -4,67 +4,63 @@ import net.minecraft.util.Mth; public class BoxBlur { - - public static void blur(ColorBuffer buf, ColorBuffer tmp, int radius) { - if (buf.width != tmp.width || buf.height != tmp.height) { - throw new IllegalArgumentException("Color buffers must have same dimensions"); - } - - if (isHomogenous(buf.data)) { + public static void blur(int[] src, int[] tmp, int width, int height, int radius) { + if (isHomogenous(src)) { return; } - blurImpl(buf.data, tmp.data, buf.width, buf.height, radius); // X-axis - blurImpl(tmp.data, buf.data, buf.width, buf.height, radius); // Y-axis + blurImpl(src, tmp, radius, width - radius, width, 0, height, height, radius); // X-axis + blurImpl(tmp, src, radius, width - radius, width, radius, height - radius, height, radius); // Y-axis } - private static void blurImpl(int[] src, int[] dst, int width, int height, int radius) { - int multiplier = getAveragingMultiplier((radius * 2) + 1); - - for (int y = 0; y < height; y++) { - int srcRowOffset = ColorBuffer.getIndex(0, y, width); - - int red, green, blue; - - { - int color = src[srcRowOffset]; - red = ColorARGB.unpackRed(color); - green = ColorARGB.unpackGreen(color); - blue = ColorARGB.unpackBlue(color); + private static void blurImpl(int[] src, int[] dst, int x0, int x1, int width, int y0, int y1, int height, int radius) { + int windowSize = (radius * 2) + 1; + int multiplier = getAveragingMultiplier(windowSize); + + for (int y = y0; y < y1; y++) { + int accR = 0; + int accG = 0; + int accB = 0; + + int windowPivotIndex = ColorBuffer.getIndex(x0, y, width); + int windowTailIndex = windowPivotIndex - radius; + int windowHeadIndex = windowPivotIndex + radius; + + // Initialize window + for (int x = -radius; x <= radius; x++) { + var color = src[windowPivotIndex + x]; + accR += ColorARGB.unpackRed(color); + accG += ColorARGB.unpackGreen(color); + accB += ColorARGB.unpackBlue(color); } - // Extend the window backwards by repeating the colors at the edge N times - red += red * radius; - green += green * radius; - blue += blue * radius; - - // Extend the window forwards by sampling ahead N times - for (int x = 1; x <= radius; x++) { - var color = src[srcRowOffset + x]; - red += ColorARGB.unpackRed(color); - green += ColorARGB.unpackGreen(color); - blue += ColorARGB.unpackBlue(color); - } + // Scan forwards + int x = x0; - for (int x = 0; x < width; x++) { + while (true) { // The x and y coordinates are transposed to flip the output image - dst[ColorBuffer.getIndex(y, x, width)] = averageRGB(red, green, blue, multiplier); + // noinspection SuspiciousNameCombination + dst[ColorBuffer.getIndex(y, x, width)] = averageRGB(accR, accG, accB, multiplier); + x++; + + if (x >= x1) { + break; + } { // Remove the color values that are behind the window - var color = src[srcRowOffset + Math.max(0, x - radius)]; - - red -= ColorARGB.unpackRed(color); - green -= ColorARGB.unpackGreen(color); - blue -= ColorARGB.unpackBlue(color); + var color = src[windowTailIndex++]; + accR -= ColorARGB.unpackRed(color); + accG -= ColorARGB.unpackGreen(color); + accB -= ColorARGB.unpackBlue(color); } { // Add the color values that are ahead of the window - var color = src[srcRowOffset + Math.min(width - 1, x + radius + 1)]; - red += ColorARGB.unpackRed(color); - green += ColorARGB.unpackGreen(color); - blue += ColorARGB.unpackBlue(color); + var color = src[++windowHeadIndex]; + accR += ColorARGB.unpackRed(color); + accG += ColorARGB.unpackGreen(color); + accB += ColorARGB.unpackBlue(color); } } } @@ -108,7 +104,7 @@ private static boolean isHomogenous(int[] array) { } public static class ColorBuffer { - protected final int[] data; + public final int[] data; protected final int width, height; public ColorBuffer(int width, int height) { @@ -121,13 +117,12 @@ public void set(int x, int y, int color) { this.data[getIndex(x, y, this.width)] = color; } - public int get(int x, int y) { return this.data[getIndex(x, y, this.width)]; } public static int getIndex(int x, int y, int width) { - return (y * width) + x; + return x + (y * width); } } } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/util/color/ColorSRGB.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/util/color/ColorSRGB.java index 1c5a1aef67..72d08484ad 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/util/color/ColorSRGB.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/util/color/ColorSRGB.java @@ -1,7 +1,8 @@ package net.caffeinemc.mods.sodium.client.util.color; -import net.minecraft.util.FastColor; + +import net.caffeinemc.mods.sodium.api.util.ColorABGR; /** * This is a port of the fast-srgb8 library from thomcc on GitHub. @@ -82,7 +83,7 @@ public static float srgbToLinear(int c) { * @param a The alpha-component in linear RGB space (0 to 255) */ public static int linearToSrgb(float r, float g, float b, int a) { - return FastColor.ABGR32.color(a, linearToSrgb(b), linearToSrgb(g), linearToSrgb(r)); + return ColorABGR.pack(linearToSrgb(r), linearToSrgb(g), linearToSrgb(b), a); } /** diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/world/LevelSlice.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/world/LevelSlice.java index c6b18eac52..4e261460d5 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/world/LevelSlice.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/world/LevelSlice.java @@ -359,8 +359,8 @@ public int getHeight() { } @Override - public int getMinBuildHeight() { - return this.level.getMinBuildHeight(); + public int getMinY() { + return this.level.getMinY(); } @Override diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/world/biome/BiomeColorMaps.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/world/biome/BiomeColorMaps.java index a06dab7bd1..87ea7b5599 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/world/biome/BiomeColorMaps.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/world/biome/BiomeColorMaps.java @@ -19,7 +19,7 @@ public static int getGrassColor(int index) { public static int getFoliageColor(int index) { if (index == INVALID_INDEX || index >= FoliageColor.pixels.length) { - return FoliageColor.getDefaultColor(); + return FoliageColor.FOLIAGE_DEFAULT; } return FoliageColor.pixels[index]; diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/world/biome/LevelBiomeSlice.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/world/biome/LevelBiomeSlice.java index 43534b8a5d..5422902d72 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/world/biome/LevelBiomeSlice.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/world/biome/LevelBiomeSlice.java @@ -41,8 +41,8 @@ public void update(ClientLevel level, ChunkRenderContext context) { private void copyBiomeData(Level level, ChunkRenderContext context) { var defaultValue = level.registryAccess() - .registryOrThrow(Registries.BIOME) - .getHolderOrThrow(Biomes.PLAINS); + .lookupOrThrow(Registries.BIOME) + .getOrThrow(Biomes.PLAINS); for (int sectionX = 0; sectionX < 3; sectionX++) { for (int sectionY = 0; sectionY < 3; sectionY++) { diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/world/biome/LevelColorCache.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/world/biome/LevelColorCache.java index 80561d0fc3..370635f67e 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/world/biome/LevelColorCache.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/world/biome/LevelColorCache.java @@ -4,7 +4,6 @@ import net.caffeinemc.mods.sodium.client.util.color.BoxBlur; import net.caffeinemc.mods.sodium.client.util.color.BoxBlur.ColorBuffer; import net.caffeinemc.mods.sodium.client.world.cloned.ChunkRenderContext; -import net.minecraft.client.renderer.BiomeColors; import net.minecraft.util.Mth; import net.minecraft.world.level.ColorResolver; import net.minecraft.world.level.biome.Biome; @@ -39,35 +38,36 @@ public LevelColorCache(LevelBiomeSlice biomeData, int blendRadius) { } public void update(ChunkRenderContext context) { - this.minBlockX = (context.getOrigin().minBlockX() - NEIGHBOR_BLOCK_RADIUS) - this.blendRadius; + this.minBlockX = (context.getOrigin().minBlockX() - NEIGHBOR_BLOCK_RADIUS); this.minBlockY = (context.getOrigin().minBlockY() - NEIGHBOR_BLOCK_RADIUS); - this.minBlockZ = (context.getOrigin().minBlockZ() - NEIGHBOR_BLOCK_RADIUS) - this.blendRadius; + this.minBlockZ = (context.getOrigin().minBlockZ() - NEIGHBOR_BLOCK_RADIUS); - this.maxBlockX = (context.getOrigin().maxBlockX() + NEIGHBOR_BLOCK_RADIUS) + this.blendRadius; + this.maxBlockX = (context.getOrigin().maxBlockX() + NEIGHBOR_BLOCK_RADIUS); this.maxBlockY = (context.getOrigin().maxBlockY() + NEIGHBOR_BLOCK_RADIUS); - this.maxBlockZ = (context.getOrigin().maxBlockZ() + NEIGHBOR_BLOCK_RADIUS) + this.blendRadius; + this.maxBlockZ = (context.getOrigin().maxBlockZ() + NEIGHBOR_BLOCK_RADIUS); this.populateStamp++; } public int getColor(ColorResolver resolver, int blockX, int blockY, int blockZ) { - var relBlockX = Mth.clamp(blockX, this.minBlockX, this.maxBlockX) - this.minBlockX; - var relBlockY = Mth.clamp(blockY, this.minBlockY, this.maxBlockY) - this.minBlockY; - var relBlockZ = Mth.clamp(blockZ, this.minBlockZ, this.maxBlockZ) - this.minBlockZ; + // Clamp inputs + blockX = Mth.clamp(blockX, this.minBlockX, this.maxBlockX) - this.minBlockX; + blockY = Mth.clamp(blockY, this.minBlockY, this.maxBlockY) - this.minBlockY; + blockZ = Mth.clamp(blockZ, this.minBlockZ, this.maxBlockZ) - this.minBlockZ; if (!this.slices.containsKey(resolver)) { this.initializeSlices(resolver); } - var slice = this.slices.get(resolver)[relBlockY]; + var slice = this.slices.get(resolver)[blockY]; if (slice.lastPopulateStamp < this.populateStamp) { - this.updateColorBuffers(relBlockY, resolver, slice); + this.updateColorBuffers(blockY, resolver, slice); } var buffer = slice.getBuffer(); - return buffer.get(relBlockX, relBlockZ); + return buffer.get(blockX + this.blendRadius, blockZ + this.blendRadius); } private void initializeSlices(ColorResolver resolver) { @@ -83,19 +83,27 @@ private void initializeSlices(ColorResolver resolver) { private void updateColorBuffers(int relY, ColorResolver resolver, Slice slice) { int blockY = this.minBlockY + relY; - for (int blockZ = this.minBlockZ; blockZ <= this.maxBlockZ; blockZ++) { - for (int blockX = this.minBlockX; blockX <= this.maxBlockX; blockX++) { + int minBlockZ = this.minBlockZ - this.blendRadius; + int minBlockX = this.minBlockX - this.blendRadius; + + int maxBlockZ = this.maxBlockZ + this.blendRadius; + int maxBlockX = this.maxBlockX + this.blendRadius; + + ColorBuffer buffer = slice.buffer; + + for (int blockZ = minBlockZ; blockZ <= maxBlockZ; blockZ++) { + for (int blockX = minBlockX; blockX <= maxBlockX; blockX++) { Biome biome = this.biomeData.getBiome(blockX, blockY, blockZ).value(); - int relBlockX = blockX - this.minBlockX; - int relBlockZ = blockZ - this.minBlockZ; + int relBlockX = blockX - minBlockX; + int relBlockZ = blockZ - minBlockZ; - slice.buffer.set(relBlockX, relBlockZ, resolver.getColor(biome, blockX, blockZ)); + buffer.set(relBlockX, relBlockZ, resolver.getColor(biome, blockX, blockZ)); } } if (this.blendRadius > 0) { - BoxBlur.blur(slice.buffer, this.tempColorBuffer, this.blendRadius); + BoxBlur.blur(buffer.data, this.tempColorBuffer.data, this.sizeXZ, this.sizeXZ, this.blendRadius); } slice.lastPopulateStamp = this.populateStamp; diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/world/cloned/ClonedChunkSection.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/world/cloned/ClonedChunkSection.java index 4ef3a083ad..6390b26921 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/world/cloned/ClonedChunkSection.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/world/cloned/ClonedChunkSection.java @@ -185,7 +185,7 @@ private static Int2ReferenceMap copyBlockEntityRenderData(Level level, I // Retrieve any render data after we have copied all block entities, as this will call into the code of // other mods. This could potentially result in the chunk being modified, which would cause problems if we // were iterating over any data in that chunk. - // See https://github.com/CaffeineMC/sodium-fabric/issues/942 for more info. + // See https://github.com/CaffeineMC/sodium/issues/942 for more info. for (var entry : Int2ReferenceMaps.fastIterable(blockEntities)) { Object data = PlatformLevelAccess.getInstance().getBlockEntityData(entry.getValue()); diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/mixin/SodiumMixinPlugin.java b/common/src/main/java/net/caffeinemc/mods/sodium/mixin/SodiumMixinPlugin.java index 3fb79d4454..091a950c4d 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/mixin/SodiumMixinPlugin.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/mixin/SodiumMixinPlugin.java @@ -40,7 +40,7 @@ public void onLoad(String mixinPackage) { @Override public String getRefMapperConfig() { - return PlatformRuntimeInformation.getInstance().platformUsesRefmap() ? "sodium.refmap.json" : null; + return null; } @Override diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/mixin/core/MinecraftMixin.java b/common/src/main/java/net/caffeinemc/mods/sodium/mixin/core/MinecraftMixin.java index df0e469db2..03dfd04a9b 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/mixin/core/MinecraftMixin.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/mixin/core/MinecraftMixin.java @@ -5,6 +5,7 @@ import net.caffeinemc.mods.sodium.client.checks.ResourcePackScanner; import net.minecraft.client.Minecraft; import net.minecraft.server.packs.resources.ReloadableResourceManager; +import net.minecraft.util.profiling.Profiler; import net.minecraft.util.profiling.ProfilerFiller; import org.lwjgl.opengl.GL32C; import org.spongepowered.asm.mixin.Final; @@ -31,7 +32,7 @@ public class MinecraftMixin { */ @Inject(method = "runTick", at = @At("HEAD")) private void preRender(boolean tick, CallbackInfo ci) { - ProfilerFiller profiler = Minecraft.getInstance().getProfiler(); + ProfilerFiller profiler = Profiler.get(); profiler.push("wait_for_gpu"); while (this.fences.size() > SodiumClientMod.options().advanced.cpuRenderAheadLimit) { diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/mixin/core/WindowMixin.java b/common/src/main/java/net/caffeinemc/mods/sodium/mixin/core/WindowMixin.java index 999262e84e..78792f3971 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/mixin/core/WindowMixin.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/mixin/core/WindowMixin.java @@ -3,23 +3,38 @@ import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import com.mojang.blaze3d.platform.Window; +import net.caffeinemc.mods.sodium.client.platform.NativeWindowHandle; import net.caffeinemc.mods.sodium.client.SodiumClientMod; import net.caffeinemc.mods.sodium.client.compatibility.workarounds.Workarounds; import net.caffeinemc.mods.sodium.client.services.PlatformRuntimeInformation; import org.lwjgl.glfw.GLFW; +import org.lwjgl.glfw.GLFWNativeWin32; +import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Redirect; @Mixin(Window.class) -public class WindowMixin { +public class WindowMixin implements NativeWindowHandle { + @Shadow + @Final + private long window; + @WrapOperation(method = "", at = @At(value = "INVOKE", target = "Lorg/lwjgl/glfw/GLFW;glfwCreateWindow(IILjava/lang/CharSequence;JJ)J"), require = 0) public long setAdditionalWindowHints(int titleEncoded, int width, CharSequence height, long title, long monitor, Operation original) { - if (!PlatformRuntimeInformation.getInstance().platformHasEarlyLoadingScreen() && SodiumClientMod.options().performance.useNoErrorGLContext && - !Workarounds.isWorkaroundEnabled(Workarounds.Reference.NO_ERROR_CONTEXT_UNSUPPORTED)) { - GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_NO_ERROR, GLFW.GLFW_TRUE); + if (!PlatformRuntimeInformation.getInstance().platformHasEarlyLoadingScreen()) { + if (SodiumClientMod.options().performance.useNoErrorGLContext) { + if (!Workarounds.isWorkaroundEnabled(Workarounds.Reference.NO_ERROR_CONTEXT_UNSUPPORTED)) { + GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_NO_ERROR, GLFW.GLFW_TRUE); + } + } } return original.call(titleEncoded, width, height, title, monitor); } + + @Override + public long getWin32Handle() { + return GLFWNativeWin32.glfwGetWin32Window(this.window); + } } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/mixin/core/model/colors/ItemColorsMixin.java b/common/src/main/java/net/caffeinemc/mods/sodium/mixin/core/model/colors/ItemColorsMixin.java deleted file mode 100644 index 9fc9f999ff..0000000000 --- a/common/src/main/java/net/caffeinemc/mods/sodium/mixin/core/model/colors/ItemColorsMixin.java +++ /dev/null @@ -1,33 +0,0 @@ -package net.caffeinemc.mods.sodium.mixin.core.model.colors; - -import it.unimi.dsi.fastutil.objects.Reference2ReferenceMap; -import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap; -import net.caffeinemc.mods.sodium.client.model.color.interop.ItemColorsExtension; -import net.minecraft.client.color.item.ItemColor; -import net.minecraft.client.color.item.ItemColors; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.ItemLike; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Unique; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -@Mixin(ItemColors.class) -public class ItemColorsMixin implements ItemColorsExtension { - @Unique - private final Reference2ReferenceMap itemsToColor = - new Reference2ReferenceOpenHashMap<>(); - - @Inject(method = "register", at = @At("TAIL")) - private void preRegisterColor(ItemColor provider, ItemLike[] items, CallbackInfo ci) { - for (ItemLike convertible : items) { - this.itemsToColor.put(convertible.asItem(), provider); - } - } - - @Override - public ItemColor sodium$getColorProvider(ItemStack stack) { - return this.itemsToColor.get(stack.getItem()); - } -} diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/mixin/core/render/immediate/consumer/SheetedDecalTextureGeneratorMixin.java b/common/src/main/java/net/caffeinemc/mods/sodium/mixin/core/render/immediate/consumer/SheetedDecalTextureGeneratorMixin.java index b08314a264..8b13db94cf 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/mixin/core/render/immediate/consumer/SheetedDecalTextureGeneratorMixin.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/mixin/core/render/immediate/consumer/SheetedDecalTextureGeneratorMixin.java @@ -101,7 +101,7 @@ private static void transform(long ptr, int count, VertexFormat format, normal.z = NormI8.unpackZ(packedNormal); Vector3f transformedNormal = inverseNormalMatrix.transform(normal); - Direction direction = Direction.getNearest(transformedNormal.x(), transformedNormal.y(), transformedNormal.z()); + Direction direction = Direction.getApproximateNearest(transformedNormal.x(), transformedNormal.y(), transformedNormal.z()); Vector4f transformedTexture = inverseTextureMatrix.transform(position); transformedTexture.rotateY(3.1415927F); diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/mixin/core/render/world/EntityRendererAccessor.java b/common/src/main/java/net/caffeinemc/mods/sodium/mixin/core/render/world/EntityRendererAccessor.java new file mode 100644 index 0000000000..2677ec74e5 --- /dev/null +++ b/common/src/main/java/net/caffeinemc/mods/sodium/mixin/core/render/world/EntityRendererAccessor.java @@ -0,0 +1,13 @@ +package net.caffeinemc.mods.sodium.mixin.core.render.world; + +import net.minecraft.client.renderer.entity.EntityRenderer; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.phys.AABB; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Invoker; + +@Mixin(EntityRenderer.class) +public interface EntityRendererAccessor { + @Invoker("getBoundingBoxForCulling") + AABB getCullingBox(Entity entity); +} diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/mixin/core/render/world/LevelRendererMixin.java b/common/src/main/java/net/caffeinemc/mods/sodium/mixin/core/render/world/LevelRendererMixin.java index 75f229c2be..ff4d721198 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/mixin/core/render/world/LevelRendererMixin.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/mixin/core/render/world/LevelRendererMixin.java @@ -16,11 +16,7 @@ import net.minecraft.client.Minecraft; import net.minecraft.client.Options; import net.minecraft.client.multiplayer.ClientLevel; -import net.minecraft.client.renderer.GameRenderer; -import net.minecraft.client.renderer.LevelRenderer; -import net.minecraft.client.renderer.LightTexture; -import net.minecraft.client.renderer.RenderBuffers; -import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.*; import net.minecraft.client.renderer.blockentity.BlockEntityRenderDispatcher; import net.minecraft.client.renderer.culling.Frustum; import net.minecraft.client.renderer.entity.EntityRenderDispatcher; @@ -207,9 +203,10 @@ private void onReload(CallbackInfo ci) { } } - @Inject(method = "renderLevel", at = @At(value = "FIELD", target = "Lnet/minecraft/client/renderer/LevelRenderer;globalBlockEntities:Ljava/util/Set;", shift = At.Shift.BEFORE, ordinal = 0)) - private void onRenderBlockEntities(DeltaTracker deltaTracker, boolean bl, Camera camera, GameRenderer gameRenderer, LightTexture lightTexture, Matrix4f matrix4f, Matrix4f matrix4f2, CallbackInfo ci, @Local(ordinal = 3) LocalBooleanRef isGlowing) { - this.renderer.renderBlockEntities(new PoseStack(), this.renderBuffers, this.destructionProgress, camera, deltaTracker.getGameTimeDeltaPartialTick(false), isGlowing); + @Overwrite + private void renderBlockEntities(PoseStack poseStack, MultiBufferSource.BufferSource bufferSource, MultiBufferSource.BufferSource bufferSource2, Camera camera, float f) { + // TODO 1.21.2: Add the NeoForge glowing patch + this.renderer.renderBlockEntities(new PoseStack(), this.renderBuffers, this.destructionProgress, camera, f, null); } // Exclusive to NeoForge, allow to fail. diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/mixin/core/world/biome/ClientLevelMixin.java b/common/src/main/java/net/caffeinemc/mods/sodium/mixin/core/world/biome/ClientLevelMixin.java index 13605e76e9..6c59f99d94 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/mixin/core/world/biome/ClientLevelMixin.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/mixin/core/world/biome/ClientLevelMixin.java @@ -29,10 +29,9 @@ private void captureSeed(ClientPacketListener packetListener, Holder dimensionType, int loadDistance, int simulationDistance, - Supplier profiler, LevelRenderer renderer, boolean isDebug, - long biomeZoomSeed, + long biomeZoomSeed, int k, CallbackInfo ci) { this.biomeZoomSeed = biomeZoomSeed; } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/mixin/core/world/map/ClientChunkCacheMixin.java b/common/src/main/java/net/caffeinemc/mods/sodium/mixin/core/world/map/ClientChunkCacheMixin.java index 8cf3495388..aa75cb4939 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/mixin/core/world/map/ClientChunkCacheMixin.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/mixin/core/world/map/ClientChunkCacheMixin.java @@ -30,7 +30,7 @@ public class ClientChunkCacheMixin { method = "drop", at = @At( value = "INVOKE", - target = "Lnet/minecraft/client/multiplayer/ClientChunkCache$Storage;replace(ILnet/minecraft/world/level/chunk/LevelChunk;Lnet/minecraft/world/level/chunk/LevelChunk;)Lnet/minecraft/world/level/chunk/LevelChunk;", + target = "Lnet/minecraft/client/multiplayer/ClientChunkCache$Storage;drop(ILnet/minecraft/world/level/chunk/LevelChunk;)V", shift = At.Shift.AFTER ) ) diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/mixin/core/world/map/ClientPacketListenerMixin.java b/common/src/main/java/net/caffeinemc/mods/sodium/mixin/core/world/map/ClientPacketListenerMixin.java index 553d818564..a5767b0f2c 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/mixin/core/world/map/ClientPacketListenerMixin.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/mixin/core/world/map/ClientPacketListenerMixin.java @@ -21,7 +21,7 @@ public class ClientPacketListenerMixin { method = "applyLightData", at = @At("RETURN") ) - private void onLightDataReceived(int x, int z, ClientboundLightUpdatePacketData data, CallbackInfo ci) { + private void onLightDataReceived(int x, int z, ClientboundLightUpdatePacketData clientboundLightUpdatePacketData, boolean bl, CallbackInfo ci) { ChunkTrackerHolder.get(this.level) .onChunkStatusAdded(x, z, ChunkStatus.FLAG_HAS_LIGHT_DATA); } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/gui/hooks/console/GameRendererMixin.java b/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/gui/hooks/console/GameRendererMixin.java index 130715ec7b..cbc5245471 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/gui/hooks/console/GameRendererMixin.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/gui/hooks/console/GameRendererMixin.java @@ -1,12 +1,14 @@ package net.caffeinemc.mods.sodium.mixin.features.gui.hooks.console; +import com.llamalad7.mixinextras.sugar.Local; import net.caffeinemc.mods.sodium.client.gui.console.ConsoleHooks; import net.minecraft.client.DeltaTracker; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.renderer.GameRenderer; import net.minecraft.client.renderer.RenderBuffers; +import net.minecraft.util.profiling.ProfilerFiller; import org.lwjgl.glfw.GLFW; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; @@ -30,7 +32,7 @@ public class GameRendererMixin { private static boolean HAS_RENDERED_OVERLAY_ONCE = false; @Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/GuiGraphics;flush()V", shift = At.Shift.AFTER)) - private void onRender(DeltaTracker deltaTracker, boolean bl, CallbackInfo ci) { + private void onRender(DeltaTracker deltaTracker, boolean bl, CallbackInfo ci, @Local ProfilerFiller profiler) { // Do not start updating the console overlay until the font renderer is ready // This prevents the console from using tofu boxes for everything during early startup if (Minecraft.getInstance().getOverlay() != null) { @@ -39,8 +41,7 @@ private void onRender(DeltaTracker deltaTracker, boolean bl, CallbackInfo ci) { } } - this.minecraft.getProfiler() - .push("sodium_console_overlay"); + profiler.push("sodium_console_overlay"); GuiGraphics drawContext = new GuiGraphics(this.minecraft, this.renderBuffers.bufferSource()); @@ -48,8 +49,7 @@ private void onRender(DeltaTracker deltaTracker, boolean bl, CallbackInfo ci) { drawContext.flush(); - this.minecraft.getProfiler() - .pop(); + profiler.pop(); HAS_RENDERED_OVERLAY_ONCE = true; } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/gui/screen/LevelLoadingScreenMixin.java b/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/gui/screen/LevelLoadingScreenMixin.java index 6f649536a0..a96f45ed47 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/gui/screen/LevelLoadingScreenMixin.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/gui/screen/LevelLoadingScreenMixin.java @@ -1,16 +1,14 @@ package net.caffeinemc.mods.sodium.mixin.features.gui.screen; -import com.mojang.blaze3d.systems.RenderSystem; -import com.mojang.blaze3d.vertex.*; import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap; -import net.caffeinemc.mods.sodium.api.vertex.format.common.ColorVertex; -import net.caffeinemc.mods.sodium.api.vertex.buffer.VertexBufferWriter; import net.caffeinemc.mods.sodium.api.util.ColorABGR; import net.caffeinemc.mods.sodium.api.util.ColorARGB; +import net.caffeinemc.mods.sodium.api.vertex.buffer.VertexBufferWriter; +import net.caffeinemc.mods.sodium.api.vertex.format.common.ColorVertex; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.screens.LevelLoadingScreen; -import net.minecraft.client.renderer.GameRenderer; +import net.minecraft.client.renderer.RenderType; import net.minecraft.server.level.progress.StoringChunkProgressListener; import net.minecraft.world.level.chunk.status.ChunkStatus; import org.joml.Matrix4f; @@ -47,7 +45,28 @@ public class LevelLoadingScreenMixin { * @author JellySquid */ @Overwrite - public static void renderChunks(GuiGraphics graphics, StoringChunkProgressListener tracker, int mapX, int mapY, int mapScale, int mapPadding) { + public static void renderChunks(GuiGraphics graphics, StoringChunkProgressListener listener, int mapX, int mapY, int mapScale, int mapPadding) { + Matrix4f pose = graphics.pose() + .last() + .pose(); + + graphics.drawSpecial((bufferSource -> { + var writer = VertexBufferWriter.of(bufferSource.getBuffer(RenderType.gui())); + + sodium$drawChunkMap(listener, mapX, mapY, mapScale, mapPadding, writer, pose); + })); + + } + + @Unique + private static void sodium$drawChunkMap(StoringChunkProgressListener listener, + int mapX, + int mapY, + int mapScale, + int mapPadding, + VertexBufferWriter writer, + Matrix4f pose) + { if (STATUS_TO_COLOR_FAST == null) { STATUS_TO_COLOR_FAST = new Reference2IntOpenHashMap<>(COLORS.size()); STATUS_TO_COLOR_FAST.put(null, NULL_STATUS_COLOR); @@ -55,21 +74,8 @@ public static void renderChunks(GuiGraphics graphics, StoringChunkProgressListen .forEach(entry -> STATUS_TO_COLOR_FAST.put(entry.getKey(), ColorARGB.toABGR(entry.getIntValue(), 0xFF))); } - RenderSystem.setShader(GameRenderer::getPositionColorShader); - - Matrix4f matrix = graphics.pose().last().pose(); - - Tesselator tessellator = Tesselator.getInstance(); - - RenderSystem.enableBlend(); - RenderSystem.defaultBlendFunc(); - - BufferBuilder bufferBuilder = tessellator.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR); - - var writer = VertexBufferWriter.of(bufferBuilder); - - int centerSize = tracker.getFullDiameter(); - int size = tracker.getDiameter(); + int centerSize = listener.getFullDiameter(); + int size = listener.getDiameter(); int tileSize = mapScale + mapPadding; @@ -77,10 +83,10 @@ public static void renderChunks(GuiGraphics graphics, StoringChunkProgressListen int mapRenderCenterSize = centerSize * tileSize - mapPadding; int radius = mapRenderCenterSize / 2 + 1; - addRect(writer, matrix, mapX - radius, mapY - radius, mapX - radius + 1, mapY + radius, DEFAULT_STATUS_COLOR); - addRect(writer, matrix, mapX + radius - 1, mapY - radius, mapX + radius, mapY + radius, DEFAULT_STATUS_COLOR); - addRect(writer, matrix, mapX - radius, mapY - radius, mapX + radius, mapY - radius + 1, DEFAULT_STATUS_COLOR); - addRect(writer, matrix, mapX - radius, mapY + radius - 1, mapX + radius, mapY + radius, DEFAULT_STATUS_COLOR); + addRect(writer, pose, mapX - radius, mapY - radius, mapX - radius + 1, mapY + radius, DEFAULT_STATUS_COLOR); + addRect(writer, pose, mapX + radius - 1, mapY - radius, mapX + radius, mapY + radius, DEFAULT_STATUS_COLOR); + addRect(writer, pose, mapX - radius, mapY - radius, mapX + radius, mapY - radius + 1, DEFAULT_STATUS_COLOR); + addRect(writer, pose, mapX - radius, mapY + radius - 1, mapX + radius, mapY + radius, DEFAULT_STATUS_COLOR); } int mapRenderSize = size * tileSize - mapPadding; @@ -96,7 +102,7 @@ public static void renderChunks(GuiGraphics graphics, StoringChunkProgressListen for (int z = 0; z < size; ++z) { int tileY = mapStartY + z * tileSize; - ChunkStatus status = tracker.getStatus(x, z); + ChunkStatus status = listener.getStatus(x, z); int color; if (prevStatus == status) { @@ -108,18 +114,9 @@ public static void renderChunks(GuiGraphics graphics, StoringChunkProgressListen prevColor = color; } - addRect(writer, matrix, tileX, tileY, tileX + mapScale, tileY + mapScale, color); + addRect(writer, pose, tileX, tileY, tileX + mapScale, tileY + mapScale, color); } } - - MeshData data = bufferBuilder.build(); - - if (data != null) { - BufferUploader.drawWithShader(data); - } - Tesselator.getInstance().clear(); - - RenderSystem.disableBlend(); } @Unique diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/options/weather/LevelRendererMixin.java b/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/options/weather/LevelRendererMixin.java index e493e263b5..207b9c4509 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/options/weather/LevelRendererMixin.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/options/weather/LevelRendererMixin.java @@ -3,13 +3,14 @@ import net.caffeinemc.mods.sodium.client.SodiumClientMod; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.LevelRenderer; +import net.minecraft.client.renderer.WeatherEffectRenderer; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Redirect; -@Mixin(LevelRenderer.class) +@Mixin(WeatherEffectRenderer.class) public class LevelRendererMixin { - @Redirect(method = "renderSnowAndRain", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Minecraft;useFancyGraphics()Z")) + @Redirect(method = "render(Lnet/minecraft/world/level/Level;Lnet/minecraft/client/renderer/MultiBufferSource;IFLnet/minecraft/world/phys/Vec3;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Minecraft;useFancyGraphics()Z")) private boolean redirectGetFancyWeather() { return SodiumClientMod.options().quality.weatherQuality.isFancy(Minecraft.getInstance().options.graphicsMode().get()); } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/render/compositing/RenderTargetMixin.java b/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/render/compositing/RenderTargetMixin.java index 3f38c0e9ac..f022966d28 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/render/compositing/RenderTargetMixin.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/render/compositing/RenderTargetMixin.java @@ -2,7 +2,6 @@ import com.mojang.blaze3d.pipeline.RenderTarget; -import net.caffeinemc.mods.sodium.client.compatibility.workarounds.Workarounds; import org.lwjgl.opengl.GL32C; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -21,29 +20,4 @@ public class RenderTargetMixin { @Shadow public int height; - /** - * @author JellySquid - * @reason Use fixed function hardware for framebuffer blits - */ - @Inject(method = "blitToScreen(IIZ)V", at = @At("HEAD"), cancellable = true) - public void blitToScreen(int width, int height, boolean disableBlend, CallbackInfo ci) { - if (Workarounds.isWorkaroundEnabled(Workarounds.Reference.INTEL_FRAMEBUFFER_BLIT_UNSUPPORTED)) { - return; - } - - if (disableBlend) { - ci.cancel(); - - // When blending is not used, we can directly copy the contents of one - // framebuffer to another using the blitting engine. This can save a lot of time - // when compared to going through the rasterization pipeline. - GL32C.glBindFramebuffer(GL32C.GL_READ_FRAMEBUFFER, this.frameBufferId); - GL32C.glBlitFramebuffer( - 0, 0, width, height, - 0, 0, width, height, - GL32C.GL_COLOR_BUFFER_BIT, GL32C.GL_LINEAR); - GL32C.glBindFramebuffer(GL32C.GL_READ_FRAMEBUFFER, 0); - } - } - } \ No newline at end of file diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/render/entity/cull/EntityRendererMixin.java b/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/render/entity/cull/EntityRendererMixin.java index 59de0bc289..462651adb7 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/render/entity/cull/EntityRendererMixin.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/render/entity/cull/EntityRendererMixin.java @@ -5,13 +5,14 @@ import net.caffeinemc.mods.sodium.client.render.SodiumWorldRenderer; import net.minecraft.client.renderer.culling.Frustum; import net.minecraft.client.renderer.entity.EntityRenderer; +import net.minecraft.client.renderer.entity.state.EntityRenderState; import net.minecraft.world.entity.Entity; import net.minecraft.world.phys.AABB; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; @Mixin(EntityRenderer.class) -public abstract class EntityRendererMixin { +public abstract class EntityRendererMixin { @WrapOperation(method = "shouldRender", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/culling/Frustum;isVisible(Lnet/minecraft/world/phys/AABB;)Z", ordinal = 0)) private boolean preShouldRender(Frustum instance, AABB aABB, Operation original, T entity) { var renderer = SodiumWorldRenderer.instanceNullable(); @@ -20,6 +21,6 @@ private boolean preShouldRender(Frustum instance, AABB aABB, Operation return original.call(instance, aABB); } - return renderer.isEntityVisible(entity) && original.call(instance, aABB); + return renderer.isEntityVisible((EntityRenderer) (Object) this, entity) && original.call(instance, aABB); } } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/render/frapi/BakedModelMixin.java b/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/render/frapi/BakedModelMixin.java index 55d863a21f..1c71d14e8e 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/render/frapi/BakedModelMixin.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/render/frapi/BakedModelMixin.java @@ -2,28 +2,40 @@ import net.caffeinemc.mods.sodium.client.render.frapi.render.AbstractBlockRenderContext; import net.caffeinemc.mods.sodium.client.render.frapi.render.ItemRenderContext; +import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter; import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel; -import net.fabricmc.fabric.api.renderer.v1.render.RenderContext; import net.minecraft.client.resources.model.BakedModel; import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; import net.minecraft.util.RandomSource; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.BlockAndTintGetter; import net.minecraft.world.level.block.state.BlockState; +import org.jetbrains.annotations.Nullable; import org.spongepowered.asm.mixin.Mixin; +import java.util.function.Predicate; import java.util.function.Supplier; @Mixin(BakedModel.class) public interface BakedModelMixin extends FabricBakedModel { @Override - default void emitBlockQuads(BlockAndTintGetter blockView, BlockState state, BlockPos pos, Supplier randomSupplier, RenderContext context) { - ((AbstractBlockRenderContext) context).bufferDefaultModel((BakedModel) this, state); + default void emitItemQuads(QuadEmitter emitter, Supplier randomSupplier) { + if (emitter instanceof ItemRenderContext.ItemEmitter itemE && !itemE.hasTransforms()) { + itemE.bufferDefaultModel((BakedModel) this); + } else { + FabricBakedModel.super.emitItemQuads(emitter, randomSupplier); + } } - // Override the default implementation to redirect to the fast ItemRenderer#renderBakedItemModel method when no transforms are applied. @Override - default void emitItemQuads(ItemStack stack, Supplier randomSupplier, RenderContext context) { - ((ItemRenderContext) context).bufferDefaultModel((BakedModel) this, null); + default void emitBlockQuads(QuadEmitter emitter, BlockAndTintGetter blockView, BlockState state, BlockPos pos, Supplier randomSupplier, Predicate<@Nullable Direction> cullTest) { + if (emitter instanceof AbstractBlockRenderContext.BlockEmitter) { + ((AbstractBlockRenderContext.BlockEmitter) emitter).bufferDefaultModel((BakedModel) this, state, cullTest); + } else if (emitter instanceof ItemRenderContext.ItemEmitter itemE && !itemE.hasTransforms()) { + itemE.bufferDefaultModel((BakedModel) this); + } else { + FabricBakedModel.super.emitBlockQuads(emitter, blockView, state, pos, randomSupplier, cullTest); + } } } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/render/frapi/ItemRendererAccessor.java b/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/render/frapi/ItemRendererAccessor.java index 0cd683bcf1..4ea1b211b9 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/render/frapi/ItemRendererAccessor.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/render/frapi/ItemRendererAccessor.java @@ -16,15 +16,18 @@ package net.caffeinemc.mods.sodium.mixin.features.render.frapi; +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.entity.ItemRenderer; -import net.minecraft.world.item.ItemStack; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Invoker; @Mixin(ItemRenderer.class) public interface ItemRendererAccessor { - @Invoker("hasAnimatedTexture") - static boolean sodium$hasAnimatedTexture(ItemStack stack) { + @Invoker("getCompassFoilBuffer") + static VertexConsumer sodium$getCompassFoilBuffer(MultiBufferSource provider, RenderType layer, PoseStack.Pose entry) { throw new AssertionError(); } } \ No newline at end of file diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/render/frapi/ItemRendererMixin.java b/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/render/frapi/ItemRendererMixin.java index a9d7ab6b83..a4e9a7a463 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/render/frapi/ItemRendererMixin.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/render/frapi/ItemRendererMixin.java @@ -20,9 +20,10 @@ import com.mojang.blaze3d.vertex.VertexConsumer; import net.caffeinemc.mods.sodium.client.render.frapi.render.ItemRenderContext; import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel; -import net.minecraft.client.color.item.ItemColors; import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.entity.ItemRenderer; +import net.minecraft.client.renderer.item.ItemStackRenderState; import net.minecraft.client.resources.model.BakedModel; import net.minecraft.world.item.ItemDisplayContext; import net.minecraft.world.item.ItemStack; @@ -39,24 +40,20 @@ */ @Mixin(ItemRenderer.class) public abstract class ItemRendererMixin { - @Final @Shadow - private ItemColors itemColors; - - @Shadow - protected abstract void renderModelLists(BakedModel bakedModel, ItemStack itemStack, int i, int j, PoseStack poseStack, VertexConsumer vertexConsumer); + private static void renderModelLists(BakedModel bakedModel, int[] is, int i, int j, PoseStack poseStack, VertexConsumer vertexConsumer) { + } @Unique - private final ItemRenderContext.VanillaModelBufferer vanillaBufferer = this::renderModelLists; + private static final ItemRenderContext.VanillaModelBufferer vanillaBufferer = ItemRendererMixin::renderModelLists; @Unique - private final ThreadLocal contexts = ThreadLocal.withInitial(() -> new ItemRenderContext(itemColors, vanillaBufferer)); + private static final ThreadLocal contexts = ThreadLocal.withInitial(() -> new ItemRenderContext(vanillaBufferer)); - @Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/resources/model/BakedModel;isCustomRenderer()Z"), cancellable = true) - private void beforeRenderItem(ItemStack stack, ItemDisplayContext transformMode, boolean invert, PoseStack matrixStack, MultiBufferSource vertexConsumerProvider, int light, int overlay, BakedModel model, CallbackInfo ci) { + @Inject(method = "renderItem", at = @At(value = "HEAD"), cancellable = true) + private static void beforeRenderItem(ItemDisplayContext itemDisplayContext, PoseStack poseStack, MultiBufferSource multiBufferSource, int light, int overlay, int[] colors, BakedModel model, RenderType renderType, ItemStackRenderState.FoilType foilType, CallbackInfo ci) { if (!((FabricBakedModel) model).isVanillaAdapter()) { - contexts.get().renderModel(stack, transformMode, invert, matrixStack, vertexConsumerProvider, light, overlay, model); - matrixStack.popPose(); + contexts.get().renderModel(itemDisplayContext, poseStack, multiBufferSource, light, overlay, model, colors, renderType, foilType); ci.cancel(); } } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/render/gui/font/BakedGlyphMixin.java b/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/render/gui/font/BakedGlyphMixin.java index 08e138f1e0..013474a809 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/render/gui/font/BakedGlyphMixin.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/render/gui/font/BakedGlyphMixin.java @@ -1,6 +1,7 @@ package net.caffeinemc.mods.sodium.mixin.features.render.gui.font; import com.mojang.blaze3d.vertex.VertexConsumer; +import net.caffeinemc.mods.sodium.api.util.ColorARGB; import net.caffeinemc.mods.sodium.client.render.vertex.VertexConsumerUtils; import net.caffeinemc.mods.sodium.api.vertex.format.common.GlyphVertex; import net.minecraft.client.gui.font.glyphs.BakedGlyph; @@ -51,8 +52,8 @@ public class BakedGlyphMixin { * @reason Use intrinsics * @author JellySquid */ - @Inject(method = "render", at = @At("HEAD"), cancellable = true) - private void drawFast(boolean italic, float x, float y, Matrix4f matrix, VertexConsumer vertexConsumer, float red, float green, float blue, float alpha, int light, CallbackInfo ci) { + @Inject(method = "render(ZFFFLorg/joml/Matrix4f;Lcom/mojang/blaze3d/vertex/VertexConsumer;IZI)V", at = @At("HEAD"), cancellable = true) + private void drawFast(boolean italic, float x, float y, float z, Matrix4f matrix, VertexConsumer vertexConsumer, int c, boolean bl2, int light, CallbackInfo ci) { var writer = VertexConsumerUtils.convertOrLog(vertexConsumer); if (writer == null) { @@ -67,23 +68,66 @@ private void drawFast(boolean italic, float x, float y, Matrix4f matrix, VertexC float h2 = y + this.down; float w1 = italic ? 1.0F - 0.25F * this.up : 0.0F; float w2 = italic ? 1.0F - 0.25F * this.down : 0.0F; + float offset = bl2 ? 0.1F : 0.0F; - int color = ColorABGR.pack(red, green, blue, alpha); + int color = ColorARGB.toABGR(c); try (MemoryStack stack = MemoryStack.stackPush()) { long buffer = stack.nmalloc(4 * GlyphVertex.STRIDE); long ptr = buffer; - write(ptr, matrix, x1 + w1, h1, 0.0F, color, this.u0, this.v0, light); + write(ptr, matrix, x1 + w1 - offset, h1 - offset, z, color, this.u0, this.v0, light); ptr += GlyphVertex.STRIDE; - write(ptr, matrix, x1 + w2, h2, 0.0F, color, this.u0, this.v1, light); + write(ptr, matrix, x1 + w2 - offset, h2 + offset, z, color, this.u0, this.v1, light); ptr += GlyphVertex.STRIDE; - write(ptr, matrix, x2 + w2, h2, 0.0F, color, this.u1, this.v1, light); + write(ptr, matrix, x2 + w2 + offset, h2 + offset, z, color, this.u1, this.v1, light); ptr += GlyphVertex.STRIDE; - write(ptr, matrix, x2 + w1, h1, 0.0F, color, this.u1, this.v0, light); + write(ptr, matrix, x2 + w1 + offset, h1 - offset, z, color, this.u1, this.v0, light); + ptr += GlyphVertex.STRIDE; + + writer.push(stack, buffer, 4, GlyphVertex.FORMAT); + } + } + + /** + * @reason Use intrinsics + * @author JellySquid + */ + @Inject(method = "buildEffect", at = @At("HEAD"), cancellable = true) + private void drawEffectFast(BakedGlyph.Effect effect, float offset, float depthOffset, int c, VertexConsumer vertexConsumer, int light, Matrix4f matrix, CallbackInfo ci) { + var writer = VertexConsumerUtils.convertOrLog(vertexConsumer); + + if (writer == null) { + return; + } + + ci.cancel(); + + float x1 = effect.x0(); + float x2 = effect.x1(); + float h1 = effect.y0(); + float h2 = effect.y1(); + float z = effect.depth() + depthOffset; + + int color = ColorARGB.toABGR(c); + + try (MemoryStack stack = MemoryStack.stackPush()) { + long buffer = stack.nmalloc(4 * GlyphVertex.STRIDE); + long ptr = buffer; + + write(ptr, matrix, x1 + offset, h1 + offset, z, color, this.u0, this.v0, light); + ptr += GlyphVertex.STRIDE; + + write(ptr, matrix, x2 + offset, h1 + offset, z, color, this.u0, this.v1, light); + ptr += GlyphVertex.STRIDE; + + write(ptr, matrix, x2 + offset, h2 + offset, z, color, this.u1, this.v1, light); + ptr += GlyphVertex.STRIDE; + + write(ptr, matrix, x1 + offset, h2 + offset, z, color, this.u1, this.v0, light); ptr += GlyphVertex.STRIDE; writer.push(stack, buffer, 4, GlyphVertex.FORMAT); diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/render/gui/outlines/LevelRendererMixin.java b/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/render/gui/outlines/LevelRendererMixin.java index e78a553301..d418106da8 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/render/gui/outlines/LevelRendererMixin.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/render/gui/outlines/LevelRendererMixin.java @@ -9,6 +9,7 @@ import net.caffeinemc.mods.sodium.api.vertex.buffer.VertexBufferWriter; import net.caffeinemc.mods.sodium.api.util.NormI8; import net.caffeinemc.mods.sodium.api.util.ColorABGR; +import net.minecraft.client.renderer.ShapeRenderer; import org.joml.Math; import org.joml.Matrix3f; import org.joml.Matrix4f; @@ -19,7 +20,7 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -@Mixin(LevelRenderer.class) +@Mixin(ShapeRenderer.class) public class LevelRendererMixin { /** * @author JellySquid diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/render/immediate/DirectionMixin.java b/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/render/immediate/DirectionMixin.java index fd639943eb..9b4ecdce74 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/render/immediate/DirectionMixin.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/render/immediate/DirectionMixin.java @@ -29,7 +29,7 @@ public class DirectionMixin { */ @SuppressWarnings({ "StatementWithEmptyBody", "JavadocReference" }) @Overwrite - public static Direction getNearest(float x, float y, float z) { + public static Direction getApproximateNearest(float x, float y, float z) { // Vanilla quirk: return NORTH if all coordinates are zero if (x == 0 && y == 0 && z == 0) return Direction.NORTH; diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/render/immediate/buffer_builder/intrinsics/BufferBuilderMixin.java b/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/render/immediate/buffer_builder/intrinsics/BufferBuilderMixin.java index 28b0af3412..a72f45f5bc 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/render/immediate/buffer_builder/intrinsics/BufferBuilderMixin.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/render/immediate/buffer_builder/intrinsics/BufferBuilderMixin.java @@ -39,7 +39,7 @@ public void putBulkData(PoseStack.Pose matrices, BakedQuad bakedQuad, float r, f ModelQuadView quad = (ModelQuadView) bakedQuad; int color = ColorABGR.pack(r, g, b, a); - BakedModelEncoder.writeQuadVertices(writer, matrices, quad, color, light, overlay); + BakedModelEncoder.writeQuadVertices(writer, matrices, quad, color, light, overlay, false); SpriteUtil.markSpriteActive(quad.getSprite()); } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/render/model/item/ItemRendererMixin.java b/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/render/model/item/ItemRendererMixin.java index dc521d3bba..12ca36097e 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/render/model/item/ItemRendererMixin.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/render/model/item/ItemRendererMixin.java @@ -4,12 +4,9 @@ import net.caffeinemc.mods.sodium.client.render.immediate.model.BakedModelEncoder; import net.caffeinemc.mods.sodium.client.render.texture.SpriteUtil; import net.caffeinemc.mods.sodium.client.render.vertex.VertexConsumerUtils; -import net.caffeinemc.mods.sodium.client.model.color.interop.ItemColorsExtension; import net.caffeinemc.mods.sodium.client.util.DirectionUtil; import net.caffeinemc.mods.sodium.api.util.ColorARGB; import net.caffeinemc.mods.sodium.api.vertex.buffer.VertexBufferWriter; -import net.minecraft.client.color.item.ItemColor; -import net.minecraft.client.color.item.ItemColors; import net.minecraft.client.renderer.block.model.BakedQuad; import net.minecraft.client.renderer.entity.ItemRenderer; import net.minecraft.client.resources.model.BakedModel; @@ -26,20 +23,21 @@ import java.util.List; @Mixin(ItemRenderer.class) -public class ItemRendererMixin { +public abstract class ItemRendererMixin { @Unique - private final RandomSource random = new SingleThreadedRandomSource(42L); + private static final ThreadLocal random = ThreadLocal.withInitial(() -> new SingleThreadedRandomSource(42L)); @Shadow - @Final - private ItemColors itemColors; + private static int getLayerColorSafe(int[] is, int i) { + throw new AssertionError("Not shadowed"); + } /** * @reason Avoid allocations * @author JellySquid */ @Inject(method = "renderModelLists", at = @At("HEAD"), cancellable = true) - private void renderModelFast(BakedModel model, ItemStack itemStack, int light, int overlay, PoseStack matrixStack, VertexConsumer vertexConsumer, CallbackInfo ci) { + private static void renderModelFast(BakedModel model, int[] colors, int light, int overlay, PoseStack poseStack, VertexConsumer vertexConsumer, CallbackInfo ci) { var writer = VertexConsumerUtils.convertOrLog(vertexConsumer); if (writer == null) { @@ -48,21 +46,17 @@ private void renderModelFast(BakedModel model, ItemStack itemStack, int light, i ci.cancel(); - RandomSource random = this.random; - PoseStack.Pose matrices = matrixStack.last(); - - ItemColor colorProvider = null; + RandomSource random = ItemRendererMixin.random.get(); + long seed = 42L; - if (!itemStack.isEmpty()) { - colorProvider = ((ItemColorsExtension) this.itemColors).sodium$getColorProvider(itemStack); - } + PoseStack.Pose matrices = poseStack.last(); for (Direction direction : DirectionUtil.ALL_DIRECTIONS) { random.setSeed(42L); List quads = model.getQuads(null, direction, random); if (!quads.isEmpty()) { - this.renderBakedItemQuads(matrices, writer, quads, itemStack, colorProvider, light, overlay); + renderBakedItemQuads(matrices, writer, quads, colors, light, overlay); } } @@ -70,13 +64,13 @@ private void renderModelFast(BakedModel model, ItemStack itemStack, int light, i List quads = model.getQuads(null, null, random); if (!quads.isEmpty()) { - this.renderBakedItemQuads(matrices, writer, quads, itemStack, colorProvider, light, overlay); + renderBakedItemQuads(matrices, writer, quads, colors, light, overlay); } } @Unique @SuppressWarnings("ForLoopReplaceableByForEach") - private void renderBakedItemQuads(PoseStack.Pose matrices, VertexBufferWriter writer, List quads, ItemStack itemStack, ItemColor colorProvider, int light, int overlay) { + private static void renderBakedItemQuads(PoseStack.Pose matrices, VertexBufferWriter writer, List quads, int[] colors, int light, int overlay) { for (int i = 0; i < quads.size(); i++) { BakedQuad bakedQuad = quads.get(i); @@ -88,11 +82,11 @@ private void renderBakedItemQuads(PoseStack.Pose matrices, VertexBufferWriter wr int color = 0xFFFFFFFF; - if (colorProvider != null && quad.hasColor()) { - color = ColorARGB.toABGR((colorProvider.getColor(itemStack, quad.getColorIndex()))); + if (bakedQuad.isTinted()) { + color = ColorARGB.toABGR(getLayerColorSafe(colors, bakedQuad.getTintIndex())); } - - BakedModelEncoder.writeQuadVertices(writer, matrices, quad, color, light, overlay); + + BakedModelEncoder.writeQuadVertices(writer, matrices, quad, color, light, overlay, BakedModelEncoder.shouldMultiplyAlpha()); SpriteUtil.markSpriteActive(quad.getSprite()); } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/render/particle/SingleQuadParticleMixin.java b/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/render/particle/SingleQuadParticleMixin.java index edfd303de7..6dd1a3e2cc 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/render/particle/SingleQuadParticleMixin.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/render/particle/SingleQuadParticleMixin.java @@ -1,9 +1,9 @@ package net.caffeinemc.mods.sodium.mixin.features.render.particle; import net.caffeinemc.mods.sodium.api.vertex.format.common.ParticleVertex; -import net.caffeinemc.mods.sodium.api.vertex.buffer.VertexBufferWriter; import com.mojang.blaze3d.vertex.VertexConsumer; import net.caffeinemc.mods.sodium.api.util.ColorABGR; +import net.caffeinemc.mods.sodium.client.render.vertex.VertexConsumerUtils; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.particle.Particle; import net.minecraft.client.particle.SingleQuadParticle; @@ -11,9 +11,11 @@ import org.joml.Vector3f; import org.lwjgl.system.MemoryStack; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Overwrite; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(SingleQuadParticle.class) public abstract class SingleQuadParticleMixin extends Particle { @@ -32,9 +34,6 @@ public abstract class SingleQuadParticleMixin extends Particle { @Shadow protected abstract float getV1(); - @Unique - private Vector3f transferVector = new Vector3f(); - protected SingleQuadParticleMixin(ClientLevel level, double x, double y, double z) { super(level, x, y, z); } @@ -43,8 +42,16 @@ protected SingleQuadParticleMixin(ClientLevel level, double x, double y, double * @reason Optimize function * @author JellySquid */ - @Overwrite - protected void renderRotatedQuad(VertexConsumer vertexConsumer, Quaternionf quaternionf, float x, float y, float z, float tickDelta) { + @Inject(method = "renderRotatedQuad(Lcom/mojang/blaze3d/vertex/VertexConsumer;Lorg/joml/Quaternionf;FFFF)V", at = @At("HEAD"), cancellable = true) + protected void renderRotatedQuad(VertexConsumer vertexConsumer, Quaternionf quaternionf, float x, float y, float z, float tickDelta, CallbackInfo ci) { + final var writer = VertexConsumerUtils.convertOrLog(vertexConsumer); + + if (writer == null) { + return; + } + + ci.cancel(); + float size = this.getQuadSize(tickDelta); float minU = this.getU0(); float maxU = this.getU1(); @@ -52,24 +59,22 @@ protected void renderRotatedQuad(VertexConsumer vertexConsumer, Quaternionf quat float maxV = this.getV1(); int light = this.getLightColor(tickDelta); - var writer = VertexBufferWriter.of(vertexConsumer); - int color = ColorABGR.pack(this.rCol, this.gCol, this.bCol, this.alpha); try (MemoryStack stack = MemoryStack.stackPush()) { long buffer = stack.nmalloc(4 * ParticleVertex.STRIDE); long ptr = buffer; - this.writeVertex(ptr, quaternionf, x, y, z, 1.0F, -1.0F, size, maxU, maxV, color, light); + this.sodium$writeVertex(ptr, quaternionf, x, y, z, 1.0F, -1.0F, size, maxU, maxV, color, light); ptr += ParticleVertex.STRIDE; - this.writeVertex(ptr, quaternionf, x, y, z, 1.0F, 1.0F, size, maxU, minV, color, light); + this.sodium$writeVertex(ptr, quaternionf, x, y, z, 1.0F, 1.0F, size, maxU, minV, color, light); ptr += ParticleVertex.STRIDE; - this.writeVertex(ptr, quaternionf, x, y, z, -1.0F, 1.0F, size, minU, minV, color, light); + this.sodium$writeVertex(ptr, quaternionf, x, y, z, -1.0F, 1.0F, size, minU, minV, color, light); ptr += ParticleVertex.STRIDE; - this.writeVertex(ptr, quaternionf, x, y, z, -1.0F, -1.0F, size, minU, maxV, color, light); + this.sodium$writeVertex(ptr, quaternionf, x, y, z, -1.0F, -1.0F, size, minU, maxV, color, light); ptr += ParticleVertex.STRIDE; writer.push(stack, buffer, 4, ParticleVertex.FORMAT); @@ -77,12 +82,16 @@ protected void renderRotatedQuad(VertexConsumer vertexConsumer, Quaternionf quat } @Unique - private void writeVertex(long ptr, Quaternionf quaternionf, float originX, float originY, float originZ, float posX, float posY, float size, float u, float v, int color, int light) { - transferVector.set(posX, posY, 0.0f); - transferVector.rotate(quaternionf); - transferVector.mul(size); - transferVector.add(originX, originY, originZ); + private final Vector3f sodium$scratchVertex = new Vector3f(); // not thread-safe - ParticleVertex.put(ptr, transferVector.x(), transferVector.y(), transferVector.z(), u, v, color, light); + @Unique + private void sodium$writeVertex(long ptr, Quaternionf quaternionf, float originX, float originY, float originZ, float posX, float posY, float size, float u, float v, int color, int light) { + final var vertex = this.sodium$scratchVertex; + vertex.set(posX, posY, 0.0f); + vertex.rotate(quaternionf); + vertex.mul(size); + vertex.add(originX, originY, originZ); + + ParticleVertex.put(ptr, vertex.x(), vertex.y(), vertex.z(), u, v, color, light); } } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/render/world/clouds/LevelRendererMixin.java b/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/render/world/clouds/LevelRendererMixin.java index a5047b7122..7bd7e6c87a 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/render/world/clouds/LevelRendererMixin.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/render/world/clouds/LevelRendererMixin.java @@ -1,17 +1,22 @@ package net.caffeinemc.mods.sodium.mixin.features.render.world.clouds; -import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.pipeline.RenderTarget; +import com.mojang.blaze3d.resource.ResourceHandle; import net.caffeinemc.mods.sodium.client.render.immediate.CloudRenderer; import net.minecraft.client.Camera; +import net.minecraft.client.CloudStatus; import net.minecraft.client.Minecraft; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.renderer.LevelRenderer; import net.minecraft.server.packs.resources.ResourceManager; +import net.minecraft.world.phys.Vec3; import org.jetbrains.annotations.Nullable; import org.joml.Matrix4f; import org.spongepowered.asm.mixin.*; import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Group; import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import java.util.Objects; @@ -34,29 +39,51 @@ public class LevelRendererMixin { * @author jellysquid3 * @reason Optimize cloud rendering */ - @Inject(method = "renderClouds", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/DimensionSpecialEffects;getCloudHeight()F"), cancellable = true) // Inject after Forge checks dimension support - public void renderClouds(PoseStack poseStack, Matrix4f matrix4f, Matrix4f projectionMatrix, float tickDelta, double x, double y, double z, CallbackInfo ci) { + @Group(name = "sodium$cloudsOverride", min = 1, max = 1) + @Dynamic + @Inject( + method = "method_62205", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/client/renderer/CloudRenderer;render(ILnet/minecraft/client/CloudStatus;FLorg/joml/Matrix4f;Lorg/joml/Matrix4f;Lnet/minecraft/world/phys/Vec3;F)V" + ), + require = 0, + cancellable = true + ) + public void renderCloudsFabric(ResourceHandle resourceHandle, int cloudColor, CloudStatus cloudStatus, float cloudHeight, Matrix4f matModelView, Matrix4f matProjection, Vec3 camera, float partialTicks, CallbackInfo ci) { ci.cancel(); + this.sodium$renderClouds(matModelView, matProjection, cloudColor); + } + + @Group(name = "sodium$cloudsOverride", min = 1, max = 1) + @Dynamic + @Inject(method = { "lambda$addCloudsPass$6" }, at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/CloudRenderer;render(ILnet/minecraft/client/CloudStatus;FLorg/joml/Matrix4f;Lorg/joml/Matrix4f;Lnet/minecraft/world/phys/Vec3;F)V"), cancellable = true, require = 0) // Inject after Forge checks dimension support + public void renderCloudsNeo(ResourceHandle resourcehandle, float p_365209_, Vec3 p_362985_, Matrix4f modelView, Matrix4f projectionMatrix, int color, CloudStatus p_364196_, float p_362337_, CallbackInfo ci) { + ci.cancel(); + + this.sodium$renderClouds(modelView, projectionMatrix, color); + } + + @Unique + private void sodium$renderClouds(Matrix4f matModelView, Matrix4f matProjection, int color) { if (this.cloudRenderer == null) { this.cloudRenderer = new CloudRenderer(this.minecraft.getResourceManager()); } - poseStack.pushPose(); - poseStack.mulPose(matrix4f); - - ClientLevel level = Objects.requireNonNull(this.level); Camera camera = this.minecraft.gameRenderer.getMainCamera(); + ClientLevel level = Objects.requireNonNull(this.level); - this.cloudRenderer.render(camera, level, projectionMatrix, poseStack, this.ticks, tickDelta); + var ticks = this.ticks; + var partialTicks = this.minecraft.getDeltaTracker().getGameTimeDeltaPartialTick(false); - poseStack.popPose(); + this.cloudRenderer.render(camera, level, matProjection, matModelView, ticks, partialTicks, color); } @Inject(method = "onResourceManagerReload(Lnet/minecraft/server/packs/resources/ResourceManager;)V", at = @At("RETURN")) private void onReload(ResourceManager manager, CallbackInfo ci) { if (this.cloudRenderer != null) { - this.cloudRenderer.reloadTextures(manager); + this.cloudRenderer.reload(manager); } } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/render/world/sky/FogRendererMixin.java b/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/render/world/sky/FogRendererMixin.java index e78b7ed58e..d042724496 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/render/world/sky/FogRendererMixin.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/render/world/sky/FogRendererMixin.java @@ -13,7 +13,7 @@ @Mixin(FogRenderer.class) public class FogRendererMixin { - @Redirect(method = "setupColor", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/CubicSampler;gaussianSampleVec3(Lnet/minecraft/world/phys/Vec3;Lnet/minecraft/util/CubicSampler$Vec3Fetcher;)Lnet/minecraft/world/phys/Vec3;")) + @Redirect(method = "computeFogColor", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/CubicSampler;gaussianSampleVec3(Lnet/minecraft/world/phys/Vec3;Lnet/minecraft/util/CubicSampler$Vec3Fetcher;)Lnet/minecraft/world/phys/Vec3;")) private static Vec3 redirectSampleColor(Vec3 pos, CubicSampler.Vec3Fetcher fetcher, Camera camera, float tickDelta, ClientLevel level, int i, float f) { float u = Mth.clamp(Mth.cos(level.getTimeOfDay(tickDelta) * 6.2831855F) * 2.0F + 0.5F, 0.0F, 1.0F); diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/render/world/sky/LevelRendererMixin.java b/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/render/world/sky/LevelRendererMixin.java index 4edcbc806d..aeef59294b 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/render/world/sky/LevelRendererMixin.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/render/world/sky/LevelRendererMixin.java @@ -1,11 +1,10 @@ package net.caffeinemc.mods.sodium.mixin.features.render.world.sky; -import com.mojang.blaze3d.vertex.PoseStack; import net.minecraft.client.Camera; +import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.FogRenderer; import net.minecraft.client.renderer.LevelRenderer; import net.minecraft.world.level.material.FogType; -import org.joml.Matrix4f; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; @@ -32,15 +31,15 @@ public abstract class LevelRendererMixin { * outside of water, so the fog should also be covering the sun and sky.

* *

When updating Sodium to new releases of the game, please check for new - * ways the fog can be reduced in {@link FogRenderer#setupFog(Camera, FogRenderer.FogMode, float, boolean, float)} ()}.

+ * ways the fog can be reduced in {@link FogRenderer#setupFog(Camera, FogRenderer.FogMode, org.joml.Vector4f, float, boolean, float)} ()}.

*/ - @Inject(method = "renderSky", at = @At("HEAD"), cancellable = true) - private void preRenderSky(Matrix4f matrix4f, Matrix4f matrix4f2, float f, Camera camera, boolean bl, Runnable runnable, CallbackInfo ci) { + @Inject(method = { "method_62215", "lambda$addSkyPass$12" }, require = 1, at = @At("HEAD"), cancellable = true) + private void preRenderSky(CallbackInfo ci) { // Cancels sky rendering when the camera is submersed underwater. // This prevents the sky from being visible through chunks culled by Sodium's fog occlusion. // Fixes https://bugs.mojang.com/browse/MC-152504. // Credit to bytzo for noticing the change in 1.18.2. - if (camera.getFluidInCamera() != FogType.NONE || this.doesMobEffectBlockSky(camera)) { + if (Minecraft.getInstance().gameRenderer.getMainCamera().getFluidInCamera() != FogType.NONE || this.doesMobEffectBlockSky(Minecraft.getInstance().gameRenderer.getMainCamera())) { ci.cancel(); } } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/shader/uniform/ShaderInstanceMixin.java b/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/shader/uniform/ShaderInstanceMixin.java index 4c37086f9f..6a6acbebce 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/shader/uniform/ShaderInstanceMixin.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/shader/uniform/ShaderInstanceMixin.java @@ -4,6 +4,7 @@ import com.mojang.blaze3d.vertex.VertexFormat; import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; +import net.minecraft.client.renderer.CompiledShaderProgram; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -14,7 +15,6 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import java.util.List; -import net.minecraft.client.renderer.ShaderInstance; import net.minecraft.server.packs.resources.ResourceProvider; /** @@ -22,48 +22,7 @@ * to happen when glGetUniformLocation and glGetInteger are called. In our case, this is rather unnecessary, since * these uniform locations can be trivially cached. */ -@Mixin(ShaderInstance.class) +@Mixin(CompiledShaderProgram.class) public class ShaderInstanceMixin { - @Shadow - @Final - private List samplerNames; - @Shadow - @Final - private int programId; - - @Unique - private Object2IntMap uniformCache; - - @Unique - private void initCache() { - this.uniformCache = new Object2IntOpenHashMap<>(); - this.uniformCache.defaultReturnValue(-1); - - for (var samplerName : this.samplerNames) { - var location = Uniform.glGetUniformLocation(this.programId, samplerName); - - if (location == -1) { - throw new IllegalStateException("Failed to find uniform '%s' during shader init".formatted(samplerName)); - } - - this.uniformCache.put(samplerName, location); - } - } - - @Inject(method = "updateLocations", at = @At("RETURN"), require = 0) - private void initCache(CallbackInfo ci) { - this.initCache(); - } - - @Redirect(method = "apply", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/shaders/Uniform;glGetUniformLocation(ILjava/lang/CharSequence;)I")) - private int redirectGetUniformLocation(int program, CharSequence name) { - var location = this.uniformCache.getInt(name); - - if (location == -1) { - throw new IllegalStateException("Failed to find uniform '%s' during shader bind"); - } - - return location; - } } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/textures/animations/tracking/GuiGraphicsMixin.java b/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/textures/animations/tracking/GuiGraphicsMixin.java index 49d71efb44..fffd02d9a3 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/textures/animations/tracking/GuiGraphicsMixin.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/textures/animations/tracking/GuiGraphicsMixin.java @@ -10,22 +10,5 @@ @Mixin(GuiGraphics.class) public class GuiGraphicsMixin { - @Inject(method = "blit(IIIIILnet/minecraft/client/renderer/texture/TextureAtlasSprite;)V", at = @At("HEAD")) - private void preDrawSprite(int x, int y, int z, - int width, int height, - TextureAtlasSprite sprite, - CallbackInfo ci) - { - SpriteUtil.markSpriteActive(sprite); - } - - @Inject(method = "blit(IIIIILnet/minecraft/client/renderer/texture/TextureAtlasSprite;FFFF)V", at = @At("HEAD")) - private void preDrawSprite(int x, int y, int z, - int width, int height, - TextureAtlasSprite sprite, - float red, float green, float blue, float alpha, - CallbackInfo ci) - { - SpriteUtil.markSpriteActive(sprite); - } + // TODO 24w34a } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/textures/animations/tracking/SpriteContentsTickerMixin.java b/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/textures/animations/tracking/SpriteContentsTickerMixin.java index b00d32e827..29b3b24ecf 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/textures/animations/tracking/SpriteContentsTickerMixin.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/textures/animations/tracking/SpriteContentsTickerMixin.java @@ -44,7 +44,7 @@ private void preTick(CallbackInfo ci) { if (onDemand && !parent.sodium$isActive()) { this.subFrame++; List frames = ((AnimatedTextureAccessor)this.animationInfo).getFrames(); - if (this.subFrame >= ((SpriteContentsFrameInfoAccessor)frames.get(this.frame)).getTime()) { + if (this.subFrame >= ((SpriteContentsFrameInfoAccessor) (Object) frames.get(this.frame)).getTime()) { this.frame = (this.frame + 1) % frames.size(); this.subFrame = 0; } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/textures/animations/upload/SpriteContentsInterpolationMixin.java b/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/textures/animations/upload/SpriteContentsInterpolationMixin.java index 23eaf24ffc..9812fb7eb2 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/textures/animations/upload/SpriteContentsInterpolationMixin.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/textures/animations/upload/SpriteContentsInterpolationMixin.java @@ -43,10 +43,10 @@ void uploadInterpolatedFrame(int x, int y, SpriteContents.Ticker arg) { SpriteContentsAnimatedTextureAccessor animation2 = (SpriteContentsAnimatedTextureAccessor) ((SpriteContentsTickerAccessor) arg).getAnimationInfo(); List frames = ((SpriteContentsAnimatedTextureAccessor) animation).getFrames(); SpriteContentsTickerAccessor accessor = (SpriteContentsTickerAccessor) arg; - SpriteContentsFrameInfoAccessor animationFrame = (SpriteContentsFrameInfoAccessor) frames.get(accessor.getFrameIndex()); + SpriteContentsFrameInfoAccessor animationFrame = (SpriteContentsFrameInfoAccessor) (Object) frames.get(accessor.getFrameIndex()); int curIndex = animationFrame.getIndex(); - int nextIndex = ((SpriteContentsFrameInfoAccessor) animation2.getFrames().get((accessor.getFrameIndex() + 1) % frames.size())).getIndex(); + int nextIndex = ((SpriteContentsFrameInfoAccessor) (Object) animation2.getFrames().get((accessor.getFrameIndex() + 1) % frames.size())).getIndex(); if (curIndex == nextIndex) { return; diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/textures/mipmaps/MipmapGeneratorMixin.java b/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/textures/mipmaps/MipmapGeneratorMixin.java index 7242f55a31..8b52a0a5e7 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/textures/mipmaps/MipmapGeneratorMixin.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/textures/mipmaps/MipmapGeneratorMixin.java @@ -7,9 +7,9 @@ */ package net.caffeinemc.mods.sodium.mixin.features.textures.mipmaps; +import net.caffeinemc.mods.sodium.api.util.ColorABGR; import net.caffeinemc.mods.sodium.client.util.color.ColorSRGB; import net.minecraft.client.renderer.texture.MipmapGenerator; -import net.minecraft.util.FastColor; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Overwrite; import org.spongepowered.asm.mixin.Unique; @@ -47,8 +47,8 @@ private static int alphaBlend(int one, int two, int three, int four, boolean che @Unique private static int weightedAverageColor(int one, int two) { - int alphaOne = FastColor.ABGR32.alpha(one); - int alphaTwo = FastColor.ABGR32.alpha(two); + int alphaOne = ColorABGR.unpackAlpha(one); + int alphaTwo = ColorABGR.unpackAlpha(two); // In the case where the alpha values of the same, we can get by with an unweighted average. if (alphaOne == alphaTwo) { @@ -73,13 +73,13 @@ private static int weightedAverageColor(int one, int two) { float relativeWeightTwo = alphaTwo * scale; // Convert the color components into linear space, then multiply the corresponding weight. - float oneR = ColorSRGB.srgbToLinear(FastColor.ABGR32.red(one)) * relativeWeightOne; - float oneG = ColorSRGB.srgbToLinear(FastColor.ABGR32.green(one)) * relativeWeightOne; - float oneB = ColorSRGB.srgbToLinear(FastColor.ABGR32.blue(one)) * relativeWeightOne; + float oneR = ColorSRGB.srgbToLinear(ColorABGR.unpackRed(one)) * relativeWeightOne; + float oneG = ColorSRGB.srgbToLinear(ColorABGR.unpackGreen(one)) * relativeWeightOne; + float oneB = ColorSRGB.srgbToLinear(ColorABGR.unpackBlue(one)) * relativeWeightOne; - float twoR = ColorSRGB.srgbToLinear(FastColor.ABGR32.red(two)) * relativeWeightTwo; - float twoG = ColorSRGB.srgbToLinear(FastColor.ABGR32.green(two)) * relativeWeightTwo; - float twoB = ColorSRGB.srgbToLinear(FastColor.ABGR32.blue(two)) * relativeWeightTwo; + float twoR = ColorSRGB.srgbToLinear(ColorABGR.unpackRed(two)) * relativeWeightTwo; + float twoG = ColorSRGB.srgbToLinear(ColorABGR.unpackGreen(two)) * relativeWeightTwo; + float twoB = ColorSRGB.srgbToLinear(ColorABGR.unpackBlue(two)) * relativeWeightTwo; // Combine the color components of each color float linearR = oneR + twoR; @@ -96,13 +96,13 @@ private static int weightedAverageColor(int one, int two) { // Computes a non-weighted average of the two sRGB colors in linear space, avoiding brightness losses. @Unique private static int averageRgb(int a, int b, int alpha) { - float ar = ColorSRGB.srgbToLinear(FastColor.ABGR32.red(a)); - float ag = ColorSRGB.srgbToLinear(FastColor.ABGR32.green(a)); - float ab = ColorSRGB.srgbToLinear(FastColor.ABGR32.blue(a)); + float ar = ColorSRGB.srgbToLinear(ColorABGR.unpackRed(a)); + float ag = ColorSRGB.srgbToLinear(ColorABGR.unpackGreen(a)); + float ab = ColorSRGB.srgbToLinear(ColorABGR.unpackBlue(a)); - float br = ColorSRGB.srgbToLinear(FastColor.ABGR32.red(b)); - float bg = ColorSRGB.srgbToLinear(FastColor.ABGR32.green(b)); - float bb = ColorSRGB.srgbToLinear(FastColor.ABGR32.blue(b)); + float br = ColorSRGB.srgbToLinear(ColorABGR.unpackRed(b)); + float bg = ColorSRGB.srgbToLinear(ColorABGR.unpackGreen(b)); + float bb = ColorSRGB.srgbToLinear(ColorABGR.unpackBlue(b)); return ColorSRGB.linearToSrgb((ar + br) * 0.5f, (ag + bg) * 0.5f, (ab + bb) * 0.5f, alpha); } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/textures/mipmaps/SpriteContentsMixin.java b/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/textures/mipmaps/SpriteContentsMixin.java index f965f8547a..549ad64188 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/textures/mipmaps/SpriteContentsMixin.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/textures/mipmaps/SpriteContentsMixin.java @@ -10,10 +10,11 @@ import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import com.mojang.blaze3d.platform.NativeImage; +import net.caffeinemc.mods.sodium.api.util.ColorABGR; import net.caffeinemc.mods.sodium.client.util.NativeImageHelper; import net.caffeinemc.mods.sodium.client.util.color.ColorSRGB; import net.minecraft.client.renderer.texture.SpriteContents; -import net.minecraft.util.FastColor; +import net.minecraft.resources.ResourceLocation; import org.lwjgl.system.MemoryUtil; import org.objectweb.asm.Opcodes; import org.spongepowered.asm.mixin.*; @@ -58,16 +59,16 @@ public class SpriteContentsMixin { long pPixel = ppPixel + (pixelIndex * 4L); int color = MemoryUtil.memGetInt(pPixel); - int alpha = FastColor.ABGR32.alpha(color); + int alpha = ColorABGR.unpackAlpha(color); // Ignore all fully-transparent pixels for the purposes of computing an average color. if (alpha != 0) { float weight = (float) alpha; // Make sure to convert to linear space so that we don't lose brightness. - r += ColorSRGB.srgbToLinear(FastColor.ABGR32.red(color)) * weight; - g += ColorSRGB.srgbToLinear(FastColor.ABGR32.green(color)) * weight; - b += ColorSRGB.srgbToLinear(FastColor.ABGR32.blue(color)) * weight; + r += ColorSRGB.srgbToLinear(ColorABGR.unpackRed(color)) * weight; + g += ColorSRGB.srgbToLinear(ColorABGR.unpackGreen(color)) * weight; + b += ColorSRGB.srgbToLinear(ColorABGR.unpackBlue(color)) * weight; totalWeight += weight; } @@ -90,7 +91,7 @@ public class SpriteContentsMixin { long pPixel = ppPixel + (pixelIndex * 4); int color = MemoryUtil.memGetInt(pPixel); - int alpha = FastColor.ABGR32.alpha(color); + int alpha = ColorABGR.unpackAlpha(color); // Replace the color values of pixels which are fully transparent, since they have no color data. if (alpha == 0) { diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/mixin/workarounds/context_creation/WindowMixin.java b/common/src/main/java/net/caffeinemc/mods/sodium/mixin/workarounds/context_creation/WindowMixin.java index cff7c45b81..42bb4d4fa8 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/mixin/workarounds/context_creation/WindowMixin.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/mixin/workarounds/context_creation/WindowMixin.java @@ -6,11 +6,11 @@ import com.mojang.blaze3d.platform.ScreenManager; import com.mojang.blaze3d.platform.Window; import com.mojang.blaze3d.platform.WindowEventHandler; -import net.caffeinemc.mods.sodium.client.compatibility.checks.PostLaunchChecks; import net.caffeinemc.mods.sodium.client.compatibility.checks.ModuleScanner; -import net.caffeinemc.mods.sodium.client.compatibility.environment.GLContextInfo; -import net.caffeinemc.mods.sodium.client.compatibility.workarounds.Workarounds; +import net.caffeinemc.mods.sodium.client.compatibility.checks.PostLaunchChecks; import net.caffeinemc.mods.sodium.client.compatibility.workarounds.nvidia.NvidiaWorkarounds; +import net.caffeinemc.mods.sodium.client.compatibility.environment.GlContextInfo; +import net.caffeinemc.mods.sodium.client.platform.NativeWindowHandle; import net.caffeinemc.mods.sodium.client.services.PlatformRuntimeInformation; import net.minecraft.Util; import org.lwjgl.glfw.GLFW; @@ -37,59 +37,46 @@ public class WindowMixin { @Final private static Logger LOGGER; - @Shadow - @Final - private long window; - @Unique private long wglPrevContext = MemoryUtil.NULL; @Redirect(method = "", at = @At(value = "INVOKE", target = "Lorg/lwjgl/glfw/GLFW;glfwCreateWindow(IILjava/lang/CharSequence;JJ)J"), expect = 0, require = 0) private long wrapGlfwCreateWindow(int width, int height, CharSequence title, long monitor, long share) { - final boolean applyNvidiaWorkarounds = Workarounds.isWorkaroundEnabled(Workarounds.Reference.NVIDIA_THREADED_OPTIMIZATIONS); - - if (applyNvidiaWorkarounds) { - NvidiaWorkarounds.install(); - } + NvidiaWorkarounds.applyEnvironmentChanges(); try { return GLFW.glfwCreateWindow(width, height, title, monitor, share); } finally { - if (applyNvidiaWorkarounds) { - NvidiaWorkarounds.uninstall(); - } + NvidiaWorkarounds.undoEnvironmentChanges(); } } @SuppressWarnings("all") @WrapOperation(method = "", at = @At(value = "INVOKE", target = "Lnet/neoforged/fml/loading/ImmediateWindowHandler;setupMinecraftWindow(Ljava/util/function/IntSupplier;Ljava/util/function/IntSupplier;Ljava/util/function/Supplier;Ljava/util/function/LongSupplier;)J"), expect = 0, require = 0) private long wrapGlfwCreateWindowForge(final IntSupplier width, final IntSupplier height, final Supplier title, final LongSupplier monitor, Operation op) { - final boolean applyNvidiaWorkarounds = Workarounds.isWorkaroundEnabled(Workarounds.Reference.NVIDIA_THREADED_OPTIMIZATIONS); + boolean applyWorkaroundsLate = !PlatformRuntimeInformation.getInstance() + .platformHasEarlyLoadingScreen(); - if (applyNvidiaWorkarounds && !PlatformRuntimeInformation.getInstance().platformHasEarlyLoadingScreen()) { - NvidiaWorkarounds.install(); + if (applyWorkaroundsLate) { + NvidiaWorkarounds.applyEnvironmentChanges(); } try { return op.call(width, height, title, monitor); } finally { - if (applyNvidiaWorkarounds) { - NvidiaWorkarounds.uninstall(); + if (applyWorkaroundsLate) { + NvidiaWorkarounds.undoEnvironmentChanges(); } } } + @Inject(method = "", at = @At(value = "INVOKE", target = "Lorg/lwjgl/opengl/GL;createCapabilities()Lorg/lwjgl/opengl/GLCapabilities;", shift = At.Shift.AFTER)) private void postContextReady(WindowEventHandler eventHandler, ScreenManager monitorTracker, DisplayData settings, String videoMode, String title, CallbackInfo ci) { - GLContextInfo driver = GLContextInfo.create(); - - if (driver == null) { - LOGGER.warn("Could not retrieve identifying strings for OpenGL implementation"); - } else { - LOGGER.info("OpenGL Vendor: {}", driver.vendor()); - LOGGER.info("OpenGL Renderer: {}", driver.renderer()); - LOGGER.info("OpenGL Version: {}", driver.version()); - } + GlContextInfo context = GlContextInfo.create(); + LOGGER.info("OpenGL Vendor: {}", context.vendor()); + LOGGER.info("OpenGL Renderer: {}", context.renderer()); + LOGGER.info("OpenGL Version: {}", context.version()); // Capture the current WGL context so that we can detect it being replaced later. if (Util.getPlatform() == Util.OS.WINDOWS) { @@ -98,11 +85,11 @@ private void postContextReady(WindowEventHandler eventHandler, ScreenManager mon this.wglPrevContext = MemoryUtil.NULL; } - PostLaunchChecks.onContextInitialized(); - ModuleScanner.checkModules(this.window); + PostLaunchChecks.onContextInitialized((NativeWindowHandle) this, context); + ModuleScanner.checkModules((NativeWindowHandle) this); } - @Inject(method = "updateDisplay", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/systems/RenderSystem;flipFrame(J)V", shift = At.Shift.AFTER)) + @Inject(method = "updateDisplay", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/systems/RenderSystem;flipFrame(JLcom/mojang/blaze3d/TracyFrameCapture;)V", shift = At.Shift.AFTER)) private void preSwapBuffers(CallbackInfo ci) { if (this.wglPrevContext == MemoryUtil.NULL) { // There is no prior recorded context. @@ -121,7 +108,7 @@ private void preSwapBuffers(CallbackInfo ci) { // Likely, this indicates a module was injected into the current process. We should check that // nothing problematic was just installed. - ModuleScanner.checkModules(this.window); + ModuleScanner.checkModules((NativeWindowHandle) this); // If we didn't find anything problematic (which would have thrown an exception), then let's just record // the new context pointer and carry on. diff --git a/common/src/main/resources/assets/minecraft/models/block/README.txt b/common/src/main/resources/assets/minecraft/models/block/README.txt new file mode 100644 index 0000000000..35dd881dcd --- /dev/null +++ b/common/src/main/resources/assets/minecraft/models/block/README.txt @@ -0,0 +1,8 @@ +These block models are based on the original game's assets, but with various optimizations applied to reduce +the number of vertices. + +The texture mapping of these block models is identical to the originals, so that resource packs which replace the +textures of blocks will not look any different. + +If you are a resource pack author and find that your models or textures no longer work correctly with these changes, +please let us know. \ No newline at end of file diff --git a/common/src/main/resources/assets/minecraft/models/block/beacon.json b/common/src/main/resources/assets/minecraft/models/block/beacon.json new file mode 100644 index 0000000000..3c6828b8e4 --- /dev/null +++ b/common/src/main/resources/assets/minecraft/models/block/beacon.json @@ -0,0 +1,46 @@ +{ + "parent": "minecraft:block/block", + "textures": { + "particle": "block/glass", + "glass": "block/glass", + "beacon": "block/beacon", + "obsidian": "block/obsidian" + }, + "elements": [ + { + "from": [ 0, 0, 0 ], + "to": [ 16, 16, 16 ], + "faces": { + "north": { "uv": [ 0, 0, 16, 16 ], "texture": "#glass", "cullface": "north" }, + "east": { "uv": [ 0, 0, 16, 16 ], "texture": "#glass", "cullface": "east" }, + "south": { "uv": [ 0, 0, 16, 16 ], "texture": "#glass", "cullface": "south" }, + "west": { "uv": [ 0, 0, 16, 16 ], "texture": "#glass", "cullface": "west" }, + "up": { "uv": [ 0, 0, 16, 16 ], "texture": "#glass", "cullface": "up" }, + "down": { "uv": [ 0, 0, 16, 16 ], "texture": "#glass", "cullface": "down" } + } + }, + { + "from": [ 2, 0.1, 2 ], + "to": [ 14, 3, 14 ], + "faces": { + "north": { "uv": [ 2, 13, 14, 16 ], "texture": "#obsidian" }, + "east": { "uv": [ 2, 13, 14, 16 ], "texture": "#obsidian" }, + "south": { "uv": [ 2, 13, 14, 16 ], "texture": "#obsidian" }, + "west": { "uv": [ 2, 13, 14, 16 ], "texture": "#obsidian" }, + "up": { "uv": [ 2, 2, 14, 14 ], "texture": "#obsidian" }, + "down": { "uv": [ 2, 2, 14, 14 ], "texture": "#obsidian", "cullface": "down" } + } + }, + { + "from": [ 3, 3, 3 ], + "to": [ 13, 14, 13 ], + "faces": { + "north": { "uv": [ 3, 2, 13, 13 ], "texture": "#beacon" }, + "east": { "uv": [ 3, 2, 13, 13 ], "texture": "#beacon" }, + "south": { "uv": [ 3, 2, 13, 13 ], "texture": "#beacon" }, + "west": { "uv": [ 3, 2, 13, 13 ], "texture": "#beacon" }, + "up": { "uv": [ 3, 3, 13, 13 ], "texture": "#beacon" } + } + } + ] +} diff --git a/common/src/main/resources/assets/minecraft/models/block/bell_floor.json b/common/src/main/resources/assets/minecraft/models/block/bell_floor.json new file mode 100644 index 0000000000..3300cc2013 --- /dev/null +++ b/common/src/main/resources/assets/minecraft/models/block/bell_floor.json @@ -0,0 +1,43 @@ +{ + "textures": { + "bar": "block/dark_oak_planks", + "post": "block/stone", + "particle": "block/bell_bottom" + }, + "elements": [ + { + "from": [ 2, 13, 7 ], + "to": [ 14, 15, 9 ], + "faces": { + "north": { "uv": [ 2, 2, 14, 4 ], "texture": "#bar" }, + "south": { "uv": [ 2, 3, 14, 5 ], "texture": "#bar" }, + "up": { "uv": [ 2, 3, 14, 5 ], "texture": "#bar" }, + "down": { "uv": [ 2, 3, 14, 5 ], "texture": "#bar" } + } + }, + { + "from": [ 14, 0, 6 ], + "to": [ 16, 16, 10 ], + "faces": { + "north": { "uv": [ 0, 1, 2, 16 ], "texture": "#post" }, + "east": { "uv": [ 0, 1, 4, 16 ], "texture": "#post", "cullface": "east" }, + "south": { "uv": [ 0, 1, 2, 16 ], "texture": "#post" }, + "west": { "uv": [ 0, 1, 4, 16 ], "texture": "#post" }, + "up": { "uv": [ 0, 0, 2, 4 ], "texture": "#post", "cullface": "up" }, + "down": { "uv": [ 0, 0, 2, 4 ], "texture": "#post", "cullface": "down" } + } + }, + { + "from": [ 0, 0, 6 ], + "to": [ 2, 16, 10 ], + "faces": { + "north": { "uv": [ 0, 1, 2, 16 ], "texture": "#post" }, + "east": { "uv": [ 0, 1, 4, 16 ], "texture": "#post" }, + "south": { "uv": [ 0, 1, 2, 16 ], "texture": "#post" }, + "west": { "uv": [ 0, 1, 4, 16 ], "texture": "#post", "cullface": "west" }, + "up": { "uv": [ 0, 0, 2, 4 ], "texture": "#post", "cullface": "up" }, + "down": { "uv": [ 0, 0, 2, 4 ], "texture": "#post", "cullface": "down" } + } + } + ] +} diff --git a/common/src/main/resources/assets/minecraft/models/block/brewing_stand.json b/common/src/main/resources/assets/minecraft/models/block/brewing_stand.json new file mode 100644 index 0000000000..172c7704f3 --- /dev/null +++ b/common/src/main/resources/assets/minecraft/models/block/brewing_stand.json @@ -0,0 +1,57 @@ +{ + "textures": { + "base": "block/brewing_stand_base", + "particle": "block/brewing_stand", + "stand": "block/brewing_stand" + }, + "elements": [ + { + "from": [ 7, 0, 7 ], + "to": [ 9, 14, 9 ], + "faces": { + "north": { "uv": [ 7, 2, 9, 16 ], "texture": "#stand" }, + "east": { "uv": [ 7, 2, 9, 16 ], "texture": "#stand" }, + "south": { "uv": [ 7, 2, 9, 16 ], "texture": "#stand" }, + "west": { "uv": [ 7, 2, 9, 16 ], "texture": "#stand" }, + "up": { "uv": [ 7, 7, 9, 9 ], "texture": "#stand" }, + "down": { "uv": [ 7, 7, 9, 9 ], "texture": "#stand", "cullface": "down" } + } + }, + { + "from": [ 9, 0, 5 ], + "to": [ 15, 2, 11 ], + "faces": { + "north": { "uv": [ 9, 14, 15, 16 ], "texture": "#base" }, + "east": { "uv": [ 5, 14, 11, 16 ], "texture": "#base" }, + "south": { "uv": [ 9, 14, 15, 16 ], "texture": "#base" }, + "west": { "uv": [ 5, 14, 11, 16 ], "texture": "#base" }, + "up": { "uv": [ 9, 5, 15, 11 ], "texture": "#base" }, + "down": { "uv": [ 9, 5, 15, 11 ], "texture": "#base", "cullface": "down" } + } + }, + { + "from": [ 1, 0, 1 ], + "to": [ 7, 2, 7 ], + "faces": { + "north": { "uv": [ 1, 14, 7, 16 ], "texture": "#base" }, + "east": { "uv": [ 1, 14, 7, 16 ], "texture": "#base" }, + "south": { "uv": [ 1, 14, 7, 16 ], "texture": "#base" }, + "west": { "uv": [ 1, 14, 7, 16 ], "texture": "#base" }, + "up": { "uv": [ 1, 1, 7, 7 ], "texture": "#base" }, + "down": { "uv": [ 1, 1, 7, 7 ], "texture": "#base", "cullface": "down" } + } + }, + { + "from": [ 1, 0, 9 ], + "to": [ 7, 2, 15 ], + "faces": { + "north": { "uv": [ 1, 14, 7, 16 ], "texture": "#base" }, + "east": { "uv": [ 9, 14, 15, 16 ], "texture": "#base" }, + "south": { "uv": [ 1, 14, 7, 16 ], "texture": "#base" }, + "west": { "uv": [ 9, 14, 15, 16 ], "texture": "#base" }, + "up": { "uv": [ 1, 9, 7, 15 ], "texture": "#base" }, + "down": { "uv": [ 1, 9, 7, 15 ], "texture": "#base", "cullface": "down" } + } + } + ] +} diff --git a/common/src/main/resources/assets/minecraft/models/block/cauldron.json b/common/src/main/resources/assets/minecraft/models/block/cauldron.json new file mode 100644 index 0000000000..3b18fff1ef --- /dev/null +++ b/common/src/main/resources/assets/minecraft/models/block/cauldron.json @@ -0,0 +1,214 @@ +{ + "ambientocclusion": false, + "textures": { + "rim": "block/cauldron_top", + "particle": "block/cauldron_side", + "outside": "block/cauldron_side", + "inside": "block/cauldron_inner", + "feet": "block/cauldron_bottom" + }, + "elements": [ + { + "from": [ 0, 3, 0 ], + "to": [ 16, 16, 16 ], + "faces": { + "north": { "uv": [ 0, 0, 16, 13 ], "texture": "#outside", "cullface": "north" }, + "east": { "uv": [ 0, 0, 16, 13 ], "texture": "#outside", "cullface": "east" }, + "south": { "uv": [ 0, 0, 16, 13 ], "texture": "#outside", "cullface": "south" }, + "west": { "uv": [ 0, 0, 16, 13 ], "texture": "#outside", "cullface": "west" }, + "down": { "uv": [ 0, 0, 16, 16 ], "texture": "#inside" } + } + }, + { + "from": [ 0, 16, 0 ], + "to": [ 14, 16, 2 ], + "faces": { + "up": { "uv": [ 0, 0, 14, 2 ], "texture": "#rim", "cullface": "up" } + } + }, + { + "from": [ 14, 16, 0 ], + "to": [ 16, 16, 14 ], + "faces": { + "up": { "uv": [ 14, 0, 16, 14 ], "texture": "#rim", "cullface": "up" } + } + }, + { + "from": [ 2, 16, 14 ], + "to": [ 16, 16, 16 ], + "faces": { + "up": { "uv": [ 2, 14, 16, 16 ], "texture": "#rim", "cullface": "up" } + } + }, + { + "from": [ 0, 16, 2 ], + "to": [ 2, 16, 16 ], + "faces": { + "up": { "uv": [ 0, 2, 2, 16 ], "texture": "#rim", "cullface": "up" } + } + }, + { + "from": [ 2, 4, 2 ], + "to": [ 14, 16, 2 ], + "faces": { + "south": { "uv": [ 2, 0, 14, 12 ], "texture": "#outside" } + } + }, + { + "from": [ 14, 4, 2 ], + "to": [ 14, 16, 14 ], + "faces": { + "west": { "uv": [ 2, 0, 14, 12 ], "texture": "#outside" } + } + }, + { + "from": [ 2, 4, 14 ], + "to": [ 14, 16, 14 ], + "faces": { + "north": { "uv": [ 2, 0, 14, 12 ], "texture": "#outside" } + } + }, + { + "from": [ 2, 4, 2 ], + "to": [ 2, 16, 14 ], + "faces": { + "east": { "uv": [ 2, 0, 14, 12 ], "texture": "#outside" } + } + }, + { + "from": [ 2, 4, 2 ], + "to": [ 14, 4, 14 ], + "faces": { + "up": { "uv": [ 2, 2, 14, 14 ], "texture": "#inside" } + } + }, + { + "from": [ 0, 0, 0 ], + "to": [ 4, 3, 4 ], + "faces": { + "north": { "uv": [ 12, 13, 16, 16 ], "texture": "#outside", "cullface": "north" }, + "west": { "uv": [ 0, 13, 4, 16 ], "texture": "#outside", "cullface": "west" } + } + }, + { + "from": [ 0, 0, 12 ], + "to": [ 4, 3, 16 ], + "faces": { + "south": { "uv": [ 0, 13, 4, 16 ], "texture": "#outside", "cullface": "south" }, + "west": { "uv": [ 12, 13, 16, 16 ], "texture": "#outside", "cullface": "west" } + } + }, + { + "from": [ 12, 0, 12 ], + "to": [ 16, 3, 16 ], + "faces": { + "east": { "uv": [ 0, 13, 4, 16 ], "texture": "#outside", "cullface": "east" }, + "south": { "uv": [ 12, 13, 16, 16 ], "texture": "#outside", "cullface": "south" } + } + }, + { + "from": [ 12, 0, 0 ], + "to": [ 16, 3, 4 ], + "faces": { + "north": { "uv": [ 0, 13, 4, 16 ], "texture": "#outside", "cullface": "north" }, + "east": { "uv": [ 12, 13, 16, 16 ], "texture": "#outside", "cullface": "east" } + } + }, + { + "from": [ 14, 0, 2 ], + "to": [ 16, 3, 4 ], + "faces": { + "south": { "uv": [ 14, 13, 16, 16 ], "texture": "#outside" }, + "west": { "uv": [ 2, 13, 4, 16 ], "texture": "#outside" }, + "down": { "uv": [ 14, 12, 16, 14 ], "texture": "#feet", "cullface": "down" } + } + }, + { + "from": [ 2, 0, 0 ], + "to": [ 4, 3, 2 ], + "faces": { + "east": { "uv": [ 14, 13, 16, 16 ], "texture": "#outside" }, + "south": { "uv": [ 2, 13, 4, 16 ], "texture": "#outside" }, + "down": { "uv": [ 2, 14, 4, 16 ], "texture": "#feet", "cullface": "down" } + } + }, + { + "from": [ 0, 0, 12 ], + "to": [ 2, 3, 14 ], + "faces": { + "north": { "uv": [ 14, 13, 16, 16 ], "texture": "#outside" }, + "east": { "uv": [ 2, 13, 4, 16 ], "texture": "#outside" }, + "down": { "uv": [ 0, 2, 2, 4 ], "texture": "#feet", "cullface": "down" } + } + }, + { + "from": [ 12, 0, 14 ], + "to": [ 14, 3, 16 ], + "faces": { + "north": { "uv": [ 2, 13, 4, 16 ], "texture": "#outside" }, + "west": { "uv": [ 14, 13, 16, 16 ], "texture": "#outside" }, + "down": { "uv": [ 12, 0, 14, 2 ], "texture": "#feet", "cullface": "down" } + } + }, + { + "from": [ 12, 0, 0 ], + "to": [ 16, 0, 2 ], + "faces": { + "down": { "uv": [ 12, 14, 16, 16 ], "texture": "#feet", "cullface": "down" } + } + }, + { + "from": [ 0, 0, 0 ], + "to": [ 2, 0, 4 ], + "faces": { + "down": { "uv": [ 0, 12, 2, 16 ], "texture": "#feet", "cullface": "down" } + } + }, + { + "from": [ 0, 0, 14 ], + "to": [ 4, 0, 16 ], + "faces": { + "down": { "uv": [ 0, 0, 4, 2 ], "texture": "#feet", "cullface": "down" } + } + }, + { + "from": [ 14, 0, 12 ], + "to": [ 16, 0, 16 ], + "faces": { + "down": { "uv": [ 14, 0, 16, 4 ], "texture": "#feet", "cullface": "down" } + } + }, + { + "from": [ 12, 0, 0 ], + "to": [ 14, 3, 2 ], + "faces": { + "south": { "uv": [ 12, 13, 14, 16 ], "texture": "#outside" }, + "west": { "uv": [ 0, 13, 2, 16 ], "texture": "#outside" } + } + }, + { + "from": [ 0, 0, 2 ], + "to": [ 2, 3, 4 ], + "faces": { + "east": { "uv": [ 12, 13, 14, 16 ], "texture": "#outside" }, + "south": { "uv": [ 0, 13, 2, 16 ], "texture": "#outside" } + } + }, + { + "from": [ 2, 0, 14 ], + "to": [ 4, 3, 16 ], + "faces": { + "north": { "uv": [ 12, 13, 14, 16 ], "texture": "#outside" }, + "east": { "uv": [ 0, 13, 2, 16 ], "texture": "#outside" } + } + }, + { + "from": [ 14, 0, 12 ], + "to": [ 16, 3, 14 ], + "faces": { + "north": { "uv": [ 0, 13, 2, 16 ], "texture": "#outside" }, + "west": { "uv": [ 12, 13, 14, 16 ], "texture": "#outside" } + } + } + ] +} diff --git a/common/src/main/resources/assets/minecraft/models/block/composter.json b/common/src/main/resources/assets/minecraft/models/block/composter.json new file mode 100644 index 0000000000..88af7aed12 --- /dev/null +++ b/common/src/main/resources/assets/minecraft/models/block/composter.json @@ -0,0 +1,85 @@ +{ + "parent": "minecraft:block/block", + "textures": { + "rim": "block/composter_top", + "particle": "block/composter_side", + "outside": "block/composter_side", + "inside": "block/composter_bottom" + }, + "elements": [ + { + "from": [ 0, 0, 0 ], + "to": [ 16, 16, 16 ], + "faces": { + "north": { "uv": [ 0, 0, 16, 16 ], "texture": "#outside", "cullface": "north" }, + "east": { "uv": [ 0, 0, 16, 16 ], "texture": "#outside", "cullface": "east" }, + "south": { "uv": [ 0, 0, 16, 16 ], "texture": "#outside", "cullface": "south" }, + "west": { "uv": [ 0, 0, 16, 16 ], "texture": "#outside", "cullface": "west" }, + "down": { "uv": [ 0, 0, 16, 16 ], "texture": "#inside", "cullface": "down" } + } + }, + { + "from": [ 0, 16, 0 ], + "to": [ 14, 16, 2 ], + "faces": { + "up": { "uv": [ 0, 0, 14, 2 ], "texture": "#rim", "cullface": "up" } + } + }, + { + "from": [ 14, 16, 0 ], + "to": [ 16, 16, 14 ], + "faces": { + "up": { "uv": [ 14, 0, 16, 14 ], "texture": "#rim", "cullface": "up" } + } + }, + { + "from": [ 2, 16, 14 ], + "to": [ 16, 16, 16 ], + "faces": { + "up": { "uv": [ 2, 14, 16, 16 ], "texture": "#rim", "cullface": "up" } + } + }, + { + "from": [ 0, 16, 2 ], + "to": [ 2, 16, 16 ], + "faces": { + "up": { "uv": [ 0, 2, 2, 16 ], "texture": "#rim", "cullface": "up" } + } + }, + { + "from": [ 2, 2, 2 ], + "to": [ 14, 16, 2 ], + "faces": { + "south": { "uv": [ 2, 0, 14, 14 ], "texture": "#outside" } + } + }, + { + "from": [ 14, 2, 2 ], + "to": [ 14, 16, 14 ], + "faces": { + "west": { "uv": [ 2, 0, 14, 14 ], "texture": "#outside" } + } + }, + { + "from": [ 2, 2, 14 ], + "to": [ 14, 16, 14 ], + "faces": { + "north": { "uv": [ 2, 0, 14, 14 ], "texture": "#outside" } + } + }, + { + "from": [ 2, 2, 2 ], + "to": [ 2, 16, 14 ], + "faces": { + "east": { "uv": [ 2, 0, 14, 14 ], "texture": "#outside" } + } + }, + { + "from": [ 2, 2, 2 ], + "to": [ 14, 2, 14 ], + "faces": { + "up": { "uv": [ 2, 2, 14, 14 ], "texture": "#inside" } + } + } + ] +} diff --git a/common/src/main/resources/assets/minecraft/models/block/flower_pot.json b/common/src/main/resources/assets/minecraft/models/block/flower_pot.json new file mode 100644 index 0000000000..fde4367ac7 --- /dev/null +++ b/common/src/main/resources/assets/minecraft/models/block/flower_pot.json @@ -0,0 +1,88 @@ +{ + "ambientocclusion": false, + "textures": { + "particle": "block/flower_pot", + "flowerpot": "block/flower_pot", + "dirt": "block/dirt" + }, + "elements": [ + { + "from": [ 5, 0, 5 ], + "to": [ 11, 6, 11 ], + "faces": { + "north": { "uv": [ 5, 10, 11, 16 ], "texture": "#flowerpot" }, + "east": { "uv": [ 5, 10, 11, 16 ], "texture": "#flowerpot" }, + "south": { "uv": [ 5, 10, 11, 16 ], "texture": "#flowerpot" }, + "west": { "uv": [ 5, 10, 11, 16 ], "texture": "#flowerpot" } + } + }, + { + "from": [ 5, 0, 5 ], + "to": [ 10, 6, 6 ], + "faces": { + "up": { "uv": [ 5, 5, 10, 6 ], "texture": "#flowerpot" }, + "down": { "uv": [ 5, 10, 10, 11 ], "texture": "#flowerpot", "cullface": "down" } + } + }, + { + "from": [ 10, 0, 5 ], + "to": [ 11, 6, 10 ], + "faces": { + "up": { "uv": [ 10, 5, 11, 10 ], "texture": "#flowerpot" }, + "down": { "uv": [ 10, 6, 11, 11 ], "texture": "#flowerpot", "cullface": "down" } + } + }, + { + "from": [ 6, 0, 10 ], + "to": [ 11, 6, 11 ], + "faces": { + "up": { "uv": [ 6, 10, 11, 11 ], "texture": "#flowerpot" }, + "down": { "uv": [ 6, 5, 11, 6 ], "texture": "#flowerpot", "cullface": "down" } + } + }, + { + "from": [ 5, 0, 6 ], + "to": [ 6, 6, 11 ], + "faces": { + "up": { "uv": [ 5, 6, 6, 11 ], "texture": "#flowerpot" }, + "down": { "uv": [ 5, 5, 6, 10 ], "texture": "#flowerpot", "cullface": "down" } + } + }, + { + "from": [ 6, 4, 6 ], + "to": [ 10, 6, 6 ], + "faces": { + "south": { "uv": [ 6, 10, 10, 12 ], "texture": "#flowerpot" } + } + }, + { + "from": [ 10, 4, 6 ], + "to": [ 10, 6, 10 ], + "faces": { + "west": { "uv": [ 6, 10, 10, 12 ], "texture": "#flowerpot" } + } + }, + { + "from": [ 6, 4, 10 ], + "to": [ 10, 6, 10 ], + "faces": { + "north": { "uv": [ 6, 10, 10, 12 ], "texture": "#flowerpot" } + } + }, + { + "from": [ 6, 4, 6 ], + "to": [ 6, 6, 10 ], + "faces": { + "east": { "uv": [ 6, 10, 10, 12 ], "texture": "#flowerpot" } + } + }, + { + "from": [ 6, 0, 6 ], + "to": [ 10, 4, 10 ], + "faces": { + "up": { "uv": [ 6, 6, 10, 10 ], "texture": "#dirt" }, + "down": { "uv": [ 6, 12, 10, 16 ], "texture": "#flowerpot", "cullface": "down" } + } + } + ] +} diff --git a/common/src/main/resources/assets/minecraft/models/block/flower_pot_cross.json b/common/src/main/resources/assets/minecraft/models/block/flower_pot_cross.json new file mode 100644 index 0000000000..d3a2fa9660 --- /dev/null +++ b/common/src/main/resources/assets/minecraft/models/block/flower_pot_cross.json @@ -0,0 +1,108 @@ +{ + "ambientocclusion": false, + "textures": { + "particle": "block/flower_pot", + "flowerpot": "block/flower_pot", + "dirt": "block/dirt" + }, + "elements": [ + { + "from": [ 5, 0, 5 ], + "to": [ 11, 6, 11 ], + "faces": { + "north": { "uv": [ 5, 10, 11, 16 ], "texture": "#flowerpot" }, + "east": { "uv": [ 5, 10, 11, 16 ], "texture": "#flowerpot" }, + "south": { "uv": [ 5, 10, 11, 16 ], "texture": "#flowerpot" }, + "west": { "uv": [ 5, 10, 11, 16 ], "texture": "#flowerpot" } + } + }, + { + "from": [ 5, 0, 5 ], + "to": [ 10, 6, 6 ], + "faces": { + "up": { "uv": [ 5, 5, 10, 6 ], "texture": "#flowerpot" }, + "down": { "uv": [ 5, 10, 10, 11 ], "texture": "#flowerpot", "cullface": "down" } + } + }, + { + "from": [ 10, 0, 5 ], + "to": [ 11, 6, 10 ], + "faces": { + "up": { "uv": [ 10, 5, 11, 10 ], "texture": "#flowerpot" }, + "down": { "uv": [ 10, 6, 11, 11 ], "texture": "#flowerpot", "cullface": "down" } + } + }, + { + "from": [ 6, 0, 10 ], + "to": [ 11, 6, 11 ], + "faces": { + "up": { "uv": [ 6, 10, 11, 11 ], "texture": "#flowerpot" }, + "down": { "uv": [ 6, 5, 11, 6 ], "texture": "#flowerpot", "cullface": "down" } + } + }, + { + "from": [ 5, 0, 6 ], + "to": [ 6, 6, 11 ], + "faces": { + "up": { "uv": [ 5, 6, 6, 11 ], "texture": "#flowerpot" }, + "down": { "uv": [ 5, 5, 6, 10 ], "texture": "#flowerpot", "cullface": "down" } + } + }, + { + "from": [ 6, 4, 6 ], + "to": [ 10, 6, 6 ], + "faces": { + "south": { "uv": [ 6, 10, 10, 12 ], "texture": "#flowerpot" } + } + }, + { + "from": [ 10, 4, 6 ], + "to": [ 10, 6, 10 ], + "faces": { + "west": { "uv": [ 6, 10, 10, 12 ], "texture": "#flowerpot" } + } + }, + { + "from": [ 6, 4, 10 ], + "to": [ 10, 6, 10 ], + "faces": { + "north": { "uv": [ 6, 10, 10, 12 ], "texture": "#flowerpot" } + } + }, + { + "from": [ 6, 4, 6 ], + "to": [ 6, 6, 10 ], + "faces": { + "east": { "uv": [ 6, 10, 10, 12 ], "texture": "#flowerpot" } + } + }, + { + "from": [ 6, 0, 6 ], + "to": [ 10, 4, 10 ], + "faces": { + "up": { "uv": [ 6, 6, 10, 10 ], "texture": "#dirt" }, + "down": { "uv": [ 6, 12, 10, 16 ], "texture": "#flowerpot", "cullface": "down" } + } + }, + { + "from": [ 8, 4, 2.6 ], + "to": [ 8, 16, 13.4 ], + "shade": false, + "rotation": { "angle": 45, "axis": "y", "origin": [ 8, 8, 8 ], "rescale": true }, + "faces": { + "east": { "uv": [ 0, 0, 16, 16 ], "texture": "#plant" }, + "west": { "uv": [ 0, 0, 16, 16 ], "texture": "#plant" } + } + }, + { + "from": [ 8, 4, 2.6 ], + "to": [ 8, 16, 13.4 ], + "shade": false, + "rotation": { "angle": -45, "axis": "y", "origin": [ 8, 8, 8 ], "rescale": true }, + "faces": { + "east": { "uv": [ 0, 0, 16, 16 ], "texture": "#plant" }, + "west": { "uv": [ 0, 0, 16, 16 ], "texture": "#plant" } + } + } + ] +} diff --git a/common/src/main/resources/assets/minecraft/models/block/heavy_core.json b/common/src/main/resources/assets/minecraft/models/block/heavy_core.json new file mode 100644 index 0000000000..6b795bcad3 --- /dev/null +++ b/common/src/main/resources/assets/minecraft/models/block/heavy_core.json @@ -0,0 +1,39 @@ +{ + "textures": { + "particle": "block/heavy_core", + "all": "block/heavy_core" + }, + "elements": [ + { + "from": [ 4, 0, 4 ], + "to": [ 12, 8, 12 ], + "faces": { + "north": { "uv": [ 0, 8, 8, 16 ], "texture": "#all" }, + "east": { "uv": [ 0, 8, 8, 16 ], "texture": "#all" }, + "south": { "uv": [ 0, 8, 8, 16 ], "texture": "#all" }, + "west": { "uv": [ 0, 8, 8, 16 ], "texture": "#all" }, + "up": { "uv": [ 0, 0, 8, 8 ], "texture": "#all" }, + "down": { "uv": [ 8, 0, 16, 8 ], "texture": "#all", "cullface": "down" } + } + } + ], + "display": { + "thirdperson_righthand": { + "rotation": [ 45, 45, 0 ], + "translation": [ 0, 3, 0 ], + "scale": [ 0.5, 0.5, 0.5 ] + }, + "ground": { + "translation": [ 0, 3, 0 ], + "scale": [ 0.5, 0.5, 0.5 ] + }, + "gui": { + "rotation": [ 30, 225, 0 ], + "translation": [ 0, 3, 0 ] + }, + "fixed": { + "rotation": [ 0, 180, 0 ], + "translation": [ 0, 4, 0 ] + } + } +} diff --git a/common/src/main/resources/assets/minecraft/models/block/hopper.json b/common/src/main/resources/assets/minecraft/models/block/hopper.json new file mode 100644 index 0000000000..b1b655b3c9 --- /dev/null +++ b/common/src/main/resources/assets/minecraft/models/block/hopper.json @@ -0,0 +1,107 @@ +{ + "ambientocclusion": false, + "textures": { + "rim": "block/hopper_top", + "particle": "block/hopper_outside", + "outside": "block/hopper_outside", + "inside": "block/hopper_inside" + }, + "elements": [ + { + "from": [ 6, 0, 6 ], + "to": [ 10, 4, 10 ], + "faces": { + "north": { "uv": [ 6, 12, 10, 16 ], "texture": "#outside" }, + "east": { "uv": [ 6, 12, 10, 16 ], "texture": "#outside" }, + "south": { "uv": [ 6, 12, 10, 16 ], "texture": "#outside" }, + "west": { "uv": [ 6, 12, 10, 16 ], "texture": "#outside" }, + "down": { "uv": [ 6, 6, 10, 10 ], "texture": "#inside", "cullface": "down" } + } + }, + { + "from": [ 4, 4, 4 ], + "to": [ 12, 10, 12 ], + "faces": { + "north": { "uv": [ 4, 6, 12, 12 ], "texture": "#outside" }, + "east": { "uv": [ 4, 6, 12, 12 ], "texture": "#outside" }, + "south": { "uv": [ 4, 6, 12, 12 ], "texture": "#outside" }, + "west": { "uv": [ 4, 6, 12, 12 ], "texture": "#outside" }, + "down": { "uv": [ 4, 4, 12, 12 ], "texture": "#inside" } + } + }, + { + "from": [ 0, 10, 0 ], + "to": [ 16, 16, 16 ], + "faces": { + "north": { "uv": [ 0, 0, 16, 6 ], "texture": "#outside", "cullface": "north" }, + "east": { "uv": [ 0, 0, 16, 6 ], "texture": "#outside", "cullface": "east" }, + "south": { "uv": [ 0, 0, 16, 6 ], "texture": "#outside", "cullface": "south" }, + "west": { "uv": [ 0, 0, 16, 6 ], "texture": "#outside", "cullface": "west" }, + "down": { "uv": [ 0, 0, 16, 16 ], "texture": "#inside" } + } + }, + { + "from": [ 0, 16, 0 ], + "to": [ 14, 16, 2 ], + "faces": { + "up": { "uv": [ 0, 0, 14, 2 ], "texture": "#rim", "cullface": "up" } + } + }, + { + "from": [ 14, 16, 0 ], + "to": [ 16, 16, 14 ], + "faces": { + "up": { "uv": [ 14, 0, 16, 14 ], "texture": "#rim", "cullface": "up" } + } + }, + { + "from": [ 2, 16, 14 ], + "to": [ 16, 16, 16 ], + "faces": { + "up": { "uv": [ 2, 14, 16, 16 ], "texture": "#rim", "cullface": "up" } + } + }, + { + "from": [ 0, 16, 2 ], + "to": [ 2, 16, 16 ], + "faces": { + "up": { "uv": [ 0, 2, 2, 16 ], "texture": "#rim", "cullface": "up" } + } + }, + { + "from": [ 2, 11, 2 ], + "to": [ 14, 16, 2 ], + "faces": { + "south": { "uv": [ 2, 0, 14, 5 ], "texture": "#outside", "cullface": "up" } + } + }, + { + "from": [ 14, 11, 2 ], + "to": [ 14, 16, 14 ], + "faces": { + "west": { "uv": [ 2, 0, 14, 5 ], "texture": "#outside", "cullface": "up" } + } + }, + { + "from": [ 2, 11, 14 ], + "to": [ 14, 16, 14 ], + "faces": { + "north": { "uv": [ 2, 0, 14, 5 ], "texture": "#outside", "cullface": "up" } + } + }, + { + "from": [ 2, 11, 2 ], + "to": [ 2, 16, 14 ], + "faces": { + "east": { "uv": [ 2, 0, 14, 5 ], "texture": "#outside", "cullface": "up" } + } + }, + { + "from": [ 2, 11, 2 ], + "to": [ 14, 11, 14 ], + "faces": { + "up": { "uv": [ 2, 2, 14, 14 ], "texture": "#inside", "cullface": "up" } + } + } + ] +} diff --git a/common/src/main/resources/assets/minecraft/models/block/hopper_side.json b/common/src/main/resources/assets/minecraft/models/block/hopper_side.json new file mode 100644 index 0000000000..10667bdac5 --- /dev/null +++ b/common/src/main/resources/assets/minecraft/models/block/hopper_side.json @@ -0,0 +1,107 @@ +{ + "ambientocclusion": false, + "textures": { + "rim": "block/hopper_top", + "particle": "block/hopper_outside", + "outside": "block/hopper_outside", + "inside": "block/hopper_inside" + }, + "elements": [ + { + "from": [ 6, 4, 0 ], + "to": [ 10, 8, 4 ], + "faces": { + "north": { "uv": [ 6, 8, 10, 12 ], "texture": "#outside", "cullface": "north" }, + "east": { "uv": [ 12, 8, 16, 12 ], "texture": "#outside" }, + "west": { "uv": [ 0, 8, 4, 12 ], "texture": "#outside" }, + "up": { "uv": [ 6, 0, 10, 4 ], "texture": "#outside" }, + "down": { "uv": [ 6, 12, 10, 16 ], "texture": "#inside" } + } + }, + { + "from": [ 4, 4, 4 ], + "to": [ 12, 10, 12 ], + "faces": { + "north": { "uv": [ 4, 6, 12, 12 ], "texture": "#outside" }, + "east": { "uv": [ 4, 6, 12, 12 ], "texture": "#outside" }, + "south": { "uv": [ 4, 6, 12, 12 ], "texture": "#outside" }, + "west": { "uv": [ 4, 6, 12, 12 ], "texture": "#outside" }, + "down": { "uv": [ 4, 4, 12, 12 ], "texture": "#inside" } + } + }, + { + "from": [ 0, 10, 0 ], + "to": [ 16, 16, 16 ], + "faces": { + "north": { "uv": [ 0, 0, 16, 6 ], "texture": "#outside", "cullface": "north" }, + "east": { "uv": [ 0, 0, 16, 6 ], "texture": "#outside", "cullface": "east" }, + "south": { "uv": [ 0, 0, 16, 6 ], "texture": "#outside", "cullface": "south" }, + "west": { "uv": [ 0, 0, 16, 6 ], "texture": "#outside", "cullface": "west" }, + "down": { "uv": [ 0, 0, 16, 16 ], "texture": "#inside" } + } + }, + { + "from": [ 0, 16, 0 ], + "to": [ 14, 16, 2 ], + "faces": { + "up": { "uv": [ 0, 0, 14, 2 ], "texture": "#rim", "cullface": "up" } + } + }, + { + "from": [ 14, 16, 0 ], + "to": [ 16, 16, 14 ], + "faces": { + "up": { "uv": [ 14, 0, 16, 14 ], "texture": "#rim", "cullface": "up" } + } + }, + { + "from": [ 2, 16, 14 ], + "to": [ 16, 16, 16 ], + "faces": { + "up": { "uv": [ 2, 14, 16, 16 ], "texture": "#rim", "cullface": "up" } + } + }, + { + "from": [ 0, 16, 2 ], + "to": [ 2, 16, 16 ], + "faces": { + "up": { "uv": [ 0, 2, 2, 16 ], "texture": "#rim", "cullface": "up" } + } + }, + { + "from": [ 2, 11, 2 ], + "to": [ 14, 16, 2 ], + "faces": { + "south": { "uv": [ 2, 0, 14, 5 ], "texture": "#outside", "cullface": "up" } + } + }, + { + "from": [ 14, 11, 2 ], + "to": [ 14, 16, 14 ], + "faces": { + "west": { "uv": [ 2, 0, 14, 5 ], "texture": "#outside", "cullface": "up" } + } + }, + { + "from": [ 2, 11, 14 ], + "to": [ 14, 16, 14 ], + "faces": { + "north": { "uv": [ 2, 0, 14, 5 ], "texture": "#outside", "cullface": "up" } + } + }, + { + "from": [ 2, 11, 2 ], + "to": [ 2, 16, 14 ], + "faces": { + "east": { "uv": [ 2, 0, 14, 5 ], "texture": "#outside", "cullface": "up" } + } + }, + { + "from": [ 2, 11, 2 ], + "to": [ 14, 11, 14 ], + "faces": { + "up": { "uv": [ 2, 2, 14, 14 ], "texture": "#inside", "cullface": "up" } + } + } + ] +} diff --git a/common/src/main/resources/assets/minecraft/models/block/inner_stairs.json b/common/src/main/resources/assets/minecraft/models/block/inner_stairs.json new file mode 100644 index 0000000000..e94935a555 --- /dev/null +++ b/common/src/main/resources/assets/minecraft/models/block/inner_stairs.json @@ -0,0 +1,61 @@ +{ + "textures": { + "particle": "#side" + }, + "elements": [ + { + "from": [ 0, 0, 0 ], + "to": [ 16, 16, 16 ], + "faces": { + "east": { "uv": [ 0, 0, 16, 16 ], "texture": "#side", "cullface": "east" }, + "south": { "uv": [ 0, 0, 16, 16 ], "texture": "#side", "cullface": "south" }, + "down": { "uv": [ 0, 0, 16, 16 ], "texture": "#bottom", "cullface": "down" } + } + }, + { + "from": [ 0, 0, 0 ], + "to": [ 8, 8, 8 ], + "faces": { + "north": { "uv": [ 8, 8, 16, 16 ], "texture": "#side", "cullface": "north" }, + "up": { "uv": [ 0, 0, 8, 8 ], "texture": "#top" } + } + }, + { + "from": [ 8, 8, 0 ], + "to": [ 16, 16, 8 ], + "faces": { + "west": { "uv": [ 0, 0, 8, 8 ], "texture": "#side" }, + "up": { "uv": [ 8, 0, 16, 8 ], "texture": "#top", "cullface": "up" } + } + }, + { + "from": [ 0, 8, 8 ], + "to": [ 8, 16, 16 ], + "faces": { + "north": { "uv": [ 8, 0, 16, 8 ], "texture": "#side" }, + "west": { "uv": [ 8, 0, 16, 8 ], "texture": "#side", "cullface": "west" } + } + }, + { + "from": [ 8, 0, 0 ], + "to": [ 16, 16, 0 ], + "faces": { + "north": { "uv": [ 0, 0, 8, 16 ], "texture": "#side", "cullface": "north" } + } + }, + { + "from": [ 0, 16, 8 ], + "to": [ 16, 16, 16 ], + "faces": { + "up": { "uv": [ 0, 8, 16, 16 ], "texture": "#top", "cullface": "up" } + } + }, + { + "from": [ 0, 0, 0 ], + "to": [ 0, 8, 16 ], + "faces": { + "west": { "uv": [ 0, 8, 16, 16 ], "texture": "#side", "cullface": "west" } + } + } + ] +} diff --git a/common/src/main/resources/assets/minecraft/models/block/lightning_rod.json b/common/src/main/resources/assets/minecraft/models/block/lightning_rod.json new file mode 100644 index 0000000000..470e5939a7 --- /dev/null +++ b/common/src/main/resources/assets/minecraft/models/block/lightning_rod.json @@ -0,0 +1,42 @@ +{ + "parent": "minecraft:block/block", + "textures": { + "particle": "block/lightning_rod", + "texture": "block/lightning_rod" + }, + "elements": [ + { + "from": [ 6, 12, 6 ], + "to": [ 10, 16, 10 ], + "faces": { + "north": { "uv": [ 0, 0, 4, 4 ], "texture": "#texture" }, + "east": { "uv": [ 0, 0, 4, 4 ], "texture": "#texture" }, + "south": { "uv": [ 0, 0, 4, 4 ], "texture": "#texture" }, + "west": { "uv": [ 0, 0, 4, 4 ], "texture": "#texture" }, + "up": { "uv": [ 4, 4, 0, 0 ], "texture": "#texture", "cullface": "up" }, + "down": { "uv": [ 0, 0, 4, 4 ], "texture": "#texture" } + } + }, + { + "from": [ 7, 0, 7 ], + "to": [ 9, 12, 9 ], + "faces": { + "north": { "uv": [ 0, 4, 2, 16 ], "texture": "#texture" }, + "east": { "uv": [ 0, 4, 2, 16 ], "texture": "#texture" }, + "south": { "uv": [ 0, 4, 2, 16 ], "texture": "#texture" }, + "west": { "uv": [ 0, 4, 2, 16 ], "texture": "#texture" }, + "down": { "uv": [ 0, 4, 2, 6 ], "texture": "#texture", "cullface": "down" } + } + } + ], + "display": { + "thirdperson_righthand": { + "translation": [ 0, 2, 0.5 ], + "scale": [ 0.4, 0.4, 0.4 ] + }, + "head": { + "rotation": [ -180, 0, 0 ], + "translation": [ 8.5, 4, 0 ] + } + } +} diff --git a/common/src/main/resources/assets/minecraft/models/block/lightning_rod_on.json b/common/src/main/resources/assets/minecraft/models/block/lightning_rod_on.json new file mode 100644 index 0000000000..272e37879c --- /dev/null +++ b/common/src/main/resources/assets/minecraft/models/block/lightning_rod_on.json @@ -0,0 +1,33 @@ +{ + "textures": { + "particle": "block/lightning_rod_on", + "texture": "block/lightning_rod_on" + }, + "elements": [ + { + "from": [ 6, 12, 6 ], + "to": [ 10, 16, 10 ], + "shade": false, + "faces": { + "north": { "uv": [ 0, 0, 4, 4 ], "texture": "#texture" }, + "east": { "uv": [ 0, 0, 4, 4 ], "texture": "#texture" }, + "south": { "uv": [ 0, 0, 4, 4 ], "texture": "#texture" }, + "west": { "uv": [ 0, 0, 4, 4 ], "texture": "#texture" }, + "up": { "uv": [ 4, 4, 0, 0 ], "texture": "#texture", "cullface": "up" }, + "down": { "uv": [ 0, 0, 4, 4 ], "texture": "#texture" } + } + }, + { + "from": [ 7, 0, 7 ], + "to": [ 9, 12, 9 ], + "shade": false, + "faces": { + "north": { "uv": [ 0, 4, 2, 16 ], "texture": "#texture" }, + "east": { "uv": [ 0, 4, 2, 16 ], "texture": "#texture" }, + "south": { "uv": [ 0, 4, 2, 16 ], "texture": "#texture" }, + "west": { "uv": [ 0, 4, 2, 16 ], "texture": "#texture" }, + "down": { "uv": [ 0, 4, 2, 16 ], "texture": "#texture", "cullface": "down" } + } + } + ] +} diff --git a/common/src/main/resources/assets/minecraft/models/block/mangrove_propagule_hanging_0.json b/common/src/main/resources/assets/minecraft/models/block/mangrove_propagule_hanging_0.json new file mode 100644 index 0000000000..858cb16623 --- /dev/null +++ b/common/src/main/resources/assets/minecraft/models/block/mangrove_propagule_hanging_0.json @@ -0,0 +1,75 @@ +{ + "parent": "minecraft:block/block", + "textures": { + "particle": "block/mangrove_propagule_hanging", + "propagule": "block/mangrove_propagule_hanging" + }, + "elements": [ + { + "from": [ 7, 13.61104, 10.07193 ], + "to": [ 9, 13.61104, 12.07193 ], + "rotation": { "angle": 22.5, "axis": "x", "origin": [ 8, 16, 8 ] }, + "faces": { + "up": { "uv": [ 8, 3, 10, 5 ], "rotation": 180, "texture": "#propagule" }, + "down": { "uv": [ 6, 3, 8, 5 ], "texture": "#propagule" } + } + }, + { + "from": [ 10.07193, 13.61104, 7 ], + "to": [ 12.07193, 13.61104, 9 ], + "rotation": { "angle": -22.5, "axis": "z", "origin": [ 8, 16, 8 ] }, + "faces": { + "up": { "uv": [ 8, 3, 10, 5 ], "rotation": 90, "texture": "#propagule" }, + "down": { "uv": [ 6, 3, 8, 5 ], "rotation": 90, "texture": "#propagule" } + } + }, + { + "from": [ 7, 13.61104, 3.92807 ], + "to": [ 9, 13.61104, 5.92807 ], + "rotation": { "angle": -22.5, "axis": "x", "origin": [ 8, 16, 8 ] }, + "faces": { + "up": { "uv": [ 8, 3, 10, 5 ], "texture": "#propagule" }, + "down": { "uv": [ 6, 3, 8, 5 ], "rotation": 180, "texture": "#propagule" } + } + }, + { + "from": [ 3.92807, 13.61104, 7 ], + "to": [ 5.92807, 13.61104, 9 ], + "rotation": { "angle": 22.5, "axis": "z", "origin": [ 8, 16, 8 ] }, + "faces": { + "up": { "uv": [ 8, 3, 10, 5 ], "rotation": 270, "texture": "#propagule" }, + "down": { "uv": [ 6, 3, 8, 5 ], "rotation": 270, "texture": "#propagule" } + } + }, + { + "from": [ 7, 13, 7 ], + "to": [ 9, 14, 9 ], + "faces": { + "north": { "uv": [ 0, 2, 2, 3 ], "texture": "#propagule" }, + "east": { "uv": [ 0, 2, 2, 3 ], "texture": "#propagule" }, + "south": { "uv": [ 0, 2, 2, 3 ], "texture": "#propagule" }, + "west": { "uv": [ 0, 2, 2, 3 ], "texture": "#propagule" }, + "up": { "uv": [ 0, 0, 2, 2 ], "texture": "#propagule" }, + "down": { "uv": [ 0, 3, 2, 5 ], "texture": "#propagule" } + } + }, + { + "from": [ 7, 14, 8 ], + "to": [ 9, 16, 8 ], + "rotation": { "angle": -45, "axis": "y", "origin": [ 8, 16, 8 ] }, + "faces": { + "north": { "uv": [ 0, 0, 2, 2 ], "texture": "#propagule" }, + "south": { "uv": [ 0, 0, 2, 2 ], "texture": "#propagule" } + } + }, + { + "from": [ 7, 14, 8 ], + "to": [ 9, 16, 8 ], + "rotation": { "angle": 45, "axis": "y", "origin": [ 8, 16, 8 ] }, + "faces": { + "north": { "uv": [ 0, 0, 2, 2 ], "texture": "#propagule" }, + "south": { "uv": [ 0, 0, 2, 2 ], "texture": "#propagule" } + } + } + ] +} diff --git a/common/src/main/resources/assets/minecraft/models/block/mangrove_propagule_hanging_1.json b/common/src/main/resources/assets/minecraft/models/block/mangrove_propagule_hanging_1.json new file mode 100644 index 0000000000..936eced044 --- /dev/null +++ b/common/src/main/resources/assets/minecraft/models/block/mangrove_propagule_hanging_1.json @@ -0,0 +1,85 @@ +{ + "parent": "minecraft:block/block", + "textures": { + "particle": "block/mangrove_propagule_hanging", + "propagule": "block/mangrove_propagule_hanging" + }, + "elements": [ + { + "from": [ 7, 13.61104, 10.07193 ], + "to": [ 9, 13.61104, 12.07193 ], + "rotation": { "angle": 22.5, "axis": "x", "origin": [ 8, 16, 8 ] }, + "faces": { + "up": { "uv": [ 8, 3, 10, 5 ], "rotation": 180, "texture": "#propagule" }, + "down": { "uv": [ 6, 3, 8, 5 ], "texture": "#propagule" } + } + }, + { + "from": [ 10.07193, 13.61104, 7 ], + "to": [ 12.07193, 13.61104, 9 ], + "rotation": { "angle": -22.5, "axis": "z", "origin": [ 8, 16, 8 ] }, + "faces": { + "up": { "uv": [ 8, 3, 10, 5 ], "rotation": 90, "texture": "#propagule" }, + "down": { "uv": [ 6, 3, 8, 5 ], "rotation": 90, "texture": "#propagule" } + } + }, + { + "from": [ 7, 13.61104, 3.92807 ], + "to": [ 9, 13.61104, 5.92807 ], + "rotation": { "angle": -22.5, "axis": "x", "origin": [ 8, 16, 8 ] }, + "faces": { + "up": { "uv": [ 8, 3, 10, 5 ], "texture": "#propagule" }, + "down": { "uv": [ 6, 3, 8, 5 ], "rotation": 180, "texture": "#propagule" } + } + }, + { + "from": [ 3.92807, 13.61104, 7 ], + "to": [ 5.92807, 13.61104, 9 ], + "rotation": { "angle": 22.5, "axis": "z", "origin": [ 8, 16, 8 ] }, + "faces": { + "up": { "uv": [ 8, 3, 10, 5 ], "rotation": 270, "texture": "#propagule" }, + "down": { "uv": [ 6, 3, 8, 5 ], "rotation": 270, "texture": "#propagule" } + } + }, + { + "from": [ 7, 13, 7 ], + "to": [ 9, 14, 9 ], + "faces": { + "north": { "uv": [ 0, 2, 2, 3 ], "texture": "#propagule" }, + "east": { "uv": [ 0, 2, 2, 3 ], "texture": "#propagule" }, + "south": { "uv": [ 0, 2, 2, 3 ], "texture": "#propagule" }, + "west": { "uv": [ 0, 2, 2, 3 ], "texture": "#propagule" }, + "up": { "uv": [ 0, 0, 2, 2 ], "texture": "#propagule" } + } + }, + { + "from": [ 7, 14, 8 ], + "to": [ 9, 16, 8 ], + "rotation": { "angle": -45, "axis": "y", "origin": [ 8, 16, 8 ] }, + "faces": { + "north": { "uv": [ 0, 0, 2, 2 ], "texture": "#propagule" }, + "south": { "uv": [ 0, 0, 2, 2 ], "texture": "#propagule" } + } + }, + { + "from": [ 7, 14, 8 ], + "to": [ 9, 16, 8 ], + "rotation": { "angle": 45, "axis": "y", "origin": [ 8, 16, 8 ] }, + "faces": { + "north": { "uv": [ 0, 0, 2, 2 ], "texture": "#propagule" }, + "south": { "uv": [ 0, 0, 2, 2 ], "texture": "#propagule" } + } + }, + { + "from": [ 7, 10, 7 ], + "to": [ 9, 13, 9 ], + "faces": { + "north": { "uv": [ 0, 7, 2, 10 ], "texture": "#propagule" }, + "east": { "uv": [ 0, 7, 2, 10 ], "texture": "#propagule" }, + "south": { "uv": [ 0, 7, 2, 10 ], "texture": "#propagule" }, + "west": { "uv": [ 0, 7, 2, 10 ], "texture": "#propagule" }, + "down": { "uv": [ 0, 5, 2, 7 ], "texture": "#propagule" } + } + } + ] +} diff --git a/common/src/main/resources/assets/minecraft/models/block/mangrove_propagule_hanging_2.json b/common/src/main/resources/assets/minecraft/models/block/mangrove_propagule_hanging_2.json new file mode 100644 index 0000000000..43ee008888 --- /dev/null +++ b/common/src/main/resources/assets/minecraft/models/block/mangrove_propagule_hanging_2.json @@ -0,0 +1,103 @@ +{ + "parent": "minecraft:block/block", + "textures": { + "particle": "block/mangrove_propagule_hanging", + "propagule": "block/mangrove_propagule_hanging" + }, + "elements": [ + { + "from": [ 7, 13.61104, 10.07193 ], + "to": [ 9, 13.61104, 12.07193 ], + "rotation": { "angle": 22.5, "axis": "x", "origin": [ 8, 16, 8 ] }, + "faces": { + "up": { "uv": [ 8, 3, 10, 5 ], "rotation": 180, "texture": "#propagule" }, + "down": { "uv": [ 6, 3, 8, 5 ], "texture": "#propagule" } + } + }, + { + "from": [ 10.07193, 13.61104, 7 ], + "to": [ 12.07193, 13.61104, 9 ], + "rotation": { "angle": -22.5, "axis": "z", "origin": [ 8, 16, 8 ] }, + "faces": { + "up": { "uv": [ 8, 3, 10, 5 ], "rotation": 90, "texture": "#propagule" }, + "down": { "uv": [ 6, 3, 8, 5 ], "rotation": 90, "texture": "#propagule" } + } + }, + { + "from": [ 7, 13.61104, 3.92807 ], + "to": [ 9, 13.61104, 5.92807 ], + "rotation": { "angle": -22.5, "axis": "x", "origin": [ 8, 16, 8 ] }, + "faces": { + "up": { "uv": [ 8, 3, 10, 5 ], "texture": "#propagule" }, + "down": { "uv": [ 6, 3, 8, 5 ], "rotation": 180, "texture": "#propagule" } + } + }, + { + "from": [ 3.92807, 13.61104, 7 ], + "to": [ 5.92807, 13.61104, 9 ], + "rotation": { "angle": 22.5, "axis": "z", "origin": [ 8, 16, 8 ] }, + "faces": { + "up": { "uv": [ 8, 3, 10, 5 ], "rotation": 270, "texture": "#propagule" }, + "down": { "uv": [ 6, 3, 8, 5 ], "rotation": 270, "texture": "#propagule" } + } + }, + { + "from": [ 7, 13, 7 ], + "to": [ 9, 14, 9 ], + "faces": { + "north": { "uv": [ 0, 2, 2, 3 ], "texture": "#propagule" }, + "east": { "uv": [ 0, 2, 2, 3 ], "texture": "#propagule" }, + "south": { "uv": [ 0, 2, 2, 3 ], "texture": "#propagule" }, + "west": { "uv": [ 0, 2, 2, 3 ], "texture": "#propagule" }, + "up": { "uv": [ 0, 0, 2, 2 ], "texture": "#propagule" } + } + }, + { + "from": [ 7, 14, 8 ], + "to": [ 9, 16, 8 ], + "rotation": { "angle": -45, "axis": "y", "origin": [ 8, 16, 8 ] }, + "faces": { + "north": { "uv": [ 0, 0, 2, 2 ], "texture": "#propagule" }, + "south": { "uv": [ 0, 0, 2, 2 ], "texture": "#propagule" } + } + }, + { + "from": [ 7, 14, 8 ], + "to": [ 9, 16, 8 ], + "rotation": { "angle": 45, "axis": "y", "origin": [ 8, 16, 8 ] }, + "faces": { + "north": { "uv": [ 0, 0, 2, 2 ], "texture": "#propagule" }, + "south": { "uv": [ 0, 0, 2, 2 ], "texture": "#propagule" } + } + }, + { + "from": [ 7, 10, 7 ], + "to": [ 9, 13, 9 ], + "faces": { + "north": { "uv": [ 0, 7, 2, 10 ], "texture": "#propagule" }, + "east": { "uv": [ 0, 7, 2, 10 ], "texture": "#propagule" }, + "south": { "uv": [ 0, 7, 2, 10 ], "texture": "#propagule" }, + "west": { "uv": [ 0, 7, 2, 10 ], "texture": "#propagule" }, + "down": { "uv": [ 0, 5, 2, 7 ], "texture": "#propagule" } + } + }, + { + "from": [ 7, 7, 8 ], + "to": [ 9, 10, 8 ], + "rotation": { "angle": 45, "axis": "y", "origin": [ 8, 16, 8 ] }, + "faces": { + "north": { "uv": [ 3, 7, 5, 10 ], "texture": "#propagule" }, + "south": { "uv": [ 3, 7, 5, 10 ], "texture": "#propagule" } + } + }, + { + "from": [ 7, 7, 8 ], + "to": [ 9, 10, 8 ], + "rotation": { "angle": -45, "axis": "y", "origin": [ 8, 16, 8 ] }, + "faces": { + "north": { "uv": [ 3, 7, 5, 10 ], "texture": "#propagule" }, + "south": { "uv": [ 3, 7, 5, 10 ], "texture": "#propagule" } + } + } + ] +} diff --git a/common/src/main/resources/assets/minecraft/models/block/mangrove_propagule_hanging_3.json b/common/src/main/resources/assets/minecraft/models/block/mangrove_propagule_hanging_3.json new file mode 100644 index 0000000000..b5669a95ad --- /dev/null +++ b/common/src/main/resources/assets/minecraft/models/block/mangrove_propagule_hanging_3.json @@ -0,0 +1,103 @@ +{ + "parent": "minecraft:block/block", + "textures": { + "particle": "block/mangrove_propagule_hanging", + "propagule": "block/mangrove_propagule_hanging" + }, + "elements": [ + { + "from": [ 7, 13.61104, 10.07193 ], + "to": [ 9, 13.61104, 12.07193 ], + "rotation": { "angle": 22.5, "axis": "x", "origin": [ 8, 16, 8 ] }, + "faces": { + "up": { "uv": [ 8, 3, 10, 5 ], "rotation": 180, "texture": "#propagule" }, + "down": { "uv": [ 6, 3, 8, 5 ], "texture": "#propagule" } + } + }, + { + "from": [ 10.07193, 13.61104, 7 ], + "to": [ 12.07193, 13.61104, 9 ], + "rotation": { "angle": -22.5, "axis": "z", "origin": [ 8, 16, 8 ] }, + "faces": { + "up": { "uv": [ 8, 3, 10, 5 ], "rotation": 90, "texture": "#propagule" }, + "down": { "uv": [ 6, 3, 8, 5 ], "rotation": 90, "texture": "#propagule" } + } + }, + { + "from": [ 7, 13.61104, 3.92807 ], + "to": [ 9, 13.61104, 5.92807 ], + "rotation": { "angle": -22.5, "axis": "x", "origin": [ 8, 16, 8 ] }, + "faces": { + "up": { "uv": [ 8, 3, 10, 5 ], "texture": "#propagule" }, + "down": { "uv": [ 6, 3, 8, 5 ], "rotation": 180, "texture": "#propagule" } + } + }, + { + "from": [ 3.92807, 13.61104, 7 ], + "to": [ 5.92807, 13.61104, 9 ], + "rotation": { "angle": 22.5, "axis": "z", "origin": [ 8, 16, 8 ] }, + "faces": { + "up": { "uv": [ 8, 3, 10, 5 ], "rotation": 270, "texture": "#propagule" }, + "down": { "uv": [ 6, 3, 8, 5 ], "rotation": 270, "texture": "#propagule" } + } + }, + { + "from": [ 7, 13, 7 ], + "to": [ 9, 14, 9 ], + "faces": { + "north": { "uv": [ 0, 2, 2, 3 ], "texture": "#propagule" }, + "east": { "uv": [ 0, 2, 2, 3 ], "texture": "#propagule" }, + "south": { "uv": [ 0, 2, 2, 3 ], "texture": "#propagule" }, + "west": { "uv": [ 0, 2, 2, 3 ], "texture": "#propagule" }, + "up": { "uv": [ 0, 0, 2, 2 ], "texture": "#propagule" } + } + }, + { + "from": [ 7, 14, 8 ], + "to": [ 9, 16, 8 ], + "rotation": { "angle": -45, "axis": "y", "origin": [ 8, 16, 8 ] }, + "faces": { + "north": { "uv": [ 0, 0, 2, 2 ], "texture": "#propagule" }, + "south": { "uv": [ 0, 0, 2, 2 ], "texture": "#propagule" } + } + }, + { + "from": [ 7, 14, 8 ], + "to": [ 9, 16, 8 ], + "rotation": { "angle": 45, "axis": "y", "origin": [ 8, 16, 8 ] }, + "faces": { + "north": { "uv": [ 0, 0, 2, 2 ], "texture": "#propagule" }, + "south": { "uv": [ 0, 0, 2, 2 ], "texture": "#propagule" } + } + }, + { + "from": [ 7, 10, 7 ], + "to": [ 9, 13, 9 ], + "faces": { + "north": { "uv": [ 0, 7, 2, 10 ], "texture": "#propagule" }, + "east": { "uv": [ 0, 7, 2, 10 ], "texture": "#propagule" }, + "south": { "uv": [ 0, 7, 2, 10 ], "texture": "#propagule" }, + "west": { "uv": [ 0, 7, 2, 10 ], "texture": "#propagule" }, + "down": { "uv": [ 0, 5, 2, 7 ], "texture": "#propagule" } + } + }, + { + "from": [ 7, 3, 8 ], + "to": [ 9, 10, 8 ], + "rotation": { "angle": 45, "axis": "y", "origin": [ 8, 16, 8 ] }, + "faces": { + "north": { "uv": [ 3, 3, 5, 10 ], "texture": "#propagule" }, + "south": { "uv": [ 3, 3, 5, 10 ], "texture": "#propagule" } + } + }, + { + "from": [ 7, 3, 8 ], + "to": [ 9, 10, 8 ], + "rotation": { "angle": -45, "axis": "y", "origin": [ 8, 16, 8 ] }, + "faces": { + "north": { "uv": [ 3, 3, 5, 10 ], "texture": "#propagule" }, + "south": { "uv": [ 3, 3, 5, 10 ], "texture": "#propagule" } + } + } + ] +} diff --git a/common/src/main/resources/assets/minecraft/models/block/mangrove_propagule_hanging_4.json b/common/src/main/resources/assets/minecraft/models/block/mangrove_propagule_hanging_4.json new file mode 100644 index 0000000000..b056b7d11a --- /dev/null +++ b/common/src/main/resources/assets/minecraft/models/block/mangrove_propagule_hanging_4.json @@ -0,0 +1,103 @@ +{ + "parent": "minecraft:block/block", + "textures": { + "particle": "block/mangrove_propagule_hanging", + "propagule": "block/mangrove_propagule_hanging" + }, + "elements": [ + { + "from": [ 7, 13.61104, 10.07193 ], + "to": [ 9, 13.61104, 12.07193 ], + "rotation": { "angle": 22.5, "axis": "x", "origin": [ 8, 16, 8 ] }, + "faces": { + "up": { "uv": [ 8, 3, 10, 5 ], "rotation": 180, "texture": "#propagule" }, + "down": { "uv": [ 6, 3, 8, 5 ], "texture": "#propagule" } + } + }, + { + "from": [ 10.07193, 13.61104, 7 ], + "to": [ 12.07193, 13.61104, 9 ], + "rotation": { "angle": -22.5, "axis": "z", "origin": [ 8, 16, 8 ] }, + "faces": { + "up": { "uv": [ 8, 3, 10, 5 ], "rotation": 90, "texture": "#propagule" }, + "down": { "uv": [ 6, 3, 8, 5 ], "rotation": 90, "texture": "#propagule" } + } + }, + { + "from": [ 7, 13.61104, 3.92807 ], + "to": [ 9, 13.61104, 5.92807 ], + "rotation": { "angle": -22.5, "axis": "x", "origin": [ 8, 16, 8 ] }, + "faces": { + "up": { "uv": [ 8, 3, 10, 5 ], "texture": "#propagule" }, + "down": { "uv": [ 6, 3, 8, 5 ], "rotation": 180, "texture": "#propagule" } + } + }, + { + "from": [ 3.92807, 13.61104, 7 ], + "to": [ 5.92807, 13.61104, 9 ], + "rotation": { "angle": 22.5, "axis": "z", "origin": [ 8, 16, 8 ] }, + "faces": { + "up": { "uv": [ 8, 3, 10, 5 ], "rotation": 270, "texture": "#propagule" }, + "down": { "uv": [ 6, 3, 8, 5 ], "rotation": 270, "texture": "#propagule" } + } + }, + { + "from": [ 7, 13, 7 ], + "to": [ 9, 14, 9 ], + "faces": { + "north": { "uv": [ 0, 2, 2, 3 ], "texture": "#propagule" }, + "east": { "uv": [ 0, 2, 2, 3 ], "texture": "#propagule" }, + "south": { "uv": [ 0, 2, 2, 3 ], "texture": "#propagule" }, + "west": { "uv": [ 0, 2, 2, 3 ], "texture": "#propagule" }, + "up": { "uv": [ 0, 0, 2, 2 ], "texture": "#propagule" } + } + }, + { + "from": [ 7, 14, 8 ], + "to": [ 9, 16, 8 ], + "rotation": { "angle": -45, "axis": "y", "origin": [ 8, 16, 8 ] }, + "faces": { + "north": { "uv": [ 0, 0, 2, 2 ], "texture": "#propagule" }, + "south": { "uv": [ 0, 0, 2, 2 ], "texture": "#propagule" } + } + }, + { + "from": [ 7, 14, 8 ], + "to": [ 9, 16, 8 ], + "rotation": { "angle": 45, "axis": "y", "origin": [ 8, 16, 8 ] }, + "faces": { + "north": { "uv": [ 0, 0, 2, 2 ], "texture": "#propagule" }, + "south": { "uv": [ 0, 0, 2, 2 ], "texture": "#propagule" } + } + }, + { + "from": [ 7, 10, 7 ], + "to": [ 9, 13, 9 ], + "faces": { + "north": { "uv": [ 0, 7, 2, 10 ], "texture": "#propagule" }, + "east": { "uv": [ 0, 7, 2, 10 ], "texture": "#propagule" }, + "south": { "uv": [ 0, 7, 2, 10 ], "texture": "#propagule" }, + "west": { "uv": [ 0, 7, 2, 10 ], "texture": "#propagule" }, + "down": { "uv": [ 0, 5, 2, 7 ], "texture": "#propagule" } + } + }, + { + "from": [ 7, 0, 8 ], + "to": [ 9, 10, 8 ], + "rotation": { "angle": 45, "axis": "y", "origin": [ 8, 16, 8 ] }, + "faces": { + "north": { "uv": [ 3, 0, 5, 10 ], "texture": "#propagule" }, + "south": { "uv": [ 3, 0, 5, 10 ], "texture": "#propagule" } + } + }, + { + "from": [ 7, 0, 8 ], + "to": [ 9, 10, 8 ], + "rotation": { "angle": -45, "axis": "y", "origin": [ 8, 16, 8 ] }, + "faces": { + "north": { "uv": [ 3, 0, 5, 10 ], "texture": "#propagule" }, + "south": { "uv": [ 3, 0, 5, 10 ], "texture": "#propagule" } + } + } + ] +} diff --git a/common/src/main/resources/assets/minecraft/models/block/potted_bamboo.json b/common/src/main/resources/assets/minecraft/models/block/potted_bamboo.json new file mode 100644 index 0000000000..0fef4d4b05 --- /dev/null +++ b/common/src/main/resources/assets/minecraft/models/block/potted_bamboo.json @@ -0,0 +1,110 @@ +{ + "ambientocclusion": false, + "textures": { + "particle": "block/flower_pot", + "flowerpot": "block/flower_pot", + "dirt": "block/dirt", + "bamboo": "block/bamboo_stalk", + "leaf": "block/bamboo_singleleaf" + }, + "elements": [ + { + "from": [ 5, 0, 5 ], + "to": [ 11, 6, 11 ], + "faces": { + "north": { "uv": [ 5, 10, 11, 16 ], "texture": "#flowerpot" }, + "east": { "uv": [ 5, 10, 11, 16 ], "texture": "#flowerpot" }, + "south": { "uv": [ 5, 10, 11, 16 ], "texture": "#flowerpot" }, + "west": { "uv": [ 5, 10, 11, 16 ], "texture": "#flowerpot" } + } + }, + { + "from": [ 5, 0, 5 ], + "to": [ 10, 6, 6 ], + "faces": { + "up": { "uv": [ 5, 5, 10, 6 ], "texture": "#flowerpot" }, + "down": { "uv": [ 5, 10, 10, 11 ], "texture": "#flowerpot", "cullface": "down" } + } + }, + { + "from": [ 10, 0, 5 ], + "to": [ 11, 6, 10 ], + "faces": { + "up": { "uv": [ 10, 5, 11, 10 ], "texture": "#flowerpot" }, + "down": { "uv": [ 10, 6, 11, 11 ], "texture": "#flowerpot", "cullface": "down" } + } + }, + { + "from": [ 6, 0, 10 ], + "to": [ 11, 6, 11 ], + "faces": { + "up": { "uv": [ 6, 10, 11, 11 ], "texture": "#flowerpot" }, + "down": { "uv": [ 6, 5, 11, 6 ], "texture": "#flowerpot", "cullface": "down" } + } + }, + { + "from": [ 5, 0, 6 ], + "to": [ 6, 6, 11 ], + "faces": { + "up": { "uv": [ 5, 6, 6, 11 ], "texture": "#flowerpot" }, + "down": { "uv": [ 5, 5, 6, 10 ], "texture": "#flowerpot", "cullface": "down" } + } + }, + { + "from": [ 6, 4, 6 ], + "to": [ 10, 6, 6 ], + "faces": { + "south": { "uv": [ 6, 10, 10, 12 ], "texture": "#flowerpot" } + } + }, + { + "from": [ 10, 4, 6 ], + "to": [ 10, 6, 10 ], + "faces": { + "west": { "uv": [ 6, 10, 10, 12 ], "texture": "#flowerpot" } + } + }, + { + "from": [ 6, 4, 10 ], + "to": [ 10, 6, 10 ], + "faces": { + "north": { "uv": [ 6, 10, 10, 12 ], "texture": "#flowerpot" } + } + }, + { + "from": [ 6, 4, 6 ], + "to": [ 6, 6, 10 ], + "faces": { + "east": { "uv": [ 6, 10, 10, 12 ], "texture": "#flowerpot" } + } + }, + { + "from": [ 6, 0, 6 ], + "to": [ 10, 4, 10 ], + "faces": { + "up": { "uv": [ 6, 6, 10, 10 ], "texture": "#dirt" }, + "down": { "uv": [ 6, 12, 10, 16 ], "texture": "#flowerpot", "cullface": "down" } + } + }, + { + "from": [ 7, 4, 7 ], + "to": [ 9, 16, 9 ], + "faces": { + "north": { "uv": [ 6, 0, 8, 12 ], "texture": "#bamboo" }, + "east": { "uv": [ 6, 0, 8, 12 ], "texture": "#bamboo" }, + "south": { "uv": [ 6, 0, 8, 12 ], "texture": "#bamboo" }, + "west": { "uv": [ 6, 0, 8, 12 ], "texture": "#bamboo" }, + "up": { "uv": [ 13, 0, 15, 2 ], "texture": "#bamboo", "cullface": "up" } + } + }, + { + "from": [ 0, 2, 8 ], + "to": [ 16, 18, 8 ], + "shade": false, + "faces": { + "north": { "uv": [ 0, 0, 16, 16 ], "texture": "#leaf" }, + "south": { "uv": [ 16, 0, 0, 16 ], "texture": "#leaf" } + } + } + ] +} diff --git a/common/src/main/resources/assets/minecraft/models/block/potted_mangrove_propagule.json b/common/src/main/resources/assets/minecraft/models/block/potted_mangrove_propagule.json new file mode 100644 index 0000000000..d06636b755 --- /dev/null +++ b/common/src/main/resources/assets/minecraft/models/block/potted_mangrove_propagule.json @@ -0,0 +1,129 @@ +{ + "ambientocclusion": false, + "textures": { + "particle": "block/flower_pot", + "flowerpot": "block/flower_pot", + "dirt": "block/dirt", + "sapling": "block/mangrove_propagule" + }, + "elements": [ + { + "from": [ 5, 0, 5 ], + "to": [ 11, 6, 11 ], + "faces": { + "north": { "uv": [ 5, 10, 11, 16 ], "texture": "#flowerpot" }, + "east": { "uv": [ 5, 10, 11, 16 ], "texture": "#flowerpot" }, + "south": { "uv": [ 5, 10, 11, 16 ], "texture": "#flowerpot" }, + "west": { "uv": [ 5, 10, 11, 16 ], "texture": "#flowerpot" } + } + }, + { + "from": [ 5, 0, 5 ], + "to": [ 10, 6, 6 ], + "faces": { + "up": { "uv": [ 5, 5, 10, 6 ], "texture": "#flowerpot" }, + "down": { "uv": [ 5, 10, 10, 11 ], "texture": "#flowerpot", "cullface": "down" } + } + }, + { + "from": [ 10, 0, 5 ], + "to": [ 11, 6, 10 ], + "faces": { + "up": { "uv": [ 10, 5, 11, 10 ], "texture": "#flowerpot" }, + "down": { "uv": [ 10, 6, 11, 11 ], "texture": "#flowerpot", "cullface": "down" } + } + }, + { + "from": [ 6, 0, 10 ], + "to": [ 11, 6, 11 ], + "faces": { + "up": { "uv": [ 6, 10, 11, 11 ], "texture": "#flowerpot" }, + "down": { "uv": [ 6, 5, 11, 6 ], "texture": "#flowerpot", "cullface": "down" } + } + }, + { + "from": [ 5, 0, 6 ], + "to": [ 6, 6, 11 ], + "faces": { + "up": { "uv": [ 5, 6, 6, 11 ], "texture": "#flowerpot" }, + "down": { "uv": [ 5, 5, 6, 10 ], "texture": "#flowerpot", "cullface": "down" } + } + }, + { + "from": [ 6, 4, 6 ], + "to": [ 10, 6, 6 ], + "faces": { + "south": { "uv": [ 6, 10, 10, 12 ], "texture": "#flowerpot" } + } + }, + { + "from": [ 10, 4, 6 ], + "to": [ 10, 6, 10 ], + "faces": { + "west": { "uv": [ 6, 10, 10, 12 ], "texture": "#flowerpot" } + } + }, + { + "from": [ 6, 4, 10 ], + "to": [ 10, 6, 10 ], + "faces": { + "north": { "uv": [ 6, 10, 10, 12 ], "texture": "#flowerpot" } + } + }, + { + "from": [ 6, 4, 6 ], + "to": [ 6, 6, 10 ], + "faces": { + "east": { "uv": [ 6, 10, 10, 12 ], "texture": "#flowerpot" } + } + }, + { + "from": [ 6, 0, 6 ], + "to": [ 10, 4, 10 ], + "faces": { + "up": { "uv": [ 6, 6, 10, 10 ], "texture": "#dirt" }, + "down": { "uv": [ 6, 12, 10, 16 ], "texture": "#flowerpot", "cullface": "down" } + } + }, + { + "from": [ 4.5, 9, 8 ], + "to": [ 11.5, 15, 8 ], + "shade": false, + "rotation": { "angle": 45, "axis": "y", "origin": [ 8, 0, 8 ], "rescale": true }, + "faces": { + "north": { "uv": [ 4, 1, 11, 7 ], "texture": "#sapling" }, + "south": { "uv": [ 4, 1, 11, 7 ], "texture": "#sapling" } + } + }, + { + "from": [ 8, 9, 4.5 ], + "to": [ 8, 15, 11.5 ], + "shade": false, + "rotation": { "angle": 45, "axis": "y", "origin": [ 8, 0, 8 ], "rescale": true }, + "faces": { + "east": { "uv": [ 4, 1, 11, 7 ], "texture": "#sapling" }, + "west": { "uv": [ 4, 1, 11, 7 ], "texture": "#sapling" } + } + }, + { + "from": [ 8, 4, 7 ], + "to": [ 8, 9, 9 ], + "shade": false, + "rotation": { "angle": 45, "axis": "y", "origin": [ 8, 0, 8 ], "rescale": true }, + "faces": { + "east": { "uv": [ 7, 7, 9, 12 ], "texture": "#sapling" }, + "west": { "uv": [ 7, 7, 9, 12 ], "texture": "#sapling" } + } + }, + { + "from": [ 7, 4, 8 ], + "to": [ 9, 9, 8 ], + "shade": false, + "rotation": { "angle": 45, "axis": "y", "origin": [ 8, 0, 8 ], "rescale": true }, + "faces": { + "north": { "uv": [ 7, 7, 9, 12 ], "texture": "#sapling" }, + "south": { "uv": [ 7, 7, 9, 12 ], "texture": "#sapling" } + } + } + ] +} diff --git a/common/src/main/resources/assets/minecraft/models/block/stairs.json b/common/src/main/resources/assets/minecraft/models/block/stairs.json new file mode 100644 index 0000000000..a96a34ff5f --- /dev/null +++ b/common/src/main/resources/assets/minecraft/models/block/stairs.json @@ -0,0 +1,62 @@ +{ + "parent": "minecraft:block/block", + "textures": { + "particle": "#side" + }, + "elements": [ + { + "from": [ 0, 0, 0 ], + "to": [ 16, 16, 16 ], + "faces": { + "east": { "uv": [ 0, 0, 16, 16 ], "texture": "#side", "cullface": "east" }, + "down": { "uv": [ 0, 0, 16, 16 ], "texture": "#bottom", "cullface": "down" } + } + }, + { + "from": [ 0, 0, 0 ], + "to": [ 8, 8, 16 ], + "faces": { + "north": { "uv": [ 8, 8, 16, 16 ], "texture": "#side", "cullface": "north" }, + "west": { "uv": [ 0, 8, 16, 16 ], "texture": "#side", "cullface": "west" }, + "up": { "uv": [ 0, 0, 8, 16 ], "texture": "#top" } + } + }, + { + "from": [ 8, 8, 0 ], + "to": [ 16, 16, 16 ], + "faces": { + "south": { "uv": [ 8, 0, 16, 8 ], "texture": "#side", "cullface": "south" }, + "west": { "uv": [ 0, 0, 16, 8 ], "texture": "#side" }, + "up": { "uv": [ 8, 0, 16, 16 ], "texture": "#top", "cullface": "up" } + } + }, + { + "from": [ 8, 0, 0 ], + "to": [ 16, 16, 0 ], + "faces": { + "north": { "uv": [ 0, 0, 8, 16 ], "texture": "#side", "cullface": "north" } + } + }, + { + "from": [ 0, 0, 16 ], + "to": [ 16, 8, 16 ], + "faces": { + "south": { "uv": [ 0, 8, 16, 16 ], "texture": "#side", "cullface": "south" } + } + } + ], + "display": { + "thirdperson_lefthand": { + "rotation": [ 75, -135, 0 ], + "translation": [ 0, 2.5, 0 ], + "scale": [ 0.375, 0.375, 0.375 ] + }, + "gui": { + "rotation": [ 30, 135, 0 ], + "scale": [ 0.625, 0.625, 0.625 ] + }, + "head": { + "rotation": [ 0, -90, 0 ] + } + } +} diff --git a/common/src/main/resources/assets/minecraft/models/block/template_anvil.json b/common/src/main/resources/assets/minecraft/models/block/template_anvil.json new file mode 100644 index 0000000000..5ba507f593 --- /dev/null +++ b/common/src/main/resources/assets/minecraft/models/block/template_anvil.json @@ -0,0 +1,60 @@ +{ + "parent": "minecraft:block/block", + "textures": { + "particle": "block/anvil", + "body": "block/anvil" + }, + "elements": [ + { + "from": [ 2, 0, 2 ], + "to": [ 14, 4, 14 ], + "faces": { + "north": { "uv": [ 2, 12, 14, 16 ], "texture": "#body" }, + "east": { "uv": [ 4, 2, 0, 14 ], "rotation": 270, "texture": "#body" }, + "south": { "uv": [ 2, 12, 14, 16 ], "texture": "#body" }, + "west": { "uv": [ 0, 2, 4, 14 ], "rotation": 90, "texture": "#body" }, + "up": { "uv": [ 2, 2, 14, 14 ], "rotation": 180, "texture": "#body" }, + "down": { "uv": [ 2, 2, 14, 14 ], "rotation": 180, "texture": "#body", "cullface": "down" } + } + }, + { + "from": [ 4, 4, 3 ], + "to": [ 12, 5, 13 ], + "faces": { + "north": { "uv": [ 4, 11, 12, 12 ], "texture": "#body" }, + "east": { "uv": [ 5, 3, 4, 13 ], "rotation": 270, "texture": "#body" }, + "south": { "uv": [ 4, 11, 12, 12 ], "texture": "#body" }, + "west": { "uv": [ 4, 3, 5, 13 ], "rotation": 90, "texture": "#body" }, + "up": { "uv": [ 4, 3, 12, 13 ], "rotation": 180, "texture": "#body" } + } + }, + { + "from": [ 6, 5, 4 ], + "to": [ 10, 10, 12 ], + "faces": { + "north": { "uv": [ 6, 6, 10, 11 ], "texture": "#body" }, + "east": { "uv": [ 10, 4, 5, 12 ], "rotation": 270, "texture": "#body" }, + "south": { "uv": [ 6, 6, 10, 11 ], "texture": "#body" }, + "west": { "uv": [ 5, 4, 10, 12 ], "rotation": 90, "texture": "#body" } + } + }, + { + "from": [ 3, 10, 0 ], + "to": [ 13, 16, 16 ], + "faces": { + "north": { "uv": [ 3, 0, 13, 6 ], "texture": "#body", "cullface": "north" }, + "east": { "uv": [ 16, 0, 10, 16 ], "rotation": 270, "texture": "#body" }, + "south": { "uv": [ 3, 0, 13, 6 ], "texture": "#body", "cullface": "south" }, + "west": { "uv": [ 10, 0, 16, 16 ], "rotation": 90, "texture": "#body" }, + "up": { "uv": [ 3, 0, 13, 16 ], "rotation": 180, "texture": "#top", "cullface": "up" }, + "down": { "uv": [ 3, 0, 13, 16 ], "rotation": 180, "texture": "#body" } + } + } + ], + "display": { + "fixed": { + "rotation": [ 0, 90, 0 ], + "scale": [ 0.5, 0.5, 0.5 ] + } + } +} diff --git a/common/src/main/resources/assets/minecraft/models/block/template_cake_with_candle.json b/common/src/main/resources/assets/minecraft/models/block/template_cake_with_candle.json new file mode 100644 index 0000000000..6455c988c2 --- /dev/null +++ b/common/src/main/resources/assets/minecraft/models/block/template_cake_with_candle.json @@ -0,0 +1,51 @@ +{ + "textures": { + "top": "block/cake_top", + "particle": "block/cake_side", + "side": "block/cake_side", + "bottom": "block/cake_bottom" + }, + "elements": [ + { + "from": [ 1, 0, 1 ], + "to": [ 15, 8, 15 ], + "faces": { + "north": { "uv": [ 1, 8, 15, 16 ], "texture": "#side" }, + "east": { "uv": [ 1, 8, 15, 16 ], "texture": "#side" }, + "south": { "uv": [ 1, 8, 15, 16 ], "texture": "#side" }, + "west": { "uv": [ 1, 8, 15, 16 ], "texture": "#side" }, + "up": { "uv": [ 1, 1, 15, 15 ], "texture": "#top" }, + "down": { "uv": [ 1, 1, 15, 15 ], "texture": "#bottom", "cullface": "down" } + } + }, + { + "from": [ 7, 8, 7 ], + "to": [ 9, 14, 9 ], + "faces": { + "north": { "uv": [ 0, 8, 2, 14 ], "texture": "#candle" }, + "east": { "uv": [ 0, 8, 2, 14 ], "texture": "#candle" }, + "south": { "uv": [ 0, 8, 2, 14 ], "texture": "#candle" }, + "west": { "uv": [ 0, 8, 2, 14 ], "texture": "#candle" }, + "up": { "uv": [ 0, 6, 2, 8 ], "texture": "#candle" } + } + }, + { + "from": [ 7.5, 14, 8 ], + "to": [ 8.5, 15, 8 ], + "rotation": { "angle": -45, "axis": "y", "origin": [ 8, 14, 8 ] }, + "faces": { + "north": { "uv": [ 0, 5, 1, 6 ], "texture": "#candle" }, + "south": { "uv": [ 0, 5, 1, 6 ], "texture": "#candle" } + } + }, + { + "from": [ 7.5, 14, 8 ], + "to": [ 8.5, 15, 8 ], + "rotation": { "angle": 45, "axis": "y", "origin": [ 8, 14, 8 ] }, + "faces": { + "north": { "uv": [ 0, 5, 1, 6 ], "texture": "#candle" }, + "south": { "uv": [ 0, 5, 1, 6 ], "texture": "#candle" } + } + } + ] +} diff --git a/common/src/main/resources/assets/minecraft/models/block/template_cauldron_full.json b/common/src/main/resources/assets/minecraft/models/block/template_cauldron_full.json new file mode 100644 index 0000000000..8b090ec92a --- /dev/null +++ b/common/src/main/resources/assets/minecraft/models/block/template_cauldron_full.json @@ -0,0 +1,221 @@ +{ + "ambientocclusion": false, + "textures": { + "rim": "block/cauldron_top", + "particle": "block/cauldron_side", + "outside": "block/cauldron_side", + "inside": "block/cauldron_inner", + "feet": "block/cauldron_bottom" + }, + "elements": [ + { + "from": [ 0, 3, 0 ], + "to": [ 16, 16, 16 ], + "faces": { + "north": { "uv": [ 0, 0, 16, 13 ], "texture": "#outside", "cullface": "north" }, + "east": { "uv": [ 0, 0, 16, 13 ], "texture": "#outside", "cullface": "east" }, + "south": { "uv": [ 0, 0, 16, 13 ], "texture": "#outside", "cullface": "south" }, + "west": { "uv": [ 0, 0, 16, 13 ], "texture": "#outside", "cullface": "west" }, + "down": { "uv": [ 0, 0, 16, 16 ], "texture": "#inside" } + } + }, + { + "from": [ 0, 16, 0 ], + "to": [ 14, 16, 2 ], + "faces": { + "up": { "uv": [ 0, 0, 14, 2 ], "texture": "#rim", "cullface": "up" } + } + }, + { + "from": [ 14, 16, 0 ], + "to": [ 16, 16, 14 ], + "faces": { + "up": { "uv": [ 14, 0, 16, 14 ], "texture": "#rim", "cullface": "up" } + } + }, + { + "from": [ 2, 16, 14 ], + "to": [ 16, 16, 16 ], + "faces": { + "up": { "uv": [ 2, 14, 16, 16 ], "texture": "#rim", "cullface": "up" } + } + }, + { + "from": [ 0, 16, 2 ], + "to": [ 2, 16, 16 ], + "faces": { + "up": { "uv": [ 0, 2, 2, 16 ], "texture": "#rim", "cullface": "up" } + } + }, + { + "from": [ 2, 4, 2 ], + "to": [ 14, 16, 2 ], + "faces": { + "south": { "uv": [ 2, 0, 14, 12 ], "texture": "#outside" } + } + }, + { + "from": [ 14, 4, 2 ], + "to": [ 14, 16, 14 ], + "faces": { + "west": { "uv": [ 2, 0, 14, 12 ], "texture": "#outside" } + } + }, + { + "from": [ 2, 4, 14 ], + "to": [ 14, 16, 14 ], + "faces": { + "north": { "uv": [ 2, 0, 14, 12 ], "texture": "#outside" } + } + }, + { + "from": [ 2, 4, 2 ], + "to": [ 2, 16, 14 ], + "faces": { + "east": { "uv": [ 2, 0, 14, 12 ], "texture": "#outside" } + } + }, + { + "from": [ 2, 4, 2 ], + "to": [ 14, 4, 14 ], + "faces": { + "up": { "uv": [ 2, 2, 14, 14 ], "texture": "#inside" } + } + }, + { + "from": [ 0, 0, 0 ], + "to": [ 4, 3, 4 ], + "faces": { + "north": { "uv": [ 12, 13, 16, 16 ], "texture": "#outside", "cullface": "north" }, + "west": { "uv": [ 0, 13, 4, 16 ], "texture": "#outside", "cullface": "west" } + } + }, + { + "from": [ 0, 0, 12 ], + "to": [ 4, 3, 16 ], + "faces": { + "south": { "uv": [ 0, 13, 4, 16 ], "texture": "#outside", "cullface": "south" }, + "west": { "uv": [ 12, 13, 16, 16 ], "texture": "#outside", "cullface": "west" } + } + }, + { + "from": [ 12, 0, 12 ], + "to": [ 16, 3, 16 ], + "faces": { + "east": { "uv": [ 0, 13, 4, 16 ], "texture": "#outside", "cullface": "east" }, + "south": { "uv": [ 12, 13, 16, 16 ], "texture": "#outside", "cullface": "south" } + } + }, + { + "from": [ 12, 0, 0 ], + "to": [ 16, 3, 4 ], + "faces": { + "north": { "uv": [ 0, 13, 4, 16 ], "texture": "#outside", "cullface": "north" }, + "east": { "uv": [ 12, 13, 16, 16 ], "texture": "#outside", "cullface": "east" } + } + }, + { + "from": [ 14, 0, 2 ], + "to": [ 16, 3, 4 ], + "faces": { + "south": { "uv": [ 14, 13, 16, 16 ], "texture": "#outside" }, + "west": { "uv": [ 2, 13, 4, 16 ], "texture": "#outside" }, + "down": { "uv": [ 14, 12, 16, 14 ], "texture": "#feet", "cullface": "down" } + } + }, + { + "from": [ 2, 0, 0 ], + "to": [ 4, 3, 2 ], + "faces": { + "east": { "uv": [ 14, 13, 16, 16 ], "texture": "#outside" }, + "south": { "uv": [ 2, 13, 4, 16 ], "texture": "#outside" }, + "down": { "uv": [ 2, 14, 4, 16 ], "texture": "#feet", "cullface": "down" } + } + }, + { + "from": [ 0, 0, 12 ], + "to": [ 2, 3, 14 ], + "faces": { + "north": { "uv": [ 14, 13, 16, 16 ], "texture": "#outside" }, + "east": { "uv": [ 2, 13, 4, 16 ], "texture": "#outside" }, + "down": { "uv": [ 0, 2, 2, 4 ], "texture": "#feet", "cullface": "down" } + } + }, + { + "from": [ 12, 0, 14 ], + "to": [ 14, 3, 16 ], + "faces": { + "north": { "uv": [ 2, 13, 4, 16 ], "texture": "#outside" }, + "west": { "uv": [ 14, 13, 16, 16 ], "texture": "#outside" }, + "down": { "uv": [ 12, 0, 14, 2 ], "texture": "#feet", "cullface": "down" } + } + }, + { + "from": [ 12, 0, 0 ], + "to": [ 16, 0, 2 ], + "faces": { + "down": { "uv": [ 12, 14, 16, 16 ], "texture": "#feet", "cullface": "down" } + } + }, + { + "from": [ 0, 0, 0 ], + "to": [ 2, 0, 4 ], + "faces": { + "down": { "uv": [ 0, 12, 2, 16 ], "texture": "#feet", "cullface": "down" } + } + }, + { + "from": [ 0, 0, 14 ], + "to": [ 4, 0, 16 ], + "faces": { + "down": { "uv": [ 0, 0, 4, 2 ], "texture": "#feet", "cullface": "down" } + } + }, + { + "from": [ 14, 0, 12 ], + "to": [ 16, 0, 16 ], + "faces": { + "down": { "uv": [ 14, 0, 16, 4 ], "texture": "#feet", "cullface": "down" } + } + }, + { + "from": [ 12, 0, 0 ], + "to": [ 14, 3, 2 ], + "faces": { + "south": { "uv": [ 12, 13, 14, 16 ], "texture": "#outside" }, + "west": { "uv": [ 0, 13, 2, 16 ], "texture": "#outside" } + } + }, + { + "from": [ 0, 0, 2 ], + "to": [ 2, 3, 4 ], + "faces": { + "east": { "uv": [ 12, 13, 14, 16 ], "texture": "#outside" }, + "south": { "uv": [ 0, 13, 2, 16 ], "texture": "#outside" } + } + }, + { + "from": [ 2, 0, 14 ], + "to": [ 4, 3, 16 ], + "faces": { + "north": { "uv": [ 12, 13, 14, 16 ], "texture": "#outside" }, + "east": { "uv": [ 0, 13, 2, 16 ], "texture": "#outside" } + } + }, + { + "from": [ 14, 0, 12 ], + "to": [ 16, 3, 14 ], + "faces": { + "north": { "uv": [ 0, 13, 2, 16 ], "texture": "#outside" }, + "west": { "uv": [ 12, 13, 14, 16 ], "texture": "#outside" } + } + }, + { + "from": [ 2, 15, 2 ], + "to": [ 14, 15, 14 ], + "faces": { + "up": { "uv": [ 2, 2, 14, 14 ], "texture": "#content", "tintindex": 0 } + } + } + ] +} diff --git a/common/src/main/resources/assets/minecraft/models/block/template_cauldron_level1.json b/common/src/main/resources/assets/minecraft/models/block/template_cauldron_level1.json new file mode 100644 index 0000000000..afea17757e --- /dev/null +++ b/common/src/main/resources/assets/minecraft/models/block/template_cauldron_level1.json @@ -0,0 +1,221 @@ +{ + "ambientocclusion": false, + "textures": { + "rim": "block/cauldron_top", + "particle": "block/cauldron_side", + "outside": "block/cauldron_side", + "inside": "block/cauldron_inner", + "feet": "block/cauldron_bottom" + }, + "elements": [ + { + "from": [ 0, 3, 0 ], + "to": [ 16, 16, 16 ], + "faces": { + "north": { "uv": [ 0, 0, 16, 13 ], "texture": "#outside", "cullface": "north" }, + "east": { "uv": [ 0, 0, 16, 13 ], "texture": "#outside", "cullface": "east" }, + "south": { "uv": [ 0, 0, 16, 13 ], "texture": "#outside", "cullface": "south" }, + "west": { "uv": [ 0, 0, 16, 13 ], "texture": "#outside", "cullface": "west" }, + "down": { "uv": [ 0, 0, 16, 16 ], "texture": "#inside" } + } + }, + { + "from": [ 0, 16, 0 ], + "to": [ 14, 16, 2 ], + "faces": { + "up": { "uv": [ 0, 0, 14, 2 ], "texture": "#rim", "cullface": "up" } + } + }, + { + "from": [ 14, 16, 0 ], + "to": [ 16, 16, 14 ], + "faces": { + "up": { "uv": [ 14, 0, 16, 14 ], "texture": "#rim", "cullface": "up" } + } + }, + { + "from": [ 2, 16, 14 ], + "to": [ 16, 16, 16 ], + "faces": { + "up": { "uv": [ 2, 14, 16, 16 ], "texture": "#rim", "cullface": "up" } + } + }, + { + "from": [ 0, 16, 2 ], + "to": [ 2, 16, 16 ], + "faces": { + "up": { "uv": [ 0, 2, 2, 16 ], "texture": "#rim", "cullface": "up" } + } + }, + { + "from": [ 2, 4, 2 ], + "to": [ 14, 16, 2 ], + "faces": { + "south": { "uv": [ 2, 0, 14, 12 ], "texture": "#outside" } + } + }, + { + "from": [ 14, 4, 2 ], + "to": [ 14, 16, 14 ], + "faces": { + "west": { "uv": [ 2, 0, 14, 12 ], "texture": "#outside" } + } + }, + { + "from": [ 2, 4, 14 ], + "to": [ 14, 16, 14 ], + "faces": { + "north": { "uv": [ 2, 0, 14, 12 ], "texture": "#outside" } + } + }, + { + "from": [ 2, 4, 2 ], + "to": [ 2, 16, 14 ], + "faces": { + "east": { "uv": [ 2, 0, 14, 12 ], "texture": "#outside" } + } + }, + { + "from": [ 2, 4, 2 ], + "to": [ 14, 4, 14 ], + "faces": { + "up": { "uv": [ 2, 2, 14, 14 ], "texture": "#inside" } + } + }, + { + "from": [ 0, 0, 0 ], + "to": [ 4, 3, 4 ], + "faces": { + "north": { "uv": [ 12, 13, 16, 16 ], "texture": "#outside", "cullface": "north" }, + "west": { "uv": [ 0, 13, 4, 16 ], "texture": "#outside", "cullface": "west" } + } + }, + { + "from": [ 0, 0, 12 ], + "to": [ 4, 3, 16 ], + "faces": { + "south": { "uv": [ 0, 13, 4, 16 ], "texture": "#outside", "cullface": "south" }, + "west": { "uv": [ 12, 13, 16, 16 ], "texture": "#outside", "cullface": "west" } + } + }, + { + "from": [ 12, 0, 12 ], + "to": [ 16, 3, 16 ], + "faces": { + "east": { "uv": [ 0, 13, 4, 16 ], "texture": "#outside", "cullface": "east" }, + "south": { "uv": [ 12, 13, 16, 16 ], "texture": "#outside", "cullface": "south" } + } + }, + { + "from": [ 12, 0, 0 ], + "to": [ 16, 3, 4 ], + "faces": { + "north": { "uv": [ 0, 13, 4, 16 ], "texture": "#outside", "cullface": "north" }, + "east": { "uv": [ 12, 13, 16, 16 ], "texture": "#outside", "cullface": "east" } + } + }, + { + "from": [ 14, 0, 2 ], + "to": [ 16, 3, 4 ], + "faces": { + "south": { "uv": [ 14, 13, 16, 16 ], "texture": "#outside" }, + "west": { "uv": [ 2, 13, 4, 16 ], "texture": "#outside" }, + "down": { "uv": [ 14, 12, 16, 14 ], "texture": "#feet", "cullface": "down" } + } + }, + { + "from": [ 2, 0, 0 ], + "to": [ 4, 3, 2 ], + "faces": { + "east": { "uv": [ 14, 13, 16, 16 ], "texture": "#outside" }, + "south": { "uv": [ 2, 13, 4, 16 ], "texture": "#outside" }, + "down": { "uv": [ 2, 14, 4, 16 ], "texture": "#feet", "cullface": "down" } + } + }, + { + "from": [ 0, 0, 12 ], + "to": [ 2, 3, 14 ], + "faces": { + "north": { "uv": [ 14, 13, 16, 16 ], "texture": "#outside" }, + "east": { "uv": [ 2, 13, 4, 16 ], "texture": "#outside" }, + "down": { "uv": [ 0, 2, 2, 4 ], "texture": "#feet", "cullface": "down" } + } + }, + { + "from": [ 12, 0, 14 ], + "to": [ 14, 3, 16 ], + "faces": { + "north": { "uv": [ 2, 13, 4, 16 ], "texture": "#outside" }, + "west": { "uv": [ 14, 13, 16, 16 ], "texture": "#outside" }, + "down": { "uv": [ 12, 0, 14, 2 ], "texture": "#feet", "cullface": "down" } + } + }, + { + "from": [ 12, 0, 0 ], + "to": [ 16, 0, 2 ], + "faces": { + "down": { "uv": [ 12, 14, 16, 16 ], "texture": "#feet", "cullface": "down" } + } + }, + { + "from": [ 0, 0, 0 ], + "to": [ 2, 0, 4 ], + "faces": { + "down": { "uv": [ 0, 12, 2, 16 ], "texture": "#feet", "cullface": "down" } + } + }, + { + "from": [ 0, 0, 14 ], + "to": [ 4, 0, 16 ], + "faces": { + "down": { "uv": [ 0, 0, 4, 2 ], "texture": "#feet", "cullface": "down" } + } + }, + { + "from": [ 14, 0, 12 ], + "to": [ 16, 0, 16 ], + "faces": { + "down": { "uv": [ 14, 0, 16, 4 ], "texture": "#feet", "cullface": "down" } + } + }, + { + "from": [ 12, 0, 0 ], + "to": [ 14, 3, 2 ], + "faces": { + "south": { "uv": [ 12, 13, 14, 16 ], "texture": "#outside" }, + "west": { "uv": [ 0, 13, 2, 16 ], "texture": "#outside" } + } + }, + { + "from": [ 0, 0, 2 ], + "to": [ 2, 3, 4 ], + "faces": { + "east": { "uv": [ 12, 13, 14, 16 ], "texture": "#outside" }, + "south": { "uv": [ 0, 13, 2, 16 ], "texture": "#outside" } + } + }, + { + "from": [ 2, 0, 14 ], + "to": [ 4, 3, 16 ], + "faces": { + "north": { "uv": [ 12, 13, 14, 16 ], "texture": "#outside" }, + "east": { "uv": [ 0, 13, 2, 16 ], "texture": "#outside" } + } + }, + { + "from": [ 14, 0, 12 ], + "to": [ 16, 3, 14 ], + "faces": { + "north": { "uv": [ 0, 13, 2, 16 ], "texture": "#outside" }, + "west": { "uv": [ 12, 13, 14, 16 ], "texture": "#outside" } + } + }, + { + "from": [ 2, 9, 2 ], + "to": [ 14, 9, 14 ], + "faces": { + "up": { "uv": [ 2, 2, 14, 14 ], "texture": "#content", "tintindex": 0 } + } + } + ] +} diff --git a/common/src/main/resources/assets/minecraft/models/block/template_cauldron_level2.json b/common/src/main/resources/assets/minecraft/models/block/template_cauldron_level2.json new file mode 100644 index 0000000000..63ee8c7596 --- /dev/null +++ b/common/src/main/resources/assets/minecraft/models/block/template_cauldron_level2.json @@ -0,0 +1,221 @@ +{ + "ambientocclusion": false, + "textures": { + "rim": "block/cauldron_top", + "particle": "block/cauldron_side", + "outside": "block/cauldron_side", + "inside": "block/cauldron_inner", + "feet": "block/cauldron_bottom" + }, + "elements": [ + { + "from": [ 0, 3, 0 ], + "to": [ 16, 16, 16 ], + "faces": { + "north": { "uv": [ 0, 0, 16, 13 ], "texture": "#outside", "cullface": "north" }, + "east": { "uv": [ 0, 0, 16, 13 ], "texture": "#outside", "cullface": "east" }, + "south": { "uv": [ 0, 0, 16, 13 ], "texture": "#outside", "cullface": "south" }, + "west": { "uv": [ 0, 0, 16, 13 ], "texture": "#outside", "cullface": "west" }, + "down": { "uv": [ 0, 0, 16, 16 ], "texture": "#inside" } + } + }, + { + "from": [ 0, 16, 0 ], + "to": [ 14, 16, 2 ], + "faces": { + "up": { "uv": [ 0, 0, 14, 2 ], "texture": "#rim", "cullface": "up" } + } + }, + { + "from": [ 14, 16, 0 ], + "to": [ 16, 16, 14 ], + "faces": { + "up": { "uv": [ 14, 0, 16, 14 ], "texture": "#rim", "cullface": "up" } + } + }, + { + "from": [ 2, 16, 14 ], + "to": [ 16, 16, 16 ], + "faces": { + "up": { "uv": [ 2, 14, 16, 16 ], "texture": "#rim", "cullface": "up" } + } + }, + { + "from": [ 0, 16, 2 ], + "to": [ 2, 16, 16 ], + "faces": { + "up": { "uv": [ 0, 2, 2, 16 ], "texture": "#rim", "cullface": "up" } + } + }, + { + "from": [ 2, 4, 2 ], + "to": [ 14, 16, 2 ], + "faces": { + "south": { "uv": [ 2, 0, 14, 12 ], "texture": "#outside" } + } + }, + { + "from": [ 14, 4, 2 ], + "to": [ 14, 16, 14 ], + "faces": { + "west": { "uv": [ 2, 0, 14, 12 ], "texture": "#outside" } + } + }, + { + "from": [ 2, 4, 14 ], + "to": [ 14, 16, 14 ], + "faces": { + "north": { "uv": [ 2, 0, 14, 12 ], "texture": "#outside" } + } + }, + { + "from": [ 2, 4, 2 ], + "to": [ 2, 16, 14 ], + "faces": { + "east": { "uv": [ 2, 0, 14, 12 ], "texture": "#outside" } + } + }, + { + "from": [ 2, 4, 2 ], + "to": [ 14, 4, 14 ], + "faces": { + "up": { "uv": [ 2, 2, 14, 14 ], "texture": "#inside" } + } + }, + { + "from": [ 0, 0, 0 ], + "to": [ 4, 3, 4 ], + "faces": { + "north": { "uv": [ 12, 13, 16, 16 ], "texture": "#outside", "cullface": "north" }, + "west": { "uv": [ 0, 13, 4, 16 ], "texture": "#outside", "cullface": "west" } + } + }, + { + "from": [ 0, 0, 12 ], + "to": [ 4, 3, 16 ], + "faces": { + "south": { "uv": [ 0, 13, 4, 16 ], "texture": "#outside", "cullface": "south" }, + "west": { "uv": [ 12, 13, 16, 16 ], "texture": "#outside", "cullface": "west" } + } + }, + { + "from": [ 12, 0, 12 ], + "to": [ 16, 3, 16 ], + "faces": { + "east": { "uv": [ 0, 13, 4, 16 ], "texture": "#outside", "cullface": "east" }, + "south": { "uv": [ 12, 13, 16, 16 ], "texture": "#outside", "cullface": "south" } + } + }, + { + "from": [ 12, 0, 0 ], + "to": [ 16, 3, 4 ], + "faces": { + "north": { "uv": [ 0, 13, 4, 16 ], "texture": "#outside", "cullface": "north" }, + "east": { "uv": [ 12, 13, 16, 16 ], "texture": "#outside", "cullface": "east" } + } + }, + { + "from": [ 14, 0, 2 ], + "to": [ 16, 3, 4 ], + "faces": { + "south": { "uv": [ 14, 13, 16, 16 ], "texture": "#outside" }, + "west": { "uv": [ 2, 13, 4, 16 ], "texture": "#outside" }, + "down": { "uv": [ 14, 12, 16, 14 ], "texture": "#feet", "cullface": "down" } + } + }, + { + "from": [ 2, 0, 0 ], + "to": [ 4, 3, 2 ], + "faces": { + "east": { "uv": [ 14, 13, 16, 16 ], "texture": "#outside" }, + "south": { "uv": [ 2, 13, 4, 16 ], "texture": "#outside" }, + "down": { "uv": [ 2, 14, 4, 16 ], "texture": "#feet", "cullface": "down" } + } + }, + { + "from": [ 0, 0, 12 ], + "to": [ 2, 3, 14 ], + "faces": { + "north": { "uv": [ 14, 13, 16, 16 ], "texture": "#outside" }, + "east": { "uv": [ 2, 13, 4, 16 ], "texture": "#outside" }, + "down": { "uv": [ 0, 2, 2, 4 ], "texture": "#feet", "cullface": "down" } + } + }, + { + "from": [ 12, 0, 14 ], + "to": [ 14, 3, 16 ], + "faces": { + "north": { "uv": [ 2, 13, 4, 16 ], "texture": "#outside" }, + "west": { "uv": [ 14, 13, 16, 16 ], "texture": "#outside" }, + "down": { "uv": [ 12, 0, 14, 2 ], "texture": "#feet", "cullface": "down" } + } + }, + { + "from": [ 12, 0, 0 ], + "to": [ 16, 0, 2 ], + "faces": { + "down": { "uv": [ 12, 14, 16, 16 ], "texture": "#feet", "cullface": "down" } + } + }, + { + "from": [ 0, 0, 0 ], + "to": [ 2, 0, 4 ], + "faces": { + "down": { "uv": [ 0, 12, 2, 16 ], "texture": "#feet", "cullface": "down" } + } + }, + { + "from": [ 0, 0, 14 ], + "to": [ 4, 0, 16 ], + "faces": { + "down": { "uv": [ 0, 0, 4, 2 ], "texture": "#feet", "cullface": "down" } + } + }, + { + "from": [ 14, 0, 12 ], + "to": [ 16, 0, 16 ], + "faces": { + "down": { "uv": [ 14, 0, 16, 4 ], "texture": "#feet", "cullface": "down" } + } + }, + { + "from": [ 12, 0, 0 ], + "to": [ 14, 3, 2 ], + "faces": { + "south": { "uv": [ 12, 13, 14, 16 ], "texture": "#outside" }, + "west": { "uv": [ 0, 13, 2, 16 ], "texture": "#outside" } + } + }, + { + "from": [ 0, 0, 2 ], + "to": [ 2, 3, 4 ], + "faces": { + "east": { "uv": [ 12, 13, 14, 16 ], "texture": "#outside" }, + "south": { "uv": [ 0, 13, 2, 16 ], "texture": "#outside" } + } + }, + { + "from": [ 2, 0, 14 ], + "to": [ 4, 3, 16 ], + "faces": { + "north": { "uv": [ 12, 13, 14, 16 ], "texture": "#outside" }, + "east": { "uv": [ 0, 13, 2, 16 ], "texture": "#outside" } + } + }, + { + "from": [ 14, 0, 12 ], + "to": [ 16, 3, 14 ], + "faces": { + "north": { "uv": [ 0, 13, 2, 16 ], "texture": "#outside" }, + "west": { "uv": [ 12, 13, 14, 16 ], "texture": "#outside" } + } + }, + { + "from": [ 2, 12, 2 ], + "to": [ 14, 12, 14 ], + "faces": { + "up": { "uv": [ 2, 2, 14, 14 ], "texture": "#content", "tintindex": 0 } + } + } + ] +} diff --git a/common/src/main/resources/assets/minecraft/models/block/template_chorus_flower.json b/common/src/main/resources/assets/minecraft/models/block/template_chorus_flower.json new file mode 100644 index 0000000000..f660d1095c --- /dev/null +++ b/common/src/main/resources/assets/minecraft/models/block/template_chorus_flower.json @@ -0,0 +1,46 @@ +{ + "parent": "minecraft:block/block", + "textures": { + "bottom": "block/chorus_plant", + "particle": "block/chorus_flower", + "texture": "block/chorus_flower" + }, + "elements": [ + { + "from": [ 0, 2, 2 ], + "to": [ 16, 14, 14 ], + "faces": { + "north": { "uv": [ 0, 2, 16, 14 ], "texture": "#bottom" }, + "east": { "uv": [ 2, 2, 14, 14 ], "texture": "#texture", "cullface": "east" }, + "south": { "uv": [ 0, 2, 16, 14 ], "texture": "#bottom" }, + "west": { "uv": [ 2, 2, 14, 14 ], "texture": "#texture", "cullface": "west" }, + "up": { "uv": [ 0, 2, 16, 14 ], "texture": "#bottom" }, + "down": { "uv": [ 0, 2, 16, 14 ], "texture": "#bottom" } + } + }, + { + "from": [ 2, 0, 2 ], + "to": [ 14, 16, 14 ], + "faces": { + "north": { "uv": [ 2, 0, 14, 16 ], "texture": "#bottom" }, + "east": { "uv": [ 2, 0, 14, 16 ], "texture": "#bottom" }, + "south": { "uv": [ 2, 0, 14, 16 ], "texture": "#bottom" }, + "west": { "uv": [ 2, 0, 14, 16 ], "texture": "#bottom" }, + "up": { "uv": [ 2, 2, 14, 14 ], "texture": "#texture", "cullface": "up" }, + "down": { "uv": [ 2, 2, 14, 14 ], "texture": "#bottom", "cullface": "down" } + } + }, + { + "from": [ 2, 2, 0 ], + "to": [ 14, 14, 16 ], + "faces": { + "north": { "uv": [ 2, 2, 14, 14 ], "texture": "#texture", "cullface": "north" }, + "east": { "uv": [ 0, 2, 16, 14 ], "texture": "#bottom" }, + "south": { "uv": [ 2, 2, 14, 14 ], "texture": "#texture", "cullface": "south" }, + "west": { "uv": [ 0, 2, 16, 14 ], "texture": "#bottom" }, + "up": { "uv": [ 2, 0, 14, 16 ], "texture": "#bottom" }, + "down": { "uv": [ 2, 0, 14, 16 ], "texture": "#bottom" } + } + } + ] +} diff --git a/common/src/main/resources/assets/minecraft/models/block/template_four_turtle_eggs.json b/common/src/main/resources/assets/minecraft/models/block/template_four_turtle_eggs.json new file mode 100644 index 0000000000..342ee8ef54 --- /dev/null +++ b/common/src/main/resources/assets/minecraft/models/block/template_four_turtle_eggs.json @@ -0,0 +1,57 @@ +{ + "parent": "minecraft:block/block", + "textures": { + "particle": "block/turtle_egg", + "all": "block/turtle_egg" + }, + "elements": [ + { + "from": [ 5, 0, 4 ], + "to": [ 9, 7, 8 ], + "faces": { + "north": { "uv": [ 1, 4, 5, 11 ], "texture": "#all" }, + "east": { "uv": [ 1, 4, 5, 11 ], "texture": "#all" }, + "south": { "uv": [ 1, 4, 5, 11 ], "texture": "#all" }, + "west": { "uv": [ 1, 4, 5, 11 ], "texture": "#all" }, + "up": { "uv": [ 0, 0, 4, 4 ], "texture": "#all" }, + "down": { "uv": [ 0, 0, 4, 4 ], "texture": "#all", "cullface": "down" } + } + }, + { + "from": [ 1, 0, 7 ], + "to": [ 5, 5, 11 ], + "faces": { + "north": { "uv": [ 10, 10, 14, 15 ], "texture": "#all" }, + "east": { "uv": [ 10, 10, 14, 15 ], "texture": "#all" }, + "south": { "uv": [ 10, 10, 14, 15 ], "texture": "#all" }, + "west": { "uv": [ 10, 10, 14, 15 ], "texture": "#all" }, + "up": { "uv": [ 6, 7, 10, 11 ], "texture": "#all" }, + "down": { "uv": [ 6, 7, 10, 11 ], "texture": "#all", "cullface": "down" } + } + }, + { + "from": [ 11, 0, 7 ], + "to": [ 14, 4, 10 ], + "faces": { + "north": { "uv": [ 8, 3, 11, 7 ], "texture": "#all" }, + "east": { "uv": [ 8, 3, 11, 7 ], "texture": "#all" }, + "south": { "uv": [ 8, 3, 11, 7 ], "texture": "#all" }, + "west": { "uv": [ 8, 3, 11, 7 ], "texture": "#all" }, + "up": { "uv": [ 5, 0, 8, 3 ], "texture": "#all" }, + "down": { "uv": [ 5, 0, 8, 3 ], "texture": "#all", "cullface": "down" } + } + }, + { + "from": [ 6, 0, 9 ], + "to": [ 10, 4, 13 ], + "faces": { + "north": { "uv": [ 4, 11, 8, 15 ], "texture": "#all" }, + "east": { "uv": [ 4, 11, 8, 15 ], "texture": "#all" }, + "south": { "uv": [ 4, 11, 8, 15 ], "texture": "#all" }, + "west": { "uv": [ 4, 11, 8, 15 ], "texture": "#all" }, + "up": { "uv": [ 0, 11, 4, 15 ], "texture": "#all" }, + "down": { "uv": [ 0, 11, 4, 15 ], "texture": "#all", "cullface": "down" } + } + } + ] +} diff --git a/common/src/main/resources/assets/minecraft/models/block/template_hanging_lantern.json b/common/src/main/resources/assets/minecraft/models/block/template_hanging_lantern.json new file mode 100644 index 0000000000..2b85e35f7b --- /dev/null +++ b/common/src/main/resources/assets/minecraft/models/block/template_hanging_lantern.json @@ -0,0 +1,51 @@ +{ + "parent": "minecraft:block/block", + "textures": { + "particle": "#lantern" + }, + "elements": [ + { + "from": [ 5, 1, 5 ], + "to": [ 11, 8, 11 ], + "faces": { + "north": { "uv": [ 0, 2, 6, 9 ], "texture": "#lantern" }, + "east": { "uv": [ 0, 2, 6, 9 ], "texture": "#lantern" }, + "south": { "uv": [ 0, 2, 6, 9 ], "texture": "#lantern" }, + "west": { "uv": [ 0, 2, 6, 9 ], "texture": "#lantern" }, + "up": { "uv": [ 0, 9, 6, 15 ], "texture": "#lantern" }, + "down": { "uv": [ 0, 9, 6, 15 ], "texture": "#lantern" } + } + }, + { + "from": [ 6, 8, 6 ], + "to": [ 10, 10, 10 ], + "faces": { + "north": { "uv": [ 1, 0, 5, 2 ], "texture": "#lantern" }, + "east": { "uv": [ 1, 0, 5, 2 ], "texture": "#lantern" }, + "south": { "uv": [ 1, 0, 5, 2 ], "texture": "#lantern" }, + "west": { "uv": [ 1, 0, 5, 2 ], "texture": "#lantern" }, + "up": { "uv": [ 1, 10, 5, 14 ], "texture": "#lantern" } + } + }, + { + "from": [ 6.5, 11, 8 ], + "to": [ 9.5, 15, 8 ], + "shade": false, + "rotation": { "angle": 45, "axis": "y", "origin": [ 8, 8, 8 ] }, + "faces": { + "north": { "uv": [ 14, 1, 11, 5 ], "texture": "#lantern" }, + "south": { "uv": [ 11, 1, 14, 5 ], "texture": "#lantern" } + } + }, + { + "from": [ 8, 10, 6.5 ], + "to": [ 8, 16, 9.5 ], + "shade": false, + "rotation": { "angle": 45, "axis": "y", "origin": [ 8, 8, 8 ] }, + "faces": { + "east": { "uv": [ 11, 6, 14, 12 ], "texture": "#lantern" }, + "west": { "uv": [ 14, 6, 11, 12 ], "texture": "#lantern" } + } + } + ] +} diff --git a/common/src/main/resources/assets/minecraft/models/block/template_item_frame.json b/common/src/main/resources/assets/minecraft/models/block/template_item_frame.json new file mode 100644 index 0000000000..a8fe9937f1 --- /dev/null +++ b/common/src/main/resources/assets/minecraft/models/block/template_item_frame.json @@ -0,0 +1,85 @@ +{ + "textures": { + "particle": "#back" + }, + "elements": [ + { + "from": [ 3, 3, 15.5 ], + "to": [ 13, 13, 16 ], + "faces": { + "north": { "uv": [ 3, 3, 13, 13 ], "texture": "#back" }, + "south": { "uv": [ 3, 3, 13, 13 ], "texture": "#back", "cullface": "south" } + } + }, + { + "from": [ 2, 2, 15 ], + "to": [ 14, 14, 16 ], + "faces": { + "east": { "uv": [ 0, 2, 1, 14 ], "texture": "#wood" }, + "west": { "uv": [ 15, 2, 16, 14 ], "texture": "#wood" }, + "up": { "uv": [ 2, 15, 14, 16 ], "texture": "#wood" }, + "down": { "uv": [ 2, 0, 14, 1 ], "texture": "#wood" } + } + }, + { + "from": [ 2, 13, 15 ], + "to": [ 13, 14, 16 ], + "faces": { + "north": { "uv": [ 3, 2, 14, 3 ], "texture": "#wood" }, + "south": { "uv": [ 2, 2, 13, 3 ], "texture": "#wood", "cullface": "south" } + } + }, + { + "from": [ 13, 3, 15 ], + "to": [ 14, 14, 16 ], + "faces": { + "north": { "uv": [ 2, 2, 3, 13 ], "texture": "#wood" }, + "south": { "uv": [ 13, 2, 14, 13 ], "texture": "#wood", "cullface": "south" } + } + }, + { + "from": [ 3, 2, 15 ], + "to": [ 14, 3, 16 ], + "faces": { + "north": { "uv": [ 2, 13, 13, 14 ], "texture": "#wood" }, + "south": { "uv": [ 3, 13, 14, 14 ], "texture": "#wood", "cullface": "south" } + } + }, + { + "from": [ 2, 2, 15 ], + "to": [ 3, 13, 16 ], + "faces": { + "north": { "uv": [ 13, 3, 14, 14 ], "texture": "#wood" }, + "south": { "uv": [ 2, 3, 3, 14 ], "texture": "#wood", "cullface": "south" } + } + }, + { + "from": [ 3, 13, 15 ], + "to": [ 13, 14, 15.5 ], + "faces": { + "down": { "uv": [ 3, 0.5, 13, 1 ], "texture": "#wood" } + } + }, + { + "from": [ 13, 3, 15 ], + "to": [ 14, 13, 15.5 ], + "faces": { + "west": { "uv": [ 15.5, 3, 16, 13 ], "texture": "#wood" } + } + }, + { + "from": [ 3, 2, 15 ], + "to": [ 13, 3, 15.5 ], + "faces": { + "up": { "uv": [ 3, 15.5, 13, 16 ], "texture": "#wood" } + } + }, + { + "from": [ 2, 3, 15 ], + "to": [ 3, 13, 15.5 ], + "faces": { + "east": { "uv": [ 0.5, 3, 1, 13 ], "texture": "#wood" } + } + } + ] +} diff --git a/common/src/main/resources/assets/minecraft/models/block/template_item_frame_map.json b/common/src/main/resources/assets/minecraft/models/block/template_item_frame_map.json new file mode 100644 index 0000000000..59220078c5 --- /dev/null +++ b/common/src/main/resources/assets/minecraft/models/block/template_item_frame_map.json @@ -0,0 +1,57 @@ +{ + "textures": { + "particle": "#back" + }, + "elements": [ + { + "from": [ 1, 1, 15 ], + "to": [ 15, 15, 16 ], + "faces": { + "north": { "uv": [ 1, 1, 15, 15 ], "texture": "#back" }, + "south": { "uv": [ 1, 1, 15, 15 ], "texture": "#back", "cullface": "south" } + } + }, + { + "from": [ 0, 0, 15 ], + "to": [ 16, 16, 16 ], + "faces": { + "east": { "uv": [ 0, 0, 1, 16 ], "texture": "#wood", "cullface": "east" }, + "west": { "uv": [ 15, 0, 16, 16 ], "texture": "#wood", "cullface": "west" }, + "up": { "uv": [ 0, 15, 16, 16 ], "texture": "#wood", "cullface": "up" }, + "down": { "uv": [ 0, 0, 16, 1 ], "texture": "#wood", "cullface": "down" } + } + }, + { + "from": [ 0, 15, 15 ], + "to": [ 15, 16, 16 ], + "faces": { + "north": { "uv": [ 1, 0, 16, 1 ], "texture": "#wood" }, + "south": { "uv": [ 0, 0, 15, 1 ], "texture": "#wood", "cullface": "south" } + } + }, + { + "from": [ 15, 1, 15 ], + "to": [ 16, 16, 16 ], + "faces": { + "north": { "uv": [ 0, 0, 1, 15 ], "texture": "#wood" }, + "south": { "uv": [ 15, 0, 16, 15 ], "texture": "#wood", "cullface": "south" } + } + }, + { + "from": [ 1, 0, 15 ], + "to": [ 16, 1, 16 ], + "faces": { + "north": { "uv": [ 0, 15, 15, 16 ], "texture": "#wood" }, + "south": { "uv": [ 1, 15, 16, 16 ], "texture": "#wood", "cullface": "south" } + } + }, + { + "from": [ 0, 0, 15 ], + "to": [ 1, 15, 16 ], + "faces": { + "north": { "uv": [ 15, 1, 16, 16 ], "texture": "#wood" }, + "south": { "uv": [ 0, 1, 1, 16 ], "texture": "#wood", "cullface": "south" } + } + } + ] +} diff --git a/common/src/main/resources/assets/minecraft/models/block/template_potted_azalea_bush.json b/common/src/main/resources/assets/minecraft/models/block/template_potted_azalea_bush.json new file mode 100644 index 0000000000..684de6b1a4 --- /dev/null +++ b/common/src/main/resources/assets/minecraft/models/block/template_potted_azalea_bush.json @@ -0,0 +1,146 @@ +{ + "ambientocclusion": false, + "textures": { + "particle": "block/flower_pot", + "flowerpot": "block/flower_pot", + "dirt": "block/dirt" + }, + "elements": [ + { + "from": [ 5, 0, 5 ], + "to": [ 11, 6, 11 ], + "faces": { + "north": { "uv": [ 5, 10, 11, 16 ], "texture": "#flowerpot" }, + "east": { "uv": [ 5, 10, 11, 16 ], "texture": "#flowerpot" }, + "south": { "uv": [ 5, 10, 11, 16 ], "texture": "#flowerpot" }, + "west": { "uv": [ 5, 10, 11, 16 ], "texture": "#flowerpot" } + } + }, + { + "from": [ 5, 0, 5 ], + "to": [ 10, 6, 6 ], + "faces": { + "up": { "uv": [ 5, 5, 10, 6 ], "texture": "#flowerpot" }, + "down": { "uv": [ 5, 10, 10, 11 ], "texture": "#flowerpot", "cullface": "down" } + } + }, + { + "from": [ 10, 0, 5 ], + "to": [ 11, 6, 10 ], + "faces": { + "up": { "uv": [ 10, 5, 11, 10 ], "texture": "#flowerpot" }, + "down": { "uv": [ 10, 6, 11, 11 ], "texture": "#flowerpot", "cullface": "down" } + } + }, + { + "from": [ 6, 0, 10 ], + "to": [ 11, 6, 11 ], + "faces": { + "up": { "uv": [ 6, 10, 11, 11 ], "texture": "#flowerpot" }, + "down": { "uv": [ 6, 5, 11, 6 ], "texture": "#flowerpot", "cullface": "down" } + } + }, + { + "from": [ 5, 0, 6 ], + "to": [ 6, 6, 11 ], + "faces": { + "up": { "uv": [ 5, 6, 6, 11 ], "texture": "#flowerpot" }, + "down": { "uv": [ 5, 5, 6, 10 ], "texture": "#flowerpot", "cullface": "down" } + } + }, + { + "from": [ 6, 4, 6 ], + "to": [ 10, 6, 6 ], + "faces": { + "south": { "uv": [ 6, 10, 10, 12 ], "texture": "#flowerpot" } + } + }, + { + "from": [ 10, 4, 6 ], + "to": [ 10, 6, 10 ], + "faces": { + "west": { "uv": [ 6, 10, 10, 12 ], "texture": "#flowerpot" } + } + }, + { + "from": [ 6, 4, 10 ], + "to": [ 10, 6, 10 ], + "faces": { + "north": { "uv": [ 6, 10, 10, 12 ], "texture": "#flowerpot" } + } + }, + { + "from": [ 6, 4, 6 ], + "to": [ 6, 6, 10 ], + "faces": { + "east": { "uv": [ 6, 10, 10, 12 ], "texture": "#flowerpot" } + } + }, + { + "from": [ 6, 0, 6 ], + "to": [ 10, 4, 10 ], + "faces": { + "up": { "uv": [ 6, 6, 10, 10 ], "texture": "#dirt" }, + "down": { "uv": [ 6, 12, 10, 16 ], "texture": "#flowerpot", "cullface": "down" } + } + }, + { + "from": [ 4, 15.9, 4 ], + "to": [ 12, 16, 12 ], + "faces": { + "up": { "uv": [ 4, 4, 12, 12 ], "texture": "#top", "cullface": "up" }, + "down": { "uv": [ 4, 12, 12, 4 ], "texture": "#top" } + } + }, + { + "from": [ 4, 8, 4 ], + "to": [ 12, 16, 4 ], + "faces": { + "north": { "uv": [ 4, 5, 12, 13 ], "texture": "#side" }, + "south": { "uv": [ 12, 5, 4, 13 ], "texture": "#side" } + } + }, + { + "from": [ 4, 8, 12 ], + "to": [ 12, 16, 12 ], + "faces": { + "north": { "uv": [ 12, 5, 4, 13 ], "texture": "#side" }, + "south": { "uv": [ 4, 5, 12, 13 ], "texture": "#side" } + } + }, + { + "from": [ 4, 8, 4 ], + "to": [ 4, 16, 12 ], + "faces": { + "east": { "uv": [ 12, 5, 4, 13 ], "texture": "#side" }, + "west": { "uv": [ 4, 5, 12, 13 ], "texture": "#side" } + } + }, + { + "from": [ 12, 8, 4 ], + "to": [ 12, 16, 12 ], + "faces": { + "east": { "uv": [ 4, 5, 12, 13 ], "texture": "#side" }, + "west": { "uv": [ 12, 5, 4, 13 ], "texture": "#side" } + } + }, + { + "from": [ 2.6, 4, 8 ], + "to": [ 13.4, 16, 8 ], + "rotation": { "angle": 45, "axis": "y", "origin": [ 8, 8, 8 ], "rescale": true }, + "faces": { + "north": { "uv": [ 0, 4, 16, 16 ], "texture": "#plant" }, + "south": { "uv": [ 0, 4, 16, 16 ], "texture": "#plant" } + } + }, + { + "from": [ 8, 4, 2.6 ], + "to": [ 8, 16, 13.4 ], + "rotation": { "angle": 45, "axis": "y", "origin": [ 8, 8, 8 ], "rescale": true }, + "faces": { + "east": { "uv": [ 0, 4, 16, 16 ], "texture": "#plant" }, + "west": { "uv": [ 0, 4, 16, 16 ], "texture": "#plant" } + } + } + ] +} diff --git a/common/src/main/resources/assets/minecraft/models/block/tinted_flower_pot_cross.json b/common/src/main/resources/assets/minecraft/models/block/tinted_flower_pot_cross.json new file mode 100644 index 0000000000..f9d90de5ef --- /dev/null +++ b/common/src/main/resources/assets/minecraft/models/block/tinted_flower_pot_cross.json @@ -0,0 +1,108 @@ +{ + "ambientocclusion": false, + "textures": { + "particle": "block/flower_pot", + "flowerpot": "block/flower_pot", + "dirt": "block/dirt" + }, + "elements": [ + { + "from": [ 5, 0, 5 ], + "to": [ 11, 6, 11 ], + "faces": { + "north": { "uv": [ 5, 10, 11, 16 ], "texture": "#flowerpot" }, + "east": { "uv": [ 5, 10, 11, 16 ], "texture": "#flowerpot" }, + "south": { "uv": [ 5, 10, 11, 16 ], "texture": "#flowerpot" }, + "west": { "uv": [ 5, 10, 11, 16 ], "texture": "#flowerpot" } + } + }, + { + "from": [ 5, 0, 5 ], + "to": [ 10, 6, 6 ], + "faces": { + "up": { "uv": [ 5, 5, 10, 6 ], "texture": "#flowerpot" }, + "down": { "uv": [ 5, 10, 10, 11 ], "texture": "#flowerpot", "cullface": "down" } + } + }, + { + "from": [ 10, 0, 5 ], + "to": [ 11, 6, 10 ], + "faces": { + "up": { "uv": [ 10, 5, 11, 10 ], "texture": "#flowerpot" }, + "down": { "uv": [ 10, 6, 11, 11 ], "texture": "#flowerpot", "cullface": "down" } + } + }, + { + "from": [ 6, 0, 10 ], + "to": [ 11, 6, 11 ], + "faces": { + "up": { "uv": [ 6, 10, 11, 11 ], "texture": "#flowerpot" }, + "down": { "uv": [ 6, 5, 11, 6 ], "texture": "#flowerpot", "cullface": "down" } + } + }, + { + "from": [ 5, 0, 6 ], + "to": [ 6, 6, 11 ], + "faces": { + "up": { "uv": [ 5, 6, 6, 11 ], "texture": "#flowerpot" }, + "down": { "uv": [ 5, 5, 6, 10 ], "texture": "#flowerpot", "cullface": "down" } + } + }, + { + "from": [ 6, 4, 6 ], + "to": [ 10, 6, 6 ], + "faces": { + "south": { "uv": [ 6, 10, 10, 12 ], "texture": "#flowerpot" } + } + }, + { + "from": [ 10, 4, 6 ], + "to": [ 10, 6, 10 ], + "faces": { + "west": { "uv": [ 6, 10, 10, 12 ], "texture": "#flowerpot" } + } + }, + { + "from": [ 6, 4, 10 ], + "to": [ 10, 6, 10 ], + "faces": { + "north": { "uv": [ 6, 10, 10, 12 ], "texture": "#flowerpot" } + } + }, + { + "from": [ 6, 4, 6 ], + "to": [ 6, 6, 10 ], + "faces": { + "east": { "uv": [ 6, 10, 10, 12 ], "texture": "#flowerpot" } + } + }, + { + "from": [ 6, 0, 6 ], + "to": [ 10, 4, 10 ], + "faces": { + "up": { "uv": [ 6, 6, 10, 10 ], "texture": "#dirt" }, + "down": { "uv": [ 6, 12, 10, 16 ], "texture": "#flowerpot", "cullface": "down" } + } + }, + { + "from": [ 8, 4, 2.6 ], + "to": [ 8, 16, 13.4 ], + "shade": false, + "rotation": { "angle": 45, "axis": "y", "origin": [ 8, 8, 8 ], "rescale": true }, + "faces": { + "east": { "uv": [ 0, 0, 16, 16 ], "texture": "#plant", "tintindex": 0 }, + "west": { "uv": [ 0, 0, 16, 16 ], "texture": "#plant", "tintindex": 0 } + } + }, + { + "from": [ 8, 4, 2.6 ], + "to": [ 8, 16, 13.4 ], + "shade": false, + "rotation": { "angle": -45, "axis": "y", "origin": [ 8, 8, 8 ], "rescale": true }, + "faces": { + "east": { "uv": [ 0, 0, 16, 16 ], "texture": "#plant", "tintindex": 0 }, + "west": { "uv": [ 0, 0, 16, 16 ], "texture": "#plant", "tintindex": 0 } + } + } + ] +} diff --git a/common/src/main/resources/assets/minecraft/shaders/core/clouds.json b/common/src/main/resources/assets/minecraft/shaders/core/clouds.json deleted file mode 100644 index 05627fdf45..0000000000 --- a/common/src/main/resources/assets/minecraft/shaders/core/clouds.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "blend": { - "func": "add", - "srcrgb": "srcalpha", - "dstrgb": "1-srcalpha" - }, - "vertex": "clouds", - "fragment": "clouds", - "attributes": [ - "Color" - ], - "samplers": [ - ], - "uniforms": [ - { "name": "ModelViewMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] }, - { "name": "ProjMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] }, - { "name": "ColorModulator", "type": "float", "count": 4, "values": [ 1.0, 1.0, 1.0, 1.0 ] }, - { "name": "FogStart", "type": "float", "count": 1, "values": [ 0.0 ] }, - { "name": "FogEnd", "type": "float", "count": 1, "values": [ 1.0 ] }, - { "name": "FogColor", "type": "float", "count": 4, "values": [ 1.0, 1.0, 1.0, 1.0 ] } - ] -} - diff --git a/common/src/main/resources/assets/sodium/lang/en_us.json b/common/src/main/resources/assets/sodium/lang/en_us.json index b128222a27..d1d1e6b6a7 100644 --- a/common/src/main/resources/assets/sodium/lang/en_us.json +++ b/common/src/main/resources/assets/sodium/lang/en_us.json @@ -12,6 +12,7 @@ "sodium.options.brightness.tooltip": "Controls the minimum brightness in the world. When increased, darker areas of the world will appear brighter. This does not affect the brightness of already well-lit areas.", "sodium.options.gui_scale.tooltip": "Sets the maximum scale factor to be used for the user interface. If \"auto\" is used, then the largest scale factor will always be used.", "sodium.options.fullscreen.tooltip": "If enabled, the game will display in full-screen (if supported).", + "sodium.options.fullscreen_resolution.tooltip": "The monitor resolution and refresh rate to be used when in fullscreen mode. Changing this option may interfere with other applications and cause a delay when switching between applications.\n\nThis is only supported on the Windows operating system.", "sodium.options.v_sync.tooltip": "If enabled, the game's frame rate will be synchronized to the monitor's refresh rate, making for a generally smoother experience at the expense of overall input latency. This setting might reduce performance if your system is too slow.", "sodium.options.fps_limit.tooltip": "Limits the maximum number of frames per second. This can help reduce battery usage and system load when multi-tasking. If VSync is enabled, this option will be ignored unless it is lower than your display's refresh rate.", "sodium.options.view_bobbing.tooltip": "If enabled, the player's view will sway and bob when moving around. Players who experience motion sickness while playing may benefit from disabling this.", diff --git a/common/src/main/resources/assets/minecraft/shaders/core/clouds.fsh b/common/src/main/resources/assets/sodium/shaders/clouds.fsh similarity index 100% rename from common/src/main/resources/assets/minecraft/shaders/core/clouds.fsh rename to common/src/main/resources/assets/sodium/shaders/clouds.fsh diff --git a/common/src/main/resources/assets/sodium/shaders/clouds.json b/common/src/main/resources/assets/sodium/shaders/clouds.json new file mode 100644 index 0000000000..3b63a93952 --- /dev/null +++ b/common/src/main/resources/assets/sodium/shaders/clouds.json @@ -0,0 +1,21 @@ +{ + "vertex": "sodium:clouds", + "fragment": "sodium:clouds", + "defines": { + "values": { + "ALPHA_CUTOUT": "0.1" + } + }, + "samplers": [ + { "name": "Sampler0" } + ], + "uniforms": [ + { "name": "ModelViewMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] }, + { "name": "ProjMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] }, + { "name": "ColorModulator", "type": "float", "count": 4, "values": [ 1.0, 1.0, 1.0, 1.0 ] }, + { "name": "FogStart", "type": "float", "count": 1, "values": [ 0.0 ] }, + { "name": "FogEnd", "type": "float", "count": 1, "values": [ 1.0 ] }, + { "name": "FogColor", "type": "float", "count": 4, "values": [ 0.0, 0.0, 0.0, 0.0 ] }, + { "name": "FogShape", "type": "int", "count": 1, "values": [ 0 ] } + ] +} diff --git a/common/src/main/resources/assets/minecraft/shaders/core/clouds.vsh b/common/src/main/resources/assets/sodium/shaders/clouds.vsh similarity index 100% rename from common/src/main/resources/assets/minecraft/shaders/core/clouds.vsh rename to common/src/main/resources/assets/sodium/shaders/clouds.vsh diff --git a/common/src/main/resources/assets/sodium/shaders/include/chunk_vertex.glsl b/common/src/main/resources/assets/sodium/shaders/include/chunk_vertex.glsl index c594aa23f2..3ce9949258 100644 --- a/common/src/main/resources/assets/sodium/shaders/include/chunk_vertex.glsl +++ b/common/src/main/resources/assets/sodium/shaders/include/chunk_vertex.glsl @@ -32,15 +32,14 @@ const float VERTEX_OFFSET = -8.0; const float TEXTURE_FUZZ_AMOUNT = 1.0 / 64.0; const float TEXTURE_GROW_FACTOR = (1.0 - TEXTURE_FUZZ_AMOUNT) / TEXTURE_MAX_COORD; -in uint a_PositionHi; -in uint a_PositionLo; +in uvec2 a_Position; in vec4 a_Color; in uvec2 a_TexCoord; in uvec4 a_LightAndData; -uvec3 _deinterleave_u20x3(uint packed_hi, uint packed_lo) { - uvec3 hi = (uvec3(packed_hi) >> uvec3(0u, 10u, 20u)) & 0x3FFu; - uvec3 lo = (uvec3(packed_lo) >> uvec3(0u, 10u, 20u)) & 0x3FFu; +uvec3 _deinterleave_u20x3(uvec2 data) { + uvec3 hi = (uvec3(data.x) >> uvec3(0u, 10u, 20u)) & 0x3FFu; + uvec3 lo = (uvec3(data.y) >> uvec3(0u, 10u, 20u)) & 0x3FFu; return (hi << 10u) | lo; } @@ -54,7 +53,7 @@ vec2 _get_texcoord_bias() { } void _vert_init() { - _vert_position = ((_deinterleave_u20x3(a_PositionHi, a_PositionLo) * VERTEX_SCALE) + VERTEX_OFFSET); + _vert_position = (_deinterleave_u20x3(a_Position) * VERTEX_SCALE) + VERTEX_OFFSET; _vert_color = a_Color; _vert_tex_diffuse_coord = _get_texcoord() + _get_texcoord_bias(); diff --git a/common/src/main/resources/sodium-common.accesswidener b/common/src/main/resources/sodium-common.accesswidener new file mode 100644 index 0000000000..b5f056958a --- /dev/null +++ b/common/src/main/resources/sodium-common.accesswidener @@ -0,0 +1,21 @@ +accessWidener v1 named + +accessible class net/minecraft/client/model/geom/ModelPart$Vertex +accessible class net/minecraft/client/model/geom/ModelPart$Polygon +accessible class net/minecraft/client/renderer/texture/SpriteContents$InterpolationData +accessible class net/minecraft/client/renderer/texture/SpriteContents$AnimatedTexture +accessible class net/minecraft/client/renderer/texture/SpriteContents$FrameInfo +accessible class net/minecraft/client/renderer/texture/SpriteContents$Ticker +accessible class net/minecraft/world/level/chunk/PalettedContainer$Data +accessible class net/minecraft/world/level/chunk/PalettedContainer$Configuration +accessible class net/minecraft/client/renderer/FogRenderer$FogData +accessible class net/minecraft/client/renderer/FogRenderer$MobEffectFogFunction +accessible class net/minecraft/client/renderer/texture/Stitcher$Holder +accessible class net/minecraft/world/level/biome/Biome$ClimateSettings +accessible method net/minecraft/client/renderer/SectionBufferBuilderPool (Ljava/util/List;)V +accessible method com/mojang/blaze3d/vertex/PoseStack$Pose (Lorg/joml/Matrix4f;Lorg/joml/Matrix3f;)V +accessible field com/mojang/blaze3d/vertex/PoseStack$Pose trustedNormals Z + +accessible field net/minecraft/world/level/GrassColor pixels [I +accessible field net/minecraft/world/level/FoliageColor pixels [I +accessible method net/minecraft/client/renderer/FogRenderer getPriorityFogFunction (Lnet/minecraft/world/entity/Entity;F)Lnet/minecraft/client/renderer/FogRenderer$MobEffectFogFunction; \ No newline at end of file diff --git a/common/src/main/resources/sodium.mixins.json b/common/src/main/resources/sodium-common.mixins.json similarity index 98% rename from common/src/main/resources/sodium.mixins.json rename to common/src/main/resources/sodium-common.mixins.json index a41962e84e..59d85915e3 100644 --- a/common/src/main/resources/sodium.mixins.json +++ b/common/src/main/resources/sodium-common.mixins.json @@ -14,7 +14,6 @@ "core.WindowMixin", "core.gui.LevelLoadStatusManagerMixin", "core.model.colors.BlockColorsMixin", - "core.model.colors.ItemColorsMixin", "core.model.TextureAtlasSpriteMixin", "core.render.BlockEntityTypeMixin", "core.render.TextureAtlasMixin", @@ -27,6 +26,7 @@ "core.render.immediate.consumer.VertexMultiConsumerMixin$DoubleMixin", "core.render.immediate.consumer.VertexMultiConsumerMixin$MultipleMixin", "core.render.texture.TextureAtlasAccessor", + "core.render.world.EntityRendererAccessor", "core.render.world.RenderBuffersMixin", "core.render.world.LevelRendererMixin", "core.world.biome.ClientLevelMixin", diff --git a/common/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/checks/GraphicsDriverChecks.java b/common/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/checks/GraphicsDriverChecks.java new file mode 100644 index 0000000000..5d410dadac --- /dev/null +++ b/common/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/checks/GraphicsDriverChecks.java @@ -0,0 +1,63 @@ +package net.caffeinemc.mods.sodium.client.compatibility.checks; + +import net.caffeinemc.mods.sodium.client.compatibility.environment.GlContextInfo; +import net.caffeinemc.mods.sodium.client.compatibility.environment.probe.GraphicsAdapterVendor; +import net.caffeinemc.mods.sodium.client.compatibility.workarounds.intel.IntelWorkarounds; +import net.caffeinemc.mods.sodium.client.compatibility.workarounds.nvidia.NvidiaDriverVersion; +import net.caffeinemc.mods.sodium.client.compatibility.workarounds.nvidia.NvidiaWorkarounds; +import net.caffeinemc.mods.sodium.client.platform.NativeWindowHandle; +import net.caffeinemc.mods.sodium.client.platform.PlatformHelper; + +class GraphicsDriverChecks { + static void postContextInit(NativeWindowHandle window, GlContextInfo context) { + var vendor = GraphicsAdapterVendor.fromContext(context); + + if (vendor == GraphicsAdapterVendor.UNKNOWN) { + return; + } + + if (vendor == GraphicsAdapterVendor.INTEL && BugChecks.ISSUE_899) { + var installedVersion = IntelWorkarounds.findIntelDriverMatchingBug899(); + + if (installedVersion != null) { + var installedVersionString = installedVersion.toString(); + + PlatformHelper.showCriticalErrorAndClose(window, + "Sodium Renderer - Unsupported Driver", + """ + The game failed to start because the currently installed Intel Graphics Driver is not \ + compatible. + + Installed version: ###CURRENT_DRIVER### + Required version: 10.18.10.5161 (or newer) + + You must update your graphics card driver in order to continue.""" + .replace("###CURRENT_DRIVER###", installedVersionString), + "https://github.com/CaffeineMC/sodium/wiki/Driver-Compatibility#windows-intel-gen7"); + } + } + + if (vendor == GraphicsAdapterVendor.NVIDIA && BugChecks.ISSUE_1486) { + var installedVersion = NvidiaWorkarounds.findNvidiaDriverMatchingBug1486(); + + if (installedVersion != null) { + var installedVersionString = NvidiaDriverVersion.parse(installedVersion) + .toString(); + + PlatformHelper.showCriticalErrorAndClose(window, + "Sodium Renderer - Unsupported Driver", + """ + The game failed to start because the currently installed NVIDIA Graphics Driver is not \ + compatible. + + Installed version: ###CURRENT_DRIVER### + Required version: 536.23 (or newer) + + You must update your graphics card driver in order to continue.""" + .replace("###CURRENT_DRIVER###", installedVersionString), + "https://github.com/CaffeineMC/sodium/wiki/Driver-Compatibility#nvidia-gpus"); + + } + } + } +} diff --git a/common/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/checks/ModuleScanner.java b/common/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/checks/ModuleScanner.java index 1cbab8713f..8107366ef7 100644 --- a/common/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/checks/ModuleScanner.java +++ b/common/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/checks/ModuleScanner.java @@ -1,11 +1,9 @@ package net.caffeinemc.mods.sodium.client.compatibility.checks; -import com.google.common.collect.ImmutableList; -import com.mojang.blaze3d.platform.Window; import com.sun.jna.Platform; import com.sun.jna.platform.win32.Kernel32Util; -import com.sun.jna.platform.win32.Tlhelp32; import net.caffeinemc.mods.sodium.client.platform.MessageBox; +import net.caffeinemc.mods.sodium.client.platform.NativeWindowHandle; import net.caffeinemc.mods.sodium.client.platform.windows.WindowsFileVersion; import net.caffeinemc.mods.sodium.client.platform.windows.api.Kernel32; import net.caffeinemc.mods.sodium.client.platform.windows.api.version.Version; @@ -15,6 +13,8 @@ import java.nio.file.Files; import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; /** @@ -36,7 +36,7 @@ public class ModuleScanner { "GTIII-OSD64.dll", "GTIII-OSD.dll" }; - public static void checkModules(long window) { + public static void checkModules(NativeWindowHandle window) { List modules; try { @@ -67,21 +67,20 @@ public static void checkModules(long window) { private static List listModules() { if (!Platform.isWindows()) { - return ImmutableList.of(); - } else { - int i = com.sun.jna.platform.win32.Kernel32.INSTANCE.GetCurrentProcessId(); - ImmutableList.Builder builder = ImmutableList.builder(); + return List.of(); + } - for(Tlhelp32.MODULEENTRY32W mODULEENTRY32W : Kernel32Util.getModules(i)) { - String string = mODULEENTRY32W.szModule(); - builder.add(string); - } + var pid = com.sun.jna.platform.win32.Kernel32.INSTANCE.GetCurrentProcessId(); + var modules = new ArrayList(); - return builder.build(); + for (var module : Kernel32Util.getModules(pid)) { + modules.add(module.szModule()); } + + return Collections.unmodifiableList(modules); } - private static void checkRTSSModules(long window) { + private static void checkRTSSModules(NativeWindowHandle window) { LOGGER.warn("RivaTuner Statistics Server (RTSS) has injected into the process! Attempting to apply workarounds for compatibility..."); @Nullable WindowsFileVersion version = null; @@ -106,10 +105,10 @@ You appear to be using an older version of RivaTuner Statistics Server (RTSS) wh You must either update to a newer version (7.3.4 and later) or close the RivaTuner Statistics Server application. For more information on how to solve this problem, click the 'Help' button.""", - "https://github.com/CaffeineMC/sodium-fabric/wiki/Known-Issues#rtss-incompatible"); + "https://github.com/CaffeineMC/sodium/wiki/Known-Issues#rtss-incompatible"); throw new RuntimeException("The installed version of RivaTuner Statistics Server (RTSS) is not compatible with Sodium, " + - "see here for more details: https://github.com/CaffeineMC/sodium-fabric/wiki/Known-Issues#rtss-incompatible"); + "see here for more details: https://github.com/CaffeineMC/sodium/wiki/Known-Issues#rtss-incompatible"); } } @@ -122,7 +121,7 @@ private static boolean isRTSSCompatible(WindowsFileVersion version) { return x > 7 || (x == 7 && y > 3) || (x == 7 && y == 3 && z >= 4); } - private static void checkASUSGpuTweakIII(long window) { + private static void checkASUSGpuTweakIII(NativeWindowHandle window) { MessageBox.showMessageBox(window, MessageBox.IconType.ERROR, "Sodium Renderer", """ ASUS GPU Tweak III is not compatible with Minecraft, and causes extreme performance issues and severe graphical corruption when used with Minecraft. @@ -134,10 +133,10 @@ private static void checkASUSGpuTweakIII(long window) { b) Completely uninstall the ASUS GPU Tweak III application. For more information on how to solve this problem, click the 'Help' button.""", - "https://github.com/CaffeineMC/sodium-fabric/wiki/Known-Issues#asus-gtiii-incompatible"); + "https://github.com/CaffeineMC/sodium/wiki/Known-Issues#asus-gtiii-incompatible"); throw new RuntimeException("ASUS GPU Tweak III is not compatible with Minecraft, " + - "see here for more details: https://github.com/CaffeineMC/sodium-fabric/wiki/Known-Issues#asus-gtiii-incompatible"); + "see here for more details: https://github.com/CaffeineMC/sodium/wiki/Known-Issues#asus-gtiii-incompatible"); } private static @Nullable WindowsFileVersion findRTSSModuleVersion() { diff --git a/common/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/checks/PostLaunchChecks.java b/common/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/checks/PostLaunchChecks.java index 311ce50f43..907b877afe 100644 --- a/common/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/checks/PostLaunchChecks.java +++ b/common/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/checks/PostLaunchChecks.java @@ -1,13 +1,12 @@ package net.caffeinemc.mods.sodium.client.compatibility.checks; -import net.caffeinemc.mods.sodium.client.compatibility.workarounds.nvidia.NvidiaDriverVersion; +import net.caffeinemc.mods.sodium.client.compatibility.environment.GlContextInfo; +import net.caffeinemc.mods.sodium.client.compatibility.workarounds.nvidia.NvidiaWorkarounds; import net.caffeinemc.mods.sodium.client.console.Console; import net.caffeinemc.mods.sodium.client.console.message.MessageLevel; -import net.caffeinemc.mods.sodium.client.compatibility.environment.GLContextInfo; +import net.caffeinemc.mods.sodium.client.platform.NativeWindowHandle; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import oshi.PlatformEnum; -import oshi.SystemInfo; /** * Performs OpenGL driver validation after the game creates an OpenGL context. This runs immediately after OpenGL @@ -16,7 +15,10 @@ public class PostLaunchChecks { private static final Logger LOGGER = LoggerFactory.getLogger("Sodium-PostlaunchChecks"); - public static void onContextInitialized() { + public static void onContextInitialized(NativeWindowHandle window, GlContextInfo context) { + GraphicsDriverChecks.postContextInit(window, context); + NvidiaWorkarounds.applyContextChanges(context); + // FIXME: This can be determined earlier, but we can't access the GUI classes in pre-launch if (isUsingPojavLauncher()) { Console.instance().logMessage(MessageLevel.SEVERE, "sodium.console.pojav_launcher", true, 30.0); @@ -26,7 +28,7 @@ public static void onContextInitialized() { } } - // https://github.com/CaffeineMC/sodium-fabric/issues/1916 + // https://github.com/CaffeineMC/sodium/issues/1916 private static boolean isUsingPojavLauncher() { if (System.getenv("POJAV_RENDERER") != null) { LOGGER.warn("Detected presence of environment variable POJAV_LAUNCHER, which seems to indicate we are running on Android"); diff --git a/common/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/checks/PreLaunchChecks.java b/common/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/checks/PreLaunchChecks.java index d6804a83d4..70cb73e27e 100644 --- a/common/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/checks/PreLaunchChecks.java +++ b/common/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/checks/PreLaunchChecks.java @@ -1,173 +1,68 @@ package net.caffeinemc.mods.sodium.client.compatibility.checks; -import net.caffeinemc.mods.sodium.client.compatibility.environment.OsUtils; -import net.caffeinemc.mods.sodium.client.compatibility.environment.probe.GraphicsAdapterProbe; -import net.caffeinemc.mods.sodium.client.compatibility.environment.probe.GraphicsAdapterVendor; -import net.caffeinemc.mods.sodium.client.compatibility.workarounds.nvidia.NvidiaDriverVersion; -import net.caffeinemc.mods.sodium.client.platform.MessageBox; -import net.caffeinemc.mods.sodium.client.platform.windows.WindowsFileVersion; -import net.caffeinemc.mods.sodium.client.platform.windows.api.d3dkmt.D3DKMT; -import org.jetbrains.annotations.Nullable; +import net.caffeinemc.mods.sodium.client.platform.PlatformHelper; import org.lwjgl.Version; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.Arrays; /** * Performs OpenGL driver validation before the game creates an OpenGL context. This runs during the earliest possible * opportunity at game startup, and uses a custom hardware prober to search for problematic drivers. */ public class PreLaunchChecks { - private static final Logger LOGGER = LoggerFactory.getLogger("Sodium-EarlyDriverScanner"); - - // This string should be determined at compile time, so it can be checked against the runtime version. - private static final String REQUIRED_LWJGL_VERSION = Version.VERSION_MAJOR + "." + Version.VERSION_MINOR + "." + Version.VERSION_REVISION; - - private static final String normalMessage = "You must change the LWJGL version in your launcher to continue. This is usually controlled by the settings for a profile or instance in your launcher."; - - private static final String prismMessage = "It appears you are using Prism Launcher to start the game. You can likely fix this problem by opening your instance settings and navigating to the Version section in the sidebar."; + // These version constants are inlined at compile time. + private static final String REQUIRED_LWJGL_VERSION = + Version.VERSION_MAJOR + "." + Version.VERSION_MINOR + "." + Version.VERSION_REVISION; - public static void beforeLWJGLInit() { + public static void checkEnvironment() { if (BugChecks.ISSUE_2561) { - if (!Version.getVersion().startsWith(REQUIRED_LWJGL_VERSION)) { - String message = normalMessage; - - if (System.getProperty("minecraft.launcher.brand", "unknown").equalsIgnoreCase("PrismLauncher")) { - message = prismMessage; - } - - showCriticalErrorAndClose("Sodium Renderer - Unsupported LWJGL", - (""" - The game failed to start because the currently active LWJGL version is not \ - compatible. - - Installed version: ###CURRENT_VERSION### - Required version: ###REQUIRED_VERSION### - - """ + message) - .replace("###CURRENT_VERSION###", org.lwjgl.Version.getVersion()) - .replace("###REQUIRED_VERSION###", REQUIRED_LWJGL_VERSION), - "https://github.com/CaffeineMC/sodium-fabric/wiki/LWJGL-Compatibility"); - - } + checkLwjglRuntimeVersion(); } } - public static void onGameInit() { - if (BugChecks.ISSUE_899) { - var installedVersion = findIntelDriverMatchingBug899(); - - if (installedVersion != null) { - showCriticalErrorAndClose("Sodium Renderer - Unsupported Driver", - """ - The game failed to start because the currently installed Intel Graphics Driver is not \ - compatible. - - Installed version: ###CURRENT_DRIVER### - Required version: 10.18.10.5161 (or newer) - - You must update your graphics card driver in order to continue.""" - .replace("###CURRENT_DRIVER###", installedVersion.toString()), - "https://github.com/CaffeineMC/sodium-fabric/wiki/Driver-Compatibility#windows-intel-gen7"); - } + private static void checkLwjglRuntimeVersion() { + if (isUsingKnownCompatibleLwjglVersion()) { + return; } - if (BugChecks.ISSUE_1486) { - var installedVersion = findNvidiaDriverMatchingBug1486(); - - if (installedVersion != null) { - showCriticalErrorAndClose("Sodium Renderer - Unsupported Driver", - """ - The game failed to start because the currently installed NVIDIA Graphics Driver is not \ - compatible. - - Installed version: ###CURRENT_DRIVER### - Required version: 536.23 (or newer) - - You must update your graphics card driver in order to continue.""" - .replace("###CURRENT_DRIVER###", NvidiaDriverVersion.parse(installedVersion).toString()), - "https://github.com/CaffeineMC/sodium-fabric/wiki/Driver-Compatibility#nvidia-gpus"); - - } + String advice; + + if (isUsingPrismLauncher()) { + advice = """ + It appears you are using Prism Launcher to start the game. You can \ + likely fix this problem by opening your instance settings and navigating to the Version\ + section in the sidebar."""; + } else { + advice = """ + You must change the LWJGL version in your launcher to continue. \ + This is usually controlled by the settings for a profile or instance in your launcher."""; } - } - - private static void showCriticalErrorAndClose(String title, String message, String url) { - // Always print the information to the log file first, just in case we can't show the message box. - LOGGER.error(""" - ###ERROR_DESCRIPTION### - - For more information, please see: ###HELP_URL###""" - .replace("###ERROR_DESCRIPTION###", message) - .replace("###HELP_URL###", url == null ? "" : url)); - // Try to show a graphical message box (if the platform supports it) and shut down the game. - MessageBox.showMessageBox(0L, MessageBox.IconType.ERROR, title, message, url); - System.exit(1 /* failure code */); + String message = """ + The game failed to start because the currently active LWJGL version is not \ + compatible. + + Installed version: ###CURRENT_VERSION### + Required version: ###REQUIRED_VERSION### + + ###ADVICE_STRING###""" + .replace("###CURRENT_VERSION###", Version.getVersion()) + .replace("###REQUIRED_VERSION###", REQUIRED_LWJGL_VERSION) + .replace("###ADVICE_STRING###", advice); + + PlatformHelper.showCriticalErrorAndClose(null, "Sodium Renderer - Unsupported LWJGL", message, + "https://github.com/CaffeineMC/sodium/wiki/LWJGL-Compatibility"); } - // https://github.com/CaffeineMC/sodium-fabric/issues/899 - private static @Nullable WindowsFileVersion findIntelDriverMatchingBug899() { - if (OsUtils.getOs() != OsUtils.OperatingSystem.WIN) { - return null; - } - - for (var adapter : GraphicsAdapterProbe.getAdapters()) { - if (adapter instanceof D3DKMT.WDDMAdapterInfo wddmAdapterInfo) { - @Nullable var driverName = wddmAdapterInfo.getOpenGlIcdName(); - - if (driverName == null) { - continue; - } - - var driverVersion = wddmAdapterInfo.openglIcdVersion(); - - // Intel OpenGL ICD for Generation 7 GPUs - if (driverName.matches("ig7icd(32|64)")) { - // https://www.intel.com/content/www/us/en/support/articles/000005654/graphics.html - // Anything which matches the 15.33 driver scheme (WDDM x.y.10.w) should be checked - // Drivers before build 5161 are assumed to have bugs with synchronization primitives - if (driverVersion.z() == 10 && driverVersion.w() < 5161) { - return driverVersion; - } - } - } - } - - return null; + private static boolean isUsingKnownCompatibleLwjglVersion() { + return Version.getVersion() + .startsWith(REQUIRED_LWJGL_VERSION); } + private static boolean isUsingPrismLauncher() { + return getLauncherBrand() + .equalsIgnoreCase("PrismLauncher"); + } - // https://github.com/CaffeineMC/sodium-fabric/issues/1486 - // The way which NVIDIA tries to detect the Minecraft process could not be circumvented until fairly recently - // So we require that an up-to-date graphics driver is installed so that our workarounds can disable the Threaded - // Optimizations driver hack. - private static @Nullable WindowsFileVersion findNvidiaDriverMatchingBug1486() { - // The Linux driver has two separate branches which have overlapping version numbers, despite also having - // different feature sets. As a result, we can't reliably determine which Linux drivers are broken... - if (OsUtils.getOs() != OsUtils.OperatingSystem.WIN) { - return null; - } - - for (var adapter : GraphicsAdapterProbe.getAdapters()) { - if (adapter.vendor() != GraphicsAdapterVendor.NVIDIA) { - continue; - } - - if (adapter instanceof D3DKMT.WDDMAdapterInfo wddmAdapterInfo) { - var driverVersion = wddmAdapterInfo.openglIcdVersion(); - - if (driverVersion.z() == 15) { // Only match 5XX.XX drivers - // Broken in x.y.15.2647 (526.47) - // Fixed in x.y.15.3623 (536.23) - if (driverVersion.w() >= 2647 && driverVersion.w() < 3623) { - return driverVersion; - } - } - } - } - - return null; + private static String getLauncherBrand() { + return System.getProperty("minecraft.launcher.brand", "unknown"); } } diff --git a/common/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/environment/GLContextInfo.java b/common/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/environment/GLContextInfo.java deleted file mode 100644 index 1a64f8ba54..0000000000 --- a/common/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/environment/GLContextInfo.java +++ /dev/null @@ -1,20 +0,0 @@ -package net.caffeinemc.mods.sodium.client.compatibility.environment; - -import org.jetbrains.annotations.Nullable; -import org.lwjgl.opengl.GL11C; - -public record GLContextInfo(String vendor, String renderer, String version) { - @Nullable - public static GLContextInfo create() { - - String vendor = GL11C.glGetString(GL11C.GL_VENDOR); - String renderer = GL11C.glGetString(GL11C.GL_RENDERER); - String version = GL11C.glGetString(GL11C.GL_VERSION); - - if (vendor == null || renderer == null || version == null) { - return null; - } - - return new GLContextInfo(vendor, renderer, version); - } -} diff --git a/common/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/environment/GlContextInfo.java b/common/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/environment/GlContextInfo.java new file mode 100644 index 0000000000..7e8f484385 --- /dev/null +++ b/common/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/environment/GlContextInfo.java @@ -0,0 +1,19 @@ +package net.caffeinemc.mods.sodium.client.compatibility.environment; + +import org.jetbrains.annotations.Nullable; +import org.lwjgl.opengl.GL11C; + +import java.util.Objects; + +public record GlContextInfo(String vendor, String renderer, String version) { + public static GlContextInfo create() { + String vendor = Objects.requireNonNull(GL11C.glGetString(GL11C.GL_VENDOR), + "GL_VENDOR is NULL"); + String renderer = Objects.requireNonNull(GL11C.glGetString(GL11C.GL_RENDERER), + "GL_RENDERER is NULL"); + String version = Objects.requireNonNull(GL11C.glGetString(GL11C.GL_VERSION), + "GL_VERSION is NULL"); + + return new GlContextInfo(vendor, renderer, version); + } +} diff --git a/common/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/environment/OsUtils.java b/common/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/environment/OsUtils.java index 4f39eb9ce0..c232c0faa9 100644 --- a/common/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/environment/OsUtils.java +++ b/common/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/environment/OsUtils.java @@ -1,21 +1,32 @@ package net.caffeinemc.mods.sodium.client.compatibility.environment; -import org.apache.commons.lang3.SystemUtils; +import java.util.Locale; public class OsUtils { + private static final OperatingSystem OS = determineOs(); - public static OperatingSystem getOs() { - if (SystemUtils.IS_OS_WINDOWS) { - return OperatingSystem.WIN; - } else if (SystemUtils.IS_OS_MAC) { - return OperatingSystem.MAC; - } else if (SystemUtils.IS_OS_LINUX) { - return OperatingSystem.LINUX; + public static OperatingSystem determineOs() { + var name = System.getProperty("os.name"); + + if (name != null) { + var normalized = name.toLowerCase(Locale.ROOT); + + if (normalized.startsWith("windows")) { + return OperatingSystem.WIN; + } else if (normalized.startsWith("mac")) { + return OperatingSystem.MAC; + } else if (normalized.startsWith("linux")) { + return OperatingSystem.LINUX; + } } return OperatingSystem.UNKNOWN; } + public static OperatingSystem getOs() { + return OS; + } + public enum OperatingSystem { WIN, MAC, diff --git a/common/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/environment/probe/GraphicsAdapterProbe.java b/common/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/environment/probe/GraphicsAdapterProbe.java index e78cb129e4..4bfb59179a 100644 --- a/common/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/environment/probe/GraphicsAdapterProbe.java +++ b/common/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/environment/probe/GraphicsAdapterProbe.java @@ -2,21 +2,28 @@ import net.caffeinemc.mods.sodium.client.compatibility.environment.OsUtils; import net.caffeinemc.mods.sodium.client.platform.windows.api.d3dkmt.D3DKMT; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import oshi.util.ExecutingCommand; +import java.io.BufferedReader; import java.io.IOException; +import java.io.InputStreamReader; import java.nio.file.Files; import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; +import java.util.*; public class GraphicsAdapterProbe { private static final Logger LOGGER = LoggerFactory.getLogger("Sodium-GraphicsAdapterProbe"); + private static final Set LINUX_PCI_CLASSES = Set.of( + "0x030000", // PCI_CLASS_DISPLAY_VGA + "0x030001", // PCI_CLASS_DISPLAY_XGA + "0x030200", // PCI_CLASS_DISPLAY_3D + "0x038000" // PCI_CLASS_DISPLAY_OTHER + ); + private static List ADAPTERS = List.of(); public static void findAdapters() { @@ -65,30 +72,23 @@ public static void findAdapters() { Iterable devicesIter = devices::iterator; for (var devicePath : devicesIter) { - // 0x030000 = VGA compatible controller - // 0x030200 = 3D controller (GPUs with no inputs attached, e.g. hybrid graphics laptops) var deviceClass = Files.readString(devicePath.resolve("class")).trim(); - if (!deviceClass.equals("0x030000") && !deviceClass.equals("0x030200")) { + + if (!LINUX_PCI_CLASSES.contains(deviceClass)) { continue; } var pciVendorId = Files.readString(devicePath.resolve("vendor")).trim(); var pciDeviceId = Files.readString(devicePath.resolve("device")).trim(); + var adapterVendor = GraphicsAdapterVendor.fromPciVendorId(pciVendorId); + var adapterName = getPciDeviceName$Linux(pciVendorId, pciDeviceId); - // The Linux kernel doesn't provide a way to get the device name, so we need to use lspci, - // since it comes with a list of known device names mapped to device IDs. - var name = ExecutingCommand // See `man lspci` for more information - .runNative("lspci -vmm -d " + pciVendorId.substring(2) + ":" + pciDeviceId.substring(2)) - .stream() - .filter(line -> line.startsWith("Device:")) - .map(line -> line.substring("Device:".length()).trim()) - .findFirst() - .orElse("unknown"); - - var vendor = GraphicsAdapterVendor.fromPciVendorId(pciVendorId); + if (adapterName == null) { + adapterName = ""; + } - var info = new GraphicsAdapterInfo.LinuxPciAdapterInfo(vendor, name, pciVendorId, pciDeviceId); + var info = new GraphicsAdapterInfo.LinuxPciAdapterInfo(adapterVendor, adapterName, pciVendorId, pciDeviceId); results.add(info); } } catch (IOException ignored) {} @@ -96,6 +96,41 @@ public static void findAdapters() { return results; } + private static @Nullable String getPciDeviceName$Linux(String vendorId, String deviceId) { + // The Linux kernel doesn't provide a way to get the device name, so we need to use lspci, + // since it comes with a list of known device names mapped to device IDs. + // See `man lspci` for more information + + // []:[][:[:]] + var deviceFilter = vendorId.substring(2) + ":" + deviceId.substring(2); + + try { + var process = Runtime.getRuntime() + .exec(new String[] { "lspci", "-vmm", "-d", deviceFilter }); + var result = process.waitFor(); + + if (result != 0) { + throw new IOException("lspci exited with error code: %s".formatted(result)); + } + + try (var reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) { + String line; + + while ((line = reader.readLine()) != null) { + if (line.startsWith("Device:")) { + return line.substring("Device:".length()).trim(); + } + } + } + + throw new IOException("lspci did not return a device name"); + } catch (Throwable e) { + LOGGER.warn("Failed to query PCI device name for %s:%s".formatted(vendorId, deviceId), e); + } + + return null; + } + public static Collection getAdapters() { if (ADAPTERS == null) { LOGGER.error("Graphics adapters not probed yet; returning an empty list."); diff --git a/common/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/environment/probe/GraphicsAdapterVendor.java b/common/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/environment/probe/GraphicsAdapterVendor.java index bb6c0c80ac..57ddb79400 100644 --- a/common/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/environment/probe/GraphicsAdapterVendor.java +++ b/common/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/environment/probe/GraphicsAdapterVendor.java @@ -1,16 +1,35 @@ package net.caffeinemc.mods.sodium.client.compatibility.environment.probe; -import org.apache.commons.io.FileUtils; -import org.apache.commons.io.FilenameUtils; -import org.apache.commons.io.file.PathUtils; +import net.caffeinemc.mods.sodium.client.compatibility.environment.GlContextInfo; import org.jetbrains.annotations.NotNull; +import java.util.Locale; +import java.util.regex.Pattern; + public enum GraphicsAdapterVendor { NVIDIA, AMD, INTEL, UNKNOWN; + // Intel Gen 4, 5, 6 - ig4icd + // Intel Gen 7 - ig7icd + // Intel Gen 7.5 - ig75icd + // Intel Gen 8 - ig8icd + // Intel Gen 9, 9.5 - ig9icd + // Intel Gen 11 - ig11icd + // Intel Gen 12 - ig12icd (UHD Graphics, with early drivers) + // igxelpicd (Xe-LP; integrated) + // igxehpicd (Xe-HP; dedicated) + private static final Pattern INTEL_ICD_PATTERN = + Pattern.compile("ig(4|7|75|8|9|11|12|xelp|xehp)icd(32|64)\\.dll", Pattern.CASE_INSENSITIVE); + + private static final Pattern NVIDIA_ICD_PATTERN = + Pattern.compile("nvoglv(32|64)\\.dll", Pattern.CASE_INSENSITIVE); + + private static final Pattern AMD_ICD_PATTERN = + Pattern.compile("(atiglpxx|atig6pxx)\\.dll", Pattern.CASE_INSENSITIVE); + @NotNull static GraphicsAdapterVendor fromPciVendorId(String vendor) { if (vendor.contains("0x1002")) { @@ -24,28 +43,34 @@ static GraphicsAdapterVendor fromPciVendorId(String vendor) { return UNKNOWN; } + @NotNull public static GraphicsAdapterVendor fromIcdName(String name) { - // Intel Gen 4, 5, 6 - ig4icd - // Intel Gen 7 - ig7icd - // Intel Gen 7.5 - ig75icd - // Intel Gen 8 - ig8icd - // Intel Gen 9, 9.5 - ig9icd - // Intel Gen 11 - ig11icd - // Intel Gen 12 - ig12icd (UHD Graphics, with early drivers) - // igxelpicd (Xe-LP; integrated) - // igxehpicd (Xe-HP; dedicated) - if (name.matches("ig(4|7|75|8|9|11|12|xelp|xehp)icd(32|64)")) { + if (matchesPattern(INTEL_ICD_PATTERN, name)) { return INTEL; - } - - if (name.matches("nvoglv(32|64)")) { + } else if (matchesPattern(NVIDIA_ICD_PATTERN, name)) { return NVIDIA; - } - - if (name.matches("atiglpxx|atig6pxx")) { + } else if (matchesPattern(AMD_ICD_PATTERN, name)) { return AMD; + } else { + return UNKNOWN; } + } - return UNKNOWN; + @NotNull + public static GraphicsAdapterVendor fromContext(GlContextInfo context) { + var vendor = context.vendor(); + + return switch (vendor) { + case "NVIDIA Corporation" -> NVIDIA; + case "Intel", "Intel Open Source Technology Center" -> INTEL; + case "AMD", "ATI Technologies Inc." -> AMD; + default -> UNKNOWN; + }; + + } + + private static boolean matchesPattern(Pattern pattern, String name) { + return pattern.matcher(name) + .matches(); } } diff --git a/common/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/workarounds/Workarounds.java b/common/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/workarounds/Workarounds.java index ed4cf40ed1..659d98b6b3 100644 --- a/common/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/workarounds/Workarounds.java +++ b/common/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/workarounds/Workarounds.java @@ -1,15 +1,15 @@ package net.caffeinemc.mods.sodium.client.compatibility.workarounds; import net.caffeinemc.mods.sodium.client.compatibility.environment.OsUtils; -import net.caffeinemc.mods.sodium.client.compatibility.environment.probe.GraphicsAdapterInfo; -import net.caffeinemc.mods.sodium.client.compatibility.environment.probe.GraphicsAdapterProbe; -import net.caffeinemc.mods.sodium.client.compatibility.environment.probe.GraphicsAdapterVendor; -import net.caffeinemc.mods.sodium.client.platform.windows.api.d3dkmt.D3DKMT; -import org.jetbrains.annotations.Nullable; +import net.caffeinemc.mods.sodium.client.compatibility.workarounds.intel.IntelWorkarounds; +import net.caffeinemc.mods.sodium.client.compatibility.workarounds.nvidia.NvidiaWorkarounds; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.*; +import java.util.Collections; +import java.util.EnumSet; +import java.util.Objects; +import java.util.Set; import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; @@ -37,14 +37,13 @@ private static Set findNecessaryWorkarounds() { var workarounds = EnumSet.noneOf(Reference.class); var operatingSystem = OsUtils.getOs(); - var graphicsAdapters = GraphicsAdapterProbe.getAdapters(); - - if (isUsingNvidiaGraphicsCard(operatingSystem, graphicsAdapters)) { - workarounds.add(Reference.NVIDIA_THREADED_OPTIMIZATIONS); + if (NvidiaWorkarounds.isNvidiaGraphicsCardPresent()) { + workarounds.add(Reference.NVIDIA_THREADED_OPTIMIZATIONS_BROKEN); } - if (checkIntelFramebufferBlitBug()) { - workarounds.add(Reference.INTEL_FRAMEBUFFER_BLIT_UNSUPPORTED); + if (IntelWorkarounds.isUsingIntelGen8OrOlder()) { + workarounds.add(Reference.INTEL_FRAMEBUFFER_BLIT_CRASH_WHEN_UNFOCUSED); + workarounds.add(Reference.INTEL_DEPTH_BUFFER_COMPARISON_UNRELIABLE); } if (operatingSystem == OsUtils.OperatingSystem.LINUX) { @@ -64,31 +63,6 @@ private static Set findNecessaryWorkarounds() { return Collections.unmodifiableSet(workarounds); } - private static boolean checkIntelFramebufferBlitBug() { - if (OsUtils.getOs() != OsUtils.OperatingSystem.WIN) { - return false; - } - - for (var adapter : GraphicsAdapterProbe.getAdapters()) { - if (adapter instanceof D3DKMT.WDDMAdapterInfo wddmAdapterInfo) { - @Nullable var driverName = wddmAdapterInfo.getOpenGlIcdName(); - - // Intel OpenGL ICD for legacy GPUs - if (driverName != null && driverName.matches("ig(7|75|8)icd(32|64)")) { - return true; - } - } - } - - return false; - } - - private static boolean isUsingNvidiaGraphicsCard(OsUtils.OperatingSystem operatingSystem, Collection adapters) { - - return (operatingSystem == OsUtils.OperatingSystem.WIN || operatingSystem == OsUtils.OperatingSystem.LINUX) && - adapters.stream().anyMatch(adapter -> adapter.vendor() == GraphicsAdapterVendor.NVIDIA); - } - public static boolean isWorkaroundEnabled(Reference id) { return ACTIVE_WORKAROUNDS.get() .contains(id); @@ -98,21 +72,29 @@ public enum Reference { /** * The NVIDIA driver applies "Threaded Optimizations" when Minecraft is detected, causing severe * performance issues and crashes. - * GitHub Issue + * GitHub Issue */ - NVIDIA_THREADED_OPTIMIZATIONS, + NVIDIA_THREADED_OPTIMIZATIONS_BROKEN, /** * Requesting a No Error Context causes a crash at startup when using a Wayland session. - * GitHub Issue + * GitHub Issue */ NO_ERROR_CONTEXT_UNSUPPORTED, /** - * Intel's graphics driver for Gen 7.5 GPUs seems to be faulty and causes a crash when calling + * Intel's graphics driver for Gen8 and older seems to be faulty and causes a crash when calling * glFramebufferBlit after the window loses focus. - * GitHub Issue + * GitHub Issue + */ + INTEL_FRAMEBUFFER_BLIT_CRASH_WHEN_UNFOCUSED, + + /** + * Intel's graphics driver for Gen8 and older does not respect depth comparison rules per the OpenGL + * specification, causing block model overlays to Z-fight when the overlay is on a different render pass than + * the base model. + * GitHub Issue */ - INTEL_FRAMEBUFFER_BLIT_UNSUPPORTED + INTEL_DEPTH_BUFFER_COMPARISON_UNRELIABLE } } diff --git a/common/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/workarounds/intel/IntelWorkarounds.java b/common/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/workarounds/intel/IntelWorkarounds.java new file mode 100644 index 0000000000..390502d152 --- /dev/null +++ b/common/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/workarounds/intel/IntelWorkarounds.java @@ -0,0 +1,59 @@ +package net.caffeinemc.mods.sodium.client.compatibility.workarounds.intel; + +import net.caffeinemc.mods.sodium.client.compatibility.environment.OsUtils; +import net.caffeinemc.mods.sodium.client.compatibility.environment.probe.GraphicsAdapterProbe; +import net.caffeinemc.mods.sodium.client.platform.windows.WindowsFileVersion; +import net.caffeinemc.mods.sodium.client.platform.windows.api.d3dkmt.D3DKMT; +import org.jetbrains.annotations.Nullable; + +public class IntelWorkarounds { + // https://github.com/CaffeineMC/sodium/issues/899 + public static @Nullable WindowsFileVersion findIntelDriverMatchingBug899() { + if (OsUtils.getOs() != OsUtils.OperatingSystem.WIN) { + return null; + } + + for (var adapter : GraphicsAdapterProbe.getAdapters()) { + if (adapter instanceof D3DKMT.WDDMAdapterInfo wddmAdapterInfo) { + @Nullable var driverName = wddmAdapterInfo.getOpenGlIcdName(); + + if (driverName == null) { + continue; + } + + var driverVersion = wddmAdapterInfo.openglIcdVersion(); + + // Intel OpenGL ICD for Generation 7 GPUs + if (driverName.matches("ig7icd(32|64).dll")) { + // https://www.intel.com/content/www/us/en/support/articles/000005654/graphics.html + // Anything which matches the 15.33 driver scheme (WDDM x.y.10.w) should be checked + // Drivers before build 5161 are assumed to have bugs with synchronization primitives + if (driverVersion.z() == 10 && driverVersion.w() < 5161) { + return driverVersion; + } + } + } + } + + return null; + } + + public static boolean isUsingIntelGen8OrOlder() { + if (OsUtils.getOs() != OsUtils.OperatingSystem.WIN) { + return false; + } + + for (var adapter : GraphicsAdapterProbe.getAdapters()) { + if (adapter instanceof D3DKMT.WDDMAdapterInfo wddmAdapterInfo) { + @Nullable var driverName = wddmAdapterInfo.getOpenGlIcdName(); + + // Intel OpenGL ICD for legacy GPUs + if (driverName != null && driverName.matches("ig(7|75|8)icd(32|64)\\.dll")) { + return true; + } + } + } + + return false; + } +} diff --git a/common/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/workarounds/nvidia/NvidiaWorkarounds.java b/common/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/workarounds/nvidia/NvidiaWorkarounds.java index ab86532358..d7c1be4da6 100644 --- a/common/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/workarounds/nvidia/NvidiaWorkarounds.java +++ b/common/src/workarounds/java/net/caffeinemc/mods/sodium/client/compatibility/workarounds/nvidia/NvidiaWorkarounds.java @@ -1,49 +1,152 @@ package net.caffeinemc.mods.sodium.client.compatibility.workarounds.nvidia; +import net.caffeinemc.mods.sodium.client.compatibility.environment.GlContextInfo; import net.caffeinemc.mods.sodium.client.compatibility.environment.OsUtils; +import net.caffeinemc.mods.sodium.client.compatibility.environment.OsUtils.OperatingSystem; +import net.caffeinemc.mods.sodium.client.compatibility.environment.probe.GraphicsAdapterProbe; +import net.caffeinemc.mods.sodium.client.compatibility.environment.probe.GraphicsAdapterVendor; +import net.caffeinemc.mods.sodium.client.compatibility.workarounds.Workarounds; import net.caffeinemc.mods.sodium.client.platform.unix.Libc; -import net.caffeinemc.mods.sodium.client.platform.windows.api.Kernel32; import net.caffeinemc.mods.sodium.client.platform.windows.WindowsCommandLine; +import net.caffeinemc.mods.sodium.client.platform.windows.WindowsFileVersion; +import net.caffeinemc.mods.sodium.client.platform.windows.api.d3dkmt.D3DKMT; +import org.jetbrains.annotations.Nullable; +import org.lwjgl.opengl.GL; +import org.lwjgl.opengl.GL32C; +import org.lwjgl.opengl.KHRDebug; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class NvidiaWorkarounds { private static final Logger LOGGER = LoggerFactory.getLogger("Sodium-NvidiaWorkarounds"); - public static void install() { - LOGGER.warn("Applying workaround: Prevent the NVIDIA OpenGL driver from using broken optimizations (NVIDIA_THREADED_OPTIMIZATIONS)"); + public static boolean isNvidiaGraphicsCardPresent() { + return GraphicsAdapterProbe.getAdapters() + .stream() + .anyMatch(adapter -> adapter.vendor() == GraphicsAdapterVendor.NVIDIA); + } - try { - switch (OsUtils.getOs()) { - case WIN -> { - // The NVIDIA drivers rely on parsing the command line arguments to detect Minecraft. If we destroy those, - // then it shouldn't be able to detect us anymore. - WindowsCommandLine.setCommandLine("net.caffeinemc.sodium"); - - // Ensures that Minecraft will run on the dedicated GPU, since the drivers can no longer detect it - Kernel32.setEnvironmentVariable("SHIM_MCCOMPAT", "0x800000001"); - } - case LINUX -> { - // Unlike Windows, we don't need to hide ourselves from the driver. We can just request that - // it not use threaded optimizations instead. - Libc.setEnvironmentVariable("__GL_THREADED_OPTIMIZATIONS", "0"); + // https://github.com/CaffeineMC/sodium/issues/1486 + // The way which NVIDIA tries to detect the Minecraft process could not be circumvented until fairly recently + // So we require that an up-to-date graphics driver is installed so that our workarounds can disable the Threaded + // Optimizations driver hack. + public static @Nullable WindowsFileVersion findNvidiaDriverMatchingBug1486() { + // The Linux driver has two separate branches which have overlapping version numbers, despite also having + // different feature sets. As a result, we can't reliably determine which Linux drivers are broken... + if (OsUtils.getOs() != OperatingSystem.WIN) { + return null; + } + + for (var adapter : GraphicsAdapterProbe.getAdapters()) { + if (adapter.vendor() != GraphicsAdapterVendor.NVIDIA) { + continue; + } + + if (adapter instanceof D3DKMT.WDDMAdapterInfo wddmAdapterInfo) { + var driverVersion = wddmAdapterInfo.openglIcdVersion(); + + if (driverVersion.z() == 15) { // Only match 5XX.XX drivers + // Broken in x.y.15.2647 (526.47) + // Fixed in x.y.15.3623 (536.23) + if (driverVersion.w() >= 2647 && driverVersion.w() < 3623) { + return driverVersion; + } } } + } + + return null; + } + + public static void applyEnvironmentChanges() { + // We can't know if the OpenGL context will actually be initialized using the NVIDIA ICD, but we need to + // modify the process environment *now* otherwise the driver will initialize with bad settings. For non-NVIDIA + // drivers, these workarounds are not likely to cause issues. + if (!isNvidiaGraphicsCardPresent()) { + return; + } + + LOGGER.info("Modifying process environment to apply workarounds for the NVIDIA graphics driver..."); + + try { + if (OsUtils.getOs() == OperatingSystem.WIN) { + applyEnvironmentChanges$Windows(); + } else if (OsUtils.getOs() == OperatingSystem.LINUX) { + applyEnvironmentChanges$Linux(); + } } catch (Throwable t) { - LOGGER.error("Failure while applying workarounds", t); + LOGGER.error("Failed to modify the process environment", t); + logWarning(); + } + } + - LOGGER.error("READ ME! The workarounds for the NVIDIA Graphics Driver did not apply correctly!"); - LOGGER.error("READ ME! You are very likely going to run into unexplained crashes and severe performance issues!"); - LOGGER.error("READ ME! Please see this issue for more information: https://github.com/CaffeineMC/sodium-fabric/issues/1816"); + private static void applyEnvironmentChanges$Windows() { + // The NVIDIA drivers rely on parsing the command line arguments to detect Minecraft. We need to + // make sure that it detects the game so that *some* important optimizations are applied. Later, + // we will try to enable GL_DEBUG_OUTPUT_SYNCHRONOUS so that "Threaded Optimizations" cannot + // be enabled. + WindowsCommandLine.setCommandLine("net.caffeinemc.sodium / net.minecraft.client.main.Main /"); + } + + private static void applyEnvironmentChanges$Linux() { + // Unlike Windows, we can just request that it not use threaded optimizations instead. + Libc.setEnvironmentVariable("__GL_THREADED_OPTIMIZATIONS", "0"); + } + + public static void undoEnvironmentChanges() { + if (OsUtils.getOs() == OperatingSystem.WIN) { + undoEnvironmentChanges$Windows(); } } - public static void uninstall() { - switch (OsUtils.getOs()) { - case WIN -> { - WindowsCommandLine.resetCommandLine(); + private static void undoEnvironmentChanges$Windows() { + WindowsCommandLine.resetCommandLine(); + } + + public static void applyContextChanges(GlContextInfo context) { + // The context may not have been initialized with the NVIDIA ICD, even if we think there is an NVIDIA + // graphics adapter in use. Because enabling these workarounds have the potential to severely hurt performance + // on other drivers, make sure we exit now. + if (GraphicsAdapterVendor.fromContext(context) != GraphicsAdapterVendor.NVIDIA) { + return; + } + + LOGGER.info("Modifying OpenGL context to apply workarounds for the NVIDIA graphics driver..."); + + if (Workarounds.isWorkaroundEnabled(Workarounds.Reference.NVIDIA_THREADED_OPTIMIZATIONS_BROKEN)) { + if (OsUtils.getOs() == OperatingSystem.WIN) { + applyContextChanges$Windows(); } - case LINUX -> { } } } + + private static void applyContextChanges$Windows() { + // On Windows, the NVIDIA drivers do not have any environment variable to control whether + // "Threaded Optimizations" are enabled. But we can enable the "GL_DEBUG_OUTPUT_SYNCHRONOUS" option to + // achieve the same effect. + var capabilities = GL.getCapabilities(); + + if (capabilities.GL_KHR_debug) { + LOGGER.info("Enabling GL_DEBUG_OUTPUT_SYNCHRONOUS to force the NVIDIA driver to disable threaded" + + "command submission"); + GL32C.glEnable(KHRDebug.GL_DEBUG_OUTPUT_SYNCHRONOUS); + } else { + LOGGER.error("GL_KHR_debug does not appear to be supported, unable to disable threaded " + + "command submission!"); + logWarning(); + } + } + + private static void logWarning() { + LOGGER.error("READ ME!"); + LOGGER.error("READ ME! The workarounds for the NVIDIA Graphics Driver did not apply correctly!"); + LOGGER.error("READ ME! You are very likely going to run into unexplained crashes and severe performance issues."); + LOGGER.error("READ ME! More information about what went wrong can be found above this message."); + LOGGER.error("READ ME!"); + LOGGER.error("READ ME! Please help us understand why this problem occurred by opening a bug report on our issue tracker:"); + LOGGER.error("READ ME! https://github.com/CaffeineMC/sodium/issues"); + LOGGER.error("READ ME!"); + + } } diff --git a/common/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/MessageBox.java b/common/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/MessageBox.java index 4eccd3aa2a..bf0cfb47cb 100644 --- a/common/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/MessageBox.java +++ b/common/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/MessageBox.java @@ -1,26 +1,23 @@ package net.caffeinemc.mods.sodium.client.platform; import net.caffeinemc.mods.sodium.client.compatibility.environment.OsUtils; -import net.caffeinemc.mods.sodium.client.platform.windows.api.msgbox.MsgBoxParamSw; -import net.caffeinemc.mods.sodium.client.platform.windows.api.msgbox.MsgBoxCallback; import net.caffeinemc.mods.sodium.client.platform.windows.api.User32; +import net.caffeinemc.mods.sodium.client.platform.windows.api.msgbox.MsgBoxCallback; +import net.caffeinemc.mods.sodium.client.platform.windows.api.msgbox.MsgBoxParamSw; import org.jetbrains.annotations.Nullable; -import org.lwjgl.glfw.GLFWNativeWin32; import org.lwjgl.system.MemoryStack; import org.lwjgl.system.MemoryUtil; -import org.lwjgl.util.tinyfd.TinyFileDialogs; import java.awt.*; import java.io.IOException; import java.net.URI; import java.nio.ByteBuffer; -import java.util.Locale; import java.util.Objects; public class MessageBox { private static final @Nullable MessageBoxImpl IMPL = MessageBoxImpl.chooseImpl(); - public static void showMessageBox(long window, + public static void showMessageBox(NativeWindowHandle window, IconType icon, String title, String description, @Nullable String helpUrl) @@ -41,33 +38,15 @@ private interface MessageBoxImpl { } } - void showMessageBox(long window, + void showMessageBox(NativeWindowHandle window, IconType icon, String title, String description, @Nullable String helpUrl); } - private static class TFDMessageBoxImpl implements MessageBoxImpl { - // This adds information about how to open the help box, since we cannot change the buttons. - private static final String NOTICE = "\n\nFor more information, click OK; otherwise, click Cancel."; - - @Override - public void showMessageBox(long window, IconType icon, String title, String description, @Nullable String helpUrl) { - boolean clicked = TinyFileDialogs.tinyfd_messageBox(title, helpUrl == null ? description : description + NOTICE, helpUrl == null ? "ok" : "okcancel", icon.name().toLowerCase(Locale.ROOT), false); - - if (clicked && helpUrl != null) { - try { - Desktop.getDesktop().browse(URI.create(helpUrl)); - } catch (IOException e) { - System.out.println("Failed to open! Giving up."); - } - } - } - } - private static class WindowsMessageBoxImpl implements MessageBoxImpl { @Override - public void showMessageBox(long window, + public void showMessageBox(NativeWindowHandle window, IconType icon, String title, String description, @Nullable String helpUrl) { @@ -91,8 +70,8 @@ public void showMessageBox(long window, final long hWndOwner; - if (window != 0L) { - hWndOwner = GLFWNativeWin32.glfwGetWin32Window(window); + if (window != null) { + hWndOwner = window.getWin32Handle(); } else { hWndOwner = MemoryUtil.NULL; } diff --git a/common/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/NativeWindowHandle.java b/common/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/NativeWindowHandle.java new file mode 100644 index 0000000000..44707a274b --- /dev/null +++ b/common/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/NativeWindowHandle.java @@ -0,0 +1,5 @@ +package net.caffeinemc.mods.sodium.client.platform; + +public interface NativeWindowHandle { + long getWin32Handle(); +} diff --git a/common/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/PlatformHelper.java b/common/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/PlatformHelper.java new file mode 100644 index 0000000000..dfc1f2b77f --- /dev/null +++ b/common/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/PlatformHelper.java @@ -0,0 +1,29 @@ +package net.caffeinemc.mods.sodium.client.platform; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class PlatformHelper { + private static final Logger LOGGER = LoggerFactory.getLogger("Sodium-EarlyDriverScanner"); + + public static void showCriticalErrorAndClose( + @Nullable NativeWindowHandle window, + @NotNull String messageTitle, + @NotNull String messageBody, + @NotNull String helpUrl) + { + // Always print the information to the log file first, just in case we can't show the message box. + LOGGER.error(""" + ###ERROR_DESCRIPTION### + + For more information, please see: ###HELP_URL###""" + .replace("###ERROR_DESCRIPTION###", messageBody) + .replace("###HELP_URL###", helpUrl)); + + // Try to show a graphical message box (if the platform supports it) and shut down the game. + MessageBox.showMessageBox(window, MessageBox.IconType.ERROR, messageTitle, messageBody, helpUrl); + System.exit(1 /* failure code */); + } +} diff --git a/common/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/api/Gdi32.java b/common/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/api/Gdi32.java index 3341963a38..680c071807 100644 --- a/common/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/api/Gdi32.java +++ b/common/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/api/Gdi32.java @@ -1,6 +1,5 @@ package net.caffeinemc.mods.sodium.client.platform.windows.api; -import org.apache.commons.lang3.Validate; import org.lwjgl.system.JNI; import org.lwjgl.system.SharedLibrary; @@ -21,19 +20,24 @@ public static boolean isD3DKMTSupported() { // https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/d3dkmthk/nf-d3dkmthk-d3dkmtqueryadapterinfo public static int /* NTSTATUS */ nd3dKmtQueryAdapterInfo(long ptr /* D3DKMT_QUERYADAPTERINFO */) { - Validate.isTrue(PFN_D3DKMTQueryAdapterInfo != NULL); - return JNI.callPI(ptr, PFN_D3DKMTQueryAdapterInfo); + return JNI.callPI(ptr, checkPfn(PFN_D3DKMTQueryAdapterInfo)); } // https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/d3dkmthk/nf-d3dkmthk-d3dkmtenumadapters2 public static int /* NTSTATUS */ nD3DKMTEnumAdapters(long ptr /* D3DKMT_ENUMADAPTERS */) { - Validate.isTrue(PFN_D3DKMTEnumAdapters != NULL); - return JNI.callPI(ptr, PFN_D3DKMTEnumAdapters); + return JNI.callPI(ptr, checkPfn(PFN_D3DKMTEnumAdapters)); } // https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/d3dkmthk/nf-d3dkmthk-d3dkmtcloseadapter public static int /* NTSTATUS */ nD3DKMTCloseAdapter(long ptr /* D3DKMT_CLOSEADAPTER */) { - Validate.isTrue(PFN_D3DKMTCloseAdapter != NULL); - return JNI.callPI(ptr, PFN_D3DKMTCloseAdapter); + return JNI.callPI(ptr, checkPfn(PFN_D3DKMTCloseAdapter)); + } + + private static long checkPfn(long pfn) { + if (pfn == NULL) { + throw new NullPointerException("Function pointer not available"); + } + + return pfn; } } diff --git a/common/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/api/d3dkmt/D3DKMT.java b/common/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/api/d3dkmt/D3DKMT.java index ed71c373bd..6881b10ea7 100644 --- a/common/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/api/d3dkmt/D3DKMT.java +++ b/common/src/workarounds/java/net/caffeinemc/mods/sodium/client/platform/windows/api/d3dkmt/D3DKMT.java @@ -1,12 +1,10 @@ package net.caffeinemc.mods.sodium.client.platform.windows.api.d3dkmt; -import com.sun.jna.platform.win32.VersionHelpers; import net.caffeinemc.mods.sodium.client.compatibility.environment.probe.GraphicsAdapterInfo; import net.caffeinemc.mods.sodium.client.compatibility.environment.probe.GraphicsAdapterVendor; import net.caffeinemc.mods.sodium.client.platform.windows.WindowsFileVersion; import net.caffeinemc.mods.sodium.client.platform.windows.api.Gdi32; import net.caffeinemc.mods.sodium.client.platform.windows.api.version.Version; -import org.apache.commons.io.FilenameUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.lwjgl.system.MemoryStack; @@ -14,6 +12,7 @@ import org.slf4j.LoggerFactory; import java.nio.ByteBuffer; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; @@ -25,12 +24,11 @@ public class D3DKMT { private static final Logger LOGGER = LoggerFactory.getLogger("Sodium-D3DKMT"); - private static final boolean SUPPORTS_D3DKMT = VersionHelpers.IsWindowsVistaOrGreater() && Gdi32.isD3DKMTSupported(); - private static final boolean SUPPORTS_QUERYING_ADAPTER_TYPE = VersionHelpers.IsWindows8OrGreater(); - public static List findGraphicsAdapters() { - if (!SUPPORTS_D3DKMT) { - LOGGER.warn("Unable to query graphics adapters when the operating system is older than Windows Vista."); + if (!Gdi32.isD3DKMTSupported()) { + // D3DKMT was introduced with Windows Vista, but it was not possible to enumerate adapters and their + // rendering capabilities until Windows 8.0. + LOGGER.warn("Unable to query graphics adapters when the operating system is older than Windows 8.0."); return List.of(); } @@ -74,14 +72,10 @@ private static void freeAdapters(@NotNull D3DKMTAdapterInfoStruct.Buffer adapter } private static @Nullable D3DKMT.WDDMAdapterInfo getAdapterInfo(int adapter) { - int adapterType = -1; - - if (SUPPORTS_QUERYING_ADAPTER_TYPE) { - adapterType = queryAdapterType(adapter); + int adapterType = queryAdapterType(adapter); - if (!isSupportedAdapterType(adapterType)) { - return null; - } + if (!isSupportedAdapterType(adapterType)) { + return null; } String adapterName = queryFriendlyName(adapter); @@ -206,8 +200,18 @@ public String toString() { } } - // Returns (null) if input is (null). - private static String getOpenGlIcdName(String path) { - return FilenameUtils.removeExtension(FilenameUtils.getName(path)); + private static String getOpenGlIcdName(@Nullable String filePath) { + if (filePath == null) { + return null; + } + + var fileName = Paths.get(filePath) + .getFileName(); + + if (fileName == null) { + return null; + } + + return fileName.toString(); } } diff --git a/fabric/build.gradle.kts b/fabric/build.gradle.kts index ce75894c4a..d8c96b3940 100644 --- a/fabric/build.gradle.kts +++ b/fabric/build.gradle.kts @@ -1,31 +1,51 @@ plugins { - id("java") - id("idea") - id("fabric-loom") version ("1.7.3") -} + id("multiloader-platform") -val MINECRAFT_VERSION: String by rootProject.extra -val PARCHMENT_VERSION: String? by rootProject.extra -val FABRIC_LOADER_VERSION: String by rootProject.extra -val FABRIC_API_VERSION: String by rootProject.extra -val MOD_VERSION: String by rootProject.extra + id("fabric-loom") version ("1.8.9") +} base { - archivesName.set("sodium-fabric") + archivesName = "sodium-fabric" +} + +val configurationCommonModJava: Configuration = configurations.create("commonJava") { + isCanBeResolved = true +} +val configurationCommonModResources: Configuration = configurations.create("commonResources") { + isCanBeResolved = true } dependencies { - minecraft("com.mojang:minecraft:${MINECRAFT_VERSION}") + configurationCommonModJava(project(path = ":common", configuration = "commonMainJava")) + configurationCommonModJava(project(path = ":common", configuration = "commonApiJava")) + configurationCommonModJava(project(path = ":common", configuration = "commonEarlyLaunchJava")) + + configurationCommonModResources(project(path = ":common", configuration = "commonMainResources")) + configurationCommonModResources(project(path = ":common", configuration = "commonApiResources")) + configurationCommonModResources(project(path = ":common", configuration = "commonEarlyLaunchResources")) +} + +sourceSets.apply { + main { + compileClasspath += configurationCommonModJava + runtimeClasspath += configurationCommonModJava + } +} + +dependencies { + minecraft("com.mojang:minecraft:${BuildConfig.MINECRAFT_VERSION}") mappings(loom.layered { officialMojangMappings() - if (PARCHMENT_VERSION != null) { - parchment("org.parchmentmc.data:parchment-${MINECRAFT_VERSION}:${PARCHMENT_VERSION}@zip") + + if (BuildConfig.PARCHMENT_VERSION != null) { + parchment("org.parchmentmc.data:parchment-${BuildConfig.MINECRAFT_VERSION}:${BuildConfig.PARCHMENT_VERSION}@zip") } }) - modImplementation("net.fabricmc:fabric-loader:$FABRIC_LOADER_VERSION") + + modImplementation("net.fabricmc:fabric-loader:${BuildConfig.FABRIC_LOADER_VERSION}") fun addEmbeddedFabricModule(name: String) { - val module = fabricApi.module(name, FABRIC_API_VERSION) + val module = fabricApi.module(name, BuildConfig.FABRIC_API_VERSION) modImplementation(module) include(module) } @@ -37,33 +57,20 @@ dependencies { addEmbeddedFabricModule("fabric-rendering-data-attachment-v1") addEmbeddedFabricModule("fabric-rendering-fluids-v1") addEmbeddedFabricModule("fabric-resource-loader-v0") - - implementation("com.google.code.findbugs:jsr305:3.0.1") - - implementation(project.project(":common").sourceSets.getByName("api").output) - implementation(project.project(":common").sourceSets.getByName("main").output) - implementation(project.project(":common").sourceSets.getByName("workarounds").output) -} - -tasks.named("compileTestJava").configure { - enabled = false -} - -tasks.named("test").configure { - enabled = false } loom { - if (project(":common").file("src/main/resources/sodium.accesswidener").exists()) - accessWidenerPath.set(project(":common").file("src/main/resources/sodium.accesswidener")) + accessWidenerPath.set(file("src/main/resources/sodium-fabric.accesswidener")) - @Suppress("UnstableApiUsage") - mixin { defaultRefmapName.set("sodium-fabric.refmap.json") } + mixin { + useLegacyMixinAp = false + } runs { named("client") { client() - configName = "Fabric Client" + configName = "Fabric/Client" + appendProjectPathToConfigName = false ideConfigGenerated(true) runDir("run") } @@ -71,22 +78,15 @@ loom { } tasks { - processResources { - from(project.project(":common").sourceSets.main.get().resources) - inputs.property("version", project.version) - - filesMatching("fabric.mod.json") { - expand(mapOf("version" to project.version)) - } - } - jar { - duplicatesStrategy = DuplicatesStrategy.EXCLUDE - - from(zipTree(project.project(":common").tasks.jar.get().archiveFile)) + from(configurationCommonModJava) + } - manifest.attributes["Main-Class"] = "net.caffeinemc.mods.sodium.desktop.LaunchWarn" + remapJar { + destinationDirectory.set(file(rootProject.layout.buildDirectory).resolve("mods")) } - remapJar.get().destinationDirectory = rootDir.resolve("build").resolve("libs") + processResources { + from(configurationCommonModResources) + } } \ No newline at end of file diff --git a/fabric/src/main/java/net/caffeinemc/mods/sodium/fabric/SodiumFabricMod.java b/fabric/src/main/java/net/caffeinemc/mods/sodium/fabric/SodiumFabricMod.java index 9aa1881b56..a2ede200f0 100644 --- a/fabric/src/main/java/net/caffeinemc/mods/sodium/fabric/SodiumFabricMod.java +++ b/fabric/src/main/java/net/caffeinemc/mods/sodium/fabric/SodiumFabricMod.java @@ -4,7 +4,7 @@ import net.caffeinemc.mods.sodium.client.render.frapi.SodiumRenderer; import net.caffeinemc.mods.sodium.client.util.FlawlessFrames; import net.fabricmc.api.ClientModInitializer; -import net.fabricmc.fabric.api.renderer.v1.RendererAccess; +import net.fabricmc.fabric.api.renderer.v1.Renderer; import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.loader.api.ModContainer; import java.util.function.Consumer; @@ -23,6 +23,6 @@ public void onInitializeClient() { .getEntrypoints("frex_flawless_frames", Consumer.class) .forEach(api -> api.accept(FlawlessFrames.getProvider())); - RendererAccess.INSTANCE.registerRenderer(SodiumRenderer.INSTANCE); + Renderer.register(SodiumRenderer.INSTANCE); } } diff --git a/fabric/src/main/java/net/caffeinemc/mods/sodium/fabric/SodiumPreLaunch.java b/fabric/src/main/java/net/caffeinemc/mods/sodium/fabric/SodiumPreLaunch.java index 167edadf8b..473c5a6e8e 100644 --- a/fabric/src/main/java/net/caffeinemc/mods/sodium/fabric/SodiumPreLaunch.java +++ b/fabric/src/main/java/net/caffeinemc/mods/sodium/fabric/SodiumPreLaunch.java @@ -8,9 +8,8 @@ public class SodiumPreLaunch implements PreLaunchEntrypoint { @Override public void onPreLaunch() { - PreLaunchChecks.beforeLWJGLInit(); + PreLaunchChecks.checkEnvironment(); GraphicsAdapterProbe.findAdapters(); - PreLaunchChecks.onGameInit(); Workarounds.init(); } } diff --git a/fabric/src/main/java/net/caffeinemc/mods/sodium/fabric/block/FabricBlockAccess.java b/fabric/src/main/java/net/caffeinemc/mods/sodium/fabric/block/FabricBlockAccess.java index 29069822a4..0fd7c13935 100644 --- a/fabric/src/main/java/net/caffeinemc/mods/sodium/fabric/block/FabricBlockAccess.java +++ b/fabric/src/main/java/net/caffeinemc/mods/sodium/fabric/block/FabricBlockAccess.java @@ -90,4 +90,9 @@ public AmbientOcclusionMode usesAmbientOcclusion(BakedModel model, BlockState st public boolean shouldBlockEntityGlow(BlockEntity blockEntity, LocalPlayer player) { return false; } + + @Override + public boolean shouldOccludeFluid(Direction adjDirection, BlockState adjBlockState, FluidState fluid) { + return adjBlockState.getFluidState().getType().isSame(fluid.getType()); + } } diff --git a/neoforge/src/main/java/net/caffeinemc/mods/sodium/neoforge/mixin/core/model/quad/BakedQuadMixin.java b/fabric/src/main/java/net/caffeinemc/mods/sodium/mixin/core/model/quad/BakedQuadMixin.java similarity index 85% rename from neoforge/src/main/java/net/caffeinemc/mods/sodium/neoforge/mixin/core/model/quad/BakedQuadMixin.java rename to fabric/src/main/java/net/caffeinemc/mods/sodium/mixin/core/model/quad/BakedQuadMixin.java index a2682022f0..38d70ac214 100644 --- a/neoforge/src/main/java/net/caffeinemc/mods/sodium/neoforge/mixin/core/model/quad/BakedQuadMixin.java +++ b/fabric/src/main/java/net/caffeinemc/mods/sodium/mixin/core/model/quad/BakedQuadMixin.java @@ -1,9 +1,10 @@ -package net.caffeinemc.mods.sodium.neoforge.mixin.core.model.quad; +package net.caffeinemc.mods.sodium.mixin.core.model.quad; import net.caffeinemc.mods.sodium.client.model.quad.BakedQuadView; import net.caffeinemc.mods.sodium.client.model.quad.properties.ModelQuadFacing; import net.caffeinemc.mods.sodium.client.model.quad.properties.ModelQuadFlags; import net.caffeinemc.mods.sodium.client.util.ModelQuadUtil; +import net.minecraft.client.renderer.LightTexture; import net.minecraft.client.renderer.block.model.BakedQuad; import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.core.Direction; @@ -16,7 +17,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(BakedQuad.class) -public class BakedQuadMixin implements BakedQuadView { +public abstract class BakedQuadMixin implements BakedQuadView { @Shadow @Final protected int[] vertices; @@ -37,6 +38,9 @@ public class BakedQuadMixin implements BakedQuadView { @Final private boolean shade; + @Shadow + public abstract int getLightEmission(); + @Unique private int flags; @@ -46,10 +50,8 @@ public class BakedQuadMixin implements BakedQuadView { @Unique private ModelQuadFacing normalFace = null; - @Inject(method = { - "([IILnet/minecraft/core/Direction;Lnet/minecraft/client/renderer/texture/TextureAtlasSprite;ZZ)V" - }, at = @At("RETURN")) - private void init(int[] is, int i, Direction face, TextureAtlasSprite arg2, boolean bl, boolean hasAmbientOcclusion, CallbackInfo ci) { + @Inject(method = "", at = @At("RETURN")) + private void init(int[] is, int i, Direction face, TextureAtlasSprite textureAtlasSprite, boolean bl, int j, CallbackInfo ci) { this.normal = this.calculateNormal(); this.normalFace = ModelQuadFacing.fromPackedNormal(this.normal); @@ -107,7 +109,7 @@ public int getFlags() { } @Override - public int getColorIndex() { + public int getTintIndex() { return this.tintIndex; } @@ -126,9 +128,19 @@ public Direction getLightFace() { return this.direction; } + @Override + public int getMaxLightQuad(int idx) { + return LightTexture.lightCoordsWithEmission(getLight(idx), getLightEmission()); + } + @Override @Unique(silent = true) // The target class has a function with the same name in a remapped environment public boolean hasShade() { return this.shade; } + + @Override + public boolean hasAO() { + return true; + } } diff --git a/fabric/src/main/java/net/caffeinemc/mods/sodium/mixin/fabric/features/model/MultiPartBakedModelMixin.java b/fabric/src/main/java/net/caffeinemc/mods/sodium/mixin/features/model/MultiPartBakedModelMixin.java similarity index 86% rename from fabric/src/main/java/net/caffeinemc/mods/sodium/mixin/fabric/features/model/MultiPartBakedModelMixin.java rename to fabric/src/main/java/net/caffeinemc/mods/sodium/mixin/features/model/MultiPartBakedModelMixin.java index af83f38319..0f90f63a90 100644 --- a/fabric/src/main/java/net/caffeinemc/mods/sodium/mixin/fabric/features/model/MultiPartBakedModelMixin.java +++ b/fabric/src/main/java/net/caffeinemc/mods/sodium/mixin/features/model/MultiPartBakedModelMixin.java @@ -1,4 +1,4 @@ -package net.caffeinemc.mods.sodium.mixin.fabric.features.model; +package net.caffeinemc.mods.sodium.mixin.features.model; import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap; import net.minecraft.client.renderer.block.model.BakedQuad; @@ -26,7 +26,7 @@ public class MultiPartBakedModelMixin { @Shadow @Final - private List, BakedModel>> selectors; + private List selectors; /** * @author JellySquid @@ -52,9 +52,9 @@ public List getQuads(BlockState state, Direction face, RandomSource r try { List modelList = new ArrayList<>(this.selectors.size()); - for (Pair, BakedModel> pair : this.selectors) { - if (pair.getLeft().test(state)) { - modelList.add(pair.getRight()); + for (MultiPartBakedModel.Selector selector : this.selectors) { + if (selector.condition().test(state)) { + modelList.add(selector.model()); } } diff --git a/fabric/src/main/java/net/caffeinemc/mods/sodium/mixin/fabric/features/model/WeightedBakedModelMixin.java b/fabric/src/main/java/net/caffeinemc/mods/sodium/mixin/features/model/WeightedBakedModelMixin.java similarity index 54% rename from fabric/src/main/java/net/caffeinemc/mods/sodium/mixin/fabric/features/model/WeightedBakedModelMixin.java rename to fabric/src/main/java/net/caffeinemc/mods/sodium/mixin/features/model/WeightedBakedModelMixin.java index 41bef68758..2668c97b96 100644 --- a/fabric/src/main/java/net/caffeinemc/mods/sodium/mixin/fabric/features/model/WeightedBakedModelMixin.java +++ b/fabric/src/main/java/net/caffeinemc/mods/sodium/mixin/features/model/WeightedBakedModelMixin.java @@ -1,10 +1,12 @@ -package net.caffeinemc.mods.sodium.mixin.fabric.features.model; +package net.caffeinemc.mods.sodium.mixin.features.model; +import net.caffeinemc.mods.sodium.client.util.WeightedRandomListExtension; import net.minecraft.client.renderer.block.model.BakedQuad; import net.minecraft.client.resources.model.BakedModel; import net.minecraft.client.resources.model.WeightedBakedModel; import net.minecraft.core.Direction; import net.minecraft.util.RandomSource; +import net.minecraft.util.random.SimpleWeightedRandomList; import net.minecraft.util.random.WeightedEntry; import net.minecraft.world.level.block.state.BlockState; import org.jetbrains.annotations.Nullable; @@ -17,30 +19,7 @@ public class WeightedBakedModelMixin { @Shadow @Final - private List> list; - - @Shadow - @Final - private int totalWeight; - - @Unique - private static T getAt(List pool, int totalWeight) { - int i = 0; - int len = pool.size(); - - T weighted; - - do { - if (i >= len) { - return null; - } - - weighted = pool.get(i++); - totalWeight -= weighted.getWeight().asInt(); - } while (totalWeight >= 0); - - return weighted; - } + private SimpleWeightedRandomList list; /** * @author JellySquid @@ -48,10 +27,10 @@ private static T getAt(List pool, int totalWeight) */ @Overwrite public List getQuads(@Nullable BlockState state, @Nullable Direction face, RandomSource random) { - WeightedEntry.Wrapper quad = getAt(this.list, Math.abs((int) random.nextLong()) % this.totalWeight); + WeightedEntry.Wrapper model = ((WeightedRandomListExtension>) list).sodium$getQuick(random); - if (quad != null) { - return quad.data() + if (model != null) { + return model.data() .getQuads(state, face, random); } diff --git a/fabric/src/main/java/net/caffeinemc/mods/sodium/mixin/features/model/WeightedRandomListMixin.java b/fabric/src/main/java/net/caffeinemc/mods/sodium/mixin/features/model/WeightedRandomListMixin.java new file mode 100644 index 0000000000..9bb5b70266 --- /dev/null +++ b/fabric/src/main/java/net/caffeinemc/mods/sodium/mixin/features/model/WeightedRandomListMixin.java @@ -0,0 +1,44 @@ +package net.caffeinemc.mods.sodium.mixin.features.model; + +import com.google.common.collect.ImmutableList; +import net.caffeinemc.mods.sodium.client.util.WeightedRandomListExtension; +import net.minecraft.util.RandomSource; +import net.minecraft.util.random.WeightedEntry; +import net.minecraft.util.random.WeightedRandomList; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +@Mixin(WeightedRandomList.class) +public class WeightedRandomListMixin implements WeightedRandomListExtension { + @Shadow + @Final + private ImmutableList items; + + @Shadow + @Final + private int totalWeight; + + @Override + public E sodium$getQuick(RandomSource random) { + if (this.totalWeight == 0) return null; + + int randomValue = Math.abs((int) random.nextLong()) % this.totalWeight; + + int i = 0; + int len = items.size(); + + E weighted; + + do { + if (i >= len) { + return null; + } + + weighted = items.get(i++); + randomValue -= weighted.getWeight().asInt(); + } while (randomValue >= 0); + + return weighted; + } +} diff --git a/fabric/src/main/java/net/caffeinemc/mods/sodium/mixin/fabric/features/render/model/block/ModelBlockRendererMixin.java b/fabric/src/main/java/net/caffeinemc/mods/sodium/mixin/features/render/model/block/ModelBlockRendererMixin.java similarity index 97% rename from fabric/src/main/java/net/caffeinemc/mods/sodium/mixin/fabric/features/render/model/block/ModelBlockRendererMixin.java rename to fabric/src/main/java/net/caffeinemc/mods/sodium/mixin/features/render/model/block/ModelBlockRendererMixin.java index 59240cbb4b..6fbc0d2ecf 100644 --- a/fabric/src/main/java/net/caffeinemc/mods/sodium/mixin/fabric/features/render/model/block/ModelBlockRendererMixin.java +++ b/fabric/src/main/java/net/caffeinemc/mods/sodium/mixin/features/render/model/block/ModelBlockRendererMixin.java @@ -1,4 +1,4 @@ -package net.caffeinemc.mods.sodium.mixin.fabric.features.render.model.block; +package net.caffeinemc.mods.sodium.mixin.features.render.model.block; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.VertexConsumer; @@ -44,7 +44,7 @@ private static void renderQuads(PoseStack.Pose matrices, VertexBufferWriter writ int color = quad.hasColor() ? defaultColor : 0xFFFFFFFF; - BakedModelEncoder.writeQuadVertices(writer, matrices, quad, color, light, overlay); + BakedModelEncoder.writeQuadVertices(writer, matrices, quad, color, light, overlay, false); SpriteUtil.markSpriteActive(quad.getSprite()); } diff --git a/fabric/src/main/java/net/caffeinemc/mods/sodium/mixin/fabric/features/world/biome/BiomeMixin.java b/fabric/src/main/java/net/caffeinemc/mods/sodium/mixin/features/world/biome/BiomeMixin.java similarity index 97% rename from fabric/src/main/java/net/caffeinemc/mods/sodium/mixin/fabric/features/world/biome/BiomeMixin.java rename to fabric/src/main/java/net/caffeinemc/mods/sodium/mixin/features/world/biome/BiomeMixin.java index 87acb700be..05c12f8caa 100644 --- a/fabric/src/main/java/net/caffeinemc/mods/sodium/mixin/fabric/features/world/biome/BiomeMixin.java +++ b/fabric/src/main/java/net/caffeinemc/mods/sodium/mixin/features/world/biome/BiomeMixin.java @@ -1,4 +1,4 @@ -package net.caffeinemc.mods.sodium.mixin.fabric.features.world.biome; +package net.caffeinemc.mods.sodium.mixin.features.world.biome; import net.caffeinemc.mods.sodium.client.world.biome.BiomeColorMaps; import net.minecraft.util.Mth; diff --git a/fabric/src/main/resources/fabric.mod.json b/fabric/src/main/resources/fabric.mod.json index ae169fd74d..981d74eca4 100644 --- a/fabric/src/main/resources/fabric.mod.json +++ b/fabric/src/main/resources/fabric.mod.json @@ -6,7 +6,7 @@ "description" : "Sodium is a powerful rendering engine for Minecraft which greatly improves frame rates and micro-stutter, while fixing many graphical issues", "authors" : [ { - "name" : "@jellysquid3", + "name" : "JellySquid (jellysquid3)", "contact" : { "email" : "jellysquid@pm.me", "homepage" : "https://jellysquid.me" @@ -14,32 +14,33 @@ } ], "contributors" : [ - "@IMS212", - "@bytzo", - "@PepperCode1", - "@FlashyReese", - "@altrisi", - "@Grayray75", - "@Madis0", - "@Johni0702", - "@comp500", - "@coderbot16", - "@Moulberry", - "@MCRcortex", - "@Altirix", - "@embeddedt", - "@pajicadvance", - "@Kroppeb", - "@douira", - "@burgerindividual", - "@TwistedZero", - "@Leo40Git", - "@haykam821" + "IMS212", + "bytzo", + "PepperCode1", + "FlashyReese", + "altrisi", + "Grayray75", + "Madis0", + "Johni0702", + "comp500", + "coderbot16", + "Moulberry", + "MCRcortex", + "Altirix", + "embeddedt", + "pajicadvance", + "Kroppeb", + "douira", + "burgerindividual", + "TwistedZero", + "Leo40Git", + "haykam821", + "muzikbike" ], "contact" : { - "homepage" : "https://github.com/CaffeineMC/sodium-fabric", - "sources" : "https://github.com/CaffeineMC/sodium-fabric", - "issues" : "https://github.com/CaffeineMC/sodium-fabric/issues" + "homepage" : "https://github.com/CaffeineMC/sodium", + "sources" : "https://github.com/CaffeineMC/sodium", + "issues" : "https://github.com/CaffeineMC/sodium/issues" }, "license" : "Polyform-Shield-1.0.0", "icon" : "sodium-icon.png", @@ -58,20 +59,20 @@ "links" : { "modmenu.donate" : "https://caffeinemc.net/donate", "modmenu.discord" : "https://caffeinemc.net/discord", - "modmenu.github_releases" : "https://github.com/CaffeineMC/sodium-fabric/releases", + "modmenu.github_releases" : "https://github.com/CaffeineMC/sodium/releases", "modmenu.kofi" : "https://caffeinemc.net/donate", "modmenu.modrinth" : "https://modrinth.com/mod/sodium" } } }, - "accessWidener" : "sodium.accesswidener", + "accessWidener" : "sodium-fabric.accesswidener", "mixins" : [ - "sodium.mixins.json", + "sodium-common.mixins.json", "sodium-fabric.mixins.json" ], "depends" : { - "minecraft" : ["1.21", "1.21.1"], - "fabricloader" : ">=0.12.0", + "minecraft": ["1.21.4"], + "fabricloader" : ">=0.16.0", "fabric-block-view-api-v2" : "*", "fabric-renderer-api-v1" : "*", "fabric-rendering-data-attachment-v1" : "*", @@ -82,23 +83,26 @@ "embeddium": "*", "optifabric" : "*", "canvas" : "*", + "vulkanmod": "*", "sodium-blendingregistry" : "*", "ocrenderfix_sodium" : "*", "betterfpsdist" : "<=1.21-4.5", - "bobby" : "<=5.2.3", - "chunksfadein" : "<=1.0.1-1.21", + "bobby" : "<5.2.4", + "chunksfadein" : "<2.0.2-1.21.2", "cull-less-leaves" : "<=1.3.0", "cullleaves" : "<=3.4.0-fabric", "custom_hud" : "<3.4.2", "farsight" : "<=1.21-4.3", - "iceberg" : "<1.2.4", - "iris" : "<=1.7.3", + "iceberg" : "<1.2.7", + "iris" : "<1.8.1", "movingelevators" : "<=1.4.7", "notenoughcrashes" : "<4.4.8", - "noxesium" : "<2.1.4", - "reeses-sodium-options" : "<=1.7.3", - "sodium-extra" : "<=0.5.7", - "sspb" : "<=3.3.1" + "noxesium" : "<2.3.3", + "reeses-sodium-options" : "<1.8.0", + "sodium-extra" : "<0.6.0", + "sspb" : "<4.0.0", + "moreculling": "<1.0.0", + "simply-no-shading": "<7.6.2" }, "provides" : [ "indium" diff --git a/common/src/main/resources/sodium.accesswidener b/fabric/src/main/resources/sodium-fabric.accesswidener similarity index 100% rename from common/src/main/resources/sodium.accesswidener rename to fabric/src/main/resources/sodium-fabric.accesswidener diff --git a/fabric/src/main/resources/sodium-fabric.mixins.json b/fabric/src/main/resources/sodium-fabric.mixins.json index 512c9ca954..123b0443f0 100644 --- a/fabric/src/main/resources/sodium-fabric.mixins.json +++ b/fabric/src/main/resources/sodium-fabric.mixins.json @@ -1,8 +1,7 @@ { - "package": "net.caffeinemc.mods.sodium.mixin.fabric", + "package": "net.caffeinemc.mods.sodium.mixin", "required": true, "compatibilityLevel": "JAVA_17", - "refmap": "sodium-fabric.refmap.json", "injectors": { "defaultRequire": 1 }, @@ -10,10 +9,11 @@ "conformVisibility": true }, "client": [ - "core.model.quad.BakedQuadMixin", - "features.model.MultiPartBakedModelMixin", - "features.model.WeightedBakedModelMixin", - "features.render.model.block.ModelBlockRendererMixin", - "features.world.biome.BiomeMixin" + "core.model.quad.BakedQuadMixin", + "features.model.MultiPartBakedModelMixin", + "features.model.WeightedBakedModelMixin", + "features.model.WeightedRandomListMixin", + "features.render.model.block.ModelBlockRendererMixin", + "features.world.biome.BiomeMixin" ] } \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 09523c0e54..df97d72b8b 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/neoforge/build.gradle.kts b/neoforge/build.gradle.kts index 3235b1ef56..4c23904cea 100644 --- a/neoforge/build.gradle.kts +++ b/neoforge/build.gradle.kts @@ -1,156 +1,142 @@ plugins { - id("idea") - id("net.neoforged.moddev") version "2.0.28-beta" - id("java-library") -} + id("multiloader-platform") -val MINECRAFT_VERSION: String by rootProject.extra -val PARCHMENT_VERSION: String? by rootProject.extra -val NEOFORGE_VERSION: String by rootProject.extra -val MOD_VERSION: String by rootProject.extra + id("net.neoforged.moddev") version("2.0.42-beta") +} base { archivesName = "sodium-neoforge" } -sourceSets { - val service = create("service") - - service.apply { - compileClasspath += main.get().compileClasspath - compileClasspath += project(":common").sourceSets.getByName("workarounds").output - } - - main.get().apply { - compileClasspath += project(":common").sourceSets.getByName("workarounds").output - runtimeClasspath += project(":common").sourceSets.getByName("workarounds").output - } -} - repositories { - maven("https://maven.pkg.github.com/ims212/Forge_Fabric_API") { + maven("https://maven.pkg.github.com/ims212/ForgifiedFabricAPI") { credentials { username = "IMS212" // Read only token password = "ghp_" + "DEuGv0Z56vnSOYKLCXdsS9svK4nb9K39C1Hn" } } + maven("https://maven.su5ed.dev/releases") maven("https://maven.neoforged.net/releases/") +} - exclusiveContent { - forRepository { - maven { - name = "Modrinth" - url = uri("https://api.modrinth.com/maven") - } - } - filter { - includeGroup("maven.modrinth") - } - } +sourceSets { + create("service") } -val serviceJar: Jar by tasks.creating(Jar::class) { - from(sourceSets.getByName("service").output) - from(project(":common").sourceSets.getByName("workarounds").output) - from(rootDir.resolve("LICENSE.md")) - manifest.attributes["FMLModType"] = "LIBRARY" - archiveClassifier = "service" +val configurationCommonModJava: Configuration = configurations.create("commonModJava") { + isCanBeResolved = true +} +val configurationCommonModResources: Configuration = configurations.create("commonModResources") { + isCanBeResolved = true } -configurations { - create("serviceConfig") { - isCanBeConsumed = true - isCanBeResolved = false - outgoing { - artifact(serviceJar) - } - } +val configurationCommonServiceJava: Configuration = configurations.create("commonServiceJava") { + isCanBeResolved = true +} +val configurationCommonServiceResources: Configuration = configurations.create("commonServiceResources") { + isCanBeResolved = true } dependencies { - jarJar(project(":neoforge", "serviceConfig")) -} + configurationCommonModJava(project(path = ":common", configuration = "commonMainJava")) + configurationCommonModJava(project(path = ":common", configuration = "commonApiJava")) + configurationCommonServiceJava(project(path = ":common", configuration = "commonEarlyLaunchJava")) -tasks.jar { - val api = project.project(":common").sourceSets.getByName("api") - from(api.output.classesDirs) - from(api.output.resourcesDir) + configurationCommonModResources(project(path = ":common", configuration = "commonMainResources")) + configurationCommonModResources(project(path = ":common", configuration = "commonApiResources")) + configurationCommonServiceResources(project(path = ":common", configuration = "commonEarlyLaunchResources")) - val main = project.project(":common").sourceSets.getByName("main") - from(main.output.classesDirs) { - exclude("/sodium.refmap.json") + fun addEmbeddedFabricModule(dependency: String) { + dependencies.implementation(dependency) + dependencies.jarJar(dependency) } - from(main.output.resourcesDir) - val desktop = project.project(":common").sourceSets.getByName("desktop") - from(desktop.output.classesDirs) - from(desktop.output.resourcesDir) + addEmbeddedFabricModule("org.sinytra.forgified-fabric-api:fabric-api-base:0.4.42+d1308ded19") + addEmbeddedFabricModule("org.sinytra.forgified-fabric-api:fabric-renderer-api-v1:5.0.0+babc52e504") + addEmbeddedFabricModule("org.sinytra.forgified-fabric-api:fabric-rendering-data-attachment-v1:0.3.48+73761d2e19") + addEmbeddedFabricModule("org.sinytra.forgified-fabric-api:fabric-block-view-api-v2:1.0.10+9afaaf8c19") + + jarJar(project(":neoforge", "service")) +} + +val serviceJar = tasks.create("serviceJar") { + from(configurationCommonServiceJava) + from(configurationCommonServiceResources) + + from(sourceSets["service"].output) from(rootDir.resolve("LICENSE.md")) - filesMatching("neoforge.mods.toml") { - expand(mapOf("version" to MOD_VERSION)) - } + manifest.attributes["FMLModType"] = "LIBRARY" + + archiveClassifier = "service" +} + +val configurationService: Configuration = configurations.create("service") { + isCanBeConsumed = true + isCanBeResolved = true - manifest.attributes["Main-Class"] = "net.caffeinemc.mods.sodium.desktop.LaunchWarn" + outgoing { + artifact(serviceJar) + } } -tasks.jar.get().destinationDirectory = rootDir.resolve("build").resolve("libs") +sourceSets { + named("service") { + compileClasspath = sourceSets["main"].compileClasspath + runtimeClasspath = sourceSets["main"].runtimeClasspath + + compileClasspath += configurationCommonServiceJava + runtimeClasspath += configurationCommonServiceJava + } + + main { + compileClasspath += configurationCommonModJava + runtimeClasspath += configurationCommonModJava + } +} neoForge { - // Specify the version of NeoForge to use. - version = NEOFORGE_VERSION + version = BuildConfig.NEOFORGE_VERSION - if (PARCHMENT_VERSION != null) { + if (BuildConfig.PARCHMENT_VERSION != null) { parchment { - minecraftVersion = MINECRAFT_VERSION - mappingsVersion = PARCHMENT_VERSION + minecraftVersion = BuildConfig.MINECRAFT_VERSION + mappingsVersion = BuildConfig.PARCHMENT_VERSION } } runs { - create("client") { + create("Client") { client() + ideName = "NeoForge/Client" } } mods { create("sodium") { - sourceSet(sourceSets.main.get()) - sourceSet(project.project(":common").sourceSets.main.get()) - sourceSet(project.project(":common").sourceSets.getByName("api")) + sourceSet(sourceSets["main"]) + sourceSet(project(":common").sourceSets["main"]) + sourceSet(project(":common").sourceSets["api"]) } - create("sodiumservice") { + create("sodium-service") { sourceSet(sourceSets["service"]) sourceSet(project(":common").sourceSets["workarounds"]) } } } -fun includeDep(dependency: String, closure: Action) { - dependencies.implementation(dependency, closure) - dependencies.jarJar(dependency, closure) -} - -fun includeDep(dependency: String) { - dependencies.implementation(dependency) - dependencies.jarJar(dependency) -} - -tasks.named("compileTestJava").configure { - enabled = false -} +tasks { + jar { + from(configurationCommonModJava) + destinationDirectory.set(file(rootProject.layout.buildDirectory).resolve("mods")) + } -dependencies { - compileOnly(project.project(":common").sourceSets.main.get().output) - compileOnly(project.project(":common").sourceSets.getByName("api").output) - includeDep("org.sinytra.forgified-fabric-api:fabric-api-base:0.4.42+d1308ded19") - includeDep("org.sinytra.forgified-fabric-api:fabric-renderer-api-v1:3.4.0+acb05a3919") - includeDep("org.sinytra.forgified-fabric-api:fabric-rendering-data-attachment-v1:0.3.48+73761d2e19") - includeDep("org.sinytra.forgified-fabric-api:fabric-block-view-api-v2:1.0.10+9afaaf8c19") + processResources { + from(configurationCommonModResources) + } } -java.toolchain.languageVersion = JavaLanguageVersion.of(21) \ No newline at end of file diff --git a/fabric/src/main/java/net/caffeinemc/mods/sodium/mixin/fabric/core/model/quad/BakedQuadMixin.java b/neoforge/src/main/java/net/caffeinemc/mods/sodium/mixin/core/model/quad/BakedQuadMixin.java similarity index 74% rename from fabric/src/main/java/net/caffeinemc/mods/sodium/mixin/fabric/core/model/quad/BakedQuadMixin.java rename to neoforge/src/main/java/net/caffeinemc/mods/sodium/mixin/core/model/quad/BakedQuadMixin.java index bed6b8dec2..6f09fa3591 100644 --- a/fabric/src/main/java/net/caffeinemc/mods/sodium/mixin/fabric/core/model/quad/BakedQuadMixin.java +++ b/neoforge/src/main/java/net/caffeinemc/mods/sodium/mixin/core/model/quad/BakedQuadMixin.java @@ -1,9 +1,10 @@ -package net.caffeinemc.mods.sodium.mixin.fabric.core.model.quad; +package net.caffeinemc.mods.sodium.mixin.core.model.quad; import net.caffeinemc.mods.sodium.client.model.quad.BakedQuadView; import net.caffeinemc.mods.sodium.client.model.quad.properties.ModelQuadFacing; import net.caffeinemc.mods.sodium.client.model.quad.properties.ModelQuadFlags; import net.caffeinemc.mods.sodium.client.util.ModelQuadUtil; +import net.minecraft.client.renderer.LightTexture; import net.minecraft.client.renderer.block.model.BakedQuad; import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.core.Direction; @@ -16,7 +17,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(BakedQuad.class) -public class BakedQuadMixin implements BakedQuadView { +public abstract class BakedQuadMixin implements BakedQuadView { @Shadow @Final protected int[] vertices; @@ -37,20 +38,25 @@ public class BakedQuadMixin implements BakedQuadView { @Final private boolean shade; + @Shadow + public abstract boolean hasAmbientOcclusion(); + + @Shadow + public abstract int getLightEmission(); + @Unique private int flags; @Unique - private int normal; + private int normal = -1; @Unique private ModelQuadFacing normalFace = null; - @Inject(method = "", at = @At("RETURN")) - private void init(int[] vertexData, int colorIndex, Direction face, TextureAtlasSprite sprite, boolean shade, CallbackInfo ci) { - this.normal = this.calculateNormal(); - this.normalFace = ModelQuadFacing.fromPackedNormal(this.normal); - + @Inject(method = { + "([IILnet/minecraft/core/Direction;Lnet/minecraft/client/renderer/texture/TextureAtlasSprite;ZIZ)V" + }, at = @At("RETURN")) + private void init(int[] is, int i, Direction face, TextureAtlasSprite arg2, boolean bl, int ambientLight, boolean hasAmbientOcclusion, CallbackInfo ci) { this.flags = ModelQuadFlags.getQuadFlags(this, face); } @@ -105,17 +111,25 @@ public int getFlags() { } @Override - public int getColorIndex() { + public int getTintIndex() { return this.tintIndex; } @Override public ModelQuadFacing getNormalFace() { + if (this.normalFace == null) { + this.normalFace = ModelQuadFacing.fromPackedNormal(this.getFaceNormal()); + } + return this.normalFace; } @Override public int getFaceNormal() { + if (this.normal == -1) { + this.normal = this.calculateNormal(); + } + return this.normal; } @@ -124,9 +138,19 @@ public Direction getLightFace() { return this.direction; } + @Override + public int getMaxLightQuad(int idx) { + return LightTexture.lightCoordsWithEmission(getLight(idx), getLightEmission()); + } + @Override @Unique(silent = true) // The target class has a function with the same name in a remapped environment public boolean hasShade() { return this.shade; } + + @Override + public boolean hasAO() { + return this.hasAmbientOcclusion(); + } } diff --git a/neoforge/src/main/java/net/caffeinemc/mods/sodium/neoforge/mixin/features/model/MultiPartBakedModelMixin.java b/neoforge/src/main/java/net/caffeinemc/mods/sodium/mixin/features/model/MultiPartBakedModelMixin.java similarity index 82% rename from neoforge/src/main/java/net/caffeinemc/mods/sodium/neoforge/mixin/features/model/MultiPartBakedModelMixin.java rename to neoforge/src/main/java/net/caffeinemc/mods/sodium/mixin/features/model/MultiPartBakedModelMixin.java index a1facd13da..c8042dfc8f 100644 --- a/neoforge/src/main/java/net/caffeinemc/mods/sodium/neoforge/mixin/features/model/MultiPartBakedModelMixin.java +++ b/neoforge/src/main/java/net/caffeinemc/mods/sodium/mixin/features/model/MultiPartBakedModelMixin.java @@ -1,8 +1,8 @@ -package net.caffeinemc.mods.sodium.neoforge.mixin.features.model; +package net.caffeinemc.mods.sodium.mixin.features.model; import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap; -import net.caffeinemc.mods.sodium.neoforge.mixin.ChunkRenderTypeSetAccessor; -import net.caffeinemc.mods.sodium.neoforge.mixin.SimpleBakedModelAccessor; +import net.caffeinemc.mods.sodium.mixin.platform.neoforge.ChunkRenderTypeSetAccessor; +import net.caffeinemc.mods.sodium.mixin.platform.neoforge.SimpleBakedModelAccessor; import net.minecraft.client.renderer.ItemBlockRenderTypes; import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.block.model.BakedQuad; @@ -15,7 +15,6 @@ import net.neoforged.neoforge.client.ChunkRenderTypeSet; import net.neoforged.neoforge.client.model.data.ModelData; import net.neoforged.neoforge.client.model.data.MultipartModelData; -import org.apache.commons.lang3.tuple.Pair; import org.jetbrains.annotations.NotNull; import org.spongepowered.asm.mixin.*; import org.spongepowered.asm.mixin.injection.At; @@ -36,14 +35,14 @@ public class MultiPartBakedModelMixin { @Shadow @Final - private List, BakedModel>> selectors; + private List selectors; @Unique private boolean canSkipRenderTypeCheck; @Inject(method = "", at = @At("RETURN")) - private void storeClassInfo(List, BakedModel>> list, CallbackInfo ci) { - this.canSkipRenderTypeCheck = this.selectors.stream().allMatch(model -> (model.getRight() instanceof SimpleBakedModel simpleModel && ((SimpleBakedModelAccessor) simpleModel).getBlockRenderTypes() == null)); + private void storeClassInfo(List selectors, CallbackInfo ci) { + this.canSkipRenderTypeCheck = this.selectors.stream().allMatch(model -> (model.model() instanceof SimpleBakedModel simpleModel && ((SimpleBakedModelAccessor) simpleModel).getBlockRenderTypes() == null)); } /** @@ -70,9 +69,9 @@ public List getQuads(@Nullable BlockState state, @Nullable Direction try { List modelList = new ArrayList<>(this.selectors.size()); - for (Pair, BakedModel> pair : this.selectors) { - if (pair.getLeft().test(state)) { - modelList.add(pair.getRight()); + for (MultiPartBakedModel.Selector selector : this.selectors) { + if (selector.condition().test(state)) { + modelList.add(selector.model()); } } @@ -123,9 +122,9 @@ public ChunkRenderTypeSet getRenderTypes(@NotNull BlockState state, @NotNull Ran try { List modelList = new ArrayList<>(this.selectors.size()); - for (Pair, BakedModel> pair : this.selectors) { - if (pair.getLeft().test(state)) { - modelList.add(pair.getRight()); + for (MultiPartBakedModel.Selector selector : this.selectors) { + if (selector.condition().test(state)) { + modelList.add(selector.model()); } } diff --git a/neoforge/src/main/java/net/caffeinemc/mods/sodium/neoforge/mixin/features/model/WeightedBakedModelMixin.java b/neoforge/src/main/java/net/caffeinemc/mods/sodium/mixin/features/model/WeightedBakedModelMixin.java similarity index 51% rename from neoforge/src/main/java/net/caffeinemc/mods/sodium/neoforge/mixin/features/model/WeightedBakedModelMixin.java rename to neoforge/src/main/java/net/caffeinemc/mods/sodium/mixin/features/model/WeightedBakedModelMixin.java index 31afaf092f..d5747a53cd 100644 --- a/neoforge/src/main/java/net/caffeinemc/mods/sodium/neoforge/mixin/features/model/WeightedBakedModelMixin.java +++ b/neoforge/src/main/java/net/caffeinemc/mods/sodium/mixin/features/model/WeightedBakedModelMixin.java @@ -1,16 +1,18 @@ -package net.caffeinemc.mods.sodium.neoforge.mixin.features.model; +package net.caffeinemc.mods.sodium.mixin.features.model; -import net.minecraft.client.renderer.RenderType; +import net.caffeinemc.mods.sodium.client.util.WeightedRandomListExtension; import net.minecraft.client.renderer.block.model.BakedQuad; import net.minecraft.client.resources.model.BakedModel; import net.minecraft.client.resources.model.WeightedBakedModel; import net.minecraft.core.Direction; import net.minecraft.util.RandomSource; +import net.minecraft.util.random.SimpleWeightedRandomList; import net.minecraft.util.random.WeightedEntry; import net.minecraft.world.level.block.state.BlockState; import net.neoforged.neoforge.client.ChunkRenderTypeSet; import net.neoforged.neoforge.client.model.data.ModelData; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.spongepowered.asm.mixin.*; import java.util.Collections; @@ -20,41 +22,18 @@ public class WeightedBakedModelMixin { @Shadow @Final - private List> list; - - @Shadow - @Final - private int totalWeight; - - @Unique - private static T getAt(List pool, int totalWeight) { - int i = 0; - int len = pool.size(); - - T weighted; - - do { - if (i >= len) { - return null; - } - - weighted = pool.get(i++); - totalWeight -= weighted.getWeight().asInt(); - } while (totalWeight >= 0); - - return weighted; - } + private SimpleWeightedRandomList list; /** * @author JellySquid * @reason Avoid excessive object allocations */ @Overwrite - public List getQuads(@javax.annotation.Nullable BlockState state, @javax.annotation.Nullable Direction face, RandomSource random, ModelData modelData, @org.jetbrains.annotations.Nullable RenderType renderType) { - WeightedEntry.Wrapper quad = getAt(this.list, Math.abs((int) random.nextLong()) % this.totalWeight); + public List getQuads(@Nullable BlockState state, @Nullable Direction face, RandomSource random, net.neoforged.neoforge.client.model.data.ModelData modelData, @org.jetbrains.annotations.Nullable net.minecraft.client.renderer.RenderType renderType) { + WeightedEntry.Wrapper model = ((WeightedRandomListExtension>) list).sodium$getQuick(random); - if (quad != null) { - return quad.data() + if (model != null) { + return model.data() .getQuads(state, face, random, modelData, renderType); } @@ -67,10 +46,10 @@ public List getQuads(@javax.annotation.Nullable BlockState state, @ja */ @Overwrite public ChunkRenderTypeSet getRenderTypes(@NotNull BlockState state, @NotNull RandomSource rand, @NotNull ModelData data) { - WeightedEntry.Wrapper quad = getAt(this.list, Math.abs((int) rand.nextLong()) % this.totalWeight); + WeightedEntry.Wrapper model = ((WeightedRandomListExtension>) list).sodium$getQuick(rand); - if (quad != null) { - return quad.data() + if (model != null) { + return model.data() .getRenderTypes(state, rand, data); } diff --git a/neoforge/src/main/java/net/caffeinemc/mods/sodium/mixin/features/model/WeightedRandomListMixin.java b/neoforge/src/main/java/net/caffeinemc/mods/sodium/mixin/features/model/WeightedRandomListMixin.java new file mode 100644 index 0000000000..9bb5b70266 --- /dev/null +++ b/neoforge/src/main/java/net/caffeinemc/mods/sodium/mixin/features/model/WeightedRandomListMixin.java @@ -0,0 +1,44 @@ +package net.caffeinemc.mods.sodium.mixin.features.model; + +import com.google.common.collect.ImmutableList; +import net.caffeinemc.mods.sodium.client.util.WeightedRandomListExtension; +import net.minecraft.util.RandomSource; +import net.minecraft.util.random.WeightedEntry; +import net.minecraft.util.random.WeightedRandomList; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +@Mixin(WeightedRandomList.class) +public class WeightedRandomListMixin implements WeightedRandomListExtension { + @Shadow + @Final + private ImmutableList items; + + @Shadow + @Final + private int totalWeight; + + @Override + public E sodium$getQuick(RandomSource random) { + if (this.totalWeight == 0) return null; + + int randomValue = Math.abs((int) random.nextLong()) % this.totalWeight; + + int i = 0; + int len = items.size(); + + E weighted; + + do { + if (i >= len) { + return null; + } + + weighted = items.get(i++); + randomValue -= weighted.getWeight().asInt(); + } while (randomValue >= 0); + + return weighted; + } +} diff --git a/neoforge/src/main/java/net/caffeinemc/mods/sodium/neoforge/mixin/features/render/model/block/ModelBlockRendererMixin.java b/neoforge/src/main/java/net/caffeinemc/mods/sodium/mixin/features/render/model/block/ModelBlockRendererMixin.java similarity index 97% rename from neoforge/src/main/java/net/caffeinemc/mods/sodium/neoforge/mixin/features/render/model/block/ModelBlockRendererMixin.java rename to neoforge/src/main/java/net/caffeinemc/mods/sodium/mixin/features/render/model/block/ModelBlockRendererMixin.java index 7feb74b4d2..c87264a58e 100644 --- a/neoforge/src/main/java/net/caffeinemc/mods/sodium/neoforge/mixin/features/render/model/block/ModelBlockRendererMixin.java +++ b/neoforge/src/main/java/net/caffeinemc/mods/sodium/mixin/features/render/model/block/ModelBlockRendererMixin.java @@ -1,4 +1,4 @@ -package net.caffeinemc.mods.sodium.neoforge.mixin.features.render.model.block; +package net.caffeinemc.mods.sodium.mixin.features.render.model.block; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.VertexConsumer; @@ -46,7 +46,7 @@ private static void renderQuads(PoseStack.Pose matrices, VertexBufferWriter writ int color = quad.hasColor() ? defaultColor : 0xFFFFFFFF; - BakedModelEncoder.writeQuadVertices(writer, matrices, quad, color, light, overlay); + BakedModelEncoder.writeQuadVertices(writer, matrices, quad, color, light, overlay, false); SpriteUtil.markSpriteActive(quad.getSprite()); } diff --git a/neoforge/src/main/java/net/caffeinemc/mods/sodium/neoforge/mixin/features/world/biome/BiomeMixin.java b/neoforge/src/main/java/net/caffeinemc/mods/sodium/mixin/features/world/biome/BiomeMixin.java similarity index 97% rename from neoforge/src/main/java/net/caffeinemc/mods/sodium/neoforge/mixin/features/world/biome/BiomeMixin.java rename to neoforge/src/main/java/net/caffeinemc/mods/sodium/mixin/features/world/biome/BiomeMixin.java index 2786ca5993..cea4041f18 100644 --- a/neoforge/src/main/java/net/caffeinemc/mods/sodium/neoforge/mixin/features/world/biome/BiomeMixin.java +++ b/neoforge/src/main/java/net/caffeinemc/mods/sodium/mixin/features/world/biome/BiomeMixin.java @@ -1,4 +1,4 @@ -package net.caffeinemc.mods.sodium.neoforge.mixin.features.world.biome; +package net.caffeinemc.mods.sodium.mixin.features.world.biome; import net.caffeinemc.mods.sodium.client.world.biome.BiomeColorMaps; import net.minecraft.util.Mth; diff --git a/neoforge/src/main/java/net/caffeinemc/mods/sodium/mixin/platform/neoforge/AbstractBlockRenderContextMixin.java b/neoforge/src/main/java/net/caffeinemc/mods/sodium/mixin/platform/neoforge/AbstractBlockRenderContextMixin.java new file mode 100644 index 0000000000..6338d1f1fc --- /dev/null +++ b/neoforge/src/main/java/net/caffeinemc/mods/sodium/mixin/platform/neoforge/AbstractBlockRenderContextMixin.java @@ -0,0 +1,37 @@ +package net.caffeinemc.mods.sodium.mixin.platform.neoforge; + +import net.caffeinemc.mods.sodium.client.render.frapi.render.AbstractBlockRenderContext; +import net.caffeinemc.mods.sodium.client.services.SodiumModelData; +import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter; +import net.minecraft.client.renderer.RenderType; +import net.neoforged.neoforge.client.model.data.ModelData; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(AbstractBlockRenderContext.BlockEmitter.class) +public abstract class AbstractBlockRenderContextMixin implements QuadEmitter { + @Unique + private AbstractBlockRenderContext parent; + + /** + * @author IMS + * @reason Access parent + */ + @Inject(method = "", at = @At("RETURN")) + public void assignParent(AbstractBlockRenderContext parent, CallbackInfo ci) { + this.parent = parent; + } + + @Override + public ModelData getModelData() { + return (ModelData) (Object) this.parent.getModelData(); + } + + @Override + public RenderType getRenderType() { + return this.parent.getRenderType(); + } +} diff --git a/neoforge/src/main/java/net/caffeinemc/mods/sodium/neoforge/mixin/AuxiliaryLightManagerMixin.java b/neoforge/src/main/java/net/caffeinemc/mods/sodium/mixin/platform/neoforge/AuxiliaryLightManagerMixin.java similarity index 83% rename from neoforge/src/main/java/net/caffeinemc/mods/sodium/neoforge/mixin/AuxiliaryLightManagerMixin.java rename to neoforge/src/main/java/net/caffeinemc/mods/sodium/mixin/platform/neoforge/AuxiliaryLightManagerMixin.java index ea4c2a7a75..40a120ca54 100644 --- a/neoforge/src/main/java/net/caffeinemc/mods/sodium/neoforge/mixin/AuxiliaryLightManagerMixin.java +++ b/neoforge/src/main/java/net/caffeinemc/mods/sodium/mixin/platform/neoforge/AuxiliaryLightManagerMixin.java @@ -1,4 +1,4 @@ -package net.caffeinemc.mods.sodium.neoforge.mixin; +package net.caffeinemc.mods.sodium.mixin.platform.neoforge; import net.caffeinemc.mods.sodium.client.world.SodiumAuxiliaryLightManager; import net.neoforged.neoforge.common.world.AuxiliaryLightManager; diff --git a/neoforge/src/main/java/net/caffeinemc/mods/sodium/neoforge/mixin/ChunkRenderTypeSetAccessor.java b/neoforge/src/main/java/net/caffeinemc/mods/sodium/mixin/platform/neoforge/ChunkRenderTypeSetAccessor.java similarity index 88% rename from neoforge/src/main/java/net/caffeinemc/mods/sodium/neoforge/mixin/ChunkRenderTypeSetAccessor.java rename to neoforge/src/main/java/net/caffeinemc/mods/sodium/mixin/platform/neoforge/ChunkRenderTypeSetAccessor.java index 367914f6bf..b12312274e 100644 --- a/neoforge/src/main/java/net/caffeinemc/mods/sodium/neoforge/mixin/ChunkRenderTypeSetAccessor.java +++ b/neoforge/src/main/java/net/caffeinemc/mods/sodium/mixin/platform/neoforge/ChunkRenderTypeSetAccessor.java @@ -1,4 +1,4 @@ -package net.caffeinemc.mods.sodium.neoforge.mixin; +package net.caffeinemc.mods.sodium.mixin.platform.neoforge; import net.neoforged.neoforge.client.ChunkRenderTypeSet; import org.spongepowered.asm.mixin.Mixin; diff --git a/neoforge/src/main/java/net/caffeinemc/mods/sodium/neoforge/mixin/ClientHooksMixin.java b/neoforge/src/main/java/net/caffeinemc/mods/sodium/mixin/platform/neoforge/ClientHooksMixin.java similarity index 92% rename from neoforge/src/main/java/net/caffeinemc/mods/sodium/neoforge/mixin/ClientHooksMixin.java rename to neoforge/src/main/java/net/caffeinemc/mods/sodium/mixin/platform/neoforge/ClientHooksMixin.java index 287c333d60..52592e3724 100644 --- a/neoforge/src/main/java/net/caffeinemc/mods/sodium/neoforge/mixin/ClientHooksMixin.java +++ b/neoforge/src/main/java/net/caffeinemc/mods/sodium/mixin/platform/neoforge/ClientHooksMixin.java @@ -1,4 +1,4 @@ -package net.caffeinemc.mods.sodium.neoforge.mixin; +package net.caffeinemc.mods.sodium.mixin.platform.neoforge; import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.neoforged.neoforge.client.ClientHooks; diff --git a/neoforge/src/main/java/net/caffeinemc/mods/sodium/neoforge/mixin/EntrypointMixin.java b/neoforge/src/main/java/net/caffeinemc/mods/sodium/mixin/platform/neoforge/EntrypointMixin.java similarity index 94% rename from neoforge/src/main/java/net/caffeinemc/mods/sodium/neoforge/mixin/EntrypointMixin.java rename to neoforge/src/main/java/net/caffeinemc/mods/sodium/mixin/platform/neoforge/EntrypointMixin.java index dc96f9a19a..5f5bf6f5d1 100644 --- a/neoforge/src/main/java/net/caffeinemc/mods/sodium/neoforge/mixin/EntrypointMixin.java +++ b/neoforge/src/main/java/net/caffeinemc/mods/sodium/mixin/platform/neoforge/EntrypointMixin.java @@ -1,4 +1,4 @@ -package net.caffeinemc.mods.sodium.neoforge.mixin; +package net.caffeinemc.mods.sodium.mixin.platform.neoforge; import net.caffeinemc.mods.sodium.client.SodiumClientMod; import net.minecraft.client.Minecraft; diff --git a/neoforge/src/main/java/net/caffeinemc/mods/sodium/neoforge/mixin/LevelSliceMixin.java b/neoforge/src/main/java/net/caffeinemc/mods/sodium/mixin/platform/neoforge/LevelSliceMixin.java similarity index 95% rename from neoforge/src/main/java/net/caffeinemc/mods/sodium/neoforge/mixin/LevelSliceMixin.java rename to neoforge/src/main/java/net/caffeinemc/mods/sodium/mixin/platform/neoforge/LevelSliceMixin.java index 784dac4ddb..42e8f89b9a 100644 --- a/neoforge/src/main/java/net/caffeinemc/mods/sodium/neoforge/mixin/LevelSliceMixin.java +++ b/neoforge/src/main/java/net/caffeinemc/mods/sodium/mixin/platform/neoforge/LevelSliceMixin.java @@ -1,8 +1,7 @@ -package net.caffeinemc.mods.sodium.neoforge.mixin; +package net.caffeinemc.mods.sodium.mixin.platform.neoforge; import net.caffeinemc.mods.sodium.client.services.SodiumModelData; -import net.caffeinemc.mods.sodium.client.services.SodiumModelDataContainer; import net.caffeinemc.mods.sodium.client.world.LevelSlice; import net.caffeinemc.mods.sodium.client.world.SodiumAuxiliaryLightManager; import net.minecraft.client.multiplayer.ClientLevel; diff --git a/neoforge/src/main/java/net/caffeinemc/mods/sodium/neoforge/mixin/ModelDataMixin.java b/neoforge/src/main/java/net/caffeinemc/mods/sodium/mixin/platform/neoforge/ModelDataMixin.java similarity index 80% rename from neoforge/src/main/java/net/caffeinemc/mods/sodium/neoforge/mixin/ModelDataMixin.java rename to neoforge/src/main/java/net/caffeinemc/mods/sodium/mixin/platform/neoforge/ModelDataMixin.java index d6a703ac36..d9aabcbc9f 100644 --- a/neoforge/src/main/java/net/caffeinemc/mods/sodium/neoforge/mixin/ModelDataMixin.java +++ b/neoforge/src/main/java/net/caffeinemc/mods/sodium/mixin/platform/neoforge/ModelDataMixin.java @@ -1,4 +1,4 @@ -package net.caffeinemc.mods.sodium.neoforge.mixin; +package net.caffeinemc.mods.sodium.mixin.platform.neoforge; import net.caffeinemc.mods.sodium.client.services.SodiumModelData; import net.neoforged.neoforge.client.model.data.ModelData; diff --git a/neoforge/src/main/java/net/caffeinemc/mods/sodium/neoforge/mixin/ResourcePackLoaderMixin.java b/neoforge/src/main/java/net/caffeinemc/mods/sodium/mixin/platform/neoforge/ResourcePackLoaderMixin.java similarity index 96% rename from neoforge/src/main/java/net/caffeinemc/mods/sodium/neoforge/mixin/ResourcePackLoaderMixin.java rename to neoforge/src/main/java/net/caffeinemc/mods/sodium/mixin/platform/neoforge/ResourcePackLoaderMixin.java index 624aae61ce..c25ad7a274 100644 --- a/neoforge/src/main/java/net/caffeinemc/mods/sodium/neoforge/mixin/ResourcePackLoaderMixin.java +++ b/neoforge/src/main/java/net/caffeinemc/mods/sodium/mixin/platform/neoforge/ResourcePackLoaderMixin.java @@ -1,4 +1,4 @@ -package net.caffeinemc.mods.sodium.neoforge.mixin; +package net.caffeinemc.mods.sodium.mixin.platform.neoforge; import it.unimi.dsi.fastutil.objects.ReferenceLinkedOpenHashSet; import net.minecraft.server.packs.repository.Pack; diff --git a/neoforge/src/main/java/net/caffeinemc/mods/sodium/neoforge/mixin/SimpleBakedModelAccessor.java b/neoforge/src/main/java/net/caffeinemc/mods/sodium/mixin/platform/neoforge/SimpleBakedModelAccessor.java similarity index 85% rename from neoforge/src/main/java/net/caffeinemc/mods/sodium/neoforge/mixin/SimpleBakedModelAccessor.java rename to neoforge/src/main/java/net/caffeinemc/mods/sodium/mixin/platform/neoforge/SimpleBakedModelAccessor.java index 9b4c2e1c04..c9fa9a7dc5 100644 --- a/neoforge/src/main/java/net/caffeinemc/mods/sodium/neoforge/mixin/SimpleBakedModelAccessor.java +++ b/neoforge/src/main/java/net/caffeinemc/mods/sodium/mixin/platform/neoforge/SimpleBakedModelAccessor.java @@ -1,4 +1,4 @@ -package net.caffeinemc.mods.sodium.neoforge.mixin; +package net.caffeinemc.mods.sodium.mixin.platform.neoforge; import net.minecraft.client.resources.model.SimpleBakedModel; import net.neoforged.neoforge.client.ChunkRenderTypeSet; diff --git a/neoforge/src/main/java/net/caffeinemc/mods/sodium/neoforge/NeoForgeRuntimeInformation.java b/neoforge/src/main/java/net/caffeinemc/mods/sodium/neoforge/NeoForgeRuntimeInformation.java index 728420c9bd..3e043843c7 100644 --- a/neoforge/src/main/java/net/caffeinemc/mods/sodium/neoforge/NeoForgeRuntimeInformation.java +++ b/neoforge/src/main/java/net/caffeinemc/mods/sodium/neoforge/NeoForgeRuntimeInformation.java @@ -1,7 +1,6 @@ package net.caffeinemc.mods.sodium.neoforge; import net.caffeinemc.mods.sodium.client.services.PlatformRuntimeInformation; -import net.neoforged.fml.loading.FMLConfig; import net.neoforged.fml.loading.FMLLoader; import net.neoforged.fml.loading.FMLPaths; import net.neoforged.fml.loading.LoadingModList; @@ -26,7 +25,7 @@ public Path getConfigDirectory() { @Override public boolean platformHasEarlyLoadingScreen() { - return FMLConfig.getBoolConfigValue(FMLConfig.ConfigValue.EARLY_WINDOW_CONTROL); + return true; } @Override diff --git a/neoforge/src/main/java/net/caffeinemc/mods/sodium/neoforge/SodiumForgeMod.java b/neoforge/src/main/java/net/caffeinemc/mods/sodium/neoforge/SodiumForgeMod.java index 4280893ae2..fe561559f5 100644 --- a/neoforge/src/main/java/net/caffeinemc/mods/sodium/neoforge/SodiumForgeMod.java +++ b/neoforge/src/main/java/net/caffeinemc/mods/sodium/neoforge/SodiumForgeMod.java @@ -2,18 +2,40 @@ import net.caffeinemc.mods.sodium.client.gui.SodiumOptionsGUI; import net.caffeinemc.mods.sodium.client.render.frapi.SodiumRenderer; -import net.caffeinemc.mods.sodium.client.render.texture.SpriteFinderCache; -import net.fabricmc.fabric.api.renderer.v1.RendererAccess; +import net.caffeinemc.mods.sodium.client.util.FlawlessFrames; +import net.fabricmc.fabric.api.renderer.v1.Renderer; +import net.neoforged.api.distmarker.Dist; import net.neoforged.bus.api.IEventBus; import net.neoforged.fml.ModContainer; +import net.neoforged.fml.ModList; import net.neoforged.fml.common.Mod; -import net.neoforged.neoforge.client.event.RegisterClientReloadListenersEvent; import net.neoforged.neoforge.client.gui.IConfigScreenFactory; +import net.neoforged.neoforgespi.language.IModInfo; -@Mod("sodium") +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.util.function.Function; + +@Mod(value = "sodium", dist = Dist.CLIENT) public class SodiumForgeMod { public SodiumForgeMod(IEventBus bus, ModContainer modContainer) { modContainer.registerExtensionPoint(IConfigScreenFactory.class, (minecraft, screen) -> SodiumOptionsGUI.createScreen(screen)); - RendererAccess.INSTANCE.registerRenderer(SodiumRenderer.INSTANCE); + Renderer.register(SodiumRenderer.INSTANCE); + + MethodHandles.Lookup lookup = MethodHandles.lookup(); + + for (IModInfo mod : ModList.get().getMods()) { + String handler = (String) mod.getModProperties().getOrDefault("frex:flawless_frames_handler", null); + + if (handler == null) { + continue; + } + + try { + lookup.findStatic(Class.forName(handler), "acceptController", MethodType.methodType(void.class, Function.class)).invoke(FlawlessFrames.getProvider()); + } catch (Throwable e) { + throw new RuntimeException("Failed to execute Flawless Frames handler for mod " + mod.getModId() + "!", e); + } + } } } \ No newline at end of file diff --git a/neoforge/src/main/java/net/caffeinemc/mods/sodium/neoforge/block/NeoForgeBlockAccess.java b/neoforge/src/main/java/net/caffeinemc/mods/sodium/neoforge/block/NeoForgeBlockAccess.java index 978c2e8fe2..0f437c4bd4 100644 --- a/neoforge/src/main/java/net/caffeinemc/mods/sodium/neoforge/block/NeoForgeBlockAccess.java +++ b/neoforge/src/main/java/net/caffeinemc/mods/sodium/neoforge/block/NeoForgeBlockAccess.java @@ -27,7 +27,7 @@ public int getLightEmission(BlockState state, BlockAndTintGetter level, BlockPos @Override public boolean shouldSkipRender(BlockGetter level, BlockState selfState, BlockState otherState, BlockPos selfPos, BlockPos otherPos, Direction facing) { - return (otherState.hidesNeighborFace(level, otherPos, selfState, DirectionUtil.getOpposite(facing))) && selfState.supportsExternalFaceHiding(); + return selfState.supportsExternalFaceHiding() && (otherState.hidesNeighborFace(level, otherPos, selfState, DirectionUtil.getOpposite(facing))); } @Override @@ -56,6 +56,12 @@ public AmbientOcclusionMode usesAmbientOcclusion(BakedModel model, BlockState st @Override public boolean shouldBlockEntityGlow(BlockEntity blockEntity, LocalPlayer player) { - return blockEntity.hasCustomOutlineRendering(player); + // TODO: NeoForge does not yet have a hook on 1.21.2. + return false; + } + + @Override + public boolean shouldOccludeFluid(Direction adjDirection, BlockState adjBlockState, FluidState fluid) { + return adjBlockState.shouldHideAdjacentFluidFace(adjDirection, fluid); } } diff --git a/neoforge/src/main/java/net/caffeinemc/mods/sodium/neoforge/mixin/AbstractBlockRenderContextMixin.java b/neoforge/src/main/java/net/caffeinemc/mods/sodium/neoforge/mixin/AbstractBlockRenderContextMixin.java deleted file mode 100644 index 7aa798c3c1..0000000000 --- a/neoforge/src/main/java/net/caffeinemc/mods/sodium/neoforge/mixin/AbstractBlockRenderContextMixin.java +++ /dev/null @@ -1,28 +0,0 @@ -package net.caffeinemc.mods.sodium.neoforge.mixin; - -import net.caffeinemc.mods.sodium.client.render.frapi.render.AbstractBlockRenderContext; -import net.caffeinemc.mods.sodium.client.services.SodiumModelData; -import net.fabricmc.fabric.api.renderer.v1.render.RenderContext; -import net.minecraft.client.renderer.RenderType; -import net.neoforged.neoforge.client.model.data.ModelData; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; - -@Mixin(AbstractBlockRenderContext.class) -public abstract class AbstractBlockRenderContextMixin implements RenderContext { - @Shadow - protected RenderType type; - - @Shadow - protected SodiumModelData modelData; - - @Override - public ModelData getModelData() { - return (ModelData) (Object) this.modelData; - } - - @Override - public RenderType getRenderType() { - return type; - } -} diff --git a/neoforge/src/main/resources/META-INF/neoforge.mods.toml b/neoforge/src/main/resources/META-INF/neoforge.mods.toml index 6fa9e9d886..0754f42f7e 100644 --- a/neoforge/src/main/resources/META-INF/neoforge.mods.toml +++ b/neoforge/src/main/resources/META-INF/neoforge.mods.toml @@ -1,5 +1,5 @@ modLoader = "javafml" -loaderVersion = "*" +loaderVersion = "[4,)" license = "Polyform-Shield-1.0.0" [[mods]] @@ -10,9 +10,9 @@ displayName = "Sodium" logoFile = "sodium-icon.png" #optional -authors = "JellySquid, IMS212" +authors = "JellySquid (jellysquid3), IMS212" -credits = "@bytzo, @PepperCode1, @FlashyReese, @altrisi, @Grayray75, @Madis0, @Johni0702, @comp500, @coderbot16, @Moulberry, @MCRcortex, @Altirix, @embeddedt, @pajicadvance, @Kroppeb, @douira, @burgerindividual, @TwistedZero, @Leo40Git, @haykam821" +credits = "bytzo, PepperCode1, FlashyReese, altrisi, Grayray75, Madis0, Johni0702, comp500, coderbot16, Moulberry, MCRcortex, Altirix, embeddedt, pajicadvance, Kroppeb, douira, burgerindividual, TwistedZero, Leo40Git, haykam821, muzikbike" description = ''' Sodium is a powerful rendering engine for Minecraft which improves frame rates and reduces lag spikes. @@ -26,7 +26,14 @@ provides = ["indium"] [[dependencies.sodium]] modId = "minecraft" type = "required" -versionRange = "[1.21,1.21.1]" +versionRange = "1.21.4" +ordering = "NONE" +side = "CLIENT" + +[[dependencies.sodium]] +modId = "neoforge" +type = "required" +versionRange = "[21.4.3-beta,)" ordering = "NONE" side = "CLIENT" @@ -39,7 +46,7 @@ ordering = "NONE" side = "CLIENT" [[mixins]] -config = "sodium.mixins.json" +config = "sodium-common.mixins.json" [[mixins]] -config = "sodium-forge.mixins.json" \ No newline at end of file +config = "sodium-neoforge.mixins.json" \ No newline at end of file diff --git a/neoforge/src/main/resources/sodium-forge.mixins.json b/neoforge/src/main/resources/sodium-forge.mixins.json deleted file mode 100644 index ae9a7b4754..0000000000 --- a/neoforge/src/main/resources/sodium-forge.mixins.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "package" : "net.caffeinemc.mods.sodium.neoforge.mixin", - "required" : true, - "compatibilityLevel" : "JAVA_17", - "refmap" : "sodium.refmap.json", - "injectors" : { - "defaultRequire" : 1 - }, - "overwrites" : { - "conformVisibility" : true - }, - "client" : [ - "core.model.quad.BakedQuadMixin", - "features.model.MultiPartBakedModelMixin", - "features.model.WeightedBakedModelMixin", - "features.render.model.block.ModelBlockRendererMixin", - "features.world.biome.BiomeMixin", - "AbstractBlockRenderContextMixin", - "AuxiliaryLightManagerMixin", - "ChunkRenderTypeSetAccessor", - "ClientHooksMixin", - "EntrypointMixin", - "LevelSliceMixin", - "ModelDataMixin", - "ResourcePackLoaderMixin", - "SimpleBakedModelAccessor" - ] -} \ No newline at end of file diff --git a/neoforge/src/main/resources/sodium-neoforge.mixins.json b/neoforge/src/main/resources/sodium-neoforge.mixins.json new file mode 100644 index 0000000000..3dbbadf5f8 --- /dev/null +++ b/neoforge/src/main/resources/sodium-neoforge.mixins.json @@ -0,0 +1,28 @@ +{ + "package" : "net.caffeinemc.mods.sodium.mixin", + "required" : true, + "compatibilityLevel" : "JAVA_17", + "injectors" : { + "defaultRequire" : 1 + }, + "overwrites" : { + "conformVisibility" : true + }, + "client" : [ + "core.model.quad.BakedQuadMixin", + "features.model.MultiPartBakedModelMixin", + "features.model.WeightedBakedModelMixin", + "features.model.WeightedRandomListMixin", + "features.render.model.block.ModelBlockRendererMixin", + "features.world.biome.BiomeMixin", + "platform.neoforge.AbstractBlockRenderContextMixin", + "platform.neoforge.AuxiliaryLightManagerMixin", + "platform.neoforge.ChunkRenderTypeSetAccessor", + "platform.neoforge.ClientHooksMixin", + "platform.neoforge.EntrypointMixin", + "platform.neoforge.LevelSliceMixin", + "platform.neoforge.ModelDataMixin", + "platform.neoforge.ResourcePackLoaderMixin", + "platform.neoforge.SimpleBakedModelAccessor" + ] +} \ No newline at end of file diff --git a/neoforge/src/service/java/net/caffeinemc/mods/sodium/service/SodiumWorkarounds.java b/neoforge/src/service/java/net/caffeinemc/mods/sodium/service/SodiumWorkarounds.java index 1d6876b8e2..eb106c0ad0 100644 --- a/neoforge/src/service/java/net/caffeinemc/mods/sodium/service/SodiumWorkarounds.java +++ b/neoforge/src/service/java/net/caffeinemc/mods/sodium/service/SodiumWorkarounds.java @@ -14,15 +14,11 @@ public String name() { @Override public void bootstrap(String[] arguments) { - PreLaunchChecks.beforeLWJGLInit(); + PreLaunchChecks.checkEnvironment(); GraphicsAdapterProbe.findAdapters(); - PreLaunchChecks.onGameInit(); Workarounds.init(); - final boolean applyNvidiaWorkarounds = Workarounds.isWorkaroundEnabled(Workarounds.Reference.NVIDIA_THREADED_OPTIMIZATIONS); - if (applyNvidiaWorkarounds) { - System.out.println("[Sodium] Applying NVIDIA workarounds earlier on Forge."); - NvidiaWorkarounds.install(); - } + // Context creation happens earlier on NeoForge, so we need to apply this now + NvidiaWorkarounds.applyEnvironmentChanges(); } } diff --git a/settings.gradle.kts b/settings.gradle.kts index 304b9d9281..0f606fd359 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -2,6 +2,7 @@ rootProject.name = "sodium" pluginManagement { repositories { + mavenLocal() maven { url = uri("https://maven.fabricmc.net/") } maven { url = uri("https://maven.neoforged.net/releases/") } gradlePluginPortal() diff --git a/thirdparty/NOTICE.txt b/thirdparty/NOTICE.txt index 536d338996..f30566170c 100644 --- a/thirdparty/NOTICE.txt +++ b/thirdparty/NOTICE.txt @@ -6,6 +6,12 @@ projects. The complete text for each software license can be found in the directory "thirdparty/licenses". +* Fabric API libraries + * Copyright (c) 2016 FabricMC + * License: Apache 2.0 (SPDX: Apache-2.0) + * Original Source: + * https://github.com/FabricMC/fabric + * lodborg's interval tree library for Java * Copyright (c) 2016 lodborg * License: MIT @@ -16,9 +22,8 @@ The complete text for each software license can be found in the directory "third * coderbot's mip-map generator patches for Minecraft * Copyright (c) 2021 Coderbot - * License: Mozilla Public License (MPL2) + * License: Mozilla Public License (SPDX: MPL2) * Original Source: * https://github.com/IrisShaders/Iris/tree/41095ac23ea0add664afd1b85c414d1f1ed94066/src/main/java/net/coderbot/iris/mixin/bettermipmaps * Files: * common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/textures/mipmaps - diff --git a/thirdparty/licenses/LICENSE-APACHE-2.0.txt b/thirdparty/licenses/LICENSE-APACHE-2.0.txt new file mode 100644 index 0000000000..d645695673 --- /dev/null +++ b/thirdparty/licenses/LICENSE-APACHE-2.0.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License.