mirror of
https://github.com/GTNewHorizons/Angelica.git
synced 2026-11-14 17:24:43 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
37a10eee3f | ||
|
|
744c1f971f | ||
|
|
32d1c27b9c | ||
|
|
f180894da7 | ||
|
|
bb67f82701 | ||
|
|
73f5556595 | ||
|
|
19bc7cbaed | ||
|
|
d189a644e2 | ||
|
|
94ba40ae4d | ||
|
|
961b23fd2d | ||
|
|
61b5301f1a | ||
|
|
0e539ae034 | ||
|
|
e581326641 | ||
|
|
c707cc51f1 | ||
|
|
7d890bf3e7 | ||
|
|
ffc324bab6 | ||
|
|
0dfffca88e | ||
|
|
a66db1517a | ||
|
|
269b7245d3 | ||
|
|
67f2484976 | ||
|
|
1c8687e6fd | ||
|
|
e6bbd155da | ||
|
|
2f64fef6a6 | ||
|
|
b0fd1ea46e |
No files matched your search
@@ -20,3 +20,9 @@ indent_size = 2
|
||||
|
||||
[*.lang]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[*.java]
|
||||
ij_java_use_single_class_imports = true
|
||||
ij_java_class_count_to_use_import_on_demand = 999
|
||||
ij_java_names_count_to_use_import_on_demand = 999
|
||||
ij_java_packages_to_use_import_on_demand = empty
|
||||
@@ -1,5 +1,7 @@
|
||||
# AI Usage Policy
|
||||
|
||||
🌐 **Languages:** [English](AI_POLICY.md) | [Українська](AI_POLICY_uk.md)
|
||||
|
||||
This project has strict rules for AI usage:
|
||||
|
||||
* **The human-in-the-loop must fully understand all code.** If you
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
# Політика використання ШІ
|
||||
|
||||
🌐 **Мови:** [English](AI_POLICY.md) | [Українська](AI_POLICY_uk.md)
|
||||
|
||||
Цей проєкт має суворі правила щодо використання штучного інтелекту (ШІ):
|
||||
|
||||
* **Людина, яка контролює процес, повинна повністю розуміти весь код.**
|
||||
Якщо ви не можете пояснити, що роблять ваші зміни та як вони взаємодіють із
|
||||
більшою системою без допомоги інструментів ШІ, не пропонуйте ці зміни.
|
||||
|
||||
* **Обговорення та задачі можуть створюватися за допомогою ШІ, але повинні перебувати
|
||||
під повним контролем людини.** Це означає, що будь-який контент, згенерований за допомогою ШІ,
|
||||
перед надсиланням має бути перевірений *та відредагований* людиною.
|
||||
Автор повинен сам розібратися в питанні та відсіяти все зайве.
|
||||
|
||||
* **Медіаконтент, згенерований ШІ, заборонений (текстури, відео, аудіо тощо).**
|
||||
Текст і код є єдиним контентом, згенерованим ШІ, який дозволено використовувати
|
||||
згідно з іншими правилами цієї політики.
|
||||
|
||||
* **Повідомлення до комітів та описи PR повинні бути написані у вашому власному стилі.**
|
||||
Коміти, повністю або частково створені ШІ, заборонені, включно з тегами `Co-authored-by` з вказівкою ШІ.
|
||||
|
||||
* **Подавайте виправлення лише для тих багів, із якими ви особисто зіткнулися в грі.**
|
||||
Якщо ви особисто не стикалися з багом, не відтворили його власноруч і ще не знаєте
|
||||
добре відповідний код, не натравлюйте на нього ШІ, щоб потім перенаправити результат нам.
|
||||
Патчі, написані для проблем, із якими автор особисто ніколи не стикався, зазвичай
|
||||
виправляють не те, що потрібно, та/або створюють нові баги.
|
||||
|
||||
* **Стиль та суть письмового контенту повинні належати вам.**
|
||||
Для документації та особливо для квестів тон і думка, яку ви намагаєтеся донести, мають
|
||||
виходити від вас, а не від ШІ. Ви можете використовувати ШІ для покращення формулювань
|
||||
або вичитки, якщо бажаєте, але якщо суть і стиль не ваші, не пропонуйте такий контент.
|
||||
|
||||
* **За зловживання ШІ можна отримати блокування.** Користувачі, які створюють
|
||||
низькоякісний контент, очевидно згенерований ШІ (слоп), можуть бути заблоковані для подальших внесків.
|
||||
Ми залюбки допомагаємо розробникам-початківцям навчатися та рости, але якщо ви зацікавлені
|
||||
в цьому, не використовуйте ШІ, і ми вам допоможемо. Нам прикро, що недобросовісні
|
||||
користувачі ШІ зіпсували це для вас.
|
||||
|
||||
|
||||
## Тут працюють люди
|
||||
|
||||
Будь ласка, пам’ятайте, що цей проєкт підтримують люди.
|
||||
|
||||
Кожне обговорення, задача та запит на злиття читаються й перевіряються
|
||||
людьми (а іноді й машинами). Це точка перетину, де люди взаємодіють
|
||||
один з одним та виконують роботу. Неґречно та неповажно приходити сюди
|
||||
з неякісною, некваліфікованою та зробленою абияк роботою, оскільки це
|
||||
перекладає тягар перевірки на мейнтейнерів.
|
||||
|
||||
В ідеальному світі ШІ щоразу створював би високоякісну та точну роботу.
|
||||
Але сьогодні ця реальність залежить від користувача ШІ. І сьогодні чимало
|
||||
користувачів використовують ШІ неналежним чином. Тому, поки це не зміниться,
|
||||
ми змушені мати суворі правила, щоб захистити мейнтейнерів.
|
||||
|
||||
Створено на основі https://github.com/ghostty-org/ghostty/blob/main/AI_POLICY.md
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
<sup>**[CurseForge](https://www.curseforge.com/minecraft/mc-mods/angelica) | [Modrinth](https://modrinth.com/mod/angelica)**</sup>
|
||||
|
||||
🌐 **Languages:** [English](README.MD) | [Українська](README_uk.MD)
|
||||
|
||||
* The little Angle [sic] that supports shaders while saving your fps from a certain death -boubou
|
||||
* Is it obtuse, or accute? -Caedis
|
||||
* It's a cute angle -mitchej123
|
||||
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
# Angelica<img src="img/logo-readme.png" align="right">
|
||||
|
||||
<sup>**[CurseForge](https://www.curseforge.com/minecraft/mc-mods/angelica) | [Modrinth](https://modrinth.com/mod/angelica)**</sup>
|
||||
|
||||
🌐 **Мови:** [English](README.MD) | [Українська](README_uk.MD)
|
||||
|
||||
* Маленький Кут [sic], що підтримує шейдери, водночас рятуючи ваші FPS від вірної смерті — boubou
|
||||
* Він тупий чи гострий? — Caedis
|
||||
* Це милий кут — mitchej123
|
||||
* Це янгол, який витягує вас із пекла OptiFine — Roadhog360
|
||||
|
||||
## **Бета-якість — Тут водяться дракони**
|
||||
* Шейдери — Бета
|
||||
* Complementary + Euphoria Patches є головною ціллю для сумісності
|
||||
* Sildurs, Seus та будь-які інші шейдери можуть працювати
|
||||
|
||||
## Базовий профіль
|
||||
Цільовим є базовий профіль GL 3.3+
|
||||
* Емуляція FFP через згенеровані GLSL-шейдери
|
||||
* Емуляція списків відображення
|
||||
* Програмні стеки матриць та керування станом через GLSM
|
||||
* Конвертація GL_QUADS → GL_TRIANGLES
|
||||
* Автоматична трансформація шейдерів з модів, які використовують вбудовані функції GLSL виключно для сумісності (`gl_ModelViewMatrix`, `texture2D` тощо)
|
||||
|
||||
|
||||
# Обов’язкові залежності
|
||||
* [UniMixins](https://github.com/LegacyModdingMC/UniMixins/releases) >= 0.1.23
|
||||
* [GTNHLib](https://github.com/GTNewHorizons/GTNHLib/releases) >= 0.11.19
|
||||
* ПРИМІТКА: Деякі моди не є обов’язковими, але якщо вони встановлені, вимагається їхня конкретна версія — див. розділ `Постійна несумісність` нижче.
|
||||
|
||||
# Відома (тимчасова) несумісність
|
||||
* Forge Relocation — Наразі не є потокобезпечним; безпечно, якщо не використовується
|
||||
* ProjectRed Frames — Наразі не є потокобезпечним; безпечно, якщо не використовується
|
||||
|
||||
# Можлива несумісність
|
||||
* EndlessIDs — Коміти для сумісності були зроблені, може працювати, а може й ні — не повністю протестовано
|
||||
|
||||
# Відома несумісність
|
||||
* FalseTweaks — Наразі не планується (Шукаємо волонтерів) [Може працювати, якщо фактично вимкнути більшу частину мода]
|
||||
* RPLE — Наразі не планується (Шукаємо волонтерів)
|
||||
* LiteLoader — Встановіть [LiteloaderLoader](https://github.com/Midnight145/LiteloaderLoader)
|
||||
|
||||
# Постійна несумісність
|
||||
* OptiFine [Вимкнено, виправлятись не буде]
|
||||
* FastCraft [Вимкнено, виправлятись не буде]
|
||||
* BetterFPS [Вимкнено, виправлятись не буде]
|
||||
* OptimizationsAndTweaks [Вимкнено, виправлятись не буде]
|
||||
* [Amazing Trophies 1.2.1 і нижче](https://github.com/GTNewHorizons/Amazing-Trophies/releases) — Використовуйте 1.2.2+
|
||||
* [LWJGL3ify 1.5.4 і нижче](https://github.com/GTNewHorizons/lwjgl3ify/releases/) — Використовуйте найновішу доступну версію, 1.5.7+
|
||||
* [ArchaicFix 0.6.2 і нижче](https://www.curseforge.com/minecraft/mc-mods/archaicfix/files/all?page=1&pageSize=20) — Використовуйте 0.7.0 або вище
|
||||
* [Hodgepodge 2.4.3 і нижче](https://github.com/GTNewHorizons/Hodgepodge/releases) — Використовуйте 2.4.5 або вище
|
||||
* CodeChicken[Core](https://github.com/GTNewHorizons/CodeChickenCore/releases)/[Lib](https://github.com/GTNewHorizons/CodeChickenLib/releases) <1.2.0 — Проблеми з багатопотоковістю, використовуйте 1.3.0+
|
||||
* Neodymium — Не перевірено, але навряд чи буде сумісним. (Побудова сітки чанків точно несумісна, інші функції можуть працювати залежно від конфігурації)
|
||||
* SmoothFont — Додано еквівалентний функціонал
|
||||
* ClientFixer — Використовуйте пакет ресурсів, як-от [GTNH-FTI-Standard-Font](https://github.com/Eldrinn-Elantey/GTNH-FTI-Standard-Font)
|
||||
|
||||
# Подяки
|
||||
* ShadersMod
|
||||
* Оригінальні ShadersMods: karyonix, sonic ether, id_miner, daxnitro
|
||||
* eigenraven, Caedis, glowredman та mitchej123 за конвертацію ASM у Mixin
|
||||
* eigenraven та vlaetansky за те, що змусили оригінальний ShadersMod знову компілюватися
|
||||
* ArchaicFix
|
||||
* Включає графічні покращення з [ArchaicFix](https://github.com/embeddedt/ArchaicFix/tree/4afb943751883b27a1f0aa7c5ba71698bc897e95) від embeddedt за ліцензією [LGPL](https://github.com/embeddedt/ArchaicFix/blob/main/LICENSE.md)
|
||||
* NotFine
|
||||
* Включає модифіковану версію [NotFine](https://github.com/jss2a98aj/NotFine/tree/a2652e24b5654f437fb7138f6d1f5e947a7d7125) від jss2a98aj за ліцензією LGPL
|
||||
* Iris Shaders
|
||||
* Команда [Iris](https://github.com/IrisShaders/Iris): coderbot, IMS212, Justsnoopy30, FoundationGames
|
||||
* Asek3 за порт Iris для Forge [Oculus](https://github.com/Asek3/Oculus/tree/839ce8eca8cf0c4f6b7a1322b906e5c99125ca69)
|
||||
* Бекпортований код за ліцензією LGPL
|
||||
* Sodium / Celeritas
|
||||
* JellySquid та команді CaffeineMC за створення Sodium
|
||||
* Embeddedt за Celeritas, що використовується за ліцензією LGPL 3.0
|
||||
* Reese's Sodium Options
|
||||
* FlashyReese за [Reese's Sodium Options](https://github.com/FlashyReese/reeses-sodium-options/tree/aa4ea67047cf53e54976e93c6bb1ab5ba16b142b) за ліцензією [MIT](https://github.com/FlashyReese/reeses-sodium-options/blob/aa4ea67047cf53e54976e93c6bb1ab5ba16b142b/LICENSE.txt)
|
||||
* Neodymium
|
||||
* Makamys за [Neodymium](https://github.com/makamys/Neodymium) — Різноманітний функціонал Tessellator -> Quad взято за ліцензією LGPL 3
|
||||
* MCPatcherForge
|
||||
* Оригінальний [MCPatcher](https://bitbucket.org/prupe/mcpatcher/src/master/): Paul Rupe
|
||||
* [Версія для Forge](https://github.com/mist475/MCPatcherForge): Mist475 за ліцензією LGPL 3.0 з типовими ctm-текстурами для скла та книжкових полиць від DarkBum за ліцензією CC-BY-SA
|
||||
* HUDCaching
|
||||
* Створено [Moulberry](https://github.com/Moulberry/MCHUDCaching), бекпортовано з мода [Patcher](https://github.com/Sk1erLLC/Patcher) від Alexdoru за ліцензією CC-BY-SA-NC 4.0
|
||||
* Angelica
|
||||
* mitchej123, Omni, Makamys, Embeddedt, NanoLive, Caedis, Cardinalstar, Alexdoru, Eigenraven, mist475, Clepto, kurrycat за допомогу в запуску та налаштуванні Angelica!
|
||||
* IMS, jss2a98aj, || dbp ||, roadhog360 за поради та моральну підтримку
|
||||
* Forge 1.12+
|
||||
* Деякі файли були взяті з Minecraft Forge за ліцензією LGPL 2.1 і позначені відповідно
|
||||
* FRAPI та Indigo
|
||||
* API для побудови моделей значною мірою натхненний/скопійований з Fabric; автори оригінального коду (upstream) можуть використовувати його за оригінальною ліцензією Apache 2.0,
|
||||
див. LICENSE у `com.gtnewhorizons.angelica.models`
|
||||
* Dynamic Lights
|
||||
* LambdAurora за [LambDynamicLights](https://github.com/LambdAurora/LambDynamicLights) за ліцензією MIT, та anthxnymc за порт для Forge [DynamicLightsReforged](https://github.com/anthxnymc/DynamicLightsReforged), також за ліцензією MIT
|
||||
* Embeddedt та Ferri_Arnus за [glsl-transformation-lib](https://github.com/TauMC/glsl-transformation-lib)
|
||||
* Також за роботу над [Monocle](https://github.com/ferriarnus/Monocle), який використовувався як еталон для реалізації glsl-transformation-lib
|
||||
* Код з обох glsl-transformation-lib та Monocle взято за їхніми відповідними ліцензіями LGPL 3.0
|
||||
* FalseTweaks
|
||||
* FalsePattern за [FalseTweaks](https://github.com/falsepattern/falsetweaks) — Оптимізації рендерингу предметів взято з FalseTweaks за ліцензією LGPL 3.0
|
||||
* Beddium
|
||||
* Venn та Falsepattern за [Beddium](https://github.com/vfx-dev/Beddium) — MinimapRenderer, SafeChunkTracker та деякі команди для налагодження адаптовано за ліцензією LGPL 3.0
|
||||
* [Mesa](https://gitlab.freedesktop.org/mesa/mesa) — Код портований з Mesa та/або натхненний нею — за ліцензією MIT
|
||||
* Розподілювач ID списків відображення портовано з [util_idalloc](https://gitlab.freedesktop.org/mesa/mesa/-/blob/main/src/util/u_idalloc.c) (Copyright 2017 Valve Corporation)
|
||||
* Генерація вершинних шейдерів FFP натхненна ffvertex_prog.c (Copyright 2007 VMware, Inc.)
|
||||
* Генерація фрагментних шейдерів FFP натхненна ff_fragment_shader.c (Copyright 2007, 2009 VMware, Inc. / 2010-2011 Intel Corporation)
|
||||
* Оптимізація параметрів туману натхненна st_nir_lower_fog.c (Copyright 2023 Google LLC) та prog_statevars.c (Copyright 1999-2007 Brian Paul)
|
||||
@@ -187,6 +187,9 @@ fun Test.configureAngelicaJava8() {
|
||||
}
|
||||
dependsOn(tasks.extractNatives2)
|
||||
jvmArgs("-Djava.library.path=${tasks.extractNatives2.get().destinationFolder.asFile.get().path}")
|
||||
if (isMacOs) {
|
||||
jvmArgs("-Dapple.awt.UIElement=true")
|
||||
}
|
||||
testLogging { events("passed", "skipped", "failed") }
|
||||
|
||||
val swaps = mapOf(
|
||||
|
||||
+4
-1
@@ -1,4 +1,7 @@
|
||||
# A specification for models
|
||||
|
||||
🌐 **Languages:** [English](model-spec.md) | [Українська](model-spec_uk.md)
|
||||
|
||||
This is borrowed from [the unofficial Minecraft wiki](https://minecraft.wiki/w/Tutorials/Models) under the CC BY-NC-SA 3.0.
|
||||
|
||||
The folder `assets/<namespace>/models/block` holds the model files for all the specified variants. The names of the files can be changed, but must always correspond with the names used in the variant files.
|
||||
@@ -8,7 +11,7 @@ The folder `assets/<namespace>/models/block` holds the model files for all the s
|
||||
* Can be set to `"builtin/generated"` to use a model that is created out of the specified icon. Note that only the first layer is supported, and rotation can be achieved only by using block states files.
|
||||
* `[TAG_byte]` **ambientocclusion**: Whether to use [ambient occlusion](http://en.wikipedia.org/wiki/Ambient_occlusion) (`true` - default), or not (`false`). Note:only works on Parent file
|
||||
* `[TAG_object]` **display**: Holds the different places where item models are displayed.
|
||||
* `[TAG_compound]` ***Position***: Named `thirdperson_righthand`, `thirdperson_lefthand`, `firstperson_righthand`, `firstperson_lefthand`, `gui`, `head`, `ground`, or `fixed`. Place where an item model is displayed. Holds its rotation, translation and scale for the specified situation. '''fixed''' refers to item frames, while the rest are as their name states. Note that translations are applied to the model before rotations.
|
||||
* `[TAG_compound]` ***Position***: Named `thirdperson_righthand`, `thirdperson_lefthand`, `firstperson_righthand`, `firstperson_lefthand`, `gui`, `head`, `ground`, or `fixed`. Place where an item model is displayed. Holds its rotation, translation and scale for the specified situation. **'''fixed'''** refers to item frames, while the rest are as their name states. Note that translations are applied to the model before rotations.
|
||||
* `[TAG_list]` **rotation**: Specifies the rotation of the model according to the scheme `[x, y, z]`.
|
||||
* `[TAG_list]` **translation**: Specifies the position of the model according to the scheme `[x, y, z]`. The values are clamped between -80 and 80.
|
||||
* `[TAG_list]` **scale**: Specifies the scale of the model according to the scheme `[x, y, z]`. If the value is greater than 4, it is displayed as 4.
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
# Специфікація моделей
|
||||
|
||||
🌐 **Мови:** [English](model-spec.md) | [Українська](model-spec_uk.md)
|
||||
|
||||
Запозичено з [неофіційної Minecraft wiki](https://minecraft.wiki/w/Tutorials/Models) на умовах ліцензії CC BY-NC-SA 3.0.
|
||||
|
||||
Папка `assets/<namespace>/models/block` містить файли моделей для усіх вказаних варіантів. Назви файлів можна змінювати, але вони завжди повинні відповідати назвам, що використовуються у файлах варіантів.
|
||||
|
||||
* `[TAG_compound]` Кореневий тег
|
||||
* `[TAG_string]` **parent**: Завантажує іншу модель із вказаного шляху у формі [локації ресурсу](https://minecraft.wiki/w/Tutorials/Models#File_path). Якщо задано і `"parent"`, і `"elements"`, тег `"elements"` перевизначає тег `"elements"` із попередньої моделі.
|
||||
* Може мати значення `"builtin/generated"`, щоб використовувати модель, створену із вказаної піктограми. Зверніть увагу: підтримується лише перший шар, а обертання можна досягти лише за допомогою файлів станів блоку.
|
||||
* `[TAG_byte]` **ambientocclusion**: Визначає, чи використовувати [розсіяне затінення](http://en.wikipedia.org/wiki/Ambient_occlusion) (`true` — за замовчуванням), чи ні (`false`). Примітка: працює лише у батьківському файлі.
|
||||
* `[TAG_object]` **display**: Містить різні місця відображення моделей предметів.
|
||||
* `[TAG_compound]` ***Position***: Має назву `thirdperson_righthand`, `thirdperson_lefthand`, `firstperson_righthand`, `firstperson_lefthand`, `gui`, `head`, `ground` або `fixed`. Місце, де відображається модель предмета. Містить її обертання, зміщення та масштаб для вказаної ситуації. **'''fixed'''** стосується рамок для предметів, а решта відповідають своїм назвам. Зверніть увагу: зміщення застосовується до моделі перед обертанням.
|
||||
* `[TAG_list]` **rotation**: Задає обертання моделі за схемою `[x, y, z]`.
|
||||
* `[TAG_list]` **translation**: Задає зміщення моделі за схемою `[x, y, z]`. Значення обмежені в діапазоні від -80 до 80.
|
||||
* `[TAG_list]` **scale**: Задає масштаб моделі за схемою `[x, y, z]`. Якщо значення більше за 4, воно відображається як 4.
|
||||
* `[TAG_compound]` **textures**: Містить текстури моделі у формі [локації ресурсу](https://minecraft.wiki/w/Tutorials/Models#File_path) або у вигляді іншої текстурної змінної.
|
||||
* `[TAG_string]` **particle**: Текстура, з якої завантажуються частинки. Ця текстура також використовується як оверлей, якщо ви перебуваєте в [порталі Незеру](https://minecraft.wiki/w/Nether_portal), а також для нерухомих текстур [води](https://minecraft.wiki/w/water) та [лави](https://minecraft.wiki/w/Lava).<sup>[[1]](https://bugs.mojang.com/browse/MC-240042)</sup> Ця текстура також вважається текстурною змінною, на яку можна посилатися як `"#particle"`. Примітка: Усі частинки руйнування для блоків без моделей вшиті в код, наприклад, для [бар’єрів](https://minecraft.wiki/w/Barrier).
|
||||
* `[TAG_string]` ***Texture variable***: Визначає текстурну змінну з ім’ям `name` та призначає її текстурі або іншій змінній.
|
||||
* `[TAG_list]` **elements**: Містить усі елементи моделі. Вони можуть мати лише кубічну форму. Якщо задано і `"parent"`, і `"elements"`, тег `"elements"` перевизначає тег `"elements"` із попередньої моделі.
|
||||
* `[TAG_compound]` Елемент.
|
||||
* `[TAG_list]` **from**: Початкова точка кубоїда за схемою `[x, y, z]`. Значення мають бути від -16 до 32.
|
||||
* `[TAG_list]` **to**: Кінцева точка кубоїда за схемою `[x, y, z]`. Значення мають бути від -16 до 32.
|
||||
* `[TAG_compound]` **rotation**: Визначає обертання елемента.
|
||||
* `[TAG_list]` **origin**: Встановлює центр обертання за схемою `[x, y, z]`.
|
||||
* `[TAG_string]` **axis**: Задає вісь обертання: може бути `"x"`, `"y"` або `"z"`.
|
||||
* `[TAG_float]` **angle**: Задає кут обертання. Може бути від 45 до -45 градусів із кроком у 22,5 градуса.
|
||||
* `[TAG_byte]` **rescale**: Визначає, чи масштабувати грані по всьому блоку. Може бути `true` або `false`. За замовчуванням — `false`.
|
||||
* `[TAG_byte]` **shade**: Визначає, чи рендерити тіні (`true` — за замовчуванням), чи ні (`false`).
|
||||
* `[TAG_compound]` **faces**: Містить усі грані кубоїда. Якщо грань пропущено, вона не рендериться.
|
||||
* `[TAG_compound]` ***Face***: Має назву `down`, `up`, `north`, `south`, `west` або `east`. Містить властивості вказаної грані.
|
||||
* `[TAG_list]` **uv**: Визначає ділянку текстури для використання за схемою `[x1, y1, x2, y2]`. Поведінка текстури стає непередбачуваною, якщо значення UV виходять за межі 0 або 16. Якщо значення `x1` та `x2` поміняти місцями (наприклад, з `0, 0, 16, 16` на `16, 0, 0, 16`), текстура віддзеркалюється. Параметр UV є необов’язковим; якщо його не вказано, він автоматично генерується на основі позиції елемента.
|
||||
* `[TAG_string]` **texture**: Вказує текстуру у формі текстурної змінної зі знаком `#` на початку.
|
||||
* `[TAG_string]` **cullface**: Визначає, чи не потрібно рендерити грань, якщо до неї в указаній позиції торкається інший блок. Позиція може бути: `down`, `up`, `north`, `south`, `west` або `east`. Цей параметр також визначає сторону блоку, з якої береться рівень освітлення для цієї грані; якщо параметр не задано, за замовчуванням використовується відповідна сторона.
|
||||
* `[TAG_int]` **rotation**: Обертає текстуру за годинниковою стрілкою на вказаний кут у градусах. Може бути 0, 90, 180 або 270. За замовчуванням — 0. Обертання не впливає на те, яка частина текстури використовується. Натомість воно зводиться до перестановки вершин вибраної текстури (вибраних неявно або явно через `uv`).
|
||||
* `[TAG_int]` **tintindex**: Визначає, чи потрібно тонувати текстуру за допомогою вшитого індексу відтінку. Значення за замовчуванням, -1, вказує на те, що відтінок не використовується. Будь-яке інше число передається в `BlockColors` для отримання значення відтінку, відповідного цьому індексу. Однак більшість блоків не мають визначеного значення відтінку (у такому разі використовується білий колір). Крім того, жоден ванільний блок наразі не використовує кілька значень відтінку, тому значення індексу відтінку ігнорується (якщо воно відмінне від -1); втім, його можна використовувати для модифікованих блоків, яким потрібні кілька різних значень відтінку в одному блоці.
|
||||
@@ -224,6 +224,9 @@ fun Test.configureGlsmJava8() {
|
||||
val extractNatives = rootProject.tasks.named("extractNatives2")
|
||||
dependsOn(extractNatives)
|
||||
jvmArgs("-Djava.library.path=${extractNatives.get().property("destinationFolder").let { (it as DirectoryProperty).asFile.get().path }}")
|
||||
if (System.getProperty("os.name").lowercase().contains("mac")) {
|
||||
jvmArgs("-Dapple.awt.UIElement=true")
|
||||
}
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
|
||||
@@ -47,6 +47,7 @@ public final class SystemProperties {
|
||||
// Flyby
|
||||
public static final String FLYBY_ROUTE = System.getProperty("angelica.flyby.route", "");
|
||||
public static final boolean FLYBY_WAIT_FOR_TRACY = Boolean.getBoolean("angelica.flyby.waitForTracy");
|
||||
public static final boolean FLYBY_WAIT_FOR_FOCUS = Boolean.getBoolean("angelica.flyby.waitForFocus");
|
||||
public static final int FLYBY_WARMUP_TICKS = Integer.getInteger("angelica.flyby.warmupTicks", 400);
|
||||
public static final int FLYBY_LENGTH = Integer.getInteger("angelica.flyby.length", 0); // Blocks for moving routes, ticks for stationary
|
||||
public static final double FLYBY_SPEED = parseDouble("angelica.flyby.speed"); // Travel speed in blocks per tick
|
||||
|
||||
@@ -307,6 +307,11 @@ public class DisplayListManager {
|
||||
}
|
||||
}
|
||||
|
||||
public static void recordSecondaryColor(float r, float g, float b) {
|
||||
drawBarrier();
|
||||
currentRecorder.writeSecondaryColor(r, g, b);
|
||||
}
|
||||
|
||||
public static void recordColorMask(boolean r, boolean g, boolean b, boolean a) {
|
||||
drawBarrier();
|
||||
currentRecorder.writeColorMask(r, g, b, a);
|
||||
@@ -841,7 +846,12 @@ public class DisplayListManager {
|
||||
if (hasCommands) {
|
||||
DisplayListVBO compiledBuffers = null;
|
||||
try {
|
||||
compiledBuffers = new DisplayListVBOBuilder().addDraws(accumulatedDraws).build();
|
||||
final CommandRecorder pausedVbo = pauseRecording();
|
||||
try {
|
||||
compiledBuffers = new DisplayListVBOBuilder().addDraws(accumulatedDraws).build();
|
||||
} finally {
|
||||
resumeRecording(pausedVbo);
|
||||
}
|
||||
final IndexedDrawBatchBuilder indexedBuilder = commandBuffer.getIndexedDraws();
|
||||
final List<IndexedDrawBatch> indexedBatches;
|
||||
if (indexedBuilder.isEmpty()) {
|
||||
|
||||
@@ -99,6 +99,7 @@ public class Feature {
|
||||
));
|
||||
attribToFeatures.put(GL11.GL_CURRENT_BIT, ImmutableSet.of(
|
||||
GLStateManager.getColor()
|
||||
, GLStateManager.getSecondaryColor() // Current secondary color
|
||||
, ShaderManager.getNormalStack() // Current normal vector
|
||||
, ShaderManager.getTexCoordStack() // Current texture coordinates
|
||||
// Current color index
|
||||
@@ -122,6 +123,7 @@ public class Feature {
|
||||
, GLStateManager.getBlendMode()
|
||||
, GLStateManager.getColorLogicOpState()
|
||||
, GLStateManager.getColorMaterial()
|
||||
, GLStateManager.getColorSumState()
|
||||
, GLStateManager.getCullState()
|
||||
, GLStateManager.getDepthTest()
|
||||
, GLStateManager.getDitherState()
|
||||
@@ -197,6 +199,7 @@ public class Feature {
|
||||
));
|
||||
attribToFeatures.put(GL11.GL_FOG_BIT, ImmutableSet.of(
|
||||
GLStateManager.getFogMode()
|
||||
, GLStateManager.getColorSumState()
|
||||
, GLStateManager.getFogState()
|
||||
// ^^ Fog density
|
||||
// ^^ Linear fog start
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.stacks.IStateStack;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.ints.IntSet;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.AlphaStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.BlendStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.BooleanStateStack;
|
||||
@@ -31,6 +35,7 @@ import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
import org.lwjgl.opengl.GL14;
|
||||
|
||||
import java.nio.DoubleBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
@@ -104,6 +109,9 @@ class GLContextState {
|
||||
public final FogStateStack fogState = new FogStateStack();
|
||||
public final BooleanStateStack fogMode = track(new BooleanStateStack(GL11.GL_FOG, false, true));
|
||||
public final Color4Stack color = new Color4Stack();
|
||||
// GL_CURRENT_SECONDARY_COLOR - alpha is always 1 and unused, the color sum only adds RGB
|
||||
public final Color4Stack secondaryColor = new Color4Stack(new Color4(0.0F, 0.0F, 0.0F, 1.0F));
|
||||
public final BooleanStateStack colorSumState = track(new BooleanStateStack(GL14.GL_COLOR_SUM, false, true));
|
||||
public final Color4Stack clearColor = new Color4Stack(new Color4(0.0F, 0.0F, 0.0F, 0.0F));
|
||||
public final ColorMaskStack colorMask = new ColorMaskStack();
|
||||
public final IntegerStateStack drawBuffer = new IntegerStateStack(GLStateManager.DEFAULT_DRAW_BUFFER);
|
||||
@@ -176,12 +184,16 @@ class GLContextState {
|
||||
public int boundVAO;
|
||||
public int boundPixelUnpackBuffer;
|
||||
public int boundPixelPackBuffer;
|
||||
public int boundCopyReadBuffer;
|
||||
public int boundCopyWriteBuffer;
|
||||
public final Int2IntMap boundOtherBuffers = new Int2IntOpenHashMap();
|
||||
public final IntSet writeMappedBuffers = new IntOpenHashSet();
|
||||
public PixelUnpackState pixelUnpackState = PixelUnpackState.DEFAULT;
|
||||
public final int[] clientAttribSavedTextureUnit = new int[GLStateManager.CLIENT_ATTRIB_STACK_DEPTH];
|
||||
public final int[] clientAttribSavedVertexFlags = new int[GLStateManager.CLIENT_ATTRIB_STACK_DEPTH];
|
||||
public int clientAttribStackPointer = 0;
|
||||
public final boolean[] restoreUnitChanged = new boolean[GLStateManager.MAX_TEXTURE_UNITS];
|
||||
public boolean restoreDepthChanged, restoreBlendChanged, restoreColorMaskChanged, restoreClearColorChanged, restoreDrawBufferChanged, restoreLogicOpChanged, restoreStencilChanged, restoreViewportChanged, restoreLineChanged, restorePointChanged, restorePolygonChanged, restoreActiveUnitChanged;
|
||||
public boolean restoreDepthChanged, restoreBlendChanged, restoreColorMaskChanged, restoreClearColorChanged, restoreDrawBufferChanged, restoreLogicOpChanged, restoreStencilChanged, restoreViewportChanged, restoreLineChanged, restorePointChanged, restorePolygonChanged, restoreActiveUnitChanged, restoreSecondaryColorChanged;
|
||||
public final BlendState vanillaBlendBefore = new BlendState();
|
||||
public final BlendState vanillaBlendAfter = new BlendState();
|
||||
public boolean vanillaBlendEnabledBefore;
|
||||
|
||||
@@ -11,6 +11,7 @@ import com.gtnewhorizons.angelica.glsm.DisplayListManager.RecordMode;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.BackendManager;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.GLDebugMessageListener;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.VSyncMode;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.FfpExtendedAttribs;
|
||||
import com.gtnewhorizons.angelica.glsm.profiling.Tracy;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.ShaderManager;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.VAOManager;
|
||||
@@ -56,6 +57,7 @@ import com.gtnewhorizons.angelica.glsm.states.TextureBinding;
|
||||
import com.gtnewhorizons.angelica.glsm.states.TextureUnitArray;
|
||||
import com.gtnewhorizons.angelica.glsm.texture.TextureInfo;
|
||||
import com.gtnewhorizons.angelica.glsm.texture.TextureInfoCache;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.ints.IntStack;
|
||||
@@ -504,6 +506,7 @@ public class GLStateManager {
|
||||
.addFeature(GL11.GL_COLOR_WRITEMASK)
|
||||
.addFeature(GL11.GL_CURRENT_COLOR)
|
||||
.addFeature(GL11.GL_CURRENT_NORMAL)
|
||||
.addFeature(GL14.GL_CURRENT_SECONDARY_COLOR)
|
||||
.addFeature(GL11.GL_CURRENT_RASTER_COLOR)
|
||||
.addFeature(GL11.GL_CURRENT_RASTER_POSITION)
|
||||
.addFeature(GL11.GL_CURRENT_RASTER_TEXTURE_COORDS)
|
||||
@@ -659,6 +662,7 @@ public class GLStateManager {
|
||||
case GL11.GL_BLEND -> enableBlend();
|
||||
case GL11.GL_COLOR_MATERIAL -> enableColorMaterial();
|
||||
case GL11.GL_COLOR_LOGIC_OP -> glCtx.colorLogicOpState.enable();
|
||||
case GL14.GL_COLOR_SUM -> glCtx.colorSumState.enable();
|
||||
case GL11.GL_CULL_FACE -> enableCull();
|
||||
case GL11.GL_DEPTH_TEST -> enableDepthTest();
|
||||
case GL11.GL_DITHER -> glCtx.ditherState.enable();
|
||||
@@ -740,6 +744,7 @@ public class GLStateManager {
|
||||
case GL11.GL_BLEND -> disableBlend();
|
||||
case GL11.GL_COLOR_MATERIAL -> disableColorMaterial();
|
||||
case GL11.GL_COLOR_LOGIC_OP -> glCtx.colorLogicOpState.disable();
|
||||
case GL14.GL_COLOR_SUM -> glCtx.colorSumState.disable();
|
||||
case GL11.GL_CULL_FACE -> disableCull();
|
||||
case GL11.GL_DEPTH_TEST -> disableDepthTest();
|
||||
case GL11.GL_DITHER -> glCtx.ditherState.disable();
|
||||
@@ -815,6 +820,7 @@ public class GLStateManager {
|
||||
case GL11.GL_BLEND -> glCtx.blendMode.isEnabled();
|
||||
case GL11.GL_COLOR_MATERIAL -> glCtx.colorMaterial.isEnabled();
|
||||
case GL11.GL_COLOR_LOGIC_OP -> glCtx.colorLogicOpState.isEnabled();
|
||||
case GL14.GL_COLOR_SUM -> glCtx.colorSumState.isEnabled();
|
||||
case GL11.GL_CULL_FACE -> glCtx.cullState.isEnabled();
|
||||
case GL11.GL_DEPTH_TEST -> glCtx.depthTest.isEnabled();
|
||||
case GL11.GL_DITHER -> glCtx.ditherState.isEnabled();
|
||||
@@ -890,6 +896,7 @@ public class GLStateManager {
|
||||
case GL11.GL_BLEND -> glCtx.blendMode.isEnabled();
|
||||
case GL11.GL_COLOR_MATERIAL -> glCtx.colorMaterial.isEnabled();
|
||||
case GL11.GL_COLOR_LOGIC_OP -> glCtx.colorLogicOpState.isEnabled();
|
||||
case GL14.GL_COLOR_SUM -> glCtx.colorSumState.isEnabled();
|
||||
case GL11.GL_CULL_FACE -> glCtx.cullState.isEnabled();
|
||||
case GL11.GL_DEPTH_TEST -> glCtx.depthTest.isEnabled();
|
||||
case GL11.GL_DEPTH_WRITEMASK -> glCtx.depthState.isEnabled();
|
||||
@@ -1133,6 +1140,7 @@ public class GLStateManager {
|
||||
case GL11.GL_TEXTURE_MATRIX -> glCtx.textures.getTextureUnitMatrix(getActiveTextureUnit()).get(0, params);
|
||||
case GL11.GL_COLOR_CLEAR_VALUE -> glCtx.clearColor.get(params);
|
||||
case GL11.GL_CURRENT_COLOR -> glCtx.color.get(params);
|
||||
case GL14.GL_CURRENT_SECONDARY_COLOR -> glCtx.secondaryColor.get(params);
|
||||
case GL11.GL_DEPTH_RANGE -> {
|
||||
final int pos = params.position();
|
||||
params.put(pos, (float) glCtx.viewportState.depthRangeNear);
|
||||
@@ -1231,7 +1239,7 @@ public class GLStateManager {
|
||||
}
|
||||
case GL11.GL_MODELVIEW_MATRIX, GL11.GL_PROJECTION_MATRIX, GL11.GL_TEXTURE_MATRIX -> widenFromFloat(pname, params, 16);
|
||||
case GL11.GL_COLOR_CLEAR_VALUE, GL11.GL_CURRENT_COLOR, GL11.GL_CURRENT_TEXTURE_COORDS, GL11.GL_FOG_COLOR,
|
||||
GL11.GL_LIGHT_MODEL_AMBIENT, GL14.GL_BLEND_COLOR -> widenFromFloat(pname, params, 4);
|
||||
GL11.GL_LIGHT_MODEL_AMBIENT, GL14.GL_BLEND_COLOR, GL14.GL_CURRENT_SECONDARY_COLOR -> widenFromFloat(pname, params, 4);
|
||||
case GL11.GL_CURRENT_NORMAL -> widenFromFloat(pname, params, 3);
|
||||
default -> {
|
||||
if (!HAS_MULTIPLE_SET.contains(pname)) {
|
||||
@@ -1754,6 +1762,40 @@ public class GLStateManager {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void glSecondaryColor3f(float red, float green, float blue) {
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordSecondaryColor(red, green, blue);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
changeSecondaryColor(red, green, blue);
|
||||
}
|
||||
|
||||
public static void glSecondaryColor3d(double red, double green, double blue) {
|
||||
glSecondaryColor3f((float) red, (float) green, (float) blue);
|
||||
}
|
||||
|
||||
public static void glSecondaryColor3b(byte red, byte green, byte blue) {
|
||||
glSecondaryColor3f(b2f(red), b2f(green), b2f(blue));
|
||||
}
|
||||
|
||||
public static void glSecondaryColor3ub(byte red, byte green, byte blue) {
|
||||
glSecondaryColor3f(ub2f(red), ub2f(green), ub2f(blue));
|
||||
}
|
||||
|
||||
/** Helper for glSecondaryColor* - the color sum is emulated by the FFP shaders, nothing is sent to the driver. */
|
||||
private static void changeSecondaryColor(float red, float green, float blue) {
|
||||
final GLContextState glCtx = ctx();
|
||||
if (!isCachingEnabled() || red != glCtx.secondaryColor.getRed() || green != glCtx.secondaryColor.getGreen() || blue != glCtx.secondaryColor.getBlue()) {
|
||||
glCtx.secondaryColor.setRed(red);
|
||||
glCtx.secondaryColor.setGreen(green);
|
||||
glCtx.secondaryColor.setBlue(blue);
|
||||
glCtx.fragmentGeneration++;
|
||||
}
|
||||
}
|
||||
|
||||
private static final Color4 DirtyColor = new Color4(-1.0F, -1.0F, -1.0F, -1.0F);
|
||||
|
||||
public static void clearCurrentColor() {
|
||||
@@ -2537,12 +2579,17 @@ public class GLStateManager {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (mode == GL11.GL_QUADS) {
|
||||
QuadConverter.drawQuadElementsAsTriangles(indices_count, type, indices_buffer_offset);
|
||||
return;
|
||||
final boolean ffpExt = FfpExtendedAttribs.maybeBindIndexed(mode, indices_count, type, indices_buffer_offset);
|
||||
try {
|
||||
if (mode == GL11.GL_QUADS) {
|
||||
QuadConverter.drawQuadElementsAsTriangles(indices_count, type, indices_buffer_offset);
|
||||
return;
|
||||
}
|
||||
preDraw(mode);
|
||||
RENDER_BACKEND.drawElements(mode, indices_count, type, indices_buffer_offset);
|
||||
} finally {
|
||||
if (ffpExt) FfpExtendedAttribs.unbind();
|
||||
}
|
||||
preDraw(mode);
|
||||
RENDER_BACKEND.drawElements(mode, indices_count, type, indices_buffer_offset);
|
||||
} finally {
|
||||
if (savedRecorder != null) DisplayListManager.resumeRecording(savedRecorder);
|
||||
}
|
||||
@@ -2652,19 +2699,24 @@ public class GLStateManager {
|
||||
FeedbackManager.processDrawArrays(mode, first, count);
|
||||
return;
|
||||
}
|
||||
if (mode == GL11.GL_QUADS) {
|
||||
QuadConverter.drawQuadsAsTriangles(first, count);
|
||||
} else if (mode == GL11.GL_QUAD_STRIP) {
|
||||
preDraw();
|
||||
RENDER_BACKEND.drawArrays(GL11.GL_TRIANGLE_STRIP, first, count & ~1);
|
||||
} else if (mode == GL11.GL_POLYGON) {
|
||||
preDraw();
|
||||
RENDER_BACKEND.drawArrays(GL11.GL_TRIANGLE_FAN, first, count);
|
||||
} else {
|
||||
preDraw(mode);
|
||||
RENDER_BACKEND.drawArrays(mode, first, count);
|
||||
final boolean ffpExt = FfpExtendedAttribs.maybeBind(mode, first, count);
|
||||
try {
|
||||
if (mode == GL11.GL_QUADS) {
|
||||
QuadConverter.drawQuadsAsTriangles(first, count);
|
||||
} else if (mode == GL11.GL_QUAD_STRIP) {
|
||||
preDraw();
|
||||
RENDER_BACKEND.drawArrays(GL11.GL_TRIANGLE_STRIP, first, count & ~1);
|
||||
} else if (mode == GL11.GL_POLYGON) {
|
||||
preDraw();
|
||||
RENDER_BACKEND.drawArrays(GL11.GL_TRIANGLE_FAN, first, count);
|
||||
} else {
|
||||
preDraw(mode);
|
||||
RENDER_BACKEND.drawArrays(mode, first, count);
|
||||
}
|
||||
} finally {
|
||||
if (ffpExt) FfpExtendedAttribs.unbind();
|
||||
if (savedRecorder != null) DisplayListManager.resumeRecording(savedRecorder);
|
||||
}
|
||||
if (savedRecorder != null) DisplayListManager.resumeRecording(savedRecorder);
|
||||
}
|
||||
|
||||
private static void ffpClientArrayPointer(int index, int size, int type, boolean normalized, int stride, long offset) {
|
||||
@@ -3656,6 +3708,9 @@ public class GLStateManager {
|
||||
glCtx.restoreDrawBufferChanged = glCtx.drawBuffer.topChanged();
|
||||
glCtx.restoreLogicOpChanged = glCtx.logicOpMode.topChanged();
|
||||
}
|
||||
if ((mask & GL11.GL_CURRENT_BIT) != 0) {
|
||||
glCtx.restoreSecondaryColorChanged = glCtx.secondaryColor.topChanged();
|
||||
}
|
||||
if ((mask & GL11.GL_STENCIL_BUFFER_BIT) != 0) {
|
||||
glCtx.restoreStencilChanged = glCtx.stencilState.topChanged();
|
||||
}
|
||||
@@ -3832,6 +3887,8 @@ public class GLStateManager {
|
||||
ShaderManager.bumpTexCoordGeneration();
|
||||
glCtx.dirtyTexCoordAttrib = true;
|
||||
}
|
||||
// The secondary color is a fragment stage uniform, not a vertex attribute
|
||||
if (glCtx.restoreSecondaryColorChanged) glCtx.fragmentGeneration++;
|
||||
}
|
||||
|
||||
postVanillaBlendChangeIfMoved(blendSnapshotted);
|
||||
@@ -5841,10 +5898,19 @@ public class GLStateManager {
|
||||
if (buffer == 0) return;
|
||||
final boolean locked = acquireDrawLock();
|
||||
try {
|
||||
FfpExtendedAttribs.onDeleteBuffer(buffer);
|
||||
if (glCtx.boundVBO == buffer) glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
if (VAOManager.boundEBO == buffer) glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
if (glCtx.boundPixelUnpackBuffer == buffer) glBindBuffer(GL21.GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
if (glCtx.boundPixelPackBuffer == buffer) glBindBuffer(GL21.GL_PIXEL_PACK_BUFFER, 0);
|
||||
if (glCtx.boundCopyReadBuffer == buffer) glBindBuffer(GL31.GL_COPY_READ_BUFFER, 0);
|
||||
if (glCtx.boundCopyWriteBuffer == buffer) glBindBuffer(GL31.GL_COPY_WRITE_BUFFER, 0);
|
||||
for (Int2IntMap.Entry e : glCtx.boundOtherBuffers.int2IntEntrySet()) {
|
||||
if (e.getIntValue() != buffer) continue;
|
||||
e.setValue(0);
|
||||
RENDER_BACKEND.bindBuffer(e.getIntKey(), 0);
|
||||
}
|
||||
glCtx.writeMappedBuffers.remove(buffer);
|
||||
|
||||
VAOManager.onDeleteBuffer(buffer);
|
||||
} finally {
|
||||
@@ -5852,20 +5918,30 @@ public class GLStateManager {
|
||||
}
|
||||
}
|
||||
|
||||
/** glBindBufferBase/Range also set the generic binding point, so they share this. */
|
||||
private static void trackBufferBinding(GLContextState glCtx, int target, int buffer) {
|
||||
if (target == GL15.GL_ARRAY_BUFFER) {
|
||||
// if (boundVBO == buffer) return; TODO figure out why this breaks switching async occlusion mode
|
||||
glCtx.boundVBO = buffer;
|
||||
} else if (target == GL15.GL_ELEMENT_ARRAY_BUFFER) {
|
||||
VAOManager.onBindEBO(buffer);
|
||||
} else if (target == GL21.GL_PIXEL_UNPACK_BUFFER) {
|
||||
glCtx.boundPixelUnpackBuffer = buffer;
|
||||
} else if (target == GL21.GL_PIXEL_PACK_BUFFER) {
|
||||
glCtx.boundPixelPackBuffer = buffer;
|
||||
} else if (target == GL31.GL_COPY_READ_BUFFER) {
|
||||
glCtx.boundCopyReadBuffer = buffer;
|
||||
} else if (target == GL31.GL_COPY_WRITE_BUFFER) {
|
||||
glCtx.boundCopyWriteBuffer = buffer;
|
||||
} else {
|
||||
glCtx.boundOtherBuffers.put(target, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
public static void glBindBuffer(int target, int buffer) {
|
||||
final GLContextState glCtx = ctx();
|
||||
final boolean locked = acquireDrawLock();
|
||||
try {
|
||||
if (target == GL15.GL_ARRAY_BUFFER) {
|
||||
// if (boundVBO == buffer) return; TODO figure out why this breaks switching async occlusion mode
|
||||
glCtx.boundVBO = buffer;
|
||||
} else if (target == GL15.GL_ELEMENT_ARRAY_BUFFER) {
|
||||
VAOManager.onBindEBO(buffer);
|
||||
} else if (target == GL21.GL_PIXEL_UNPACK_BUFFER) {
|
||||
glCtx.boundPixelUnpackBuffer = buffer;
|
||||
} else if (target == GL21.GL_PIXEL_PACK_BUFFER) {
|
||||
glCtx.boundPixelPackBuffer = buffer;
|
||||
}
|
||||
trackBufferBinding(ctx(), target, buffer);
|
||||
RENDER_BACKEND.bindBuffer(target, buffer);
|
||||
} finally {
|
||||
if (locked) releaseDrawLock();
|
||||
@@ -5944,15 +6020,78 @@ public class GLStateManager {
|
||||
for (int i = 0; i < buffers.length; i++) buffers[i] = RENDER_BACKEND.createBuffers();
|
||||
}
|
||||
|
||||
public static void glBufferData(int target, long size, int usage) { RENDER_BACKEND.bufferData(target, size, usage); }
|
||||
public static void glBufferData(int target, ByteBuffer data, int usage) { RENDER_BACKEND.bufferData(target, data, usage); }
|
||||
public static void glBufferData(int target, ShortBuffer data, int usage) { RENDER_BACKEND.bufferData(target, data, usage); }
|
||||
public static void glBufferData(int target, IntBuffer data, int usage) { RENDER_BACKEND.bufferData(target, data, usage); }
|
||||
public static void glBufferData(int target, FloatBuffer data, int usage) { RENDER_BACKEND.bufferData(target, data, usage); }
|
||||
public static void glBufferData(int target, DoubleBuffer data, int usage) { RENDER_BACKEND.bufferData(target, data, usage); }
|
||||
private static final int MAP_PERSISTENT_BIT = 0x40;
|
||||
|
||||
private static int bufferForTarget(int target) {
|
||||
final GLContextState glCtx = ctx();
|
||||
if (target == GL15.GL_ARRAY_BUFFER) return glCtx.boundVBO;
|
||||
if (target == GL15.GL_ELEMENT_ARRAY_BUFFER) return VAOManager.boundEBO;
|
||||
if (target == GL31.GL_COPY_READ_BUFFER) return glCtx.boundCopyReadBuffer;
|
||||
if (target == GL31.GL_COPY_WRITE_BUFFER) return glCtx.boundCopyWriteBuffer;
|
||||
if (target == GL21.GL_PIXEL_UNPACK_BUFFER) return glCtx.boundPixelUnpackBuffer;
|
||||
if (target == GL21.GL_PIXEL_PACK_BUFFER) return glCtx.boundPixelPackBuffer;
|
||||
return glCtx.boundOtherBuffers.get(target);
|
||||
}
|
||||
|
||||
private static void onBufferRespec(int target) {
|
||||
if (FfpExtendedAttribs.isEmpty()) return;
|
||||
final int buffer = bufferForTarget(target);
|
||||
if (buffer != 0) FfpExtendedAttribs.onBufferRespecified(buffer);
|
||||
}
|
||||
|
||||
private static void onBufferMap(int target, int access) {
|
||||
if (access == GL15.GL_READ_ONLY) return;
|
||||
markWriteMapped(target);
|
||||
onBufferRespec(target);
|
||||
}
|
||||
|
||||
private static void onBufferMapRange(int target, int access) {
|
||||
if ((access & GL30.GL_MAP_WRITE_BIT) == 0) return;
|
||||
if ((access & MAP_PERSISTENT_BIT) != 0) {
|
||||
markPersistentlyMapped(target);
|
||||
return;
|
||||
}
|
||||
markWriteMapped(target);
|
||||
onBufferRespec(target);
|
||||
}
|
||||
|
||||
private static void markWriteMapped(int target) {
|
||||
final int buffer = bufferForTarget(target);
|
||||
if (buffer != 0) ctx().writeMappedBuffers.add(buffer);
|
||||
}
|
||||
|
||||
private static void onBufferUnmap(int target) {
|
||||
final int buffer = bufferForTarget(target);
|
||||
if (buffer == 0 || !ctx().writeMappedBuffers.remove(buffer)) return;
|
||||
onBufferRespec(target);
|
||||
}
|
||||
|
||||
private static void onBufferStorage(int target, int flags) {
|
||||
if ((flags & MAP_PERSISTENT_BIT) != 0) {
|
||||
markPersistentlyMapped(target);
|
||||
return;
|
||||
}
|
||||
onBufferRespec(target);
|
||||
}
|
||||
|
||||
private static void markPersistentlyMapped(int target) {
|
||||
FfpExtendedAttribs.markPersistent(bufferForTarget(target));
|
||||
}
|
||||
|
||||
private static void onNamedBufferRespec(int buffer) {
|
||||
if (FfpExtendedAttribs.isEmpty()) return;
|
||||
FfpExtendedAttribs.onBufferRespecified(buffer);
|
||||
}
|
||||
|
||||
public static void glBufferData(int target, long size, int usage) { onBufferRespec(target); RENDER_BACKEND.bufferData(target, size, usage); }
|
||||
public static void glBufferData(int target, ByteBuffer data, int usage) { onBufferRespec(target); RENDER_BACKEND.bufferData(target, data, usage); }
|
||||
public static void glBufferData(int target, ShortBuffer data, int usage) { onBufferRespec(target); RENDER_BACKEND.bufferData(target, data, usage); }
|
||||
public static void glBufferData(int target, IntBuffer data, int usage) { onBufferRespec(target); RENDER_BACKEND.bufferData(target, data, usage); }
|
||||
public static void glBufferData(int target, FloatBuffer data, int usage) { onBufferRespec(target); RENDER_BACKEND.bufferData(target, data, usage); }
|
||||
public static void glBufferData(int target, DoubleBuffer data, int usage) { onBufferRespec(target); RENDER_BACKEND.bufferData(target, data, usage); }
|
||||
public static void glBufferData(int target, LongBuffer data, int usage) { glBufferData(target, MemoryUtilities.memByteBuffer(data), usage); }
|
||||
public static void glBufferData(int target, int[] data, int usage) { RENDER_BACKEND.bufferData(target, data, usage); }
|
||||
public static void glBufferData(int target, float[] data, int usage) { RENDER_BACKEND.bufferData(target, data, usage); }
|
||||
public static void glBufferData(int target, int[] data, int usage) { onBufferRespec(target); RENDER_BACKEND.bufferData(target, data, usage); }
|
||||
public static void glBufferData(int target, float[] data, int usage) { onBufferRespec(target); RENDER_BACKEND.bufferData(target, data, usage); }
|
||||
public static void glBufferData(int target, short[] data, int usage) {
|
||||
final ByteBuffer copy = copyOf(data);
|
||||
glBufferData(target, copy, usage);
|
||||
@@ -5977,11 +6116,11 @@ public class GLStateManager {
|
||||
glBufferData(target, MemoryUtilities.memByteBuffer(data, checkedSize(size)), usage);
|
||||
}
|
||||
|
||||
public static void glBufferSubData(int target, long offset, ByteBuffer data) { RENDER_BACKEND.bufferSubData(target, offset, data); }
|
||||
public static void glBufferSubData(int target, long offset, ShortBuffer data) { RENDER_BACKEND.bufferSubData(target, offset, data); }
|
||||
public static void glBufferSubData(int target, long offset, IntBuffer data) { RENDER_BACKEND.bufferSubData(target, offset, data); }
|
||||
public static void glBufferSubData(int target, long offset, FloatBuffer data) { RENDER_BACKEND.bufferSubData(target, offset, data); }
|
||||
public static void glBufferSubData(int target, long offset, DoubleBuffer data) { RENDER_BACKEND.bufferSubData(target, offset, data); }
|
||||
public static void glBufferSubData(int target, long offset, ByteBuffer data) { onBufferRespec(target); RENDER_BACKEND.bufferSubData(target, offset, data); }
|
||||
public static void glBufferSubData(int target, long offset, ShortBuffer data) { onBufferRespec(target); RENDER_BACKEND.bufferSubData(target, offset, data); }
|
||||
public static void glBufferSubData(int target, long offset, IntBuffer data) { onBufferRespec(target); RENDER_BACKEND.bufferSubData(target, offset, data); }
|
||||
public static void glBufferSubData(int target, long offset, FloatBuffer data) { onBufferRespec(target); RENDER_BACKEND.bufferSubData(target, offset, data); }
|
||||
public static void glBufferSubData(int target, long offset, DoubleBuffer data) { onBufferRespec(target); RENDER_BACKEND.bufferSubData(target, offset, data); }
|
||||
public static void glBufferSubData(int target, long offset, LongBuffer data) { glBufferSubData(target, offset, MemoryUtilities.memByteBuffer(data)); }
|
||||
public static void glBufferSubData(int target, long offset, short[] data) {
|
||||
final ByteBuffer copy = copyOf(data);
|
||||
@@ -6011,13 +6150,13 @@ public class GLStateManager {
|
||||
|
||||
public static void nglBufferSubData(int target, long offset, long size, long data) { glBufferSubData(target, offset, MemoryUtilities.memByteBuffer(data, checkedSize(size))); }
|
||||
|
||||
public static ByteBuffer glMapBuffer(int target, int access) { return RENDER_BACKEND.mapBuffer(target, access); }
|
||||
public static ByteBuffer glMapBuffer(int target, int access) { onBufferMap(target, access); return RENDER_BACKEND.mapBuffer(target, access); }
|
||||
public static ByteBuffer glMapBuffer(int target, int access, ByteBuffer old_buffer) { return glMapBuffer(target, access); }
|
||||
public static ByteBuffer glMapBuffer(int target, int access, long length, ByteBuffer old_buffer) { return RENDER_BACKEND.mapBuffer(target, access, length, old_buffer); }
|
||||
public static ByteBuffer glMapBufferRange(int target, long offset, long length, int access) { return RENDER_BACKEND.mapBufferRange(target, offset, length, access); }
|
||||
public static ByteBuffer glMapBuffer(int target, int access, long length, ByteBuffer old_buffer) { onBufferMap(target, access); return RENDER_BACKEND.mapBuffer(target, access, length, old_buffer); }
|
||||
public static ByteBuffer glMapBufferRange(int target, long offset, long length, int access) { onBufferMapRange(target, access); return RENDER_BACKEND.mapBufferRange(target, offset, length, access); }
|
||||
public static ByteBuffer glMapBufferRange(int target, long offset, long length, int access, ByteBuffer old_buffer) { return glMapBufferRange(target, offset, length, access); }
|
||||
public static void glFlushMappedBufferRange(int target, long offset, long length) { RENDER_BACKEND.flushMappedBufferRange(target, offset, length); }
|
||||
public static boolean glUnmapBuffer(int target) { return RENDER_BACKEND.unmapBuffer(target); }
|
||||
public static boolean glUnmapBuffer(int target) { onBufferUnmap(target); return RENDER_BACKEND.unmapBuffer(target); }
|
||||
|
||||
public static void glGetBufferSubData(int target, long offset, ByteBuffer data) { RENDER_BACKEND.getBufferSubData(target, offset, data); }
|
||||
public static void glGetBufferSubData(int target, long offset, ShortBuffer data) { RENDER_BACKEND.getBufferSubData(target, offset, data); }
|
||||
@@ -6064,8 +6203,8 @@ public class GLStateManager {
|
||||
public static void glGetBufferParameteriv(int target, int pname, IntBuffer params) { params.put(params.position(), glGetBufferParameteri(target, pname)); }
|
||||
public static void glGetBufferParameteriv(int target, int pname, int[] params) { params[0] = glGetBufferParameteri(target, pname); }
|
||||
|
||||
public static void glBufferStorage(int target, long size, int flags) { RENDER_BACKEND.bufferStorage(target, size, flags); }
|
||||
public static void glBufferStorage(int target, ByteBuffer data, int flags) { RENDER_BACKEND.bufferStorage(target, data, flags); }
|
||||
public static void glBufferStorage(int target, long size, int flags) { onBufferStorage(target, flags); RENDER_BACKEND.bufferStorage(target, size, flags); }
|
||||
public static void glBufferStorage(int target, ByteBuffer data, int flags) { onBufferStorage(target, flags); RENDER_BACKEND.bufferStorage(target, data, flags); }
|
||||
public static void glBufferStorage(int target, ShortBuffer data, int flags) { glBufferStorage(target, MemoryUtilities.memByteBuffer(data), flags); }
|
||||
public static void glBufferStorage(int target, IntBuffer data, int flags) { glBufferStorage(target, MemoryUtilities.memByteBuffer(data), flags); }
|
||||
public static void glBufferStorage(int target, FloatBuffer data, int flags) { glBufferStorage(target, MemoryUtilities.memByteBuffer(data), flags); }
|
||||
@@ -6091,9 +6230,9 @@ public class GLStateManager {
|
||||
MemoryUtilities.memFree(copy);
|
||||
}
|
||||
|
||||
public static void glNamedBufferData(int buffer, long size, int usage) { RENDER_BACKEND.namedBufferData(buffer, size, usage); }
|
||||
public static void glNamedBufferData(int buffer, ByteBuffer data, int usage) { RENDER_BACKEND.namedBufferData(buffer, data, usage); }
|
||||
public static void glNamedBufferData(int buffer, FloatBuffer data, int usage) { RENDER_BACKEND.namedBufferData(buffer, data, usage); }
|
||||
public static void glNamedBufferData(int buffer, long size, int usage) { onNamedBufferRespec(buffer); RENDER_BACKEND.namedBufferData(buffer, size, usage); }
|
||||
public static void glNamedBufferData(int buffer, ByteBuffer data, int usage) { onNamedBufferRespec(buffer); RENDER_BACKEND.namedBufferData(buffer, data, usage); }
|
||||
public static void glNamedBufferData(int buffer, FloatBuffer data, int usage) { onNamedBufferRespec(buffer); RENDER_BACKEND.namedBufferData(buffer, data, usage); }
|
||||
public static void glNamedBufferData(int buffer, ShortBuffer data, int usage) { glNamedBufferData(buffer, MemoryUtilities.memByteBuffer(data), usage); }
|
||||
public static void glNamedBufferData(int buffer, IntBuffer data, int usage) { glNamedBufferData(buffer, MemoryUtilities.memByteBuffer(data), usage); }
|
||||
public static void glNamedBufferData(int buffer, LongBuffer data, int usage) { glNamedBufferData(buffer, MemoryUtilities.memByteBuffer(data), usage); }
|
||||
@@ -6124,7 +6263,7 @@ public class GLStateManager {
|
||||
MemoryUtilities.memFree(copy);
|
||||
}
|
||||
|
||||
public static void glNamedBufferSubData(int buffer, long offset, ByteBuffer data) { RENDER_BACKEND.namedBufferSubData(buffer, offset, data); }
|
||||
public static void glNamedBufferSubData(int buffer, long offset, ByteBuffer data) { onNamedBufferRespec(buffer); RENDER_BACKEND.namedBufferSubData(buffer, offset, data); }
|
||||
public static void glNamedBufferSubData(int buffer, long offset, ShortBuffer data) { glNamedBufferSubData(buffer, offset, MemoryUtilities.memByteBuffer(data)); }
|
||||
public static void glNamedBufferSubData(int buffer, long offset, IntBuffer data) { glNamedBufferSubData(buffer, offset, MemoryUtilities.memByteBuffer(data)); }
|
||||
public static void glNamedBufferSubData(int buffer, long offset, LongBuffer data) { glNamedBufferSubData(buffer, offset, MemoryUtilities.memByteBuffer(data)); }
|
||||
@@ -6156,15 +6295,15 @@ public class GLStateManager {
|
||||
MemoryUtilities.memFree(copy);
|
||||
}
|
||||
|
||||
public static void glClearBufferSubData(int target, int internalFormat, long offset, long size, int format, int type, ByteBuffer data) { RENDER_BACKEND.clearBufferSubData(target, internalFormat, offset, size, format, type, data); }
|
||||
public static void glClearBufferData(int target, int internalformat, int format, int type, ByteBuffer data) { RENDER_BACKEND.clearBufferData(target, internalformat, format, type, data); }
|
||||
public static void glClearBufferSubData(int target, int internalFormat, long offset, long size, int format, int type, ByteBuffer data) { onBufferRespec(target); RENDER_BACKEND.clearBufferSubData(target, internalFormat, offset, size, format, type, data); }
|
||||
public static void glClearBufferData(int target, int internalformat, int format, int type, ByteBuffer data) { onBufferRespec(target); RENDER_BACKEND.clearBufferData(target, internalformat, format, type, data); }
|
||||
public static boolean glIsBuffer(int buffer) { return RENDER_BACKEND.isBuffer(buffer); }
|
||||
public static boolean glIsTexture(int texture) { return RENDER_BACKEND.isTexture(texture); }
|
||||
public static boolean glIsFramebuffer(int framebuffer) { return RENDER_BACKEND.isFramebuffer(framebuffer); }
|
||||
public static boolean glIsRenderbuffer(int renderbuffer) { return RENDER_BACKEND.isRenderbuffer(renderbuffer); }
|
||||
public static boolean glIsSampler(int sampler) { return RENDER_BACKEND.isSampler(sampler); }
|
||||
public static boolean glIsQuery(int query) { return RENDER_BACKEND.isQuery(query); }
|
||||
public static void glCopyBufferSubData(int readTarget, int writeTarget, long readOffset, long writeOffset, long size) { RENDER_BACKEND.copyBufferSubData(readTarget, writeTarget, readOffset, writeOffset, size); }
|
||||
public static void glCopyBufferSubData(int readTarget, int writeTarget, long readOffset, long writeOffset, long size) { onBufferRespec(writeTarget); RENDER_BACKEND.copyBufferSubData(readTarget, writeTarget, readOffset, writeOffset, size); }
|
||||
|
||||
private static int checkedSize(long size) {
|
||||
if (size < 0 || size > Integer.MAX_VALUE) {
|
||||
@@ -6328,7 +6467,26 @@ public class GLStateManager {
|
||||
public static void glGetQueryObjectui(int id, int pname, IntBuffer params) { RENDER_BACKEND.getQueryObjectui(id, pname, params); }
|
||||
public static int glGetQueryObjecti(int id, int pname) { return RENDER_BACKEND.getQueryObjecti(id, pname); }
|
||||
|
||||
public static void glBindBufferBase(int target, int index, int buffer) { RENDER_BACKEND.bindBufferBase(target, index, buffer); }
|
||||
public static void glBindBufferBase(int target, int index, int buffer) {
|
||||
final boolean locked = acquireDrawLock();
|
||||
try {
|
||||
trackBufferBinding(ctx(), target, buffer);
|
||||
RENDER_BACKEND.bindBufferBase(target, index, buffer);
|
||||
} finally {
|
||||
if (locked) releaseDrawLock();
|
||||
}
|
||||
}
|
||||
|
||||
public static void glBindBufferRange(int target, int index, int buffer, long offset, long size) {
|
||||
final boolean locked = acquireDrawLock();
|
||||
try {
|
||||
trackBufferBinding(ctx(), target, buffer);
|
||||
RENDER_BACKEND.bindBufferRange(target, index, buffer, offset, size);
|
||||
} finally {
|
||||
if (locked) releaseDrawLock();
|
||||
}
|
||||
}
|
||||
|
||||
public static int glGetUniformBlockIndex(int program, CharSequence name) { return RENDER_BACKEND.getUniformBlockIndex(program, name); }
|
||||
public static void glUniformBlockBinding(int program, int blockIndex, int binding) { RENDER_BACKEND.uniformBlockBinding(program, blockIndex, binding); }
|
||||
public static void glBindFragDataLocation(int program, int colorNumber, CharSequence name) { RENDER_BACKEND.bindFragDataLocation(program, colorNumber, name); }
|
||||
@@ -6366,6 +6524,7 @@ public class GLStateManager {
|
||||
}
|
||||
}
|
||||
public static long nglMapBuffer(int target, int access) {
|
||||
onBufferMap(target, access);
|
||||
final ByteBuffer buf = RENDER_BACKEND.mapBuffer(target, access);
|
||||
return buf == null ? 0L : MemoryUtilities.memAddress0(buf);
|
||||
}
|
||||
@@ -7411,6 +7570,14 @@ public class GLStateManager {
|
||||
return ctx().color;
|
||||
}
|
||||
|
||||
public static Color4Stack getSecondaryColor() {
|
||||
return ctx().secondaryColor;
|
||||
}
|
||||
|
||||
public static BooleanStateStack getColorSumState() {
|
||||
return ctx().colorSumState;
|
||||
}
|
||||
|
||||
public static Color4Stack getClearColor() {
|
||||
return ctx().clearColor;
|
||||
}
|
||||
@@ -7675,6 +7842,14 @@ public class GLStateManager {
|
||||
return ctx().boundVBO;
|
||||
}
|
||||
|
||||
public static int getBoundCopyReadBuffer() {
|
||||
return ctx().boundCopyReadBuffer;
|
||||
}
|
||||
|
||||
public static int getBoundCopyWriteBuffer() {
|
||||
return ctx().boundCopyWriteBuffer;
|
||||
}
|
||||
|
||||
public static int getBoundVAO() {
|
||||
return ctx().boundVAO;
|
||||
}
|
||||
|
||||
@@ -559,7 +559,7 @@ public class RenderSystem {
|
||||
}
|
||||
|
||||
public static void bindBufferBase(int target, int index, int buffer) {
|
||||
RENDER_BACKEND.bindBufferBase(target, index, buffer);
|
||||
GLStateManager.glBindBufferBase(target, index, buffer);
|
||||
}
|
||||
|
||||
public static void bufferStorage(int target, long size, int flags) {
|
||||
|
||||
@@ -103,6 +103,7 @@ public final class FFPUniformBlock {
|
||||
public static final int TEX_ENV_COLOR_2 = vec4("u_TexEnvColor2");
|
||||
public static final int TEX_ENV_COLOR_3 = vec4("u_TexEnvColor3");
|
||||
public static final int OVERLAY_COLOR = vec4("u_OverlayColor");
|
||||
public static final int SECONDARY_COLOR = vec3("u_SecondaryColor");
|
||||
public static final int FOG_PARAMS = vec4("u_FogParams");
|
||||
public static final int FOG_COLOR = vec4("u_FogColor");
|
||||
|
||||
|
||||
@@ -0,0 +1,583 @@
|
||||
package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.cel.api.util.NormI8;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormatElement.Usage;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMHooks;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.ImmediateExtendedAttribHandler;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.ints.IntSet;
|
||||
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectSet;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
import org.lwjgl.opengl.GL31;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.HashMap;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAddress0;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAlloc;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memCalloc;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memFree;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memGetByte;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memGetInt;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memGetShort;
|
||||
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
|
||||
|
||||
/**
|
||||
* Supplies {@code mc_midTexCoord}/{@code at_tangent} for mod geometry drawn through the FFPclient-array path
|
||||
* ({@code glVertexPointer}/{@code glTexCoordPointer} + {@code glDrawArrays}) out of a VBO, which bypasses the
|
||||
* Tessellator and display-list ext-injection paths.
|
||||
*
|
||||
* <p>Tangents are reconstructed from the flat face normal (handler's {@code normalOffset < 0} path).
|
||||
*/
|
||||
public final class FfpExtendedAttribs {
|
||||
|
||||
private FfpExtendedAttribs() {}
|
||||
|
||||
private static final int POSITION_LOC = Usage.POSITION.getAttributeLocation();
|
||||
private static final int UV_LOC = Usage.PRIMARY_UV.getAttributeLocation();
|
||||
private static final int NORMAL_LOC = Usage.NORMAL.getAttributeLocation();
|
||||
private static final int SRC_TEX_OFFSET = 12;
|
||||
private static final int SRC_NORMAL_OFFSET = 20;
|
||||
private static final int SRC_STRIDE_FLAT = 20;
|
||||
private static final int SRC_STRIDE_SMOOTH = 24;
|
||||
private static final int MAX_CACHE = 1024;
|
||||
private static final int MAX_EXT_VERTS = 1 << 20;
|
||||
private static final int MAX_RESPECS = 8;
|
||||
private static final int RESPEC_DECAY_FRAMES = 2;
|
||||
private static final int MIN_STREAM_SPAN_FRAMES = 4;
|
||||
private static final int STREAM_EXPIRE_FRAMES = 60;
|
||||
|
||||
private static final HashMap<Key, ExtBuffer> cache = new HashMap<>();
|
||||
private static final Key lookup = new Key();
|
||||
private static int internalGlDepth = 0;
|
||||
|
||||
private static final HashMap<EboKey, Boolean> eboPatternCache = new HashMap<>();
|
||||
private static final EboKey eboLookup = new EboKey();
|
||||
|
||||
private static final Int2ObjectMap<ObjectSet<Key>> keysBySource = new Int2ObjectOpenHashMap<>();
|
||||
private static final Int2ObjectMap<ObjectSet<EboKey>> eboKeysBySource = new Int2ObjectOpenHashMap<>();
|
||||
private static final Int2IntMap respecCounts = new Int2IntOpenHashMap();
|
||||
private static final Int2IntMap respecFrames = new Int2IntOpenHashMap();
|
||||
private static final Int2IntMap respecRunStart = new Int2IntOpenHashMap();
|
||||
private static final IntSet streamingSources = new IntOpenHashSet();
|
||||
private static final IntSet persistentSources = new IntOpenHashSet();
|
||||
private static int frame;
|
||||
|
||||
public static void endFrame() { frame++; }
|
||||
|
||||
public static boolean isEmpty() {
|
||||
return cache.isEmpty() && eboPatternCache.isEmpty();
|
||||
}
|
||||
|
||||
private static int internalDrawDepth = 0;
|
||||
|
||||
public static void beginInternalDraw() { internalDrawDepth++; }
|
||||
|
||||
public static void endInternalDraw() { if (internalDrawDepth > 0) internalDrawDepth--; }
|
||||
|
||||
private static void beginInternalGl() { internalGlDepth++; }
|
||||
|
||||
private static void endInternalGl() { if (internalGlDepth > 0) internalGlDepth--; }
|
||||
|
||||
private static boolean isNotConventionalTexturedArray(VAOManager.Attrib a) {
|
||||
return a == null || !a.enabled || a.genericPointer || a.vboId == 0 || a.size < 2;
|
||||
}
|
||||
|
||||
private static boolean isStreaming(int vboId) {
|
||||
if (vboId == 0 || !streamingSources.contains(vboId)) return false;
|
||||
if (persistentSources.contains(vboId)) return true;
|
||||
if (frame - respecFrames.get(vboId) > STREAM_EXPIRE_FRAMES) {
|
||||
streamingSources.remove(vboId);
|
||||
respecFrames.remove(vboId);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean maybeBind(int mode, int first, int count) {
|
||||
final ImmediateExtendedAttribHandler h = GLSMHooks.immediateExtendedHandler;
|
||||
if (h == null) return false;
|
||||
if (internalDrawDepth > 0) return false;
|
||||
final int extPrim = ImmediateExtendedAttribHandler.extPrimVerts(mode, count);
|
||||
if (extPrim == 0 || first < 0) return false;
|
||||
if (!h.wantsExtended()) return false;
|
||||
|
||||
final VAOManager.Attrib pos = VAOManager.get(POSITION_LOC);
|
||||
final VAOManager.Attrib uv = VAOManager.get(UV_LOC);
|
||||
if (isNotConventionalTexturedArray(pos) || isNotConventionalTexturedArray(uv)) return false;
|
||||
if (isStreaming(pos.vboId) || isStreaming(uv.vboId)) return false;
|
||||
|
||||
VAOManager.Attrib normal = extPrim == 3 ? VAOManager.get(NORMAL_LOC) : null;
|
||||
if (normal != null && (!normal.enabled || normal.genericPointer || normal.vboId == 0 || normal.size < 3
|
||||
|| isStreaming(normal.vboId))) {
|
||||
normal = null;
|
||||
}
|
||||
|
||||
return bindExt(h, mode, first, count, extPrim, pos, uv, normal);
|
||||
}
|
||||
|
||||
public static boolean maybeBindIndexed(int mode, int indexCount, int indexType, long indicesOffset) {
|
||||
final ImmediateExtendedAttribHandler h = GLSMHooks.immediateExtendedHandler;
|
||||
if (h == null) return false;
|
||||
if (internalDrawDepth > 0) return false;
|
||||
if (mode != GL11.GL_TRIANGLES || indexCount <= 0 || indexCount % 6 != 0) return false;
|
||||
if (!h.wantsExtended()) return false;
|
||||
|
||||
final VAOManager.Attrib pos = VAOManager.get(POSITION_LOC);
|
||||
final VAOManager.Attrib uv = VAOManager.get(UV_LOC);
|
||||
if (isNotConventionalTexturedArray(pos) || isNotConventionalTexturedArray(uv)) return false;
|
||||
if (isStreaming(pos.vboId) || isStreaming(uv.vboId) || isStreaming(VAOManager.boundEBO)) return false;
|
||||
|
||||
final int indexSize = indexTypeBytes(indexType);
|
||||
if (indexSize == 0) return false;
|
||||
if (indicesOffset % (6L * indexSize) != 0) return false;
|
||||
final int first = (int) (indicesOffset / indexSize) / 6 * 4;
|
||||
final int count = indexCount / 6 * 4;
|
||||
|
||||
if (!isQuadPatternEbo(VAOManager.boundEBO, indicesOffset, indexCount, indexType, indexSize, first)) return false;
|
||||
|
||||
// Build/bind as GL_QUADS (the real primitive): face-normal tangents, 4-vertex tiles, no per-vertex normal.
|
||||
return bindExt(h, GL11.GL_QUADS, first, count, 4, pos, uv, null);
|
||||
}
|
||||
|
||||
private static boolean isQuadPatternEbo(int ebo, long offset, int indexCount, int indexType, int indexSize, int firstVertex) {
|
||||
if (ebo == 0) return false;
|
||||
eboLookup.set(ebo, offset, indexCount, indexType);
|
||||
final Boolean cached = eboPatternCache.get(eboLookup);
|
||||
if (cached != null) return cached;
|
||||
if (eboPatternCache.size() >= MAX_CACHE) {
|
||||
eboPatternCache.clear();
|
||||
eboKeysBySource.clear();
|
||||
}
|
||||
|
||||
final ByteBuffer buf = memCalloc(indexCount, indexSize);
|
||||
RENDER_BACKEND.getBufferSubData(GL15.GL_ELEMENT_ARRAY_BUFFER, offset, buf);
|
||||
final long addr = memAddress0(buf);
|
||||
boolean match = true;
|
||||
for (int q = 0, quadCount = indexCount / 6; q < quadCount; q++) {
|
||||
final int base = firstVertex + q * 4;
|
||||
final long p = addr + (long) q * 6 * indexSize;
|
||||
if (readIndex(p, 0, indexSize) != base
|
||||
|| readIndex(p, 1, indexSize) != base + 1
|
||||
|| readIndex(p, 2, indexSize) != base + 2
|
||||
|| readIndex(p, 3, indexSize) != base + 2
|
||||
|| readIndex(p, 4, indexSize) != base + 3
|
||||
|| readIndex(p, 5, indexSize) != base) {
|
||||
match = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
memFree(buf);
|
||||
final EboKey stored = eboLookup.copy();
|
||||
eboPatternCache.put(stored, match);
|
||||
ObjectSet<EboKey> ebos = eboKeysBySource.get(stored.ebo);
|
||||
if (ebos == null) eboKeysBySource.put(stored.ebo, ebos = new ObjectOpenHashSet<>());
|
||||
ebos.add(stored);
|
||||
return match;
|
||||
}
|
||||
|
||||
private static long readIndex(long base, int i, int indexSize) {
|
||||
return switch (indexSize) {
|
||||
case 1 -> memGetByte(base + i) & 0xFFL;
|
||||
case 2 -> memGetShort(base + (long) i * 2) & 0xFFFFL;
|
||||
default -> memGetInt(base + (long) i * 4) & 0xFFFFFFFFL;
|
||||
};
|
||||
}
|
||||
|
||||
private static int indexTypeBytes(int indexType) {
|
||||
return switch (indexType) {
|
||||
case GL11.GL_UNSIGNED_BYTE -> 1;
|
||||
case GL11.GL_UNSIGNED_SHORT -> 2;
|
||||
case GL11.GL_UNSIGNED_INT -> 4;
|
||||
default -> 0;
|
||||
};
|
||||
}
|
||||
|
||||
private static boolean bindExt(ImmediateExtendedAttribHandler h, int mode, int first, int count, int extPrim,
|
||||
VAOManager.Attrib pos, VAOManager.Attrib uv, VAOManager.Attrib normal) {
|
||||
final int extVbo = getOrBuild(h, pos, uv, normal, first, count, extPrim, mode);
|
||||
if (extVbo == 0) return false;
|
||||
|
||||
final int savedVBO = GLStateManager.getBoundVBO();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, extVbo);
|
||||
ImmediateExtendedAttribHandler.setupExtAttribPointers(0L, ImmediateExtendedAttribHandler.EXT_STRIDE);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, savedVBO);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void unbind() {
|
||||
GLStateManager.glDisableVertexAttribArray(ImmediateExtendedAttribHandler.LOC_MID_TEX);
|
||||
GLStateManager.glDisableVertexAttribArray(ImmediateExtendedAttribHandler.LOC_TANGENT);
|
||||
setNeutralCurrentValues();
|
||||
}
|
||||
|
||||
public static void setNeutralCurrentValues() {
|
||||
GLStateManager.glVertexAttrib2f(ImmediateExtendedAttribHandler.LOC_MID_TEX, 0.5f, 0.5f);
|
||||
GLStateManager.glVertexAttrib4f(ImmediateExtendedAttribHandler.LOC_TANGENT, 1.0f, 0.0f, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
public static void onDeleteBuffer(int vboId) {
|
||||
if (internalGlDepth > 0 || vboId == 0) return;
|
||||
dropDerivedFrom(vboId);
|
||||
respecCounts.remove(vboId);
|
||||
respecFrames.remove(vboId);
|
||||
respecRunStart.remove(vboId);
|
||||
streamingSources.remove(vboId);
|
||||
persistentSources.remove(vboId);
|
||||
}
|
||||
|
||||
public static void onBufferRespecified(int vboId) {
|
||||
if (internalGlDepth > 0 || vboId == 0) return;
|
||||
if (streamingSources.contains(vboId)) {
|
||||
respecFrames.put(vboId, frame);
|
||||
return;
|
||||
}
|
||||
if (!dropDerivedFrom(vboId)) return;
|
||||
final boolean sameRun = respecCounts.containsKey(vboId) && frame - respecFrames.get(vboId) <= RESPEC_DECAY_FRAMES;
|
||||
if (!sameRun) {
|
||||
startRespecRun(vboId);
|
||||
return;
|
||||
}
|
||||
final int respecs = respecCounts.get(vboId) + 1;
|
||||
final int span = frame - respecRunStart.get(vboId);
|
||||
if (respecs >= MAX_RESPECS && span >= MIN_STREAM_SPAN_FRAMES && span <= respecs) {
|
||||
markStreaming(vboId);
|
||||
return;
|
||||
}
|
||||
if (span > respecs) {
|
||||
startRespecRun(vboId);
|
||||
return;
|
||||
}
|
||||
respecCounts.put(vboId, respecs);
|
||||
respecFrames.put(vboId, frame);
|
||||
}
|
||||
|
||||
private static void startRespecRun(int vboId) {
|
||||
respecCounts.put(vboId, 1);
|
||||
respecRunStart.put(vboId, frame);
|
||||
respecFrames.put(vboId, frame);
|
||||
}
|
||||
|
||||
public static void markStreaming(int vboId) {
|
||||
if (internalGlDepth > 0 || vboId == 0) return;
|
||||
respecCounts.remove(vboId);
|
||||
respecRunStart.remove(vboId);
|
||||
respecFrames.put(vboId, frame);
|
||||
if (streamingSources.add(vboId)) dropDerivedFrom(vboId);
|
||||
}
|
||||
|
||||
public static void markPersistent(int vboId) {
|
||||
if (internalGlDepth > 0 || vboId == 0) return;
|
||||
markStreaming(vboId);
|
||||
persistentSources.add(vboId);
|
||||
}
|
||||
|
||||
public static void reset() {
|
||||
invalidateAll();
|
||||
respecCounts.clear();
|
||||
respecFrames.clear();
|
||||
respecRunStart.clear();
|
||||
streamingSources.clear();
|
||||
persistentSources.clear();
|
||||
}
|
||||
|
||||
private static boolean dropDerivedFrom(int srcId) {
|
||||
final ObjectSet<EboKey> ebos = eboKeysBySource.remove(srcId);
|
||||
if (ebos != null) {
|
||||
for (EboKey k : ebos) eboPatternCache.remove(k);
|
||||
}
|
||||
final ObjectSet<Key> keys = keysBySource.get(srcId);
|
||||
if (keys == null || keys.isEmpty()) return ebos != null;
|
||||
IntArrayList toFree = null;
|
||||
for (Key k : keys.toArray(new Key[0])) {
|
||||
final ExtBuffer buf = cache.remove(k);
|
||||
if (buf != null && buf.vbo != 0) {
|
||||
if (toFree == null) toFree = new IntArrayList();
|
||||
toFree.add(buf.vbo);
|
||||
}
|
||||
unindexKey(k);
|
||||
}
|
||||
freeExtVbos(toFree);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void indexKey(Key k) {
|
||||
addToBucket(k.posVBO, k);
|
||||
if (k.uvVBO != k.posVBO) addToBucket(k.uvVBO, k);
|
||||
if (k.normalVBO != k.posVBO && k.normalVBO != k.uvVBO) addToBucket(k.normalVBO, k);
|
||||
}
|
||||
|
||||
private static void unindexKey(Key k) {
|
||||
removeFromBucket(k.posVBO, k);
|
||||
if (k.uvVBO != k.posVBO) removeFromBucket(k.uvVBO, k);
|
||||
if (k.normalVBO != k.posVBO && k.normalVBO != k.uvVBO) removeFromBucket(k.normalVBO, k);
|
||||
}
|
||||
|
||||
private static void addToBucket(int src, Key k) {
|
||||
if (src == 0) return;
|
||||
ObjectSet<Key> set = keysBySource.get(src);
|
||||
if (set == null) keysBySource.put(src, set = new ObjectOpenHashSet<>());
|
||||
set.add(k);
|
||||
}
|
||||
|
||||
private static void removeFromBucket(int src, Key k) {
|
||||
if (src == 0) return;
|
||||
final ObjectSet<Key> set = keysBySource.get(src);
|
||||
if (set != null && set.remove(k) && set.isEmpty()) keysBySource.remove(src);
|
||||
}
|
||||
|
||||
private static int getOrBuild(ImmediateExtendedAttribHandler h, VAOManager.Attrib pos, VAOManager.Attrib uv,
|
||||
VAOManager.Attrib normal, int first, int count, int extPrim, int mode) {
|
||||
if (first + count > MAX_EXT_VERTS) return 0;
|
||||
lookup.set(pos, uv, normal, mode, first % extPrim);
|
||||
ExtBuffer buf = cache.get(lookup);
|
||||
if (buf == null) {
|
||||
if (cache.size() >= MAX_CACHE) invalidateExtBuffers();
|
||||
buf = new ExtBuffer();
|
||||
final Key stored = lookup.copy();
|
||||
cache.put(stored, buf);
|
||||
indexKey(stored);
|
||||
}
|
||||
|
||||
final long range = (long) first << 32 | (count & 0xFFFFFFFFL);
|
||||
if (buf.vbo != 0 && buf.builtRanges.contains(range)) return buf.vbo;
|
||||
if (!ensureCapacity(buf, first + count)) return 0;
|
||||
buildRange(h, buf.vbo, pos, uv, normal, first, count, extPrim);
|
||||
buf.builtRanges.add(range);
|
||||
return buf.vbo;
|
||||
}
|
||||
|
||||
private static boolean ensureCapacity(ExtBuffer buf, int neededVerts) {
|
||||
if (buf.vbo != 0 && buf.capacityVerts >= neededVerts) return true;
|
||||
final int extStride = ImmediateExtendedAttribHandler.EXT_STRIDE;
|
||||
final int newCap = Math.min(MAX_EXT_VERTS, Math.max(neededVerts, buf.capacityVerts * 2));
|
||||
beginInternalGl();
|
||||
try {
|
||||
final int vbo = GLStateManager.glGenBuffers();
|
||||
if (vbo == 0) return false;
|
||||
final int savedVBO = GLStateManager.getBoundVBO();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
|
||||
GLStateManager.glBufferData(GL15.GL_ARRAY_BUFFER, (long) newCap * extStride, GL15.GL_STATIC_DRAW);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, savedVBO);
|
||||
if (buf.vbo != 0) {
|
||||
final int savedRead = GLStateManager.getBoundCopyReadBuffer();
|
||||
final int savedWrite = GLStateManager.getBoundCopyWriteBuffer();
|
||||
GLStateManager.glBindBuffer(GL31.GL_COPY_READ_BUFFER, buf.vbo);
|
||||
GLStateManager.glBindBuffer(GL31.GL_COPY_WRITE_BUFFER, vbo);
|
||||
GLStateManager.glCopyBufferSubData(GL31.GL_COPY_READ_BUFFER, GL31.GL_COPY_WRITE_BUFFER, 0L, 0L,
|
||||
(long) buf.capacityVerts * extStride);
|
||||
GLStateManager.glBindBuffer(GL31.GL_COPY_READ_BUFFER, savedRead);
|
||||
GLStateManager.glBindBuffer(GL31.GL_COPY_WRITE_BUFFER, savedWrite);
|
||||
GLStateManager.glDeleteBuffers(buf.vbo);
|
||||
}
|
||||
buf.vbo = vbo;
|
||||
buf.capacityVerts = newCap;
|
||||
return true;
|
||||
} finally {
|
||||
endInternalGl();
|
||||
}
|
||||
}
|
||||
|
||||
private static void buildRange(ImmediateExtendedAttribHandler h, int extVbo, VAOManager.Attrib pos,
|
||||
VAOManager.Attrib uv, VAOManager.Attrib normal, int first, int count, int extPrim) {
|
||||
final int posStride = pos.effectiveStride();
|
||||
final int uvStride = uv.effectiveStride();
|
||||
final boolean smooth = normal != null;
|
||||
final int normalStride = smooth ? normal.effectiveStride() : 0;
|
||||
final int srcStride = smooth ? SRC_STRIDE_SMOOTH : SRC_STRIDE_FLAT;
|
||||
final int srcNormalOffset = smooth ? SRC_NORMAL_OFFSET : -1;
|
||||
|
||||
final ByteBuffer posBuf = readRange(pos, first, count, posStride);
|
||||
final ByteBuffer uvBuf = readRange(uv, first, count, uvStride);
|
||||
final ByteBuffer normalBuf = smooth ? readRange(normal, first, count, normalStride) : null;
|
||||
|
||||
final ByteBuffer src = memAlloc(count * srcStride).order(ByteOrder.nativeOrder());
|
||||
for (int i = 0; i < count; i++) {
|
||||
final int pb = i * posStride, ub = i * uvStride, d = i * srcStride;
|
||||
src.putFloat(d, pos.readComponent(posBuf, pb, 0));
|
||||
src.putFloat(d + 4, pos.readComponent(posBuf, pb, 1));
|
||||
src.putFloat(d + 8, pos.size >= 3 ? pos.readComponent(posBuf, pb, 2) : 0f);
|
||||
src.putFloat(d + SRC_TEX_OFFSET, uv.readComponent(uvBuf, ub, 0));
|
||||
src.putFloat(d + SRC_TEX_OFFSET + 4, uv.readComponent(uvBuf, ub, 1));
|
||||
if (smooth) {
|
||||
final int nb = i * normalStride;
|
||||
src.putInt(d + SRC_NORMAL_OFFSET, NormI8.pack(
|
||||
normal.readComponent(normalBuf, nb, 0),
|
||||
normal.readComponent(normalBuf, nb, 1),
|
||||
normal.readComponent(normalBuf, nb, 2)));
|
||||
}
|
||||
}
|
||||
|
||||
final int extStride = ImmediateExtendedAttribHandler.EXT_STRIDE;
|
||||
final ByteBuffer ext = memCalloc(count, extStride);
|
||||
h.buildPacked(memAddress0(src), srcStride, 0, SRC_TEX_OFFSET, srcNormalOffset, count, extPrim, memAddress0(ext), extStride);
|
||||
|
||||
beginInternalGl();
|
||||
try {
|
||||
final int savedVBO = GLStateManager.getBoundVBO();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, extVbo);
|
||||
ext.position(0).limit(count * extStride);
|
||||
GLStateManager.glBufferSubData(GL15.GL_ARRAY_BUFFER, (long) first * extStride, ext);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, savedVBO);
|
||||
} finally {
|
||||
endInternalGl();
|
||||
}
|
||||
|
||||
memFree(posBuf);
|
||||
memFree(uvBuf);
|
||||
if (normalBuf != null) memFree(normalBuf);
|
||||
memFree(src);
|
||||
memFree(ext);
|
||||
}
|
||||
|
||||
private static ByteBuffer readRange(VAOManager.Attrib a, int first, int count, int stride) {
|
||||
final int elementBytes = a.size * a.typeSizeBytes();
|
||||
final int bytes = (count - 1) * stride + elementBytes;
|
||||
final ByteBuffer dst = memAlloc(bytes).order(ByteOrder.nativeOrder());
|
||||
dst.position(0).limit(bytes);
|
||||
final int savedVBO = GLStateManager.getBoundVBO();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, a.vboId);
|
||||
RENDER_BACKEND.getBufferSubData(GL15.GL_ARRAY_BUFFER, a.offset + (long) first * stride, dst);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, savedVBO);
|
||||
dst.position(0).limit(bytes);
|
||||
return dst;
|
||||
}
|
||||
|
||||
private static void invalidateAll() {
|
||||
eboPatternCache.clear();
|
||||
eboKeysBySource.clear();
|
||||
invalidateExtBuffers();
|
||||
}
|
||||
|
||||
private static void invalidateExtBuffers() {
|
||||
if (cache.isEmpty()) return;
|
||||
final IntArrayList toFree = new IntArrayList(cache.size());
|
||||
for (ExtBuffer buf : cache.values()) {
|
||||
if (buf.vbo != 0) toFree.add(buf.vbo);
|
||||
}
|
||||
cache.clear();
|
||||
keysBySource.clear();
|
||||
freeExtVbos(toFree);
|
||||
}
|
||||
|
||||
private static void freeExtVbos(IntArrayList vbos) {
|
||||
if (vbos == null || vbos.isEmpty()) return;
|
||||
beginInternalGl();
|
||||
try {
|
||||
for (int i = 0; i < vbos.size(); i++) {
|
||||
GLStateManager.glDeleteBuffers(vbos.getInt(i));
|
||||
}
|
||||
} finally {
|
||||
endInternalGl();
|
||||
}
|
||||
}
|
||||
|
||||
private static final class ExtBuffer {
|
||||
int vbo;
|
||||
int capacityVerts;
|
||||
final LongOpenHashSet builtRanges = new LongOpenHashSet();
|
||||
}
|
||||
|
||||
private static final class EboKey {
|
||||
int ebo, count, type;
|
||||
long offset;
|
||||
|
||||
void set(int ebo, long offset, int count, int type) {
|
||||
this.ebo = ebo;
|
||||
this.offset = offset;
|
||||
this.count = count;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
EboKey copy() {
|
||||
final EboKey k = new EboKey();
|
||||
k.set(ebo, offset, count, type);
|
||||
return k;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof EboKey k)) return false;
|
||||
return ebo == k.ebo && count == k.count && type == k.type && offset == k.offset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int h = ebo;
|
||||
h = h * 31 + count;
|
||||
h = h * 31 + type;
|
||||
h = h * 31 + Long.hashCode(offset);
|
||||
return h;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class Key {
|
||||
int posVBO, uvVBO, normalVBO, posStride, uvStride, normalStride, posFmt, uvFmt, normalFmt, mode, phase;
|
||||
long posOffset, uvOffset, normalOffset;
|
||||
|
||||
void set(VAOManager.Attrib pos, VAOManager.Attrib uv, VAOManager.Attrib normal, int mode, int phase) {
|
||||
posVBO = pos.vboId; posOffset = pos.offset; posStride = pos.effectiveStride(); posFmt = fmt(pos);
|
||||
uvVBO = uv.vboId; uvOffset = uv.offset; uvStride = uv.effectiveStride(); uvFmt = fmt(uv);
|
||||
if (normal != null) {
|
||||
normalVBO = normal.vboId; normalOffset = normal.offset;
|
||||
normalStride = normal.effectiveStride(); normalFmt = fmt(normal);
|
||||
} else {
|
||||
normalVBO = 0; normalOffset = 0; normalStride = 0; normalFmt = 0;
|
||||
}
|
||||
this.mode = mode;
|
||||
this.phase = phase;
|
||||
}
|
||||
|
||||
private static int fmt(VAOManager.Attrib a) {
|
||||
return (a.size & 0x7F) | (a.normalized ? 0x80 : 0) | (a.type << 8);
|
||||
}
|
||||
|
||||
Key copy() {
|
||||
final Key k = new Key();
|
||||
k.posVBO = posVBO; k.posOffset = posOffset; k.posStride = posStride; k.posFmt = posFmt;
|
||||
k.uvVBO = uvVBO; k.uvOffset = uvOffset; k.uvStride = uvStride; k.uvFmt = uvFmt;
|
||||
k.normalVBO = normalVBO; k.normalOffset = normalOffset; k.normalStride = normalStride; k.normalFmt = normalFmt;
|
||||
k.mode = mode; k.phase = phase;
|
||||
return k;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof Key k)) return false;
|
||||
return posVBO == k.posVBO && uvVBO == k.uvVBO && normalVBO == k.normalVBO
|
||||
&& posStride == k.posStride && uvStride == k.uvStride && normalStride == k.normalStride
|
||||
&& posFmt == k.posFmt && uvFmt == k.uvFmt && normalFmt == k.normalFmt
|
||||
&& mode == k.mode && phase == k.phase
|
||||
&& posOffset == k.posOffset && uvOffset == k.uvOffset && normalOffset == k.normalOffset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int h = posVBO;
|
||||
h = h * 31 + uvVBO;
|
||||
h = h * 31 + normalVBO;
|
||||
h = h * 31 + posStride;
|
||||
h = h * 31 + uvStride;
|
||||
h = h * 31 + normalStride;
|
||||
h = h * 31 + posFmt;
|
||||
h = h * 31 + uvFmt;
|
||||
h = h * 31 + normalFmt;
|
||||
h = h * 31 + mode;
|
||||
h = h * 31 + phase;
|
||||
h = h * 31 + Long.hashCode(posOffset);
|
||||
h = h * 31 + Long.hashCode(uvOffset);
|
||||
h = h * 31 + Long.hashCode(normalOffset);
|
||||
return h;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,7 @@ import org.lwjgl.opengl.GL13;
|
||||
* Packed-long fragment shader permutation key for FFP emulation.
|
||||
*
|
||||
* Layout:
|
||||
* long[0]: global bits (12) + unit 0 (47 bits at offset 12)
|
||||
* long[0]: global bits (13) + unit 0 (47 bits at offset 13)
|
||||
* long[1..3]: unit 1..3 (47 bits each, low-aligned)
|
||||
* Only max(1, nrEnabledUnits) longs are significant.
|
||||
*
|
||||
@@ -63,7 +63,7 @@ public final class FragmentKey {
|
||||
public static final int FOG_EXP = 2;
|
||||
public static final int FOG_EXP2 = 3;
|
||||
|
||||
private static final int GLOBAL_BITS = 12;
|
||||
private static final int GLOBAL_BITS = 13;
|
||||
private static final int BIT_FOG_MODE = 0; // 2 bits
|
||||
private static final int BIT_ALPHA_TEST = 2; // 1 bit
|
||||
private static final int BIT_ALPHA_FUNC = 3; // 3 bits
|
||||
@@ -71,6 +71,7 @@ public final class FragmentKey {
|
||||
private static final int BIT_NR_ENABLED_UNITS = 7; // 3 bits
|
||||
private static final int BIT_OVERLAY_ENABLED = 10; // 1 bit
|
||||
private static final int BIT_LINE_STIPPLE = 11; // 1 bit
|
||||
private static final int BIT_COLOR_SUM = 12; // 1 bit
|
||||
|
||||
private static final int U_ENABLED = 0;
|
||||
private static final int U_MODE = 1; // 3 bits
|
||||
@@ -130,10 +131,12 @@ public final class FragmentKey {
|
||||
global |= (1L << BIT_LINE_STIPPLE);
|
||||
}
|
||||
|
||||
// Separate specular
|
||||
// Separate specular / color sum
|
||||
if (GLStateManager.getLightingState().isEnabled()
|
||||
&& GLStateManager.getLightModel().colorControl == GL12.GL_SEPARATE_SPECULAR_COLOR) {
|
||||
global |= (1L << BIT_SEPARATE_SPECULAR);
|
||||
} else if (GLStateManager.getColorSumState().isEnabled()) {
|
||||
global |= (1L << BIT_COLOR_SUM);
|
||||
}
|
||||
|
||||
// Per-unit state
|
||||
@@ -238,6 +241,7 @@ public final class FragmentKey {
|
||||
public boolean alphaTestEnabled() { return ((packed[0] >> BIT_ALPHA_TEST) & 1) != 0; }
|
||||
public int alphaTestFunc() { return (int) ((packed[0] >> BIT_ALPHA_FUNC) & 0x7); }
|
||||
public boolean separateSpecular() { return ((packed[0] >> BIT_SEPARATE_SPECULAR) & 1) != 0; }
|
||||
public boolean colorSum() { return ((packed[0] >> BIT_COLOR_SUM) & 1) != 0; }
|
||||
public boolean lineStipple() { return ((packed[0] >> BIT_LINE_STIPPLE) & 1) != 0; }
|
||||
public int nrEnabledUnits() { return (int) ((packed[0] >> BIT_NR_ENABLED_UNITS) & 0x7); }
|
||||
public boolean overlayEnabled() { return ((packed[0] >> BIT_OVERLAY_ENABLED) & 1) != 0; }
|
||||
@@ -374,10 +378,10 @@ public final class FragmentKey {
|
||||
default -> "?";
|
||||
};
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append(String.format("FFPFragmentKey[fog=%s alpha=%b(%s) specSep=%b overlay=%b units=%d",
|
||||
sb.append(String.format("FFPFragmentKey[fog=%s alpha=%b(%s) specSep=%b colorSum=%b overlay=%b units=%d",
|
||||
fogName, alphaTestEnabled(),
|
||||
alphaTestEnabled() ? String.format("0x%04X", decodeAlphaFunc(alphaTestFunc())) : "-",
|
||||
separateSpecular(), overlayEnabled(), nrEnabledUnits()));
|
||||
separateSpecular(), colorSum(), overlayEnabled(), nrEnabledUnits()));
|
||||
for (int i = 0; i < nrEnabledUnits(); i++) {
|
||||
if (!unitEnabled(i)) {
|
||||
sb.append(String.format(" u%d=OFF", i));
|
||||
|
||||
@@ -276,6 +276,10 @@ public final class FragmentShaderGenerator {
|
||||
sb.append(" // Add separate specular\n");
|
||||
sb.append(" color.rgb += v_SpecularColor;\n");
|
||||
}
|
||||
if (key.colorSum()) {
|
||||
sb.append(" // Color sum - GL_COLOR_SUM with the current secondary color\n");
|
||||
sb.append(" color.rgb += u_SecondaryColor;\n");
|
||||
}
|
||||
}
|
||||
|
||||
private static void emitAlphaTest(StringBuilder sb, FragmentKey key) {
|
||||
|
||||
@@ -2,12 +2,12 @@ package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
public final class InstancedAttribs {
|
||||
|
||||
public static final int LOC_MATRIX_COL0 = 7;
|
||||
public static final int LOC_MATRIX_COL1 = 8;
|
||||
public static final int LOC_MATRIX_COL2 = 9;
|
||||
public static final int LOC_MATRIX_COL3 = 10;
|
||||
public static final int LOC_COLOR = 11;
|
||||
public static final int LOC_LIGHTMAP = 12;
|
||||
public static final int LOC_MATRIX_COL0 = 5;
|
||||
public static final int LOC_MATRIX_COL1 = 6;
|
||||
public static final int LOC_MATRIX_COL2 = 7;
|
||||
public static final int LOC_MATRIX_COL3 = 8;
|
||||
public static final int LOC_COLOR = 9;
|
||||
public static final int LOC_LIGHTMAP = 10;
|
||||
|
||||
public static final int OFFSET_MATRIX = 0;
|
||||
public static final int OFFSET_COLOR = 64;
|
||||
|
||||
@@ -80,6 +80,10 @@ public final class ShaderManager {
|
||||
warmUp();
|
||||
enabled = true;
|
||||
|
||||
if (GLStateManager.getActiveProgram() == 0) {
|
||||
activate();
|
||||
}
|
||||
|
||||
GLStateManager.LOGGER.info("FFP shader emulation enabled");
|
||||
}
|
||||
|
||||
@@ -179,6 +183,7 @@ public final class ShaderManager {
|
||||
sm.lastFramePreDrawCalls = sm.preDrawCalls;
|
||||
sm.preDrawCalls = 0;
|
||||
sm.uniforms.endFrame();
|
||||
FfpExtendedAttribs.endFrame();
|
||||
}
|
||||
|
||||
public static void setCurrentNormal(float x, float y, float z) {
|
||||
|
||||
@@ -27,7 +27,6 @@ import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memCalloc;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memFree;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memGetInt;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memPutInt;
|
||||
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
|
||||
|
||||
/**
|
||||
* Maintains the shared {@link FFPUniformBlock} image from GLSM cached state and streams it to the UBO ring.
|
||||
@@ -209,7 +208,7 @@ public class Uniforms {
|
||||
ring = new UniformRingBuffer(RING_CAPACITY, FFPUniformBlock.SIZE);
|
||||
}
|
||||
final int offset = ring.writeBlock(staging);
|
||||
RENDER_BACKEND.bindBufferRange(GL31.GL_UNIFORM_BUFFER, FFPUniformBlock.BINDING_POINT,
|
||||
GLStateManager.glBindBufferRange(GL31.GL_UNIFORM_BUFFER, FFPUniformBlock.BINDING_POINT,
|
||||
ring.getBufferId(), offset, FFPUniformBlock.SIZE);
|
||||
bound = true;
|
||||
blockWrites++;
|
||||
@@ -331,6 +330,9 @@ public class Uniforms {
|
||||
GLStateManager.getOverlayR(), GLStateManager.getOverlayG(),
|
||||
GLStateManager.getOverlayB(), GLStateManager.getOverlayA());
|
||||
|
||||
final var secondary = GLStateManager.getSecondaryColor();
|
||||
putVec3(FFPUniformBlock.SECONDARY_COLOR, secondary.getRed(), secondary.getGreen(), secondary.getBlue());
|
||||
|
||||
// Mesa STATE_FOG_PARAMS_OPTIMIZED
|
||||
final FogState fog = GLStateManager.getFogState();
|
||||
final float start = fog.getStart();
|
||||
|
||||
@@ -16,6 +16,7 @@ public final class GLSMConfig {
|
||||
|
||||
public static boolean hudCacheOverride;
|
||||
|
||||
public static boolean extendedAttribsExpected;
|
||||
public static volatile boolean expandVertexFormats;
|
||||
|
||||
private GLSMConfig() {}
|
||||
|
||||
@@ -9,6 +9,7 @@ public final class GLSMHooks {
|
||||
public static DeferredAlphaHandler alphaHandler;
|
||||
public static DeferredDepthColorHandler depthColorHandler;
|
||||
public static GlintColorHandler glintColorHandler;
|
||||
public static ImmediateExtendedAttribHandler immediateExtendedHandler;
|
||||
public static ShaderWorkSubmitter shaderWorkSubmitter;
|
||||
public static ShaderTransformPostProcessor postTransformProcessor;
|
||||
public static PerFrameUniformBlock perFrameUniformBlock;
|
||||
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
package com.gtnewhorizons.angelica.glsm.hooks;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormat;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormatElement;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
public interface ImmediateExtendedAttribHandler {
|
||||
|
||||
int EXT_STRIDE = 12;
|
||||
int LOC_MC_ENTITY = 11;
|
||||
int LOC_MID_TEX = 12;
|
||||
int LOC_TANGENT = 13;
|
||||
int LOC_MIDBLOCK = 14;
|
||||
|
||||
int RAW_NORMAL_INDEX = 6;
|
||||
|
||||
boolean wantsExtended();
|
||||
|
||||
boolean wantsTangent();
|
||||
|
||||
boolean wantsExtendedCapture();
|
||||
|
||||
void build(int[] rawBuffer, int vertexCount, int vertsPerPrim, int normalIntIndex, long dstAddr, int dstStride);
|
||||
|
||||
void buildPacked(long srcBase, int stride, int posOffset, int texOffset, int normalOffset,
|
||||
int vertexCount, int vertsPerPrim, long dstAddr, int dstStride);
|
||||
|
||||
static int extPrimVerts(int drawMode) {
|
||||
if (drawMode == GL11.GL_QUADS) return 4;
|
||||
if (drawMode == GL11.GL_TRIANGLES) return 3;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int extPrimVerts(int drawMode, int vertexCount) {
|
||||
final int p = extPrimVerts(drawMode);
|
||||
return (p != 0 && vertexCount > 0 && vertexCount % p == 0) ? p : 0;
|
||||
}
|
||||
|
||||
static int texOffset(VertexFormat format) {
|
||||
int offset = 0;
|
||||
for (VertexFormatElement element : format.elementsArray) {
|
||||
if (element.getUsage() == VertexFormatElement.Usage.PRIMARY_UV) return offset;
|
||||
offset += element.getByteSize();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int normalOffset(VertexFormat format) {
|
||||
int offset = 0;
|
||||
for (VertexFormatElement element : format.elementsArray) {
|
||||
if (element.getUsage() == VertexFormatElement.Usage.NORMAL) return offset;
|
||||
offset += element.getByteSize();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void setupExtAttribPointers(long baseOffset, int stride) {
|
||||
GLStateManager.glEnableVertexAttribArray(LOC_MID_TEX);
|
||||
GLStateManager.glVertexAttribPointer(LOC_MID_TEX, 2, GL11.GL_FLOAT, false, stride, baseOffset);
|
||||
GLStateManager.glEnableVertexAttribArray(LOC_TANGENT);
|
||||
GLStateManager.glVertexAttribPointer(LOC_TANGENT, 4, GL11.GL_BYTE, true, stride, baseOffset + 8);
|
||||
}
|
||||
}
|
||||
+1
@@ -4,5 +4,6 @@ import net.minecraftforge.eventbus.api.event.MutableEvent;
|
||||
|
||||
public final class TextureUnitStateEvent extends MutableEvent {
|
||||
public int unit;
|
||||
public int cap;
|
||||
public boolean enabled;
|
||||
}
|
||||
+7
@@ -274,6 +274,13 @@ public final class CommandBufferExecutor {
|
||||
ptr += 16;
|
||||
GLStateManager.glColor4f(r, g, b, a);
|
||||
}
|
||||
case GLCommand.SECONDARY_COLOR -> {
|
||||
final float r = memGetFloat(ptr);
|
||||
final float g = memGetFloat(ptr + 4);
|
||||
final float b = memGetFloat(ptr + 8);
|
||||
ptr += 12;
|
||||
GLStateManager.glSecondaryColor3f(r, g, b);
|
||||
}
|
||||
case GLCommand.CLEAR_COLOR -> {
|
||||
final float r = memGetFloat(ptr);
|
||||
final float g = memGetFloat(ptr + 4);
|
||||
|
||||
@@ -356,6 +356,14 @@ public final class CommandRecorder {
|
||||
writeFloat(a);
|
||||
}
|
||||
|
||||
public void writeSecondaryColor(float r, float g, float b) {
|
||||
ensureCapacity(16);
|
||||
writeInt(GLCommand.SECONDARY_COLOR);
|
||||
writeFloat(r);
|
||||
writeFloat(g);
|
||||
writeFloat(b);
|
||||
}
|
||||
|
||||
public void writeClearColor(float r, float g, float b, float a) {
|
||||
ensureCapacity(20);
|
||||
writeInt(GLCommand.CLEAR_COLOR);
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
package com.gtnewhorizons.angelica.glsm.recording;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vao.IVertexArrayObject;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.ImmediateExtendedAttribHandler;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.FfpExtendedAttribs;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.VAOManager;
|
||||
|
||||
/**
|
||||
@@ -9,15 +13,26 @@ import com.gtnewhorizons.angelica.glsm.ffp.VAOManager;
|
||||
public final class DisplayListVBO {
|
||||
|
||||
private final SubVBO[] vbos;
|
||||
private final int[] extVbos;
|
||||
|
||||
DisplayListVBO(SubVBO[] vbos) {
|
||||
this(vbos, null);
|
||||
}
|
||||
|
||||
DisplayListVBO(SubVBO[] vbos, int[] extVbos) {
|
||||
this.vbos = vbos;
|
||||
this.extVbos = extVbos;
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
for (SubVBO vbo : vbos) {
|
||||
vbo.delete();
|
||||
}
|
||||
if (extVbos != null) {
|
||||
for (int extVbo : extVbos) {
|
||||
if (extVbo != 0) GLStateManager.glDeleteBuffers(extVbo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public SubVBO[] getVBOs() {
|
||||
@@ -39,6 +54,9 @@ public final class DisplayListVBO {
|
||||
private final int start;
|
||||
private final int count;
|
||||
private final int vertexFlags;
|
||||
private int pendingExtVbo = 0;
|
||||
|
||||
void setPendingExtVbo(int extVbo) { this.pendingExtVbo = extVbo; }
|
||||
|
||||
public SubVBO(IVertexArrayObject vao, int drawMode, int start, int count, int vertexFlags) {
|
||||
this.vao = vao;
|
||||
@@ -67,9 +85,20 @@ public final class DisplayListVBO {
|
||||
public void render() {
|
||||
if (vao == null) return;
|
||||
vao.bind();
|
||||
if (pendingExtVbo != 0) {
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, pendingExtVbo);
|
||||
ImmediateExtendedAttribHandler.setupExtAttribPointers(0L, ImmediateExtendedAttribHandler.EXT_STRIDE);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
pendingExtVbo = 0;
|
||||
}
|
||||
if (count <= 0) { vao.unbind(); return; }
|
||||
VAOManager.setCurrentVertexFlags(vertexFlags);
|
||||
vao.draw(drawMode, start, count);
|
||||
FfpExtendedAttribs.beginInternalDraw();
|
||||
try {
|
||||
vao.draw(drawMode, start, count);
|
||||
} finally {
|
||||
FfpExtendedAttribs.endInternalDraw();
|
||||
}
|
||||
vao.unbind();
|
||||
}
|
||||
|
||||
|
||||
+84
-1
@@ -1,5 +1,6 @@
|
||||
package com.gtnewhorizons.angelica.glsm.recording;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vao.BaseVAO;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vao.IVertexArrayObject;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vao.IndexBuffer;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vao.IndexedVAO;
|
||||
@@ -8,7 +9,13 @@ import com.gtnewhorizon.gtnhlib.client.renderer.vbo.IVertexBuffer;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.DefaultVertexFormat;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFlags;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormat;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMConfig;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMHooks;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.ImmediateExtendedAttribHandler;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
@@ -16,6 +23,7 @@ import java.util.List;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAddress0;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAlloc;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memCalloc;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memCopy;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memFree;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.nmemFree;
|
||||
@@ -48,15 +56,29 @@ public final class DisplayListVBOBuilder {
|
||||
}
|
||||
|
||||
public DisplayListVBO build() {
|
||||
final ImmediateExtendedAttribHandler extHandler = GLSMHooks.immediateExtendedHandler;
|
||||
final DisplayListVBO.SubVBO[] vbos = new DisplayListVBO.SubVBO[count];
|
||||
final ArrayList<ByteBuffer> allBuffers = new ArrayList<>();
|
||||
final IntArrayList extVbos = new IntArrayList();
|
||||
for (int i = 0; i < VertexFlags.BITSET_SIZE; i++) {
|
||||
List<FormatData> formatData = formats[i];
|
||||
if (formatData == null) continue;
|
||||
final VertexFormat format = DefaultVertexFormat.ALL_FORMATS[i];
|
||||
final boolean wantExt = extHandler != null && format.hasTexture();
|
||||
if (extHandler == null && GLSMConfig.extendedAttribsExpected && format.hasTexture()) {
|
||||
GLStateManager.warnOnce("dl-ext-no-handler",
|
||||
"Textured display list compiled before the immediate ext-attribute handler was installed! POM data will be missing for this geometry.");
|
||||
}
|
||||
int start = 0;
|
||||
final IVertexArrayObject vao = VAOManager.createStorageVAO(format, -1, 0); // drawMode will be ignored
|
||||
final IVertexBuffer vbo = vao.getVBO();
|
||||
|
||||
// SubVBO slots that need the ext attributes attached on their first draw.
|
||||
final IntArrayList extDrawIndexes = wantExt ? new IntArrayList() : null;
|
||||
final IntArrayList extStarts = wantExt ? new IntArrayList() : null;
|
||||
final IntArrayList extCounts = wantExt ? new IntArrayList() : null;
|
||||
final IntArrayList extPrims = wantExt ? new IntArrayList() : null;
|
||||
|
||||
for (FormatData data : formatData) {
|
||||
int vertexCount;
|
||||
final List<ByteBuffer> drawBuffers = data.buffers;
|
||||
@@ -76,6 +98,23 @@ public final class DisplayListVBOBuilder {
|
||||
if (data.drawMode == GL11.GL_QUADS) {
|
||||
final IVertexArrayObject indexedVAO = new IndexedVAO(vbo, IndexBuffer.convertQuadsToTrigs(start, start + vertexCount));
|
||||
vbos[data.drawIndex] = new DisplayListVBO.SubVBO(indexedVAO, GL11.GL_TRIANGLES, 0, vertexCount / 4 * 6, flags);
|
||||
if (wantExt) {
|
||||
extDrawIndexes.add(data.drawIndex);
|
||||
extStarts.add(start);
|
||||
extCounts.add(vertexCount);
|
||||
extPrims.add(4);
|
||||
}
|
||||
} else if (data.drawMode == GL11.GL_TRIANGLES) {
|
||||
if (wantExt) {
|
||||
final IVertexArrayObject triVao = new BaseVAO(vbo);
|
||||
vbos[data.drawIndex] = new DisplayListVBO.SubVBO(triVao, GL11.GL_TRIANGLES, start, vertexCount, flags);
|
||||
extDrawIndexes.add(data.drawIndex);
|
||||
extStarts.add(start);
|
||||
extCounts.add(vertexCount);
|
||||
extPrims.add(3);
|
||||
} else {
|
||||
vbos[data.drawIndex] = new DisplayListVBO.SubVBO(vao, GL11.GL_TRIANGLES, start, vertexCount, flags);
|
||||
}
|
||||
} else if (data.drawMode == GL11.GL_QUAD_STRIP) {
|
||||
// GL_QUAD_STRIP is removed in core profile; an even-length GL_TRIANGLE_STRIP produces the same quads
|
||||
vbos[data.drawIndex] = new DisplayListVBO.SubVBO(vao, GL11.GL_TRIANGLE_STRIP, start, vertexCount & ~1, flags);
|
||||
@@ -91,11 +130,55 @@ public final class DisplayListVBOBuilder {
|
||||
}
|
||||
ByteBuffer bigBuffer = mergeAndDelete(allBuffers);
|
||||
vbo.allocate(bigBuffer, start);
|
||||
|
||||
if (wantExt && !extDrawIndexes.isEmpty()) {
|
||||
final int extVbo = buildExt(extHandler, format, bigBuffer, extStarts, extCounts, extPrims);
|
||||
if (extVbo != 0) {
|
||||
extVbos.add(extVbo);
|
||||
for (int e = 0; e < extDrawIndexes.size(); e++) {
|
||||
vbos[extDrawIndexes.getInt(e)].setPendingExtVbo(extVbo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
memFree(bigBuffer);
|
||||
|
||||
allBuffers.clear();
|
||||
}
|
||||
return new DisplayListVBO(vbos);
|
||||
return new DisplayListVBO(vbos, extVbos.toIntArray());
|
||||
}
|
||||
|
||||
private static int buildExt(ImmediateExtendedAttribHandler handler, VertexFormat format, ByteBuffer bigBuffer,
|
||||
IntArrayList extStarts, IntArrayList extCounts, IntArrayList extPrims) {
|
||||
final int extStride = ImmediateExtendedAttribHandler.EXT_STRIDE;
|
||||
final int stride = format.getVertexSize();
|
||||
final int posOffset = 0;
|
||||
final int texOffset = ImmediateExtendedAttribHandler.texOffset(format);
|
||||
final int normalOffset = ImmediateExtendedAttribHandler.normalOffset(format);
|
||||
|
||||
int extVerts = 0;
|
||||
for (int q = 0; q < extStarts.size(); q++) {
|
||||
extVerts = Math.max(extVerts, extStarts.getInt(q) + extCounts.getInt(q));
|
||||
}
|
||||
|
||||
final ByteBuffer ext = memCalloc(extVerts, extStride);
|
||||
final long extAddr = memAddress0(ext);
|
||||
final long srcBase = memAddress0(bigBuffer);
|
||||
for (int q = 0; q < extStarts.size(); q++) {
|
||||
final int rStart = extStarts.getInt(q);
|
||||
final int rCount = extCounts.getInt(q);
|
||||
final int rPrim = extPrims.getInt(q);
|
||||
handler.buildPacked(srcBase + (long) rStart * stride, stride, posOffset, texOffset, normalOffset, rCount, rPrim,
|
||||
extAddr + (long) rStart * extStride, extStride);
|
||||
}
|
||||
|
||||
final int extVbo = GLStateManager.glGenBuffers();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, extVbo);
|
||||
ext.position(0).limit(extVerts * extStride);
|
||||
GLStateManager.glBufferData(GL15.GL_ARRAY_BUFFER, ext, GL15.GL_STATIC_DRAW);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
memFree(ext);
|
||||
return extVbo;
|
||||
}
|
||||
|
||||
private static ByteBuffer mergeAndDelete(List<ByteBuffer> buffers) {
|
||||
|
||||
@@ -61,6 +61,7 @@ public final class GLCommand {
|
||||
public static final int COLOR = 54; // [cmd:4][r:4f][g:4f][b:4f][a:4f]
|
||||
public static final int CLEAR_COLOR = 55; // [cmd:4][r:4f][g:4f][b:4f][a:4f]
|
||||
public static final int BLEND_COLOR = 56; // [cmd:4][r:4f][g:4f][b:4f][a:4f]
|
||||
public static final int SECONDARY_COLOR = 57; // [cmd:4][r:4f][g:4f][b:4f]
|
||||
|
||||
// === Mixed int+float commands ===
|
||||
public static final int ALPHA_FUNC = 60; // [cmd:4][func:4][ref:4f]
|
||||
@@ -155,6 +156,7 @@ public final class GLCommand {
|
||||
case COLOR -> "COLOR";
|
||||
case CLEAR_COLOR -> "CLEAR_COLOR";
|
||||
case BLEND_COLOR -> "BLEND_COLOR";
|
||||
case SECONDARY_COLOR -> "SECONDARY_COLOR";
|
||||
case ALPHA_FUNC -> "ALPHA_FUNC";
|
||||
case FOGF -> "FOGF";
|
||||
case LIGHTF -> "LIGHTF";
|
||||
@@ -211,7 +213,7 @@ public final class GLCommand {
|
||||
// Three int commands (16 bytes)
|
||||
case GLCommand.STENCIL_FUNC, GLCommand.STENCIL_OP, GLCommand.TEX_PARAMETERI,
|
||||
GLCommand.LIGHTF, GLCommand.LIGHTI,
|
||||
GLCommand.MATERIALF, GLCommand.TEX_PARAMETERF -> 16;
|
||||
GLCommand.MATERIALF, GLCommand.TEX_PARAMETERF, GLCommand.SECONDARY_COLOR -> 16;
|
||||
|
||||
// Four int commands (20 bytes)
|
||||
case GLCommand.VIEWPORT, GLCommand.BLEND_FUNC, GLCommand.COLOR_MASK,
|
||||
|
||||
@@ -142,6 +142,7 @@ public class GLSMRedirector {
|
||||
private static final String GLU = "org/lwjgl/util/glu/GLU";
|
||||
private static final String OpenGlHelper = "net/minecraft/client/renderer/OpenGlHelper";
|
||||
private static final String EXTBlendFunc = "org/lwjgl/opengl/EXTBlendFuncSeparate";
|
||||
private static final String EXTSecondaryColor = "org/lwjgl/opengl/EXTSecondaryColor";
|
||||
private static final String ARBMultiTexture = "org/lwjgl/opengl/ARBMultitexture";
|
||||
private static final String ARBShaderObjects = "org/lwjgl/opengl/ARBShaderObjects";
|
||||
private static final String ARBInstancedArrays = "org/lwjgl/opengl/ARBInstancedArrays";
|
||||
@@ -403,7 +404,12 @@ public class GLSMRedirector {
|
||||
.add("glBlendEquation")
|
||||
.add("glMultiDrawArrays")
|
||||
.add("glPointParameterf")
|
||||
.add("glPointParameteri");
|
||||
.add("glPointParameteri")
|
||||
// Removed in core profile - emulated by the FFP shaders, calling through would abort the JVM
|
||||
.add("glSecondaryColor3f")
|
||||
.add("glSecondaryColor3d")
|
||||
.add("glSecondaryColor3b")
|
||||
.add("glSecondaryColor3ub");
|
||||
final var gl15 = RedirectMap.newMap()
|
||||
.add("glGenBuffers")
|
||||
.add("glBindBuffer")
|
||||
@@ -511,7 +517,8 @@ public class GLSMRedirector {
|
||||
.add("glFramebufferRenderbuffer")
|
||||
.add("glIsFramebuffer")
|
||||
.add("glIsRenderbuffer")
|
||||
.add("glBindBufferBase");
|
||||
.add("glBindBufferBase")
|
||||
.add("glBindBufferRange");
|
||||
final var gl31 = RedirectMap.newMap()
|
||||
.add("glDrawElementsInstanced")
|
||||
.add("glDrawArraysInstanced")
|
||||
@@ -589,6 +596,11 @@ public class GLSMRedirector {
|
||||
.add("isFramebufferEnabled")
|
||||
);
|
||||
methodRedirects.put(EXTBlendFunc, RedirectMap.newMap().add("glBlendFuncSeparateEXT", "tryBlendFuncSeparate"));
|
||||
methodRedirects.put(EXTSecondaryColor, RedirectMap.newMap()
|
||||
.add("glSecondaryColor3bEXT", "glSecondaryColor3b")
|
||||
.add("glSecondaryColor3fEXT", "glSecondaryColor3f")
|
||||
.add("glSecondaryColor3dEXT", "glSecondaryColor3d")
|
||||
.add("glSecondaryColor3ubEXT", "glSecondaryColor3ub"));
|
||||
|
||||
// ARB
|
||||
methodRedirects.put(ARBMultiTexture, RedirectMap.newMap()
|
||||
@@ -711,6 +723,7 @@ public class GLSMRedirector {
|
||||
);
|
||||
final var uniformBufferObject = RedirectMap.newMap()
|
||||
.add("glBindBufferBase")
|
||||
.add("glBindBufferRange")
|
||||
.add("glGetUniformBlockIndex")
|
||||
.add("glUniformBlockBinding");
|
||||
methodRedirects.put(ARBUniformBufferObject, uniformBufferObject);
|
||||
|
||||
+1
@@ -44,6 +44,7 @@ public class TextureUnitBooleanStateStack extends BooleanStateStack {
|
||||
|
||||
if (GLSMHooks.TEXTURE_UNIT_STATE.hasListeners()) {
|
||||
GLSMHooks.textureUnitStateEvent.unit = unitIndex;
|
||||
GLSMHooks.textureUnitStateEvent.cap = glCap;
|
||||
GLSMHooks.textureUnitStateEvent.enabled = enabled;
|
||||
GLSMHooks.TEXTURE_UNIT_STATE.post(GLSMHooks.textureUnitStateEvent);
|
||||
}
|
||||
|
||||
+150
-4
@@ -4,12 +4,16 @@ import com.gtnewhorizon.gtnhlib.client.renderer.DirectTessellator;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.DefaultVertexFormat;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFlags;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormat;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormatElement;
|
||||
import com.gtnewhorizons.angelica.config.SystemProperties;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.QuadConverter;
|
||||
import com.gtnewhorizons.angelica.glsm.RenderSystem;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.FfpExtendedAttribs;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.ShaderManager;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMConfig;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMHooks;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.ImmediateExtendedAttribHandler;
|
||||
import com.gtnewhorizons.angelica.glsm.profiling.Tracy;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
@@ -22,6 +26,7 @@ import java.nio.ByteBuffer;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAddress0;
|
||||
import static org.joml.Math.clamp;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAlloc;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memCopy;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memFree;
|
||||
|
||||
/**
|
||||
@@ -36,6 +41,7 @@ public class TessellatorStreamingDrawer {
|
||||
private static final Tracy.ZoneId Z_SDL_STREAM_DRAW = Tracy.zoneId("sdlStreamDraw", Tracy.COLOR_FFP);
|
||||
|
||||
private static final Logger LOGGER = LogManager.getLogger("TessellatorStreamingDrawer");
|
||||
|
||||
private static final int FORMAT_COUNT = VertexFlags.BITSET_SIZE; // 16
|
||||
|
||||
private static PersistentStreamingBuffer persistentBuffer;
|
||||
@@ -44,6 +50,12 @@ public class TessellatorStreamingDrawer {
|
||||
private static final int[] persistentVAOs = new int[FORMAT_COUNT];
|
||||
private static final int[] orphanVAOs = new int[FORMAT_COUNT];
|
||||
|
||||
private static final int[] extendedPersistentVAOs = new int[FORMAT_COUNT];
|
||||
private static final int[] extendedOrphanVAOs = new int[FORMAT_COUNT];
|
||||
private static ByteBuffer extScratch;
|
||||
private static long extScratchAddress;
|
||||
private static int extScratchCapacity;
|
||||
|
||||
private static ByteBuffer repackBuffer;
|
||||
private static long repackAddress;
|
||||
private static int repackCapacity;
|
||||
@@ -153,7 +165,25 @@ public class TessellatorStreamingDrawer {
|
||||
repackBuffer.position(0);
|
||||
repackBuffer.limit((int)(writePtr - repackAddress));
|
||||
|
||||
uploadAndDraw(repackBuffer, effectiveFlags, format, vertexSize, tess.drawMode, vertexCount);
|
||||
final ImmediateExtendedAttribHandler extHandler = GLSMHooks.immediateExtendedHandler;
|
||||
final int extPrim = extHandler == null ? 0 : ImmediateExtendedAttribHandler.extPrimVerts(tess.drawMode, vertexCount);
|
||||
final boolean wantsExt = extHandler != null && extHandler.wantsExtended();
|
||||
final boolean doExt = extPrim != 0 && tess.hasTexture && wantsExt;
|
||||
if (doExt) {
|
||||
final int combinedStride = vertexSize + ImmediateExtendedAttribHandler.EXT_STRIDE;
|
||||
final int combinedBytes = vertexCount * combinedStride;
|
||||
ensureExtScratch(combinedBytes);
|
||||
interleaveBase(repackAddress, extScratchAddress, vertexCount, vertexSize, combinedStride);
|
||||
// format expansion backfills a normal into rawBuffer, so trust effectiveFlags rather than tess.hasNormals
|
||||
final int normalIndex = (effectiveFlags & VertexFlags.NORMAL_BIT) != 0 ? ImmediateExtendedAttribHandler.RAW_NORMAL_INDEX : -1;
|
||||
extHandler.build(tess.rawBuffer, vertexCount, extPrim, normalIndex, extScratchAddress + vertexSize, combinedStride);
|
||||
extScratch.position(0);
|
||||
extScratch.limit(combinedBytes);
|
||||
uploadAndDrawExtended(extScratch, effectiveFlags, format, combinedStride, tess.drawMode, vertexCount);
|
||||
shrinkExtScratchIfOversized(combinedBytes);
|
||||
} else {
|
||||
uploadAndDraw(repackBuffer, effectiveFlags, format, vertexSize, tess.drawMode, vertexCount);
|
||||
}
|
||||
} finally {
|
||||
if (locked) GLStateManager.releaseDrawLock();
|
||||
}
|
||||
@@ -184,6 +214,7 @@ public class TessellatorStreamingDrawer {
|
||||
final ByteBuffer buffer = dt.getWriteBuffer();
|
||||
final int vertexSize = format.getVertexSize();
|
||||
|
||||
if (tryExtendedPacked(buffer, flags, format, vertexSize, drawMode, vertexCount)) return;
|
||||
uploadAndDraw(buffer, flags, format, vertexSize, drawMode, vertexCount);
|
||||
}
|
||||
|
||||
@@ -197,9 +228,37 @@ public class TessellatorStreamingDrawer {
|
||||
public static void drawPacked(ByteBuffer packedData, int drawMode, int flags, int vertexCount) {
|
||||
final VertexFormat format = DefaultVertexFormat.ALL_FORMATS[flags];
|
||||
final int vertexSize = format.getVertexSize();
|
||||
if (tryExtendedPacked(packedData, flags, format, vertexSize, drawMode, vertexCount)) return;
|
||||
uploadAndDraw(packedData, flags, format, vertexSize, drawMode, vertexCount);
|
||||
}
|
||||
|
||||
private static boolean tryExtendedPacked(ByteBuffer packed, int flags, VertexFormat format, int vertexSize, int drawMode, int vertexCount) {
|
||||
final ImmediateExtendedAttribHandler extHandler = GLSMHooks.immediateExtendedHandler;
|
||||
if (extHandler == null || !extHandler.wantsExtended()) return false;
|
||||
final int extPrim = ImmediateExtendedAttribHandler.extPrimVerts(drawMode, vertexCount);
|
||||
if (extPrim == 0 || !format.hasTexture()) return false;
|
||||
|
||||
final long srcBase = memAddress0(packed) + packed.position();
|
||||
final int texOffset = ImmediateExtendedAttribHandler.texOffset(format);
|
||||
final int normalOffset = ImmediateExtendedAttribHandler.normalOffset(format);
|
||||
final int combinedStride = vertexSize + ImmediateExtendedAttribHandler.EXT_STRIDE;
|
||||
final int combinedBytes = vertexCount * combinedStride;
|
||||
ensureExtScratch(combinedBytes);
|
||||
interleaveBase(srcBase, extScratchAddress, vertexCount, vertexSize, combinedStride);
|
||||
extHandler.buildPacked(srcBase, vertexSize, 0, texOffset, normalOffset, vertexCount, extPrim, extScratchAddress + vertexSize, combinedStride);
|
||||
extScratch.position(0);
|
||||
extScratch.limit(combinedBytes);
|
||||
uploadAndDrawExtended(extScratch, flags, format, combinedStride, drawMode, vertexCount);
|
||||
shrinkExtScratchIfOversized(combinedBytes);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void interleaveBase(long src, long dst, int vertexCount, int vertexSize, int combinedStride) {
|
||||
for (int i = 0; i < vertexCount; i++) {
|
||||
memCopy(src + (long) i * vertexSize, dst + (long) i * combinedStride, vertexSize);
|
||||
}
|
||||
}
|
||||
|
||||
private static String cachedDebugInfo = "Stream: not initialized";
|
||||
private static long lastDebugUpdateNanos;
|
||||
private static final long DEBUG_UPDATE_INTERVAL_NS = 500_000_000L; // 500ms
|
||||
@@ -294,10 +353,89 @@ public class TessellatorStreamingDrawer {
|
||||
}
|
||||
|
||||
private static void drawWithQuadConversion(int drawMode, int firstVertex, int vertexCount) {
|
||||
if (drawMode == GL11.GL_QUADS) {
|
||||
QuadConverter.drawQuadsAsTriangles(firstVertex, vertexCount);
|
||||
FfpExtendedAttribs.beginInternalDraw();
|
||||
try {
|
||||
if (drawMode == GL11.GL_QUADS) {
|
||||
QuadConverter.drawQuadsAsTriangles(firstVertex, vertexCount);
|
||||
} else {
|
||||
GLStateManager.glDrawArrays(drawMode, firstVertex, vertexCount);
|
||||
}
|
||||
} finally {
|
||||
FfpExtendedAttribs.endInternalDraw();
|
||||
}
|
||||
}
|
||||
|
||||
private static void uploadAndDrawExtended(ByteBuffer combined, int flags, VertexFormat format, int combinedStride, int drawMode, int vertexCount) {
|
||||
ensureVAO(flags, format);
|
||||
ensureExtendedVAOs(flags, format);
|
||||
|
||||
int firstVertex = -1;
|
||||
|
||||
if (persistentBuffer != null) {
|
||||
firstVertex = persistentBuffer.upload(combined, combinedStride);
|
||||
}
|
||||
|
||||
if (firstVertex >= 0) {
|
||||
GLStateManager.glBindVertexArray(extendedPersistentVAOs[flags]);
|
||||
} else {
|
||||
GLStateManager.glDrawArrays(drawMode, firstVertex, vertexCount);
|
||||
GLStateManager.glBindVertexArray(extendedOrphanVAOs[flags]);
|
||||
orphanBuffers[flags].upload(combined);
|
||||
firstVertex = 0;
|
||||
}
|
||||
|
||||
drawWithQuadConversion(drawMode, firstVertex, vertexCount);
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
}
|
||||
|
||||
private static void ensureExtendedVAOs(int flags, VertexFormat format) {
|
||||
if (extendedOrphanVAOs[flags] == 0) {
|
||||
extendedOrphanVAOs[flags] = GLStateManager.glGenVertexArrays();
|
||||
GLStateManager.glBindVertexArray(extendedOrphanVAOs[flags]);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, orphanBuffers[flags].getBufferId());
|
||||
setupExtendedBufferState(format);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
}
|
||||
|
||||
if (persistentBuffer != null && extendedPersistentVAOs[flags] == 0) {
|
||||
extendedPersistentVAOs[flags] = GLStateManager.glGenVertexArrays();
|
||||
GLStateManager.glBindVertexArray(extendedPersistentVAOs[flags]);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, persistentBuffer.getBufferId());
|
||||
setupExtendedBufferState(format);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
}
|
||||
}
|
||||
|
||||
private static void setupExtendedBufferState(VertexFormat format) {
|
||||
final int combinedStride = format.getVertexSize() + ImmediateExtendedAttribHandler.EXT_STRIDE;
|
||||
long offset = 0;
|
||||
for (final VertexFormatElement element : format.elementsArray) {
|
||||
element.setupBufferState(offset, combinedStride);
|
||||
offset += element.getByteSize();
|
||||
}
|
||||
ImmediateExtendedAttribHandler.setupExtAttribPointers(offset, combinedStride);
|
||||
}
|
||||
|
||||
private static void ensureExtScratch(int requiredBytes) {
|
||||
if (extScratch != null && requiredBytes <= extScratchCapacity) return;
|
||||
|
||||
int newCapacity = Math.max(0x4000, extScratchCapacity);
|
||||
while (newCapacity < requiredBytes) {
|
||||
newCapacity *= 2;
|
||||
}
|
||||
if (extScratch != null) memFree(extScratch);
|
||||
extScratch = memAlloc(newCapacity);
|
||||
extScratchAddress = memAddress0(extScratch);
|
||||
extScratchCapacity = newCapacity;
|
||||
}
|
||||
|
||||
private static void shrinkExtScratchIfOversized(int lastRequiredBytes) {
|
||||
if (extScratchCapacity > 0x20000 && lastRequiredBytes < (extScratchCapacity >> 3)) {
|
||||
memFree(extScratch);
|
||||
extScratchCapacity = 0x4000;
|
||||
extScratch = memAlloc(extScratchCapacity);
|
||||
extScratchAddress = memAddress0(extScratch);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -358,8 +496,16 @@ public class TessellatorStreamingDrawer {
|
||||
for (int i = 0; i < FORMAT_COUNT; i++) {
|
||||
if (persistentVAOs[i] != 0) { GLStateManager.glDeleteVertexArrays(persistentVAOs[i]); persistentVAOs[i] = 0; }
|
||||
if (orphanVAOs[i] != 0) { GLStateManager.glDeleteVertexArrays(orphanVAOs[i]); orphanVAOs[i] = 0; }
|
||||
if (extendedPersistentVAOs[i] != 0) { GLStateManager.glDeleteVertexArrays(extendedPersistentVAOs[i]); extendedPersistentVAOs[i] = 0; }
|
||||
if (extendedOrphanVAOs[i] != 0) { GLStateManager.glDeleteVertexArrays(extendedOrphanVAOs[i]); extendedOrphanVAOs[i] = 0; }
|
||||
if (orphanBuffers[i] != null) { orphanBuffers[i].destroy(); orphanBuffers[i] = null; }
|
||||
}
|
||||
if (extScratch != null) {
|
||||
memFree(extScratch);
|
||||
extScratch = null;
|
||||
extScratchAddress = 0;
|
||||
extScratchCapacity = 0;
|
||||
}
|
||||
if (persistentBuffer != null) {
|
||||
persistentBuffer.destroy();
|
||||
persistentBuffer = null;
|
||||
|
||||
@@ -4,8 +4,11 @@ import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL14;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
@@ -22,6 +25,7 @@ public class GLSM_CoreRemovedCaps_GLTest {
|
||||
cap("GL_POLYGON_STIPPLE", GL11.GL_POLYGON_STIPPLE),
|
||||
cap("GL_INDEX_LOGIC_OP", GL11.GL_INDEX_LOGIC_OP),
|
||||
cap("GL_AUTO_NORMAL", GL11.GL_AUTO_NORMAL),
|
||||
cap("GL_COLOR_SUM", GL14.GL_COLOR_SUM),
|
||||
cap("GL_MAP1_COLOR_4", GL11.GL_MAP1_COLOR_4),
|
||||
cap("GL_MAP1_INDEX", GL11.GL_MAP1_INDEX),
|
||||
cap("GL_MAP1_NORMAL", GL11.GL_MAP1_NORMAL),
|
||||
@@ -46,6 +50,14 @@ public class GLSM_CoreRemovedCaps_GLTest {
|
||||
return Arguments.of(name, value);
|
||||
}
|
||||
|
||||
private static void assertSecondaryColor(float red, float green, float blue) {
|
||||
final FloatBuffer params = BufferUtils.createFloatBuffer(4);
|
||||
GLStateManager.glGetFloat(GL14.GL_CURRENT_SECONDARY_COLOR, params);
|
||||
assertEquals(red, params.get(0), "red must round-trip through the cache");
|
||||
assertEquals(green, params.get(1), "green must round-trip through the cache");
|
||||
assertEquals(blue, params.get(2), "blue must round-trip through the cache");
|
||||
}
|
||||
|
||||
private static void assertNoGlError(String what) {
|
||||
final int error = GL11.glGetError();
|
||||
assertEquals(GL11.GL_NO_ERROR, error, () -> what + " raised 0x" + Integer.toHexString(error));
|
||||
@@ -83,6 +95,71 @@ public class GLSM_CoreRemovedCaps_GLTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void secondaryColorIsCacheOnly() {
|
||||
try {
|
||||
// Calling through to the driver here aborts the JVM in a core profile - the color sum is emulated by the FFP shaders
|
||||
GLStateManager.glSecondaryColor3f(0.25F, 0.5F, 0.75F);
|
||||
assertNoGlError("glSecondaryColor3f");
|
||||
|
||||
assertSecondaryColor(0.25F, 0.5F, 0.75F);
|
||||
} finally {
|
||||
GLStateManager.glSecondaryColor3f(0.0F, 0.0F, 0.0F);
|
||||
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void secondaryColorIsRestoredByPopAttrib() {
|
||||
try {
|
||||
GLStateManager.glSecondaryColor3f(0.125F, 0.25F, 0.375F);
|
||||
GLStateManager.glPushAttrib(GL11.GL_CURRENT_BIT);
|
||||
GLStateManager.glSecondaryColor3f(1.0F, 1.0F, 1.0F);
|
||||
GLStateManager.glPopAttrib();
|
||||
assertNoGlError("glPushAttrib/glSecondaryColor3f/glPopAttrib");
|
||||
|
||||
assertSecondaryColor(0.125F, 0.25F, 0.375F);
|
||||
} finally {
|
||||
GLStateManager.glSecondaryColor3f(0.0F, 0.0F, 0.0F);
|
||||
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void colorSumIsRestoredByFogBitPopAttrib() {
|
||||
try {
|
||||
GLStateManager.glPushAttrib(GL11.GL_FOG_BIT);
|
||||
GLStateManager.glEnable(GL14.GL_COLOR_SUM);
|
||||
GLStateManager.glPopAttrib();
|
||||
assertNoGlError("glPushAttrib(GL_FOG_BIT)/glEnable(GL_COLOR_SUM)/glPopAttrib");
|
||||
assertFalse(GLStateManager.glIsEnabled(GL14.GL_COLOR_SUM));
|
||||
} finally {
|
||||
GLStateManager.glDisable(GL14.GL_COLOR_SUM);
|
||||
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void secondaryColorReplaysFromDisplayList() {
|
||||
final int list = GLStateManager.glGenLists(1);
|
||||
try {
|
||||
GLStateManager.glNewList(list, GL11.GL_COMPILE);
|
||||
GLStateManager.glSecondaryColor3f(0.5F, 0.625F, 0.75F);
|
||||
GLStateManager.glEndList();
|
||||
assertNoGlError("glSecondaryColor3f display list compile");
|
||||
|
||||
GLStateManager.glSecondaryColor3f(0.0F, 0.0F, 0.0F);
|
||||
GLStateManager.glCallList(list);
|
||||
assertNoGlError("glSecondaryColor3f display list replay");
|
||||
|
||||
assertSecondaryColor(0.5F, 0.625F, 0.75F);
|
||||
} finally {
|
||||
GLStateManager.glDeleteLists(list, 1);
|
||||
GLStateManager.glSecondaryColor3f(0.0F, 0.0F, 0.0F);
|
||||
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void displayListReplayIsCacheOnly() {
|
||||
final int list = GLStateManager.glGenLists(1);
|
||||
|
||||
@@ -6,7 +6,7 @@ log4j = "2.0-beta9"
|
||||
asmCompile = "5.0.3"
|
||||
# End Frozen
|
||||
|
||||
lwjgl3 = "3.4.2-20260602.093430-9"
|
||||
lwjgl3 = "3.4.2"
|
||||
lwjgl3ify = "3.0.31"
|
||||
|
||||
celeritas = "2.5.10-GTNH"
|
||||
|
||||
@@ -466,11 +466,15 @@ public final class FrameManager {
|
||||
|
||||
private volatile boolean acquireThreadReported;
|
||||
|
||||
static boolean acquireAffinityViolated(Thread self, Thread window) {
|
||||
return window != null && window != self;
|
||||
}
|
||||
|
||||
private void checkAcquireOnWindowThread() {
|
||||
if (acquireThreadReported) return;
|
||||
final Thread self = Thread.currentThread();
|
||||
final Thread window = device.getWindowThread();
|
||||
if (window == null || window == self) return;
|
||||
if (!acquireAffinityViolated(self, window)) return;
|
||||
acquireThreadReported = true;
|
||||
|
||||
final String message = "Swapchain acquired on '" + self.getName() + "', window created on '" + window.getName() + "'";
|
||||
|
||||
+5
-1
@@ -33,10 +33,14 @@ public final class SplashDispatcher {
|
||||
return (long) w << 32 | (h & 0xffffffffL);
|
||||
}
|
||||
|
||||
static boolean intervalElapsed(long now, long lastBlitNanos, boolean force) {
|
||||
return force || now - lastBlitNanos >= MIN_INTERVAL_NS;
|
||||
}
|
||||
|
||||
public static void tryDispatch(SDLGPURenderBackend backend, OffscreenTarget target, boolean force) {
|
||||
if (!frameReady || target == null) return;
|
||||
final long now = System.nanoTime();
|
||||
if (!force && now - lastBlitNanos < MIN_INTERVAL_NS) return;
|
||||
if (!intervalElapsed(now, lastBlitNanos, force)) return;
|
||||
lastBlitNanos = now;
|
||||
frameReady = false;
|
||||
backend.dispatchSplashBlit(target);
|
||||
|
||||
@@ -6,6 +6,7 @@ import com.gtnewhorizons.angelica.sdlgpu.frame.ContextState;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.frame.FrameManager;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.resource.ResourceManager;
|
||||
import org.lwjgl.sdl.SDLError;
|
||||
import org.lwjgl.sdl.SDLHints;
|
||||
import org.lwjgl.sdl.SDLInit;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
@@ -67,8 +68,13 @@ public final class SdlTestRig {
|
||||
|
||||
private static final int SHADER_FORMATS = SDL_GPU_SHADERFORMAT_SPIRV | SDL_GPU_SHADERFORMAT_MSL | SDL_GPU_SHADERFORMAT_DXBC | SDL_GPU_SHADERFORMAT_DXIL;
|
||||
|
||||
public static void initSdlVideo() {
|
||||
SDLHints.SDL_SetHint(SDLHints.SDL_HINT_MAC_BACKGROUND_APP, "1");
|
||||
assumeTrue(SDLInit.SDL_Init(SDL_INIT_VIDEO), () -> "SDL_Init failed: " + SDLError.SDL_GetError());
|
||||
}
|
||||
|
||||
public static SdlTestRig acquireRealDevice() throws ReflectiveOperationException {
|
||||
assumeTrue(SDLInit.SDL_Init(SDL_INIT_VIDEO), "SDL_Init failed: " + SDLError.SDL_GetError());
|
||||
initSdlVideo();
|
||||
realSdlDevice = SDL_CreateGPUDevice(SHADER_FORMATS, true, (CharSequence) null);
|
||||
assumeTrue(realSdlDevice != 0, "No SDL GPU device: " + SDLError.SDL_GetError());
|
||||
|
||||
|
||||
+2
-4
@@ -1,16 +1,14 @@
|
||||
package com.gtnewhorizons.angelica.sdlgpu.device;
|
||||
|
||||
import com.gtnewhorizons.angelica.sdlgpu.SdlTestRig;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.lwjgl.sdl.SDLError;
|
||||
import org.lwjgl.sdl.SDLInit;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assumptions.assumeTrue;
|
||||
import static org.lwjgl.sdl.SDLInit.SDL_INIT_VIDEO;
|
||||
|
||||
class DeviceLifecycleGpuTest {
|
||||
|
||||
@@ -18,7 +16,7 @@ class DeviceLifecycleGpuTest {
|
||||
|
||||
@BeforeAll
|
||||
static void initSdl() {
|
||||
assumeTrue(SDLInit.SDL_Init(SDL_INIT_VIDEO), () -> "SDL_Init failed: " + SDLError.SDL_GetError());
|
||||
SdlTestRig.initSdlVideo();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
package com.gtnewhorizons.angelica.sdlgpu.frame;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class AcquireAffinityTest {
|
||||
|
||||
private static final Thread WINDOW = new Thread("window");
|
||||
private static final Thread OTHER = new Thread("other");
|
||||
|
||||
@Test
|
||||
void acquiringOnTheWindowThreadIsTheOnlyAcceptedCase() {
|
||||
assertFalse(FrameManager.acquireAffinityViolated(WINDOW, WINDOW));
|
||||
}
|
||||
|
||||
@Test
|
||||
void acquiringOnAnyOtherThreadViolatesTheSdlContract() {
|
||||
assertTrue(FrameManager.acquireAffinityViolated(OTHER, WINDOW));
|
||||
}
|
||||
|
||||
@Test
|
||||
void anUnknownWindowThreadCannotBeJudged() {
|
||||
assertFalse(FrameManager.acquireAffinityViolated(OTHER, null),
|
||||
"the check runs before the window is claimed; judging a null window would throw at startup");
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package com.gtnewhorizons.angelica.sdlgpu.splash;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class SplashDispatcherTest {
|
||||
|
||||
private static final long INTERVAL = 16_000_000L;
|
||||
|
||||
@Test
|
||||
void theIntervalGateSwallowsFramesArrivingTooSoon() {
|
||||
assertFalse(SplashDispatcher.intervalElapsed(INTERVAL - 1, 0L, false));
|
||||
assertTrue(SplashDispatcher.intervalElapsed(INTERVAL, 0L, false));
|
||||
}
|
||||
|
||||
@Test
|
||||
void theFinalFrameIsNotSwallowedByTheIntervalGate() {
|
||||
assertTrue(SplashDispatcher.intervalElapsed(1L, 0L, true), "teardown must present the last splash frame whenever it arrives");
|
||||
}
|
||||
}
|
||||
@@ -204,10 +204,19 @@ public class BatchingFontRenderer {
|
||||
return (argb & 0xFF000000) | (red << 16) | (green << 8) | blue;
|
||||
}
|
||||
|
||||
/**
|
||||
* True only for a perspective projection (world-space text: signs, nameplates, etc).
|
||||
* GUI/HUD text uses an orthographic projection and must never be shaded by world lighting -
|
||||
* mods rely on getting back the exact color they requested (e.g. GTNHLib's ColorResource).
|
||||
*/
|
||||
private static boolean isWorldSpaceText() {
|
||||
return GLStateManager.getProjectionMatrix().m33() == 0.0f;
|
||||
}
|
||||
|
||||
private void captureLightmapState() {
|
||||
final int unitBinding = GLStateManager.getTextures().getTextureUnitBindings(LIGHTMAP_TEX_UNIT).getBinding();
|
||||
|
||||
final boolean active = GLStateManager.getProjectionMatrix().m33() == 0.0f && unitBinding != 0;
|
||||
final boolean active = isWorldSpaceText() && unitBinding != 0;
|
||||
|
||||
float u = 0.0f;
|
||||
float v = 0.0f;
|
||||
@@ -242,6 +251,7 @@ public class BatchingFontRenderer {
|
||||
int packedLight;
|
||||
float normalX, normalY, normalZ;
|
||||
int blockEntityId;
|
||||
boolean depthTest;
|
||||
}
|
||||
|
||||
// 16-bit EBO index range
|
||||
@@ -507,6 +517,7 @@ public class BatchingFontRenderer {
|
||||
segment.normalY = normal.y;
|
||||
segment.normalZ = normal.z;
|
||||
segment.blockEntityId = CapturedRenderingState.INSTANCE.getCurrentRenderedBlockEntity();
|
||||
segment.depthTest = GLStateManager.getDepthTest().isEnabled();
|
||||
batchSegments.add(segment);
|
||||
batchSealedEnd = end;
|
||||
}
|
||||
@@ -594,6 +605,7 @@ public class BatchingFontRenderer {
|
||||
final boolean isAlphaTestEnabledBefore = GLStateManager.isEffectiveAlphaTestEnabled();
|
||||
GLStateManager.getEffectiveBlendState(deferredBlendStateBefore);
|
||||
final int boundTextureBefore = GLStateManager.glGetInteger(GL11.GL_TEXTURE_BINDING_2D);
|
||||
final boolean depthTestBefore = GLStateManager.getDepthTest().isEnabled();
|
||||
final boolean depthMaskBefore = GLStateManager.isEffectiveDepthMaskEnabled();
|
||||
final int prevBlockEntityId = CapturedRenderingState.INSTANCE.getCurrentRenderedBlockEntity();
|
||||
boolean textureChanged = false;
|
||||
@@ -610,6 +622,7 @@ public class BatchingFontRenderer {
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
GLStateManager.glPushMatrix();
|
||||
try {
|
||||
boolean curDepthTest = depthTestBefore;
|
||||
for (final TextSegment segment : deferredSegments) {
|
||||
final BatchingFontRenderer owner = segment.owner;
|
||||
if (segment.cmdStart == segment.cmdEnd) continue;
|
||||
@@ -617,6 +630,11 @@ public class BatchingFontRenderer {
|
||||
GLStateManager.setModelViewMatrix(segment.modelView);
|
||||
CapturedRenderingState.INSTANCE.setCurrentBlockEntity(segment.blockEntityId);
|
||||
|
||||
if (segment.depthTest != curDepthTest) {
|
||||
if (segment.depthTest) GLStateManager.enableDepthTest(); else GLStateManager.disableDepthTest();
|
||||
curDepthTest = segment.depthTest;
|
||||
}
|
||||
|
||||
final FontDrawCmd[] cmdsData = owner.batchCommands.elements();
|
||||
Arrays.sort(cmdsData, segment.cmdStart, segment.cmdEnd, FontDrawCmd.DRAW_ORDER_COMPARATOR);
|
||||
|
||||
@@ -628,7 +646,7 @@ public class BatchingFontRenderer {
|
||||
CapturedRenderingState.INSTANCE.setCurrentBlockEntity(prevBlockEntityId);
|
||||
GbufferPrograms.endTranslucencyDeclaration(prevTranslucency);
|
||||
|
||||
GLStateManager.glDepthMask(depthMaskBefore);
|
||||
restoreDepth(depthTestBefore, depthMaskBefore);
|
||||
if (isTextureEnabledBefore) {
|
||||
GLStateManager.enableTexture();
|
||||
} else {
|
||||
@@ -676,12 +694,17 @@ public class BatchingFontRenderer {
|
||||
try {
|
||||
try (MemoryStack stack = stackPush()) {
|
||||
final FloatBuffer mvpBuf = stack.mallocFloat(16);
|
||||
boolean curDepthTest = depthTestBefore;
|
||||
for (final TextSegment segment : deferredSegments) {
|
||||
mvpBuf.clear();
|
||||
segment.mvp.get(mvpBuf);
|
||||
GLStateManager.glUniformMatrix4(segment.owner.mvpMatrixLocation, false, mvpBuf);
|
||||
uploadLightmap(segment.owner.lightmapLocation, segment.lightmapActive, segment.lightmapU,
|
||||
segment.lightmapV, segment.lightmapTexture);
|
||||
if (segment.depthTest != curDepthTest) {
|
||||
if (segment.depthTest) GLStateManager.enableDepthTest(); else GLStateManager.disableDepthTest();
|
||||
curDepthTest = segment.depthTest;
|
||||
}
|
||||
drawCommands(segment.owner.batchCommands.elements(), segment.cmdStart, segment.cmdEnd, segment.owner);
|
||||
}
|
||||
}
|
||||
@@ -1236,7 +1259,7 @@ public class BatchingFontRenderer {
|
||||
|
||||
this.beginBatch();
|
||||
this.captureLightmapState();
|
||||
this.lightingFactorActive = FFPVertexLighting.modulatesVertexColor(this.lightingFactor);
|
||||
this.lightingFactorActive = isWorldSpaceText() && FFPVertexLighting.modulatesVertexColor(this.lightingFactor);
|
||||
float curX = anchorX;
|
||||
try {
|
||||
final int totalStringLength = string.length();
|
||||
|
||||
@@ -4,24 +4,27 @@ import com.google.common.collect.HashMultiset;
|
||||
import com.gtnewhorizons.angelica.config.FontConfig;
|
||||
import com.gtnewhorizons.angelica.mixins.interfaces.ResourceAccessor;
|
||||
import cpw.mods.fml.client.SplashProgress;
|
||||
import cpw.mods.fml.common.versioning.DefaultArtifactVersion;
|
||||
import lombok.Getter;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.FontRenderer;
|
||||
import net.minecraft.client.resources.DefaultResourcePack;
|
||||
import net.minecraft.client.resources.SimpleReloadableResourceManager;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import cpw.mods.fml.common.Loader;
|
||||
|
||||
import java.awt.Font;
|
||||
import java.awt.FontFormatException;
|
||||
import java.awt.GraphicsEnvironment;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class FontStrategist {
|
||||
@@ -29,42 +32,46 @@ public class FontStrategist {
|
||||
@Getter
|
||||
private static final Font[] availableFonts;
|
||||
public static final Logger LOGGER = LogManager.getLogger("Angelica");
|
||||
private static final boolean isSafeToUseAwtEnvironmentData = checkIsSafeToUseAwtEnvironmentData();
|
||||
|
||||
static {
|
||||
HashMap<String, Font> fontSet = new HashMap<>();
|
||||
|
||||
if (GraphicsEnvironment.isHeadless()) {
|
||||
LOGGER.warn("GraphicsEnvironment.isHeadless() returned true! Only bundled fonts will be available. This is likely a MacOS issue.");
|
||||
// get available fonts without duplicates (250 copies of dialog.plain need not apply)
|
||||
final Font[] availableFontsDirty;
|
||||
final HashMultiset<String> duplicates = HashMultiset.create(); // for debugging
|
||||
|
||||
if (isSafeToUseAwtEnvironmentData) {
|
||||
availableFontsDirty = GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts();
|
||||
} else {
|
||||
// get available fonts without duplicates (250 copies of dialog.plain need not apply)
|
||||
Font[] availableFontsDirty = GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts();
|
||||
HashMultiset<String> duplicates = HashMultiset.create(); // for debugging
|
||||
|
||||
for (Font font : availableFontsDirty) {
|
||||
String fontName = font.getFontName();
|
||||
if (fontSet.containsKey(fontName)) {
|
||||
duplicates.add(fontName);
|
||||
} else {
|
||||
fontSet.put(fontName, font);
|
||||
}
|
||||
}
|
||||
|
||||
if (!duplicates.isEmpty()) {
|
||||
StringBuilder sb = new StringBuilder(duplicates.size() + " duplicate font(s) found in the list reported by Java: ");
|
||||
for (Iterator<String> iter = duplicates.stream().distinct().iterator(); iter.hasNext(); ) {
|
||||
String dupe = iter.next();
|
||||
sb.append(duplicates.count(dupe)).append("x ").append(dupe);
|
||||
if (iter.hasNext()) {
|
||||
sb.append(", ");
|
||||
}
|
||||
}
|
||||
sb.append(". Some fonts may be missing from the font selection menu.");
|
||||
LOGGER.warn(sb.toString());
|
||||
}
|
||||
|
||||
LOGGER.info("Got {} fonts from GraphicsEnvironment ({} after deduplication)", availableFontsDirty.length, fontSet.size());
|
||||
LOGGER.warn("System font enumeration is disabled because AWT API is unsafe in this environment. Update lwjgl3ify!");
|
||||
availableFontsDirty = new Font[0];
|
||||
}
|
||||
|
||||
for (Font font : availableFontsDirty) {
|
||||
String fontName = font.getFontName();
|
||||
if (fontSet.containsKey(fontName)) {
|
||||
duplicates.add(fontName);
|
||||
} else {
|
||||
fontSet.put(fontName, font);
|
||||
}
|
||||
}
|
||||
|
||||
if (!duplicates.isEmpty()) {
|
||||
StringBuilder sb = new StringBuilder(duplicates.size() + " duplicate font(s) found in the list reported by Java: ");
|
||||
for (Iterator<String> iter = duplicates.stream().distinct().iterator(); iter.hasNext(); ) {
|
||||
String dupe = iter.next();
|
||||
sb.append(duplicates.count(dupe)).append("x ").append(dupe);
|
||||
if (iter.hasNext()) {
|
||||
sb.append(", ");
|
||||
}
|
||||
}
|
||||
sb.append(". Some fonts may be missing from the font selection menu.");
|
||||
LOGGER.warn(sb.toString());
|
||||
}
|
||||
|
||||
LOGGER.info("Got {} fonts from GraphicsEnvironment ({} after deduplication)", availableFontsDirty.length, fontSet.size());
|
||||
|
||||
loadBundledFonts(fontSet);
|
||||
|
||||
availableFonts = fontSet.values().stream().sorted(Comparator.comparing(Font::getFontName)).toArray(Font[]::new);
|
||||
@@ -76,22 +83,46 @@ public class FontStrategist {
|
||||
packMap.put(FontProviderCustom.getFallback().getAtlasResourceName(i), new File(FontProviderCustom.getFallback().getAtlasFullPath(i)));
|
||||
}
|
||||
|
||||
Minecraft mc = Minecraft.getMinecraft();
|
||||
DefaultResourcePack fontResourcePack = new DefaultResourcePack(packMap);
|
||||
List defaultResourcePacks = ((ResourceAccessor) Minecraft.getMinecraft()).angelica$getDefaultResourcePacks();
|
||||
defaultResourcePacks.add(fontResourcePack);
|
||||
|
||||
((ResourceAccessor) mc).angelica$getDefaultResourcePacks().add(fontResourcePack);
|
||||
((SimpleReloadableResourceManager) mc.getResourceManager()).reloadResourcePack(fontResourcePack);
|
||||
}
|
||||
|
||||
private static boolean checkIsSafeToUseAwtEnvironmentData() {
|
||||
if (!GraphicsEnvironment.isHeadless()) return true;
|
||||
|
||||
try (InputStream stream = ClassLoader.getSystemResourceAsStream(
|
||||
"META-INF/lwjgl3ify-forgePatches-version.txt")) {
|
||||
|
||||
if (stream == null) {
|
||||
LOGGER.warn("Failed to identify lwjgl3ify version");
|
||||
return false;
|
||||
}
|
||||
|
||||
String version;
|
||||
try (BufferedReader reader = new BufferedReader(
|
||||
new InputStreamReader(stream, StandardCharsets.UTF_8))) {
|
||||
version = reader.readLine();
|
||||
}
|
||||
|
||||
// See https://github.com/GTNewHorizons/RetroFuturaBootstrap/commit/6d39c56ba0f1496b5599a3660d1578df3b28ff17
|
||||
// This change corresponds to RFB 1.0.14, used since lwjgl3ify 3.0.8
|
||||
return new DefaultArtifactVersion(version)
|
||||
.compareTo(new DefaultArtifactVersion("3.0.8")) >= 0;
|
||||
} catch (IOException e) {
|
||||
LOGGER.warn("Failed to identify lwjgl3ify version", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Load .ttf/.otf shipped with the pack so customFontName* can point at a font that isn't installed
|
||||
// on the system. fontfiles/ is SmoothFont's folder, kept so packs built for it work unchanged.
|
||||
private static void loadBundledFonts(HashMap<String, Font> fontSet) {
|
||||
File configDir = Loader.instance().getConfigDir();
|
||||
if (configDir == null) {
|
||||
LOGGER.warn("Loader.instance().getConfigDir() returned null. Bundled fonts will not be loaded.");
|
||||
return;
|
||||
}
|
||||
File parent = configDir.getParentFile();
|
||||
File[] fontDirs = { new File(parent, "fontfiles"), new File(configDir, "angelica/fonts") };
|
||||
GraphicsEnvironment ge = GraphicsEnvironment.isHeadless() ? null : GraphicsEnvironment.getLocalGraphicsEnvironment();
|
||||
File gameDir = Minecraft.getMinecraft().mcDataDir;
|
||||
File[] fontDirs = { new File(gameDir, "fontfiles"), new File(gameDir, "config/angelica/fonts") };
|
||||
GraphicsEnvironment ge = !isSafeToUseAwtEnvironmentData ? null : GraphicsEnvironment.getLocalGraphicsEnvironment();
|
||||
int loaded = 0;
|
||||
for (File dir : fontDirs) {
|
||||
File[] files = dir.listFiles((d, name) -> name.toLowerCase().endsWith(".ttf") || name.toLowerCase().endsWith(".otf"));
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
package com.gtnewhorizons.angelica.client.gui;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public record DynamicLightsEntityFilter(List<String> modPrefixes, List<String> nameTerms, DynamicLightsEntityFilter.State state) {
|
||||
|
||||
public enum State {
|
||||
ANY,
|
||||
ENABLED,
|
||||
DISABLED
|
||||
}
|
||||
|
||||
private static final Pattern WHITESPACE = Pattern.compile("\\s+");
|
||||
private static final DynamicLightsEntityFilter MATCH_ALL = new DynamicLightsEntityFilter(List.of(), List.of(), State.ANY);
|
||||
|
||||
public DynamicLightsEntityFilter {
|
||||
modPrefixes = List.copyOf(modPrefixes);
|
||||
nameTerms = List.copyOf(nameTerms);
|
||||
}
|
||||
|
||||
public static DynamicLightsEntityFilter parse(String query) {
|
||||
if (query == null) {
|
||||
return MATCH_ALL;
|
||||
}
|
||||
|
||||
final var normalized = query.strip().toLowerCase(Locale.ROOT);
|
||||
if (normalized.isEmpty()) {
|
||||
return MATCH_ALL;
|
||||
}
|
||||
|
||||
final var modPrefixes = new ArrayList<String>();
|
||||
final var nameTerms = new ArrayList<String>();
|
||||
var wantEnabled = false;
|
||||
var wantDisabled = false;
|
||||
|
||||
for (final var token : WHITESPACE.split(normalized)) {
|
||||
switch (token) {
|
||||
case "$on" -> wantEnabled = true;
|
||||
case "$off" -> wantDisabled = true;
|
||||
default -> {
|
||||
if (token.length() > 1 && token.charAt(0) == '@') {
|
||||
modPrefixes.add(token.substring(1));
|
||||
} else {
|
||||
nameTerms.add(token);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final var state = wantEnabled == wantDisabled ? State.ANY : wantEnabled ? State.ENABLED : State.DISABLED;
|
||||
if (modPrefixes.isEmpty() && nameTerms.isEmpty() && state == State.ANY) {
|
||||
return MATCH_ALL;
|
||||
}
|
||||
return new DynamicLightsEntityFilter(modPrefixes, nameTerms, state);
|
||||
}
|
||||
|
||||
public boolean isMatchAll() {
|
||||
return modPrefixes.isEmpty() && nameTerms.isEmpty() && state == State.ANY;
|
||||
}
|
||||
|
||||
public boolean matches(String lowerName, String lowerModId, boolean enabled) {
|
||||
return matchesState(enabled) && matchesAnyMod(lowerModId) && containsAllTerms(lowerName);
|
||||
}
|
||||
|
||||
private boolean matchesState(boolean enabled) {
|
||||
return switch (state) {
|
||||
case ANY -> true;
|
||||
case ENABLED -> enabled;
|
||||
case DISABLED -> !enabled;
|
||||
};
|
||||
}
|
||||
|
||||
private boolean matchesAnyMod(String lowerModId) {
|
||||
if (modPrefixes.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
for (final var prefix : modPrefixes) {
|
||||
if (lowerModId.startsWith(prefix)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean containsAllTerms(String lowerName) {
|
||||
for (final var term : nameTerms) {
|
||||
if (!lowerName.contains(term)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,242 @@
|
||||
package com.gtnewhorizons.angelica.client.gui;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import net.minecraft.client.gui.GuiButton;
|
||||
import net.minecraft.client.gui.GuiScreen;
|
||||
import net.minecraft.client.gui.GuiTextField;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.client.resources.I18n;
|
||||
import net.minecraft.util.EnumChatFormatting;
|
||||
|
||||
import org.lwjgl.input.Keyboard;
|
||||
import org.lwjgl.input.Mouse;
|
||||
|
||||
import com.gtnewhorizons.angelica.dynamiclights.config.EntityLightConfig;
|
||||
import com.gtnewhorizons.angelica.dynamiclights.config.EntityTypeEntry;
|
||||
|
||||
import net.coderbot.iris.gui.element.IrisGuiSlot;
|
||||
import net.coderbot.iris.gui.element.widget.IrisButton;
|
||||
|
||||
public class DynamicLightsEntityScreen extends GuiScreen {
|
||||
|
||||
private static final String ENABLED_PREFIX = EnumChatFormatting.GREEN + "[x] " + EnumChatFormatting.WHITE;
|
||||
private static final String DISABLED_PREFIX = EnumChatFormatting.DARK_GRAY + "[ ] " + EnumChatFormatting.GRAY;
|
||||
|
||||
private static List<EntityTypeEntry> entityTypes;
|
||||
|
||||
private final GuiScreen parent;
|
||||
private final String title = I18n.format("options.dynamiclights.entities.title");
|
||||
private final String info = I18n.format("options.dynamiclights.entities.info");
|
||||
|
||||
private List<EntityTypeEntry> displayed;
|
||||
private String countLabel;
|
||||
private String search = "";
|
||||
private boolean dirty;
|
||||
|
||||
private GuiTextField searchBox;
|
||||
private EntityRowList rowList;
|
||||
private IrisButton toggleButton;
|
||||
|
||||
public DynamicLightsEntityScreen(GuiScreen parent) {
|
||||
this.parent = parent;
|
||||
this.refilter();
|
||||
}
|
||||
|
||||
private static List<EntityTypeEntry> entityTypes() {
|
||||
if (entityTypes == null) {
|
||||
entityTypes = EntityLightConfig.getAllEntityTypes();
|
||||
}
|
||||
return entityTypes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initGui() {
|
||||
Keyboard.enableRepeatEvents(true);
|
||||
|
||||
this.searchBox = new GuiTextField(this.fontRendererObj, this.width / 2 - 150, 30, 300, 20);
|
||||
this.searchBox.setMaxStringLength(64);
|
||||
this.searchBox.setText(this.search);
|
||||
this.rowList = new EntityRowList();
|
||||
|
||||
this.toggleButton = new IrisButton(this.width / 2 - 154, this.height - 27, 150, 20, "", button -> this.toggleVisible());
|
||||
this.updateToggleButton();
|
||||
|
||||
this.buttonList.add(this.toggleButton);
|
||||
this.buttonList.add(new IrisButton(this.width / 2 + 4, this.height - 27, 150, 20, I18n.format("gui.done"), button -> this.onClose()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGuiClosed() {
|
||||
Keyboard.enableRepeatEvents(false);
|
||||
}
|
||||
|
||||
private void onClose() {
|
||||
if (this.dirty) {
|
||||
EntityLightConfig.save();
|
||||
this.dirty = false;
|
||||
}
|
||||
this.mc.displayGuiScreen(this.parent);
|
||||
}
|
||||
|
||||
private void refilter() {
|
||||
final var filter = DynamicLightsEntityFilter.parse(this.search);
|
||||
if (filter.isMatchAll()) {
|
||||
this.displayed = entityTypes();
|
||||
} else {
|
||||
final var matches = new ArrayList<EntityTypeEntry>();
|
||||
for (final var entry : entityTypes()) {
|
||||
if (filter.matches(entry.getSearchName(), entry.getSearchModId(), EntityLightConfig.isEntityTypeEnabled(entry.getEntityClass()))) {
|
||||
matches.add(entry);
|
||||
}
|
||||
}
|
||||
this.displayed = matches;
|
||||
}
|
||||
this.countLabel = this.displayed.size() + " / " + entityTypes().size();
|
||||
this.updateToggleButton();
|
||||
}
|
||||
|
||||
private boolean anyDisplayedEnabled() {
|
||||
for (final var entry : this.displayed) {
|
||||
if (EntityLightConfig.isEntityTypeEnabled(entry.getEntityClass())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void updateToggleButton() {
|
||||
if (this.toggleButton == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final var subset = this.displayed.size() != entityTypes().size();
|
||||
final var key = this.anyDisplayedEnabled()
|
||||
? (subset ? "options.dynamiclights.entities.disableshown" : "options.dynamiclights.entities.disableall")
|
||||
: (subset ? "options.dynamiclights.entities.enableshown" : "options.dynamiclights.entities.enableall");
|
||||
|
||||
this.toggleButton.displayString = I18n.format(key);
|
||||
this.toggleButton.enabled = !this.displayed.isEmpty();
|
||||
}
|
||||
|
||||
private void toggleVisible() {
|
||||
final var enable = !this.anyDisplayedEnabled();
|
||||
for (final var entry : this.displayed) {
|
||||
EntityLightConfig.setEntityTypeEnabled(entry.getEntityClass(), enable);
|
||||
}
|
||||
this.dirty = true;
|
||||
this.refilter();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawScreen(int mouseX, int mouseY, float partialTicks) {
|
||||
this.drawDefaultBackground();
|
||||
this.rowList.drawScreen(mouseX, mouseY, partialTicks);
|
||||
super.drawScreen(mouseX, mouseY, partialTicks);
|
||||
|
||||
drawCenteredString(this.fontRendererObj, this.title, this.width / 2, 8, 0xFFFFFF);
|
||||
drawCenteredString(this.fontRendererObj, this.info, this.width / 2, 20, 0xA0A0A0);
|
||||
this.searchBox.drawTextBox();
|
||||
if (!this.searchBox.isFocused() && this.search.isEmpty()) {
|
||||
this.drawString(this.fontRendererObj, I18n.format("options.dynamiclights.entities.search"), this.width / 2 - 144, 36, 0x808080);
|
||||
}
|
||||
drawCenteredString(this.fontRendererObj, this.countLabel, this.width / 2, this.height - 39, 0x808080);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateScreen() {
|
||||
this.searchBox.updateCursorCounter();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void mouseClicked(int mouseX, int mouseY, int mouseButton) {
|
||||
if (!this.rowList.mouseClicked(mouseX, mouseY, mouseButton)) {
|
||||
super.mouseClicked(mouseX, mouseY, mouseButton);
|
||||
}
|
||||
this.searchBox.mouseClicked(mouseX, mouseY, mouseButton);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void mouseMovedOrUp(int mouseX, int mouseY, int state) {
|
||||
if (state == -1 || !this.rowList.mouseReleased(mouseX, mouseY, Mouse.getEventButton())) {
|
||||
super.mouseMovedOrUp(mouseX, mouseY, state);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void actionPerformed(GuiButton button) {
|
||||
if (button.enabled && button instanceof IrisButton irisButton) {
|
||||
irisButton.onPress();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void keyTyped(char typedChar, int keyCode) {
|
||||
if (this.searchBox.isFocused()) {
|
||||
if (keyCode == Keyboard.KEY_ESCAPE) {
|
||||
this.searchBox.setFocused(false);
|
||||
return;
|
||||
}
|
||||
this.searchBox.textboxKeyTyped(typedChar, keyCode);
|
||||
if (!this.searchBox.getText().equals(this.search)) {
|
||||
this.search = this.searchBox.getText();
|
||||
this.refilter();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (keyCode == Keyboard.KEY_ESCAPE) {
|
||||
this.onClose();
|
||||
return;
|
||||
}
|
||||
super.keyTyped(typedChar, keyCode);
|
||||
}
|
||||
|
||||
private class EntityRowList extends IrisGuiSlot {
|
||||
|
||||
EntityRowList() {
|
||||
super(DynamicLightsEntityScreen.this.mc, DynamicLightsEntityScreen.this.width, DynamicLightsEntityScreen.this.height, 56, DynamicLightsEntityScreen.this.height - 44, 18);
|
||||
this.setRenderBackground(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getSize() {
|
||||
return displayed.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isSelected(int index) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void drawBackground() {}
|
||||
|
||||
@Override
|
||||
public int getListWidth() {
|
||||
return Math.min(400, DynamicLightsEntityScreen.this.width - 40);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean elementClicked(int index, boolean doubleClick, int mouseX, int mouseY, int button) {
|
||||
if (button != 0 || index >= displayed.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final var entry = displayed.get(index);
|
||||
EntityLightConfig.setEntityTypeEnabled(entry.getEntityClass(), !EntityLightConfig.isEntityTypeEnabled(entry.getEntityClass()));
|
||||
dirty = true;
|
||||
updateToggleButton();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void drawSlot(int index, int x, int y, int slotHeight, Tessellator tessellator, int mouseX, int mouseY) {
|
||||
final var entry = displayed.get(index);
|
||||
final var enabled = EntityLightConfig.isEntityTypeEnabled(entry.getEntityClass());
|
||||
final var label = (enabled ? ENABLED_PREFIX : DISABLED_PREFIX) + entry.getDisplayName() + EnumChatFormatting.DARK_GRAY + " (" + entry.getModId() + ")";
|
||||
|
||||
drawString(fontRendererObj, fontRendererObj.trimStringToWidth(label, this.getListWidth() - 8), x, y + 5, 0xFFFFFF);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,105 +1,32 @@
|
||||
package com.gtnewhorizons.angelica.client.gui;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.gtnewhorizons.angelica.dynamiclights.DynamicLights;
|
||||
import com.gtnewhorizons.angelica.dynamiclights.config.EntityLightConfig;
|
||||
import com.gtnewhorizons.angelica.dynamiclights.config.EntityLightConfigStorage;
|
||||
import com.gtnewhorizons.angelica.dynamiclights.config.EntityTypeEntry;
|
||||
import jss.notfine.core.Settings;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.Option;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.OptionGroup;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.OptionImpl;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.OptionImpact;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.OptionPage;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.control.TickBoxControl;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.SubScreenOption;
|
||||
import net.minecraft.client.resources.I18n;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class DynamicLightsOptionPages {
|
||||
|
||||
public static OptionPage dynamicLights() {
|
||||
if (!DynamicLights.configEnabled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<OptionGroup> groups = new ArrayList<>();
|
||||
|
||||
groups.add(OptionGroup.createBuilder()
|
||||
.add(Settings.DYNAMIC_LIGHTS.option)
|
||||
.add(Settings.DYNAMIC_LIGHTS_SHADER_FORCE.option)
|
||||
.build());
|
||||
|
||||
groups.add(OptionGroup.createBuilder()
|
||||
.add(Settings.DYNAMIC_LIGHTS_FRUSTUM_CULLING.option)
|
||||
.add(Settings.DYNAMIC_LIGHTS_ADAPTIVE_TICKING.option)
|
||||
.add(Settings.DYNAMIC_LIGHTS_CULL_TIMEOUT.option)
|
||||
.build());
|
||||
|
||||
groups.add(OptionGroup.createBuilder()
|
||||
.add(Settings.DYNAMIC_LIGHTS_SLOW_DIST.option)
|
||||
.add(Settings.DYNAMIC_LIGHTS_SLOWER_DIST.option)
|
||||
.add(Settings.DYNAMIC_LIGHTS_BACKGROUND_DIST.option)
|
||||
.build());
|
||||
|
||||
// Entity type toggles - grouped by mod ID
|
||||
List<EntityTypeEntry> allEntities = EntityLightConfig.getAllEntityTypes();
|
||||
Map<String, List<EntityTypeEntry>> byMod = new LinkedHashMap<>();
|
||||
|
||||
for (EntityTypeEntry entry : allEntities) {
|
||||
byMod.computeIfAbsent(entry.getModId(), _ -> new ArrayList<>()).add(entry);
|
||||
}
|
||||
|
||||
// Create a group for each mod's entities
|
||||
for (Map.Entry<String, List<EntityTypeEntry>> modEntry : byMod.entrySet()) {
|
||||
String modId = modEntry.getKey();
|
||||
List<EntityTypeEntry> entities = modEntry.getValue();
|
||||
|
||||
OptionGroup.Builder groupBuilder = OptionGroup.createBuilder();
|
||||
|
||||
for (EntityTypeEntry entity : entities) {
|
||||
Option<Boolean> option = createEntityOption(entity);
|
||||
if (option != null) {
|
||||
groupBuilder.add(option);
|
||||
}
|
||||
}
|
||||
|
||||
// Only add group if it has options
|
||||
OptionGroup group = groupBuilder.build();
|
||||
if (!group.getOptions().isEmpty()) {
|
||||
groups.add(group);
|
||||
}
|
||||
}
|
||||
|
||||
return new OptionPage(I18n.format("options.dynamiclights.page"), ImmutableList.copyOf(groups));
|
||||
}
|
||||
|
||||
private static Option<Boolean> createEntityOption(EntityTypeEntry entry) {
|
||||
final String className = entry.getClassName();
|
||||
|
||||
// Resolve class once during option creation, not on every get/set
|
||||
Class<?> resolvedClass;
|
||||
try {
|
||||
resolvedClass = Class.forName(className);
|
||||
} catch (ClassNotFoundException e) {
|
||||
// Class not loaded, skip this option
|
||||
return null;
|
||||
}
|
||||
|
||||
final Class<?> entityClass = resolvedClass;
|
||||
|
||||
return OptionImpl.createBuilder(Boolean.class, EntityLightConfigStorage.INSTANCE)
|
||||
.setName(entry.getDisplayName() + " (" + entry.getModId() + ")")
|
||||
.setTooltip(I18n.format("options.dynamiclights.entity.tooltip", entry.getDisplayName()))
|
||||
.setControl(TickBoxControl::new)
|
||||
.setBinding(
|
||||
(_, value) -> EntityLightConfig.setEntityTypeEnabled(entityClass, value),
|
||||
storage -> EntityLightConfig.isEntityTypeEnabled(entityClass)
|
||||
)
|
||||
.setImpact(OptionImpact.LOW)
|
||||
.build();
|
||||
return new OptionPage(I18n.format("options.dynamiclights.page"), ImmutableList.of(
|
||||
OptionGroup.createBuilder()
|
||||
.add(Settings.DYNAMIC_LIGHTS.option)
|
||||
.add(Settings.DYNAMIC_LIGHTS_SHADER_FORCE.option)
|
||||
.build(),
|
||||
OptionGroup.createBuilder()
|
||||
.add(Settings.DYNAMIC_LIGHTS_FRUSTUM_CULLING.option)
|
||||
.add(Settings.DYNAMIC_LIGHTS_ADAPTIVE_TICKING.option)
|
||||
.add(Settings.DYNAMIC_LIGHTS_CULL_TIMEOUT.option)
|
||||
.build(),
|
||||
OptionGroup.createBuilder()
|
||||
.add(Settings.DYNAMIC_LIGHTS_SLOW_DIST.option)
|
||||
.add(Settings.DYNAMIC_LIGHTS_SLOWER_DIST.option)
|
||||
.add(Settings.DYNAMIC_LIGHTS_BACKGROUND_DIST.option)
|
||||
.build(),
|
||||
OptionGroup.createBuilder()
|
||||
.add(new SubScreenOption(I18n.format("options.dynamiclights.entities"), I18n.format("options.dynamiclights.entities.tooltip"), DynamicLightsEntityScreen::new))
|
||||
.build()));
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,10 @@ public final class BopFogColorSource implements BopFogBlend.ColorSource {
|
||||
return this;
|
||||
}
|
||||
|
||||
public void freeWorld() {
|
||||
this.world = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int fogColour(int x, int y, int z) {
|
||||
final BiomeGenBase biome = FogBiomeCache.get(world, x, z);
|
||||
|
||||
@@ -9,7 +9,7 @@ public final class FogBiomeCache {
|
||||
private static final int MAX_ENTRIES = 16384;
|
||||
|
||||
private static final Long2ObjectOpenHashMap<BiomeGenBase> CACHE = new Long2ObjectOpenHashMap<>();
|
||||
private static World lastWorld;
|
||||
private static World cachedWorld;
|
||||
private static int generation;
|
||||
|
||||
private FogBiomeCache() {}
|
||||
@@ -18,10 +18,14 @@ public final class FogBiomeCache {
|
||||
return generation;
|
||||
}
|
||||
|
||||
public static void invalidate() {
|
||||
cachedWorld = null;
|
||||
}
|
||||
|
||||
public static BiomeGenBase get(World world, int x, int z) {
|
||||
if (world != lastWorld) {
|
||||
if (world != cachedWorld) {
|
||||
CACHE.clear();
|
||||
lastWorld = world;
|
||||
cachedWorld = world;
|
||||
generation++;
|
||||
}
|
||||
final long key = ((long) x << 32) | (z & 0xFFFFFFFFL);
|
||||
|
||||
@@ -61,13 +61,17 @@ public class NativeImage extends BufferedImage {
|
||||
// final int width = GL11.glGetTexLevelParameteri(GL11.GL_TEXTURE_2D, level, GL11.GL_TEXTURE_WIDTH);
|
||||
// final int height = GL11.glGetTexLevelParameteri(GL11.GL_TEXTURE_2D, level, GL11.GL_TEXTURE_HEIGHT);
|
||||
|
||||
final IntBuffer buffer = MemoryUtilities.memAllocInt(size);
|
||||
final int pixels = width * height;
|
||||
final IntBuffer buffer = MemoryUtilities.memAllocInt(pixels);
|
||||
|
||||
try {
|
||||
GLStateManager.glGetTexImage(GL11.GL_TEXTURE_2D, level, format.glFormat, GL12.GL_UNSIGNED_INT_8_8_8_8_REV, buffer);
|
||||
|
||||
int[] data = new int[size];
|
||||
buffer.get(data);
|
||||
int[] data = new int[pixels];
|
||||
buffer.get(data, 0, pixels);
|
||||
for (int i = 0; i < pixels; i++) {
|
||||
data[i] = swapRedBlue(data[i]);
|
||||
}
|
||||
setRGB(0, 0, width, height, data, 0, width);
|
||||
} finally {
|
||||
MemoryUtilities.memFree(buffer);
|
||||
@@ -90,7 +94,7 @@ public class NativeImage extends BufferedImage {
|
||||
}
|
||||
|
||||
public static int getR(int i) {
|
||||
return i >> 0 & 255;
|
||||
return i & 255;
|
||||
}
|
||||
|
||||
public static int getG(int i) {
|
||||
@@ -102,11 +106,15 @@ public class NativeImage extends BufferedImage {
|
||||
}
|
||||
|
||||
public int getPixelRGBA(int x, int y) {
|
||||
return getRGB(x, y);
|
||||
return swapRedBlue(getRGB(x, y));
|
||||
}
|
||||
|
||||
public void setPixelRGBA(int x, int y, int rgb) {
|
||||
setRGB(x, y, rgb);
|
||||
setRGB(x, y, swapRedBlue(rgb));
|
||||
}
|
||||
|
||||
private static int swapRedBlue(int c) {
|
||||
return (c & 0xFF00FF00) | ((c >>> 16) & 0xFF) | ((c & 0xFF) << 16);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -452,6 +452,18 @@ public class AngelicaConfig {
|
||||
@Config.RequiresMcRestart
|
||||
public static GLProfile glProfile;
|
||||
|
||||
@Config.Comment("Enable random top-face texture orientation for configured blocks")
|
||||
@Config.DefaultBoolean(true)
|
||||
public static boolean enableNaturalTextures = true;
|
||||
|
||||
@Config.Comment("List of block registry names to apply random top-face texture rotation to")
|
||||
@Config.DefaultStringList({
|
||||
"minecraft:andesite", "minecraft:dirt", "minecraft:granite", "minecraft:grass", "minecraft:mycelium", "minecraft:sand", "minecraft:soul_sand", "etfuturum:calcite", "etfuturum:coarse_dirt", "etfuturum:cobbled_deepslate", "etfuturum:concrete_powder", "etfuturum:deepslate", "etfuturum:polished_deepslate", "etfuturum:soul_soil", "etfuturum:tuff", "BiomesOPlenty:ash", "BiomesOPlenty:cragRock", "BiomesOPlenty:driedDirt", "BiomesOPlenty:hardDirt", "BiomesOPlenty:hardSand", "BiomesOPlenty:mud", "BiomesOPlenty:newBopDirt", "Botania:dirtPath", "Botania:enchantedSoil", "Botania:livingrock", "Botania:prismarine", "Botania:shimmerrock", "Botany:loam", "Botany:loamNoWeed", "Botany:soil", "Botany:soilNoWeed", "chisel:moss", "chisel:moss_carpet", "DraconicEvolution:earth", "dreamcraft:SandClayMix", "ExtraUtilities:color_hellsand", "ExtraUtilities:cursedearthside", "GalaxySpace:acentauribbgrunt", "GalaxySpace:acentauribbsubgrunt", "GalaxySpace:barnardaCdirt", "GalaxySpace:barnardaEgrunt", "GalaxySpace:barnardaEsubgrunt", "GalaxySpace:barnardaFgrunt", "GalaxySpace:barnardaFsubgrunt", "GalaxySpace:callistoblocks", "GalaxySpace:ceresblocks", "GalaxySpace:deimosblocks", "GalaxySpace:europagrunt", "GalaxySpace:ganymedeblocks", "GalaxySpace:haumeablocks", "GalaxySpace:ioblocks", "GalaxySpace:makemakegrunt", "GalaxySpace:mercuryblocks", "GalaxySpace:mirandablocks", "GalaxySpace:oberonblocks", "GalaxySpace:phobosblocks", "GalaxySpace:proteusblocks", "GalaxySpace:tcetieblocks", "GalaxySpace:titanblocks", "GalaxySpace:tritonblocks", "GalaxySpace:vegabgrunt", "GalaxySpace:vegabsubgrunt", "GalaxySpace:venusblocks", "gregtech:gt.blockgranites", "IC2:blockBasalt", "MagicBees:magicbees.enchantedEarth", "Natura:heatsand", "RandomThings:fertilizedDirt", "TConstruct:CraftedSoil", "TConstruct:SpeedBlock", "thaumicbases:oldCobble", "thaumicbases:oldCobbleMossy", "ToxicEverglades:blockDarkWorldGround2", "VillageNames:concretePowder", "witchery:pitdirt"
|
||||
})
|
||||
public static String[] naturalTextureBlocks = new String[] {
|
||||
"minecraft:andesite", "minecraft:dirt", "minecraft:granite", "minecraft:grass", "minecraft:mycelium", "minecraft:sand", "minecraft:soul_sand", "etfuturum:calcite", "etfuturum:coarse_dirt", "etfuturum:cobbled_deepslate", "etfuturum:concrete_powder", "etfuturum:deepslate", "etfuturum:polished_deepslate", "etfuturum:soul_soil", "etfuturum:tuff", "BiomesOPlenty:ash", "BiomesOPlenty:cragRock", "BiomesOPlenty:driedDirt", "BiomesOPlenty:hardDirt", "BiomesOPlenty:hardSand", "BiomesOPlenty:mud", "BiomesOPlenty:newBopDirt", "Botania:dirtPath", "Botania:enchantedSoil", "Botania:livingrock", "Botania:prismarine", "Botania:shimmerrock", "Botany:loam", "Botany:loamNoWeed", "Botany:soil", "Botany:soilNoWeed", "chisel:moss", "chisel:moss_carpet", "DraconicEvolution:earth", "dreamcraft:SandClayMix", "ExtraUtilities:color_hellsand", "ExtraUtilities:cursedearthside", "GalaxySpace:acentauribbgrunt", "GalaxySpace:acentauribbsubgrunt", "GalaxySpace:barnardaCdirt", "GalaxySpace:barnardaEgrunt", "GalaxySpace:barnardaEsubgrunt", "GalaxySpace:barnardaFgrunt", "GalaxySpace:barnardaFsubgrunt", "GalaxySpace:callistoblocks", "GalaxySpace:ceresblocks", "GalaxySpace:deimosblocks", "GalaxySpace:europagrunt", "GalaxySpace:ganymedeblocks", "GalaxySpace:haumeablocks", "GalaxySpace:ioblocks", "GalaxySpace:makemakegrunt", "GalaxySpace:mercuryblocks", "GalaxySpace:mirandablocks", "GalaxySpace:oberonblocks", "GalaxySpace:phobosblocks", "GalaxySpace:proteusblocks", "GalaxySpace:tcetieblocks", "GalaxySpace:titanblocks", "GalaxySpace:tritonblocks", "GalaxySpace:vegabgrunt", "GalaxySpace:vegabsubgrunt", "GalaxySpace:venusblocks", "gregtech:gt.blockgranites", "IC2:blockBasalt", "MagicBees:magicbees.enchantedEarth", "Natura:heatsand", "RandomThings:fertilizedDirt", "TConstruct:CraftedSoil", "TConstruct:SpeedBlock", "thaumicbases:oldCobble", "thaumicbases:oldCobbleMossy", "ToxicEverglades:blockDarkWorldGround2", "VillageNames:concretePowder", "witchery:pitdirt"
|
||||
};
|
||||
|
||||
public static void applyGpuCullingMode() {
|
||||
GpuCulling.setMode(gpuCullingMode == null ? GpuCullingMode.CPU_ONLY : gpuCullingMode);
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import net.minecraft.util.EnumChatFormatting;
|
||||
import net.minecraft.world.WorldServer;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.lwjgl.opengl.Display;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
@@ -41,9 +42,12 @@ public final class FlybyRunner {
|
||||
private int runTicks;
|
||||
private int tick;
|
||||
private boolean waitForTracy;
|
||||
private boolean waitForFocus;
|
||||
private boolean exitWhenDone;
|
||||
private boolean startedFromProperties;
|
||||
private volatile boolean freezeRequested;
|
||||
private boolean pauseOnLostFocusSaved;
|
||||
private boolean pauseOnLostFocusOverridden;
|
||||
|
||||
private double originX, originY, originZ;
|
||||
private float originYaw, originPitch;
|
||||
@@ -83,10 +87,11 @@ public final class FlybyRunner {
|
||||
this.startedFromProperties = true;
|
||||
this.start(configured, SystemProperties.FLYBY_LENGTH, SystemProperties.FLYBY_WARMUP_TICKS, SystemProperties.FLYBY_SPEED);
|
||||
this.waitForTracy = SystemProperties.FLYBY_WAIT_FOR_TRACY;
|
||||
this.waitForFocus = SystemProperties.FLYBY_WAIT_FOR_FOCUS;
|
||||
this.exitWhenDone = SystemProperties.FLYBY_EXIT_WHEN_DONE;
|
||||
LOGGER.info("Flyby started from properties: route={} warmup={} length={} {} ({} ticks) waitForTracy={} exitWhenDone={}",
|
||||
LOGGER.info("Flyby started from properties: route={} warmup={} length={} {} ({} ticks) waitForTracy={} waitForFocus={} exitWhenDone={}",
|
||||
configured.id(), this.warmupTicks, this.runLength, configured.lengthUnit(), this.runTicks,
|
||||
this.waitForTracy, this.exitWhenDone);
|
||||
this.waitForTracy, this.waitForFocus, this.exitWhenDone);
|
||||
}
|
||||
|
||||
public void start(FlybyRoute route, int length, int warmupTicks, double speed) {
|
||||
@@ -99,6 +104,7 @@ public final class FlybyRunner {
|
||||
this.frameCount = 0;
|
||||
this.framesTruncated = false;
|
||||
this.waitForTracy = false;
|
||||
this.waitForFocus = false;
|
||||
this.exitWhenDone = false;
|
||||
this.state = State.WAITING;
|
||||
}
|
||||
@@ -123,6 +129,7 @@ public final class FlybyRunner {
|
||||
switch (this.state) {
|
||||
case WAITING -> {
|
||||
if (this.waitForTracy && !Tracy.isConnected()) return;
|
||||
if (this.waitForFocus && !Display.isActive()) return;
|
||||
this.begin(mc, player);
|
||||
}
|
||||
case WARMUP -> {
|
||||
@@ -172,6 +179,15 @@ public final class FlybyRunner {
|
||||
}
|
||||
|
||||
private void begin(Minecraft mc, EntityClientPlayerMP player) {
|
||||
if (mc.currentScreen != null) {
|
||||
mc.displayGuiScreen(null);
|
||||
}
|
||||
if (!this.pauseOnLostFocusOverridden) {
|
||||
this.pauseOnLostFocusSaved = mc.gameSettings.pauseOnLostFocus;
|
||||
this.pauseOnLostFocusOverridden = true;
|
||||
mc.gameSettings.pauseOnLostFocus = false;
|
||||
}
|
||||
|
||||
this.originX = player.posX;
|
||||
this.originY = player.posY;
|
||||
this.originZ = player.posZ;
|
||||
@@ -397,11 +413,18 @@ public final class FlybyRunner {
|
||||
Tracy.message("flyby teardown");
|
||||
|
||||
this.returnToOrigin(player);
|
||||
this.restorePauseOnLostFocus(mc);
|
||||
|
||||
this.tick = 0;
|
||||
this.state = this.exitWhenDone ? State.EXITING : State.DONE;
|
||||
}
|
||||
|
||||
private void restorePauseOnLostFocus(Minecraft mc) {
|
||||
if (!this.pauseOnLostFocusOverridden) return;
|
||||
mc.gameSettings.pauseOnLostFocus = this.pauseOnLostFocusSaved;
|
||||
this.pauseOnLostFocusOverridden = false;
|
||||
}
|
||||
|
||||
private void returnToOrigin(EntityClientPlayerMP player) {
|
||||
player.motionX = 0.0D;
|
||||
player.motionY = 0.0D;
|
||||
@@ -448,6 +471,7 @@ public final class FlybyRunner {
|
||||
Tracy.sectionLeave(this.runSection);
|
||||
this.runSection = 0L;
|
||||
FramePacer.endStats();
|
||||
this.restorePauseOnLostFocus(Minecraft.getMinecraft());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-3
@@ -71,7 +71,6 @@ public final class EntityLightConfig {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static List<EntityTypeEntry> getAllEntityTypes() {
|
||||
List<EntityTypeEntry> entries = new ArrayList<>();
|
||||
Set<String> seenClasses = new HashSet<>();
|
||||
@@ -88,9 +87,8 @@ public final class EntityLightConfig {
|
||||
|
||||
String modId = getModIdForEntity(entityClass, entityId);
|
||||
String displayName = getDisplayName(entityId, entityClass);
|
||||
boolean enabled = !disabledClassNames.contains(className);
|
||||
|
||||
entries.add(new EntityTypeEntry(displayName, className, modId, enabled));
|
||||
entries.add(new EntityTypeEntry(displayName, entityClass, modId));
|
||||
}
|
||||
|
||||
// Sort by mod ID, then by display name
|
||||
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.dynamiclights.config;
|
||||
|
||||
import me.jellysquid.mods.sodium.client.gui.options.storage.OptionStorage;
|
||||
|
||||
public class EntityLightConfigStorage implements OptionStorage<EntityLightConfigStorage> {
|
||||
|
||||
public static final EntityLightConfigStorage INSTANCE = new EntityLightConfigStorage();
|
||||
|
||||
private EntityLightConfigStorage() {}
|
||||
|
||||
@Override
|
||||
public EntityLightConfigStorage getData() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save() {
|
||||
EntityLightConfig.save();
|
||||
}
|
||||
}
|
||||
+20
-15
@@ -1,42 +1,47 @@
|
||||
package com.gtnewhorizons.angelica.dynamiclights.config;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class EntityTypeEntry {
|
||||
private final String displayName;
|
||||
private final String className;
|
||||
private final String modId;
|
||||
private boolean enabled;
|
||||
public final class EntityTypeEntry {
|
||||
|
||||
public EntityTypeEntry(@NotNull String displayName, @NotNull String className, @NotNull String modId, boolean enabled) {
|
||||
private final String displayName;
|
||||
private final Class<?> entityClass;
|
||||
private final String modId;
|
||||
private final String searchName;
|
||||
private final String searchModId;
|
||||
|
||||
public EntityTypeEntry(@NotNull String displayName, @NotNull Class<?> entityClass, @NotNull String modId) {
|
||||
this.displayName = displayName;
|
||||
this.className = className;
|
||||
this.entityClass = entityClass;
|
||||
this.modId = modId;
|
||||
this.enabled = enabled;
|
||||
this.searchName = displayName.toLowerCase(Locale.ROOT);
|
||||
this.searchModId = modId.toLowerCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
public String getClassName() {
|
||||
return className;
|
||||
public Class<?> getEntityClass() {
|
||||
return entityClass;
|
||||
}
|
||||
|
||||
public String getModId() {
|
||||
return modId;
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
public String getSearchName() {
|
||||
return searchName;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
public String getSearchModId() {
|
||||
return searchModId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return displayName + " (" + modId + ") [" + (enabled ? "ON" : "OFF") + "]";
|
||||
return displayName + " (" + modId + ")";
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,7 @@
|
||||
🌐 **Languages:** [English](LICENSE-HUDCaching.md) | [Українська](LICENSE-HUDCaching_uk.md)
|
||||
|
||||
---
|
||||
|
||||
HUDCaching was backported from https://github.com/Sk1erLLC/Patcher under the following license:
|
||||
|
||||
# Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International
|
||||
|
||||
@@ -0,0 +1,179 @@
|
||||
🌐 **Мови:** [English](LICENSE-HUDCaching.md) | [Українська](LICENSE-HUDCaching_uk.md)
|
||||
|
||||
---
|
||||
|
||||
HUDCaching було бекпортовано з https://github.com/Sk1erLLC/Patcher на умовах наступної ліцензії:
|
||||
|
||||
# Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International
|
||||
|
||||
Корпорація Creative Commons («Creative Commons») не є юридичною фірмою і не надає юридичних послуг чи юридичних консультацій. Поширення публічних ліцензій Creative Commons не створює відносин клієнт-адвокат чи будь-яких інших відносин. Creative Commons надає свої ліцензії та пов’язану інформацію на умовах «як є». Creative Commons не надає жодних гарантій щодо своїх ліцензій, будь-яких матеріалів, ліцензованих на їхніх умовах, або будь-якої пов’язаної інформації. Creative Commons знімає з себе всю відповідальність за збитки, що виникають внаслідок їх використання, в максимально можливій мірі, дозволеній законом.
|
||||
|
||||
### Використання публічних ліцензій Creative Commons
|
||||
|
||||
Публічні ліцензії Creative Commons надають стандартний набір положень та умов, які творці та інші власники прав можуть використовувати для поширення оригінальних творів авторства та інших матеріалів, що підпадають під дію авторського права та певних інших прав, зазначених у публічній ліцензії нижче. Наступні міркування мають виключно інформаційний характер, не є вичерпними і не входять до складу наших ліцензій.
|
||||
|
||||
* __Міркування для ліцензіарів:__ Наші публічні ліцензії призначені для використання тими, хто має право надавати громадськості дозвіл використовувати матеріали способами, які інакше обмежені авторським правом та певними іншими правами. Наші ліцензії є безвідкличними. Ліцензіари повинні прочитати та зрозуміти положення та умови обраної ними ліцензії перед її застосуванням. Ліцензіари також повинні забезпечити всі необхідні права перед застосуванням наших ліцензій, щоб громадськість могла повторно використовувати матеріал так, як очікується. Ліцензіари повинні чітко маркувати будь-які матеріали, на які ліцензія не поширюється. Це стосується інших матеріалів, ліцензійних матеріалів CC, або матеріалів, які використовуються на основі винятків чи обмежень авторського права. [Додаткові міркування для ліцензіарів](http://wiki.creativecommons.org/Considerations_for_licensors_and_licensees#Considerations_for_licensors).
|
||||
|
||||
* __Міркування для громадськості:__ Використовуючи одну з наших публічних ліцензій, ліцензіар надає громадськості дозвіл використовувати ліцензований матеріал на визначених положеннях та умовах. Якщо дозвіл ліцензіатора не потрібен з будь-якої причини — наприклад, через будь-який застосовний виняток або обмеження авторського права — тоді таке використання не регулюється ліцензією. Наші ліцензії надають лише дозволи в межах авторського права та певних інших прав, на надання яких ліцензіар має повноваження. Використання ліцензованого матеріалу все одно може бути обмежене з інших причин, зокрема через те, що інші особи мають авторські чи інші права на цей матеріал. Ліцензіар може висувати спеціальні вимоги, наприклад просити, щоб усі зміни були позначені або описані. Хоча це не вимагається нашими ліцензіями, вам рекомендується дотримуватися таких вимог, де це є розумним. [Додаткові міркування для громадськості](http://wiki.creativecommons.org/Considerations_for_licensors_and_licensees#Considerations_for_licensees).
|
||||
|
||||
## Публічна ліцензія Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International
|
||||
|
||||
Здійснюючи Ліцензовані Права (визначені нижче), Ви приймаєте та погоджуєтеся дотримуватися положень та умов цієї Публічної ліцензії Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International («Публічна ліцензія»). Оскільки цю Публічну ліцензію можна тлумачити як договір, Ліцензовані Права надаються Вам в обмін на Ваше прийняття цих положень та умов, а Ліцензіар надає Вам такі права в обмін на вигоди, які Ліцензіар отримує від надання Ліцензованого Матеріалу на цих умовах.
|
||||
|
||||
### Розділ 1 — Визначення.
|
||||
|
||||
a. __Адаптований матеріал__ означає матеріал, що підпадає під дію Авторського права та Подібних прав, який походить від Ліцензованого Матеріалу або створений на його основі, і в якому Ліцензований Матеріал перекладено, змінено, впорядковано, трансформаційно перероблено або іншим чином модифіковано у спосіб, що вимагає дозволу згідно з Авторським правом та Подібними правами, якими володіє Ліцензіар. Для цілей цієї Публічної ліцензії, якщо Ліцензований Матеріал є музичним твором, виконанням або звукозаписом, Адаптований матеріал створюється завжди, коли Ліцензований Матеріал синхронізовано в часі з рухомим зображенням.
|
||||
|
||||
b. __Ліцензія адаптера__ означає ліцензію, яку Ви застосовуєте до Вашого Авторського права та Подібних прав на Ваш внесок до Адаптованого матеріалу відповідно до положень та умов цієї Публічної ліцензії.
|
||||
|
||||
c. __Сумісна з BY-NC-SA ліцензія__ означає ліцензію, перелічену на сайті [creativecommons.org/compatiblelicenses](http://creativecommons.org/compatiblelicenses), яка схвалена Creative Commons як суттєвий еквівалент цієї Публічної ліцензії.
|
||||
|
||||
d. __Авторське право та подібні права__ означають авторське право та/або подібні права, тісно пов’язані з авторським правом, включаючи, без обмежень, права на виконання, трансляцію, звукозапис та Права sui generis на бази даних, незалежно від того, як ці права позначені або класифіковані. Для цілей цієї Публічної ліцензії права, зазначені в Розділі 2(b)(1)-(2), не є Авторським правом та Подібними правами.
|
||||
|
||||
e. __Ефективні технологічні заходи__ означають такі заходи, які за відсутності належних повноважень не можуть бути обійдені згідно із законодавством, що виконує зобов’язання за Статтею 11 Договору ВОІВ про авторське право, прийнятого 20 грудня 1996 року, та/або подібними міжнародними угодами.
|
||||
|
||||
f. __Винятки та обмеження__ означають добросовісне використання та/або будь-які інші винятки чи обмеження Авторського права та Подібних прав, які застосовуються до використання Вами Ліцензованого Матеріалу.
|
||||
|
||||
g. __Елементи ліцензії__ означають атрибути ліцензії, перелічені в назві Публічної ліцензії Creative Commons. Елементами ліцензії цієї Публічної ліцензії є «Із зазначенням авторства», «Некомерційна» та «Поширення на тих самих умовах».
|
||||
|
||||
h. __Ліцензований матеріал__ означає художній чи літературний твір, базу даних або інший матеріал, до якого Ліцензіар застосував цю Публічну ліцензію.
|
||||
|
||||
i. __Ліцензовані права__ означають права, надані Вам відповідно до положень та умов цієї Публічної ліцензії, які обмежуються всіма Авторськими та Подібними правами, що застосовуються до використання Вами Ліцензованого Матеріалу і на ліцензування яких Ліцензіар має повноваження.
|
||||
|
||||
h. __Ліцензіар__ означає фізичну особу (осіб) або юридичну особу (осіб), що надають права за цією Публічною ліцензією.
|
||||
|
||||
i. __Некомерційний__ означає такий, що не призначений переважно для або спрямований на комерційну вигоду чи грошову компенсацію. Для цілей цієї Публічної ліцензії обмін Ліцензованого Матеріалу на інший матеріал, що підпадає під дію Авторського права та Подібних прав, за допомогою цифрового обміну файлами або подібних засобів є некомерційним за умови відсутності виплати грошової компенсації у зв’язку з таким обміном.
|
||||
|
||||
j. __Поширювати__ означає надавати матеріал громадськості за допомогою будь-якого засобу або процесу, що вимагає дозволу згідно з Ліцензованими Правами, такого як відтворення, публічний показ, публічне виконання, дистрибуція, розповсюдження, комунікація або імпорт, а також робити матеріал доступним для громадськості, зокрема у спосіб, за якого члени громадськості можуть отримати доступ до матеріалу з місця та в час, індивідуально обрані ними.
|
||||
|
||||
k. __Права sui generis на бази даних__ означають права, інші ніж авторське право, що випливають з Директиви Європейського Парламенту та Ради 96/9/ЄС від 11 березня 1996 року про правовий захист баз даних, із поправками та/або наступними змінами, а також інші по суті еквівалентні права в будь-якій точці світу.
|
||||
|
||||
l. __Ви__ означаєте фізичну особу або юридичну особу, яка здійснює Ліцензовані Права за цією Публічною ліцензією. Займенники «Ваш», «Ваші» тощо мають відповідне значення.
|
||||
|
||||
### Розділ 2 — Сфера дії.
|
||||
|
||||
a. ___Надання ліцензії.___
|
||||
|
||||
1. Відповідно до положень та умов цієї Публічної ліцензії, Ліцензіар цим надає Вам всесвітню, безоплатну, не субліцензовану, невиключну, безвідкличну ліцензію на здійснення Ліцензованих Прав щодо Ліцензованого Матеріалу з метою:
|
||||
|
||||
A. відтворення та Поширення Ліцензованого Матеріалу повністю або частково виключно в некомерційних цілях; та
|
||||
|
||||
B. створення, відтворення та Поширення Адаптованого матеріалу виключно в некомерційних цілях.
|
||||
|
||||
2. __Винятки та обмеження.__ З метою уникнення непорозумінь, якщо до Вашого використання застосовуються Винятки та обмеження, ця Публічна ліцензія не застосовується, і Ви не зобов’язані дотримуватися її положень та умов.
|
||||
|
||||
3. __Строк дії.__ Строк дії цієї Публічної ліцензії визначено в Розділі 6(a).
|
||||
|
||||
4. __Носії та формати; дозволені технічні модифікації.__ Ліцензіар уповноважує Вас здійснювати Ліцензовані Права на будь-яких носіях та у будь-форматах, незалежно від того, чи відомі вони зараз, чи будуть створені надалі, а також здійснювати технічні модифікації, необхідні для цього. Ліцензіар відмовляється від будь-яких прав чи повноважень забороняти Вам вносити технічні модифікації, необхідні для здійснення Ліцензованих Прав, включаючи технічні модифікації, необхідні для обходу Ефективних технологічних заходів, та/або погоджується не заявляти про такі права. Для цілей цієї Публічної ліцензії просте внесення модифікацій, дозволених цим Розділом 2(a)(4), ніколи не створює Адаптований матеріал.
|
||||
|
||||
5. __Подальші отримувачі.__
|
||||
|
||||
A. __Пропозиція від Ліцензіара — Ліцензований матеріал.__ Кожен отримувач Ліцензованого Матеріалу автоматично отримує від Ліцензіара пропозицію здійснювати Ліцензовані Права на умовах цієї Публічної ліцензії.
|
||||
|
||||
B. __Додаткова пропозиція від Ліцензіара — Адаптований матеріал.__ Кожен отримувач Адаптованого матеріалу від Вас автоматично отримує від Ліцензіара пропозицію здійснювати Ліцензовані Права на Адаптований матеріал на умовах Ліцензії адаптера, яку Ви застосовуєте.
|
||||
|
||||
C. __Жодних обмежень для наступних отримувачів.__ Ви не можете пропонувати або накладати будь-які додаткові чи інші положення чи умови на Ліцензований матеріал, а також застосовувати до нього Ефективні технологічні заходи, якщо це обмежує здійснення Ліцензованих Прав будь-яким отримувачем Ліцензованого Матеріалу.
|
||||
|
||||
6. __Відсутність схвалення.__ Жодні положення цієї Публічної ліцензії не становлять і не можуть тлумачитися як дозвіл стверджувати чи мати на увазі, що Ви або використання Вами Ліцензованого Матеріалу пов’язані з Ліцензіаром чи іншими особами, призначеними для отримання згадки про авторство згідно з Розділом 3(a)(1)(A)(i), або що вони спонсорують, схвалюють чи надають офіційний статус цьому.
|
||||
|
||||
b. ___Інші права.___
|
||||
|
||||
1. Особисті немайнові права, такі як право на недоторканність твору, не ліцензуються за цією Публічною ліцензією, як і права на публічність, приватність та/або інші подібні особисті права; однак, у міру можливості, Ліцензіар відмовляється від будь-яких таких прав, що належать Ліцензіару, та/або погоджується не заявляти про них у мінімально необхідному обсязі, щоб дозволити Вам здійснювати Ліцензовані Права, але не інакше.
|
||||
|
||||
2. Права на патенти та торговельні марки не ліцензуються за цією Публічною ліцензією.
|
||||
|
||||
3. У міру можливості Ліцензіар відмовляється від будь-якого права збирати з Вас роялті за здійснення Ліцензованих Прав, безпосередньо чи через організацію колективного управління згідно з будь-якою добровільною або відмовною законодавчою чи обов’язковою ліцензійною схемою. У всіх інших випадках Ліцензіар прямо зберігає за собою будь-яке право збирати такі роялті, зокрема коли Ліцензований матеріал використовується в інших цілях, ніж некомерційні.
|
||||
|
||||
### Розділ 3 — Умови ліцензії.
|
||||
|
||||
Здійснення Вами Ліцензованих Прав прямо підпорядковане наступним умовам.
|
||||
|
||||
a. ___Зазначення авторства.___
|
||||
|
||||
1. Якщо Ви поширюєте Ліцензований матеріал (зокрема у зміненому вигляді), Ви повинні:
|
||||
|
||||
A. зберегти наступне, якщо воно надається Ліцензіаром разом із Ліцензованим матеріалом:
|
||||
|
||||
i. ідентифікацію автора(-ів) Ліцензованого Матеріалу та будь-яких інших осіб, призначених для отримання згадки про авторство, у будь-який розумний спосіб, вимаганий Ліцензіаром (зокрема під псевдонімом, якщо він призначений);
|
||||
|
||||
ii. повідомлення про авторське право;
|
||||
|
||||
iii. повідомлення, що посилається на цю Публічну ліцензію;
|
||||
|
||||
iv. повідомлення, що посилається на відмову від гарантій;
|
||||
|
||||
v. URI або гіперпосилання на Ліцензований матеріал, наскільки це є розумно можливим і практичним;
|
||||
|
||||
B. вказати, якщо Ви змінили Ліцензований матеріал, та зберегти вказівку на будь-які попередні модифікації; та
|
||||
|
||||
C. вказати, що Ліцензований матеріал ліцензовано за цією Публічною ліцензією, а також додати текст або URI/гіперпосилання на цю Публічну ліцензію.
|
||||
|
||||
2. Ви можете виконати умови Розділу 3(a)(1) у будь-який розумний спосіб залежно від носія, засобів та контексту, в якому Ви поширюєте Ліцензований матеріал. Наприклад, може бути розумним виконати умови шляхом надання URI або гіперпосилання на ресурс, що містить необхідну інформацію.
|
||||
|
||||
3. На вимогу Ліцензіара Ви повинні видалити будь-яку інформацію, що вимагається згідно з Розділом 3(a)(1)(A), наскільки це є розумо можливим.
|
||||
|
||||
b. ___Поширення на тих самих умовах.___
|
||||
|
||||
На додаток до умов Розділу 3(a), якщо Ви поширюєте створений Вами Адаптований матеріал, також застосовуються такі умови.
|
||||
|
||||
1. Ліцензія адаптера, яку Ви застосовуєте, має бути ліцензією Creative Commons із тими ж Елементами ліцензії, цієї ж або пізнішої версії, або сумісною з BY-NC-SA ліцензією.
|
||||
|
||||
2. Ви повинні включити текст або URI/гіперпосилання на Ліцензію адаптера, яку Ви застосовуєте. Ви можете виконати цю умову у будь-який розумний спосіб залежно від носія, засобів та контексту, в якому Ви поширюєте Адаптований матеріал.
|
||||
|
||||
3. Ви не можете пропонувати або накладати будь-які додаткові чи інші положення чи умови на Адаптований матеріал, а також застосовувати до нього Ефективні технологічні заходи, які обмежують здійснення прав, наданих за Ліцензією адаптера, яку Ви застосовуєте.
|
||||
|
||||
### Розділ 4 — Права sui generis на бази даних.
|
||||
|
||||
Якщо Ліцензовані Права включають Права sui generis на бази даних, які застосовуються до використання Вами Ліцензованого Матеріалу:
|
||||
|
||||
a. з метою уникнення непорозумінь, Розділ 2(a)(1) надає Вам право витягувати, повторно використовувати, відтворювати та поширювати всю або суттєву частину вмісту бази даних виключно в некомерційних цілях;
|
||||
|
||||
b. якщо Ви включаєте всю або суттєву частину вмісту бази даних у базу даних, на яку Ви маєте Права sui generis на бази даних, тоді база даних, на яку Ви маєте ці права (але не її окремий вміст), є Адаптованим матеріалом, зокрема для цілей Розділу 3(b); і
|
||||
|
||||
c. Ви повинні дотримуватися умов Розділу 3(a), якщо Ви поширюєте всю або суттєву частину вмісту бази даних.
|
||||
|
||||
Для уникнення непорозумінь, цей Розділ 4 доповнює, а не замінює Ваші зобов’язання за цією Публічною ліцензією, коли Ліцензовані Права включають інші Авторські та Подібні права.
|
||||
|
||||
### Розділ 5 — Відмова від гарантій та обмеження відповідальності.
|
||||
|
||||
a. __Якщо Ліцензіар окремо не взяв на себе інших зобов’язань, у міру можливості Ліцензіар надає Ліцензований Матеріал на умовах «як є» та «як доступно» і не дає жодних обіцянок чи гарантій будь-якого роду щодо Ліцензованого Матеріалу, будь то прямих, непрямих, передбачених законом чи інших. Це включає, без обмежень, гарантії права власності, придатності для продажу, придатності для конкретної мети, непорушення прав третіх осіб, відсутності прихованих чи інших дефектів, точності, а також наявності чи відсутності помилок, незалежно від того, чи є вони відомими або чи можливо їх виявити. Якщо відмова від гарантій не допускається повністю або частково, ця відмова може не поширюватися на Вас.__
|
||||
|
||||
b. __У міру можливості за жодних обставин Ліцензіар не несе відповідальності перед Вами за жодною правовою теорією (включаючи, без обмежень, недбалість) або іншим чином за будь-які прямі, спеціальні, непрямі, побічні, опосередковані, штрафні, каральні чи інші втрати, витрати, видатки або збитки, що виникають внаслідок цієї Публічної ліцензії чи використання Ліцензованого Матеріалу, навіть якщо Ліцензіар був попереджений про можливість таких втрат, витрат, видатків чи збитків. Якщо обмеження відповідальності не допускається повністю або частково, це обмеження може не поширюватися на Вас.__
|
||||
|
||||
c. Зазначені вище відмова від гарантій та обмеження відповідальності повинні тлумачитися у спосіб, який, наскільки це можливо, найбільше наближається до абсолютної відмови та звільнення від будь-якої відповідальності.
|
||||
|
||||
### Розділ 6 — Строк дії та припинення.
|
||||
|
||||
a. Ця Публічна ліцензія діє протягом строку дії Авторського права та Подібних прав, що ліцензуються тут. Однак, якщо Ви не дотримуєтеся цієї Публічної ліцензії, Ваші права за цією Публічною ліцензією автоматично припиняються.
|
||||
|
||||
b. Якщо Ваше право на використання Ліцензованого Матеріалу припинено згідно з Розділом 6(a), воно поновлюється:
|
||||
|
||||
1. автоматично з дати усунення порушення, за умови, що воно усунено протягом 30 днів з моменту виявлення Вами порушення; або
|
||||
|
||||
2. у разі прямого поновлення Ліцензіаром.
|
||||
|
||||
Для уникнення непорозумінь, цей Розділ 6(b) не впливає на жодні права, які може мати Ліцензіар щодо пошуку засобів правового захисту у зв’язку з порушеннями Вами цієї Публічної ліцензії.
|
||||
|
||||
c. Для уникнення непорозумінь, Ліцензіар також може пропонувати Ліцензований матеріал на окремих положеннях чи умовах або припинити розповсюдження Ліцензованого Матеріалу в будь-який час; однак це не призведе до припинення дії цієї Публічної ліцензії.
|
||||
|
||||
d. Розділи 1, 5, 6, 7 та 8 залишаються чинними після припинення дії цієї Публічної ліцензії.
|
||||
|
||||
### Розділ 7 — Інші положення та умови.
|
||||
|
||||
a. Ліцензіар не зв’язаний жодними додатковими чи іншими положеннями чи умовами, повідомленими Вами, якщо на це немає прямої згоди.
|
||||
|
||||
b. Будь-які домовленості, розуміння або угоди щодо Ліцензованого Матеріалу, не зазначені тут, є окремими та незалежними від положень та умов цієї Публічної ліцензії.
|
||||
|
||||
### Розділ 8 — Тлумачення.
|
||||
|
||||
a. З метою уникнення непорозумінь, ця Публічна ліцензія не зменшує, не обмежує та не накладає умов на будь-яке використання Ліцензованого Матеріалу, яке може законно здійснюватися без дозволу за цією Публічною ліцензією, і не повинна так тлумачитися.
|
||||
|
||||
b. Наскільки це можливо, якщо будь-яке положення цієї Публічної ліцензії буде визнано таким, що не має законної сили, воно повинно бути автоматично змінено в мінімально необхідному обсязі, щоб зробити його здійсненним. Якщо положення не може бути змінено, воно підлягає відокремленню від цієї Публічної ліцензії без впливу на примусове виконання решти положень та умов.
|
||||
|
||||
c. Жодне положення чи умова цієї Публічної ліцензії не вважатиметься скасованим, і жодна згода на недотримання не надаватиметься, якщо на те немає прямої згоди Ліцензіара.
|
||||
|
||||
d. Жодне положення цієї Публічної ліцензії не становить і не може тлумачитися як обмеження чи відмова від будь-яких привілеїв та імунітетів, які застосовуються до Ліцензіара чи Вас, включаючи імунітет від судових процесів будь-якої юрисдикції чи органу влади.
|
||||
|
||||
> Creative Commons не є стороною своїх публічних ліцензій. Незважаючи на це, Creative Commons може на власний розсуд застосувати одну зі своїх публічних ліцензій до матеріалів, які вона публікує, і в таких випадках вважатиметься «Ліцензіаром». За винятком обмеженої мети вказівки на те, що матеріал поширюється за публічною ліцензією Creative Commons, або як це іншим чином дозволено політикою Creative Commons, опублікованою на [creativecommons.org/policies](http://creativecommons.org/policies), Creative Commons не дає дозволу на використання товарного знака «Creative Commons» або будь-якого іншого товарного знака чи логотипу Creative Commons без її попередньої письмової згоди, зокрема у зв’язку з будь-якими несанкціонованими модифікаціями будь-яких її публічних ліцензій чи будь-якими іншими домовленостями, угодами чи домовленостями щодо використання ліцензованого матеріалу. Для уникнення непорозумінь, цей абзац не є частиною публічних ліцензій.
|
||||
>
|
||||
> Зв’язатися з Creative Commons можна на сайті creativecommons.org
|
||||
@@ -0,0 +1,315 @@
|
||||
package com.gtnewhorizons.angelica.iris;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.FfpExtendedAttribs;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.ImmediateExtendedAttribHandler;
|
||||
import com.gtnewhorizons.angelica.rendering.items.ItemRenderListManager;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.TesrBatchRenderer;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
|
||||
import net.coderbot.iris.Iris;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.texture.ITextureObject;
|
||||
import net.minecraft.client.renderer.texture.TextureMap;
|
||||
import net.coderbot.iris.vertices.IrisQuadView;
|
||||
import net.coderbot.iris.vertices.NormI8;
|
||||
import net.coderbot.iris.vertices.NormalHelper;
|
||||
import org.joml.Vector3f;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memGetFloat;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memGetInt;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memPutFloat;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memPutInt;
|
||||
|
||||
/**
|
||||
* Supplies per-quad {@code mc_midTexCoord} + {@code at_tangent} for immediate-mode draws so parallax/POM shader packs
|
||||
* reconstruct sprite bounds correctly.
|
||||
*/
|
||||
public final class ImmediateExtendedAttribs implements ImmediateExtendedAttribHandler {
|
||||
|
||||
private static final int VERTEX_SIZE = 8;
|
||||
private static final int X = 0, Y = 1, Z = 2, U = 3, V = 4;
|
||||
|
||||
private static final float UV_EPS = 1.0e-5f;
|
||||
private static final float POM_SUPPRESS_EPS = 1.0e-4f;
|
||||
|
||||
private static final float POM_MAX_TILE_EXTENT = 0.125f;
|
||||
|
||||
private static final int WANTS_MID = 1;
|
||||
private static final int WANTS_TANGENT = 2;
|
||||
|
||||
private static final int NEUTRAL_TANGENT = NormI8.pack(1.0f, 0.0f, 0.0f, 1.0f);
|
||||
|
||||
private static volatile int currentProgramWants = 0;
|
||||
|
||||
private static final Int2IntOpenHashMap programWantsExtCache = new Int2IntOpenHashMap();
|
||||
|
||||
public static void onProgramBound(int program) {
|
||||
if (program == 0) {
|
||||
currentProgramWants = 0;
|
||||
return;
|
||||
}
|
||||
int wants;
|
||||
if (programWantsExtCache.containsKey(program)) {
|
||||
wants = programWantsExtCache.get(program);
|
||||
} else {
|
||||
wants = 0;
|
||||
if (GLStateManager.glGetAttribLocation(program, "mc_midTexCoord") >= 0) wants |= WANTS_MID;
|
||||
if (GLStateManager.glGetAttribLocation(program, "at_tangent") >= 0) wants |= WANTS_TANGENT;
|
||||
programWantsExtCache.put(program, wants);
|
||||
}
|
||||
currentProgramWants = wants;
|
||||
}
|
||||
|
||||
public static void onProgramDeleted(int program) {
|
||||
programWantsExtCache.remove(program);
|
||||
}
|
||||
|
||||
public static void onShaderPackChanged() {
|
||||
currentProgramWants = 0;
|
||||
programWantsExtCache.clear();
|
||||
ItemRenderListManager.clearCache();
|
||||
FfpExtendedAttribs.reset();
|
||||
TesrBatchRenderer.INSTANCE.clearRetained();
|
||||
}
|
||||
|
||||
private final Vector3f normal = new Vector3f();
|
||||
private final QuadView quad = new QuadView();
|
||||
private final PackedQuadView packedQuad = new PackedQuadView();
|
||||
|
||||
private boolean limitTileExtent = true;
|
||||
private boolean needTangent = true;
|
||||
|
||||
@Override
|
||||
public boolean wantsExtended() {
|
||||
return Iris.enabled && currentProgramWants != 0 && GLStateManager.getActiveProgram() != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean wantsTangent() {
|
||||
return (currentProgramWants & WANTS_TANGENT) != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean wantsExtendedCapture() {
|
||||
return Iris.enabled && Iris.getCurrentPack().isPresent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void build(int[] rawBuffer, int vertexCount, int vertsPerPrim, int normalIntIndex, long dstAddr, int dstStride) {
|
||||
final boolean smooth = normalIntIndex >= 0;
|
||||
limitTileExtent = isAtlasNotBound();
|
||||
needTangent = wantsTangent();
|
||||
long ptr = dstAddr;
|
||||
for (int v = 0; v < vertexCount; v += vertsPerPrim) {
|
||||
quad.setup(rawBuffer, v, normalIntIndex);
|
||||
if (vertsPerPrim == 4) {
|
||||
writeQuadExt(quad, ptr, dstStride);
|
||||
} else {
|
||||
writeTriExt(quad, smooth, ptr, dstStride);
|
||||
}
|
||||
ptr += (long) dstStride * vertsPerPrim;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildPacked(long srcBase, int stride, int posOffset, int texOffset, int normalOffset,
|
||||
int vertexCount, int vertsPerPrim, long dstAddr, int dstStride) {
|
||||
final boolean smooth = normalOffset >= 0;
|
||||
limitTileExtent = isAtlasNotBound();
|
||||
needTangent = wantsTangent();
|
||||
long ptr = dstAddr;
|
||||
for (int v = 0; v < vertexCount; v += vertsPerPrim) {
|
||||
packedQuad.setup(srcBase + (long) v * stride, stride, posOffset, texOffset, normalOffset);
|
||||
if (vertsPerPrim == 4) {
|
||||
writeQuadExt(packedQuad, ptr, dstStride);
|
||||
} else {
|
||||
writeTriExt(packedQuad, smooth, ptr, dstStride);
|
||||
}
|
||||
ptr += (long) dstStride * vertsPerPrim;
|
||||
}
|
||||
}
|
||||
|
||||
private void writeQuadExt(IrisQuadView quad, long dstAddr, int dstStride) {
|
||||
final float u0 = quad.u(0), u1 = quad.u(1), u2 = quad.u(2), u3 = quad.u(3);
|
||||
final float v0 = quad.v(0), v1 = quad.v(1), v2 = quad.v(2), v3 = quad.v(3);
|
||||
|
||||
final float umin = min4(u0, u1, u2, u3), umax = max4(u0, u1, u2, u3);
|
||||
final float vmin = min4(v0, v1, v2, v3), vmax = max4(v0, v1, v2, v3);
|
||||
|
||||
final boolean tileable = formsRectAxisQuad(u0, u1, u2, u3) && formsRectAxisQuad(v0, v1, v2, v3)
|
||||
&& (!limitTileExtent || ((umax - umin) <= POM_MAX_TILE_EXTENT && (vmax - vmin) <= POM_MAX_TILE_EXTENT));
|
||||
|
||||
final int tangent;
|
||||
if (needTangent) {
|
||||
NormalHelper.computeFaceNormal(normal, quad);
|
||||
tangent = NormalHelper.computeTangent(normal.x, normal.y, normal.z, quad);
|
||||
} else {
|
||||
tangent = NEUTRAL_TANGENT;
|
||||
}
|
||||
|
||||
long ptr = dstAddr;
|
||||
if (!tileable) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
memPutFloat(ptr, quad.u(i) + POM_SUPPRESS_EPS);
|
||||
memPutFloat(ptr + 4, quad.v(i) + POM_SUPPRESS_EPS);
|
||||
memPutInt(ptr + 8, tangent);
|
||||
ptr += dstStride;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
final float midU = (umin + umax) * 0.5f;
|
||||
final float midV = (vmin + vmax) * 0.5f;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
memPutFloat(ptr, midU);
|
||||
memPutFloat(ptr + 4, midV);
|
||||
memPutInt(ptr + 8, tangent);
|
||||
ptr += dstStride;
|
||||
}
|
||||
}
|
||||
|
||||
private void writeTriExt(NormalSource view, boolean smooth, long dstAddr, int dstStride) {
|
||||
final float u0 = view.u(0), u1 = view.u(1), u2 = view.u(2);
|
||||
final float v0 = view.v(0), v1 = view.v(1), v2 = view.v(2);
|
||||
|
||||
final float umin = Math.min(u0, Math.min(u1, u2)), umax = Math.max(u0, Math.max(u1, u2));
|
||||
final float vmin = Math.min(v0, Math.min(v1, v2)), vmax = Math.max(v0, Math.max(v1, v2));
|
||||
|
||||
final boolean tileable = formsRectAxis(u0, u1, u2) && formsRectAxis(v0, v1, v2)
|
||||
&& (!limitTileExtent || ((umax - umin) <= POM_MAX_TILE_EXTENT && (vmax - vmin) <= POM_MAX_TILE_EXTENT));
|
||||
if (!tileable) {
|
||||
writeTriSuppressed(view, dstAddr, dstStride);
|
||||
return;
|
||||
}
|
||||
|
||||
final float midU = (umin + umax) * 0.5f;
|
||||
final float midV = (vmin + vmax) * 0.5f;
|
||||
|
||||
long ptr = dstAddr;
|
||||
if (smooth) {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
final int n = view.packedNormal(i);
|
||||
final int tangent = needTangent
|
||||
? NormalHelper.computeTangentSmooth(NormI8.unpackX(n), NormI8.unpackY(n), NormI8.unpackZ(n), view)
|
||||
: NEUTRAL_TANGENT;
|
||||
memPutFloat(ptr, midU);
|
||||
memPutFloat(ptr + 4, midV);
|
||||
memPutInt(ptr + 8, tangent);
|
||||
ptr += dstStride;
|
||||
}
|
||||
} else {
|
||||
final int tangent;
|
||||
if (needTangent) {
|
||||
NormalHelper.computeFaceNormalTri(normal, view);
|
||||
tangent = NormalHelper.computeTangent(normal.x, normal.y, normal.z, view);
|
||||
} else {
|
||||
tangent = NEUTRAL_TANGENT;
|
||||
}
|
||||
for (int i = 0; i < 3; i++) {
|
||||
memPutFloat(ptr, midU);
|
||||
memPutFloat(ptr + 4, midV);
|
||||
memPutInt(ptr + 8, tangent);
|
||||
ptr += dstStride;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void writeTriSuppressed(NormalSource view, long dstAddr, int dstStride) {
|
||||
final int tangent;
|
||||
if (needTangent) {
|
||||
NormalHelper.computeFaceNormalTri(normal, view);
|
||||
tangent = NormalHelper.computeTangent(normal.x, normal.y, normal.z, view);
|
||||
} else {
|
||||
tangent = NEUTRAL_TANGENT;
|
||||
}
|
||||
long ptr = dstAddr;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
memPutFloat(ptr, view.u(i) + POM_SUPPRESS_EPS);
|
||||
memPutFloat(ptr + 4, view.v(i) + POM_SUPPRESS_EPS);
|
||||
memPutInt(ptr + 8, tangent);
|
||||
ptr += dstStride;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean formsRectAxis(float a, float b, float c) {
|
||||
final boolean ab = Math.abs(a - b) <= UV_EPS;
|
||||
final boolean ac = Math.abs(a - c) <= UV_EPS;
|
||||
final boolean bc = Math.abs(b - c) <= UV_EPS;
|
||||
return (ab || ac || bc) && !(ab && ac && bc);
|
||||
}
|
||||
|
||||
private static boolean formsRectAxisQuad(float a, float b, float c, float d) {
|
||||
final float lo = min4(a, b, c, d), hi = max4(a, b, c, d);
|
||||
if (hi - lo <= UV_EPS) return false;
|
||||
return atEdge(a, lo, hi) && atEdge(b, lo, hi) && atEdge(c, lo, hi) && atEdge(d, lo, hi);
|
||||
}
|
||||
|
||||
private static boolean atEdge(float x, float lo, float hi) {
|
||||
return Math.abs(x - lo) <= UV_EPS || Math.abs(x - hi) <= UV_EPS;
|
||||
}
|
||||
|
||||
private static boolean isAtlasNotBound() {
|
||||
final int tex = GLStateManager.getBoundTextureForServerState(0);
|
||||
if (tex <= 0) return true;
|
||||
final Minecraft mc = Minecraft.getMinecraft();
|
||||
if (mc == null || mc.getTextureManager() == null) return true;
|
||||
final ITextureObject blocks = mc.getTextureManager().getTexture(TextureMap.locationBlocksTexture);
|
||||
if (blocks != null && blocks.getGlTextureId() == tex) return false;
|
||||
final ITextureObject items = mc.getTextureManager().getTexture(TextureMap.locationItemsTexture);
|
||||
return items == null || items.getGlTextureId() != tex;
|
||||
}
|
||||
|
||||
private static float min4(float a, float b, float c, float d) {
|
||||
return Math.min(Math.min(a, b), Math.min(c, d));
|
||||
}
|
||||
|
||||
private static float max4(float a, float b, float c, float d) {
|
||||
return Math.max(Math.max(a, b), Math.max(c, d));
|
||||
}
|
||||
|
||||
private interface NormalSource extends IrisQuadView {
|
||||
int packedNormal(int i);
|
||||
}
|
||||
|
||||
private static final class QuadView implements NormalSource {
|
||||
private int[] raw;
|
||||
private int base;
|
||||
private int normalIndex;
|
||||
|
||||
void setup(int[] raw, int firstVertex, int normalIndex) {
|
||||
this.raw = raw;
|
||||
this.base = firstVertex;
|
||||
this.normalIndex = normalIndex;
|
||||
}
|
||||
|
||||
@Override public float x(int i) { return Float.intBitsToFloat(raw[(base + i) * VERTEX_SIZE + X]); }
|
||||
@Override public float y(int i) { return Float.intBitsToFloat(raw[(base + i) * VERTEX_SIZE + Y]); }
|
||||
@Override public float z(int i) { return Float.intBitsToFloat(raw[(base + i) * VERTEX_SIZE + Z]); }
|
||||
@Override public float u(int i) { return Float.intBitsToFloat(raw[(base + i) * VERTEX_SIZE + U]); }
|
||||
@Override public float v(int i) { return Float.intBitsToFloat(raw[(base + i) * VERTEX_SIZE + V]); }
|
||||
@Override public int packedNormal(int i) { return raw[(base + i) * VERTEX_SIZE + normalIndex]; }
|
||||
}
|
||||
|
||||
private static final class PackedQuadView implements NormalSource {
|
||||
private long base;
|
||||
private int stride;
|
||||
private int posOffset;
|
||||
private int texOffset;
|
||||
private int normalOffset;
|
||||
|
||||
void setup(long base, int stride, int posOffset, int texOffset, int normalOffset) {
|
||||
this.base = base;
|
||||
this.stride = stride;
|
||||
this.posOffset = posOffset;
|
||||
this.texOffset = texOffset;
|
||||
this.normalOffset = normalOffset;
|
||||
}
|
||||
|
||||
@Override public float x(int i) { return memGetFloat(base + (long) i * stride + posOffset); }
|
||||
@Override public float y(int i) { return memGetFloat(base + (long) i * stride + posOffset + 4); }
|
||||
@Override public float z(int i) { return memGetFloat(base + (long) i * stride + posOffset + 8); }
|
||||
@Override public float u(int i) { return memGetFloat(base + (long) i * stride + texOffset); }
|
||||
@Override public float v(int i) { return memGetFloat(base + (long) i * stride + texOffset + 4); }
|
||||
@Override public int packedNormal(int i) { return memGetInt(base + (long) i * stride + normalOffset); }
|
||||
}
|
||||
}
|
||||
@@ -29,6 +29,7 @@ import net.coderbot.iris.samplers.IrisSamplers;
|
||||
import net.coderbot.iris.texture.pbr.PBRTextureManager;
|
||||
import net.coderbot.iris.uniforms.SystemTimeUniforms;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
public class IrisGLSMBridge {
|
||||
|
||||
@@ -67,6 +68,10 @@ public class IrisGLSMBridge {
|
||||
StateUpdateNotifiers.colorModulatorNotifier = listener -> colorModulatorListener = listener;
|
||||
}
|
||||
|
||||
public static void installImmediateExtendedHandler() {
|
||||
GLSMHooks.immediateExtendedHandler = new ImmediateExtendedAttribs();
|
||||
}
|
||||
|
||||
public static void installPostTransformHook() {
|
||||
if (GLSMHooks.postTransformProcessor != null) return;
|
||||
GLSMHooks.postTransformProcessor = new ShaderTransformPostProcessor() {
|
||||
@@ -253,6 +258,7 @@ public class IrisGLSMBridge {
|
||||
|
||||
GLSMHooks.TEXTURE_UNIT_STATE.addListener(event -> {
|
||||
if (!Iris.enabled) return;
|
||||
if (event.cap != GL11.GL_TEXTURE_2D) return;
|
||||
boolean updatePipeline = false;
|
||||
if (event.unit == IrisSamplers.ALBEDO_TEXTURE_UNIT) {
|
||||
StateTracker.INSTANCE.albedoSampler = event.enabled;
|
||||
@@ -298,6 +304,14 @@ public class IrisGLSMBridge {
|
||||
}
|
||||
});
|
||||
|
||||
GLSMHooks.PROGRAM_CHANGE.addListener(event -> {
|
||||
if (Iris.enabled && event.postBind) {
|
||||
ImmediateExtendedAttribs.onProgramBound(event.newProgram);
|
||||
}
|
||||
});
|
||||
|
||||
GLSMHooks.PROGRAM_DELETE.addListener(event -> ImmediateExtendedAttribs.onProgramDeleted(event.program));
|
||||
|
||||
GLSMHooks.PROGRAM_CHANGE.addListener(event -> {
|
||||
if (!Iris.enabled) return;
|
||||
if (event.postBind) return;
|
||||
|
||||
+2
-1
@@ -7,6 +7,7 @@ import com.gtnewhorizons.angelica.loading.shared.transformers.TileEntityMarkerTr
|
||||
import net.minecraft.launchwrapper.IClassTransformer;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
|
||||
/** IClassTransformer wrapper for {@link CeleritasBlockTransform} */
|
||||
@@ -18,7 +19,7 @@ public class CeleritasBlockTransformer implements IClassTransformer {
|
||||
|
||||
public CeleritasBlockTransformer() {
|
||||
this.inner = new CeleritasBlockTransform(AngelicaClientTweaker.isObfEnv());
|
||||
this.tileEntities = new TileEntityMarkerTransform(AngelicaClientTweaker.isObfEnv());
|
||||
this.tileEntities = new TileEntityMarkerTransform(AngelicaClientTweaker.isObfEnv(), Opcodes.ASM5);
|
||||
this.exclusions = inner.getTransformerExclusions();
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -6,6 +6,7 @@ import com.gtnewhorizons.angelica.loading.shared.transformers.TileEntityMarkerTr
|
||||
import com.gtnewhorizons.retrofuturabootstrap.api.ClassHeaderMetadata;
|
||||
import com.gtnewhorizons.retrofuturabootstrap.api.ClassNodeHandle;
|
||||
import com.gtnewhorizons.retrofuturabootstrap.api.ExtensibleClassLoader;
|
||||
import com.gtnewhorizons.retrofuturabootstrap.api.RetroFuturaBootstrap;
|
||||
import com.gtnewhorizons.retrofuturabootstrap.api.RfbClassTransformer;
|
||||
import org.intellij.lang.annotations.Pattern;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -21,7 +22,7 @@ public class RFBCeleritasBlockTransformer implements RfbClassTransformer {
|
||||
|
||||
public RFBCeleritasBlockTransformer(boolean isObf) {
|
||||
inner = new CeleritasBlockTransform(isObf);
|
||||
tileEntities = new TileEntityMarkerTransform(isObf);
|
||||
tileEntities = new TileEntityMarkerTransform(isObf, RetroFuturaBootstrap.API.newestAsmVersion());
|
||||
}
|
||||
|
||||
@Pattern("[a-z0-9-]+")
|
||||
|
||||
+10
-3
@@ -32,11 +32,18 @@ public final class TileEntityMarkerTransform {
|
||||
|
||||
private record Marker(int bit, String method, String desc, String iface) {}
|
||||
|
||||
/**
|
||||
* A visitor refuses to visit attributes newer than its api level, such as the NestMember one javac emits for
|
||||
* classes with nested classes, so the caller passes the highest level its class loader supports.
|
||||
*/
|
||||
private final int asmApi;
|
||||
|
||||
private final Marker[] markers;
|
||||
private final ClassConstantPoolParser prefilter;
|
||||
private final Set<String> tileEntities = Collections.newSetFromMap(new ConcurrentHashMap<>());
|
||||
|
||||
public TileEntityMarkerTransform(boolean isObf) {
|
||||
public TileEntityMarkerTransform(boolean isObf, int asmApi) {
|
||||
this.asmApi = asmApi;
|
||||
this.markers = new Marker[] {
|
||||
new Marker(MARK_DESCRIPTION_PACKET, isObf ? "func_145844_m" : "getDescriptionPacket", "()Lnet/minecraft/network/Packet;", SENDS_DESCRIPTION_PACKET),
|
||||
new Marker(MARK_SHOULD_RENDER_IN_PASS, "shouldRenderInPass", "(I)Z", OVERRIDES_SHOULD_RENDER_IN_PASS),
|
||||
@@ -67,7 +74,7 @@ public final class TileEntityMarkerTransform {
|
||||
public byte[] addMarkers(byte[] classBytes, int markers) {
|
||||
final ClassReader cr = new ClassReader(classBytes);
|
||||
final ClassWriter cw = new ClassWriter(cr, 0);
|
||||
cr.accept(new ClassVisitor(Opcodes.ASM5, cw) {
|
||||
cr.accept(new ClassVisitor(asmApi, cw) {
|
||||
|
||||
@Override
|
||||
public void visit(int version, int access, String name, String sig, String superName, String[] interfaces) {
|
||||
@@ -112,7 +119,7 @@ public final class TileEntityMarkerTransform {
|
||||
private int alreadyPresent;
|
||||
|
||||
MarkerScanner() {
|
||||
super(Opcodes.ASM5);
|
||||
super(asmApi);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -159,6 +159,7 @@ public enum Mixins implements IMixins {
|
||||
.setApplyIf(() -> AngelicaConfig.enableTESRChestCache)
|
||||
.addClientMixins(
|
||||
"angelica.tesr.MixinTileEntityChestRenderer",
|
||||
"angelica.tesr.MixinTileEntityChestRenderer_BindTexture",
|
||||
"angelica.tesr.MixinTileEntityEnderChestRenderer"
|
||||
)
|
||||
),
|
||||
@@ -395,6 +396,12 @@ public enum Mixins implements IMixins {
|
||||
)
|
||||
),
|
||||
|
||||
CELERITAS_CUBIC_CHUNKS(new MixinBuilder()
|
||||
.setPhase(Phase.EARLY)
|
||||
.addRequiredMod(TargetedMod.CUBIC_CHUNKS)
|
||||
.addClientMixins("celeritas.terrain.MixinCubicChunksCube")
|
||||
),
|
||||
|
||||
CELERITAS_COLORED_LIGHT(new MixinBuilder("Colored light infrastructure for celeritas light pipeline")
|
||||
.setPhase(Phase.EARLY)
|
||||
.setApplyIf(() -> {
|
||||
|
||||
@@ -12,6 +12,7 @@ public enum TargetedMod implements ITargetMod {
|
||||
CAMPFIRE_BACKPORT(null, "campfirebackport"),
|
||||
CHISEL(null, "chisel"),
|
||||
COFHCORE( "cofh.asm.LoadingPlugin", "CoFHCore"),
|
||||
CUBIC_CHUNKS("com.cardinalstar.cubicchunks.asm.coremod.CubicChunksCoreMod", "cubicchunks"),
|
||||
DYNAMIC_SURROUNDINGS_MIST("org.blockartistry.mod.DynSurround.mixinplugin.DynamicSurroundingsEarlyMixins", "dsurround"),
|
||||
DYNAMIC_SURROUNDINGS_ORIGINAL("org.blockartistry.mod.DynSurround.asm.TransformLoader", "dsurround"),
|
||||
DRAGON_API("Reika.DragonAPI.Auxiliary.DragonAPIASMHandler", "DragonAPI"),
|
||||
|
||||
@@ -97,6 +97,12 @@ import net.coderbot.iris.client.IrisDebugScreenHandler;
|
||||
|
||||
public final class ClientProxy extends CommonProxy {
|
||||
|
||||
/**
|
||||
* So you don't need to be in a dev env to have access to the commands.
|
||||
* Do not commit with this set to true.
|
||||
*/
|
||||
private static final boolean FORCE_DEBUG_MODE = false;
|
||||
|
||||
public static BlockError blockError;
|
||||
public static final ManagedEnum<AnimationMode> animationsMode = new ManagedEnum<>(AnimationMode.VISIBLE_ONLY);
|
||||
private static final Logger LOGGER = LogManager.getLogger("Angelica");
|
||||
@@ -136,6 +142,7 @@ public final class ClientProxy extends CommonProxy {
|
||||
ModelRegistry.registerModid(MOD_ID);
|
||||
blockError = new BlockError();
|
||||
if (AngelicaConfig.enableIris) {
|
||||
IrisGLSMBridge.installImmediateExtendedHandler();
|
||||
Iris.warmupShaderTransforms();
|
||||
}
|
||||
}
|
||||
@@ -190,7 +197,7 @@ public final class ClientProxy extends CommonProxy {
|
||||
}
|
||||
|
||||
// Debug tooling
|
||||
if (!AngelicaClientTweaker.isObfEnv()) {
|
||||
if (FORCE_DEBUG_MODE || !AngelicaClientTweaker.isObfEnv()) {
|
||||
ClientCommandHandler.instance.registerCommand(new AngelicaCommand());
|
||||
|
||||
FMLCommonHandler.instance().bus().register(FlybyRunner.INSTANCE);
|
||||
|
||||
+10
@@ -11,6 +11,8 @@ import com.gtnewhorizons.angelica.rendering.celeritas.api.IrisShaderProviderHold
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.iris.BlockRenderContext;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.iris.ContextAwareChunkVertexEncoder;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.world.WorldSlice;
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
import com.gtnewhorizons.angelica.utils.NaturalTextureUtils;
|
||||
import com.prupe.mcpatcher.mal.block.RenderBlocksUtils;
|
||||
import it.unimi.dsi.fastutil.floats.FloatArrayList;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2ReferenceMap;
|
||||
@@ -361,8 +363,16 @@ public abstract class AngelicaChunkBuilderMeshingTask extends ChunkBuilderTask<C
|
||||
setRenderPass(pass);
|
||||
// Trigger side effects from canRenderInPass (some ISBRHs like BuildCraft set global state in this method that gets read elsewhere renderWorldBlock)
|
||||
block.canRenderInPass(pass);
|
||||
|
||||
// Some blocks get randomly rotated top faces
|
||||
final boolean applyNaturalTex = AngelicaConfig.enableNaturalTextures && NaturalTextureUtils.isNaturalBlock(block);
|
||||
if (applyNaturalTex) { renderBlocks.uvRotateTop = NaturalTextureUtils.getTopRotation(x, y, z); }
|
||||
|
||||
tessellator.startDrawingQuads();
|
||||
renderBlocks.renderBlockByRenderType(block, x, y, z);
|
||||
|
||||
if (applyNaturalTex) { renderBlocks.uvRotateTop = 0; } // Reset
|
||||
|
||||
boolean blockAllowsSmoothLighting = Minecraft.isAmbientOcclusionEnabled() // smooth lighting on
|
||||
&& block.getLightValue() == 0; // does not emit real block light
|
||||
buildContext.copyRawBuffer(tessellator.rawBuffer, tessellator.vertexCount,
|
||||
|
||||
+6
@@ -6,6 +6,8 @@ import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import com.gtnewhorizons.angelica.compat.bop.BopFogBlend;
|
||||
import com.gtnewhorizons.angelica.compat.bop.FogBiomeCache;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.multiplayer.WorldClient;
|
||||
import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher;
|
||||
@@ -43,6 +45,7 @@ import com.gtnewhorizons.angelica.compat.cubicchunks.CubicChunksAPI;
|
||||
import com.gtnewhorizons.angelica.dynamiclights.DynamicLights;
|
||||
import com.gtnewhorizons.angelica.dynamiclights.IDynamicLightWorldRenderer;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.FfpExtendedAttribs;
|
||||
import com.gtnewhorizons.angelica.glsm.profiling.Tracy;
|
||||
import com.gtnewhorizons.angelica.mixins.interfaces.ITileEntityBoundingBoxCache;
|
||||
import com.gtnewhorizons.angelica.mixins.interfaces.OverridesShouldRenderInPass;
|
||||
@@ -166,6 +169,9 @@ public class CeleritasWorldRenderer extends SimpleWorldRenderer<WorldClient, Ang
|
||||
TesrBatchRenderer.INSTANCE.clearRetained();
|
||||
ModelPartBatcher.INSTANCE.clear();
|
||||
GpuCulling.onWorldUnload();
|
||||
FfpExtendedAttribs.reset();
|
||||
BopFogBlend.invalidate();
|
||||
FogBiomeCache.invalidate();
|
||||
super.unloadWorld();
|
||||
}
|
||||
|
||||
|
||||
+77
-6
@@ -31,7 +31,11 @@ import com.gtnewhorizon.gtnhlib.client.renderer.vao.IVertexArrayObject;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vao.IndexBuffer;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vao.VAOManager;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.DefaultVertexFormat;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormat;
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMHooks;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.ImmediateExtendedAttribHandler;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap;
|
||||
import lombok.Data;
|
||||
@@ -39,6 +43,13 @@ import lombok.NoArgsConstructor;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.resources.IReloadableResourceManager;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAddress0;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memCalloc;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memFree;
|
||||
|
||||
public class ItemRenderListManager {
|
||||
// Least used element is at position 0. This is in theory slightly faster.
|
||||
@@ -102,18 +113,21 @@ public class ItemRenderListManager {
|
||||
public static void registerReloadListener(){
|
||||
IReloadableResourceManager resourceManager = (IReloadableResourceManager) Minecraft.getMinecraft()
|
||||
.getResourceManager();
|
||||
resourceManager.registerReloadListener(_ -> {
|
||||
for (CachedVBO value : vboCache.values()) {
|
||||
value.delete();
|
||||
}
|
||||
vboCache.clear();
|
||||
});
|
||||
resourceManager.registerReloadListener(_ -> clearCache());
|
||||
}
|
||||
|
||||
public static void clearCache() {
|
||||
for (CachedVBO value : vboCache.values()) {
|
||||
value.delete();
|
||||
}
|
||||
vboCache.clear();
|
||||
}
|
||||
|
||||
public static final class CachedVBO {
|
||||
private final IVertexArrayObject vertexBuffer;
|
||||
private final IndexBuffer ebo;
|
||||
private int expiry;
|
||||
private int extVbo = 0;
|
||||
|
||||
public CachedVBO() {
|
||||
this.ebo = new IndexBuffer();
|
||||
@@ -126,6 +140,59 @@ public class ItemRenderListManager {
|
||||
|
||||
private void allocate(DirectTessellator tessellator) {
|
||||
tessellator.allocateToVBO(vertexBuffer, ebo);
|
||||
attachExtAttribs(tessellator);
|
||||
}
|
||||
|
||||
private void attachExtAttribs(DirectTessellator tessellator) {
|
||||
final ImmediateExtendedAttribHandler handler = GLSMHooks.immediateExtendedHandler;
|
||||
final VertexFormat format = tessellator.getVertexFormat();
|
||||
final int vertexCount = tessellator.getVertexCount();
|
||||
final int extPrim = handler == null ? 0 : ImmediateExtendedAttribHandler.extPrimVerts(tessellator.getDrawMode(), vertexCount);
|
||||
final boolean capture = handler != null && format != null && format.hasTexture()
|
||||
&& extPrim != 0
|
||||
&& (extVbo != 0 || handler.wantsExtendedCapture());
|
||||
if (!capture) {
|
||||
detachExtAttribs();
|
||||
return;
|
||||
}
|
||||
|
||||
final int extStride = ImmediateExtendedAttribHandler.EXT_STRIDE;
|
||||
final int stride = format.getVertexSize();
|
||||
final int texOffset = ImmediateExtendedAttribHandler.texOffset(format);
|
||||
final int normalOffset = ImmediateExtendedAttribHandler.normalOffset(format);
|
||||
final ByteBuffer packed = tessellator.getWriteBuffer();
|
||||
final long srcBase = memAddress0(packed) + packed.position();
|
||||
|
||||
final ByteBuffer ext = memCalloc(vertexCount, extStride);
|
||||
handler.buildPacked(srcBase, stride, 0, texOffset, normalOffset, vertexCount, extPrim, memAddress0(ext), extStride);
|
||||
|
||||
final boolean firstAttach = extVbo == 0;
|
||||
if (firstAttach) {
|
||||
extVbo = GLStateManager.glGenBuffers();
|
||||
}
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, extVbo);
|
||||
ext.position(0).limit(vertexCount * extStride);
|
||||
GLStateManager.glBufferData(GL15.GL_ARRAY_BUFFER, ext, GL15.GL_STATIC_DRAW);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
memFree(ext);
|
||||
|
||||
if (firstAttach) {
|
||||
vertexBuffer.bind();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, extVbo);
|
||||
ImmediateExtendedAttribHandler.setupExtAttribPointers(0L, extStride);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
vertexBuffer.unbind();
|
||||
}
|
||||
}
|
||||
|
||||
private void detachExtAttribs() {
|
||||
if (extVbo == 0) return;
|
||||
vertexBuffer.bind();
|
||||
GLStateManager.glDisableVertexAttribArray(ImmediateExtendedAttribHandler.LOC_MID_TEX);
|
||||
GLStateManager.glDisableVertexAttribArray(ImmediateExtendedAttribHandler.LOC_TANGENT);
|
||||
vertexBuffer.unbind();
|
||||
GLStateManager.glDeleteBuffers(extVbo);
|
||||
extVbo = 0;
|
||||
}
|
||||
|
||||
private void render(int elapsedTicks) {
|
||||
@@ -136,6 +203,10 @@ public class ItemRenderListManager {
|
||||
private void delete() {
|
||||
vertexBuffer.delete();
|
||||
// EBO gets deleted by vertexBuffer.delete()
|
||||
if (extVbo != 0) {
|
||||
GLStateManager.glDeleteBuffers(extVbo);
|
||||
extVbo = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+37
@@ -2,6 +2,9 @@ package com.gtnewhorizons.angelica.rendering.tesr;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFlags;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.ImmediateExtendedAttribHandler;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMHooks;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.FfpExtendedAttribs;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.InstancedAttribs;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.VAOManager;
|
||||
import com.gtnewhorizons.angelica.glsm.streaming.OrphanStreamingBuffer;
|
||||
@@ -17,6 +20,8 @@ import org.lwjgl.opengl.GL15;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAddress0;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memCalloc;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memFree;
|
||||
import static com.gtnewhorizon.gtnhlib.client.renderer.cel.util.ModelQuadUtil.COLOR_INDEX;
|
||||
import static com.gtnewhorizon.gtnhlib.client.renderer.cel.util.ModelQuadUtil.NORMAL_INDEX;
|
||||
import static com.gtnewhorizon.gtnhlib.client.renderer.cel.util.ModelQuadUtil.TEX_X_INDEX;
|
||||
@@ -34,6 +39,7 @@ final class InstancedTemplateRenderer {
|
||||
private static final class TemplateMesh {
|
||||
int vao;
|
||||
int vbo;
|
||||
int extVbo;
|
||||
int vertexCount;
|
||||
int drawMode;
|
||||
long lastUsedMs;
|
||||
@@ -96,6 +102,10 @@ final class InstancedTemplateRenderer {
|
||||
int draws = 0;
|
||||
long recordOffset = ringBase;
|
||||
final int ringId = uploadBufferId;
|
||||
final ImmediateExtendedAttribHandler extHandler = GLSMHooks.immediateExtendedHandler;
|
||||
if (extHandler != null && extHandler.wantsExtended()) {
|
||||
FfpExtendedAttribs.setNeutralCurrentValues();
|
||||
}
|
||||
GLStateManager.instancedFfpDrawActive = true;
|
||||
for (int b = 0, n = liveBuckets.size(); b < n; b++) {
|
||||
final Bucket bucket = liveBuckets.get(b);
|
||||
@@ -179,6 +189,13 @@ final class InstancedTemplateRenderer {
|
||||
GLStateManager.glVertexAttribPointer(4, 3, GL11.GL_BYTE, true, TEMPLATE_STRIDE, NORMAL_INDEX * 4L);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
|
||||
mesh.extVbo = buildExt(template);
|
||||
if (mesh.extVbo != 0) {
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, mesh.extVbo);
|
||||
ImmediateExtendedAttribHandler.setupExtAttribPointers(0L, ImmediateExtendedAttribHandler.EXT_STRIDE);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
}
|
||||
|
||||
for (int loc = InstancedAttribs.LOC_MATRIX_COL0; loc <= InstancedAttribs.LOC_LIGHTMAP; loc++) {
|
||||
GLStateManager.glEnableVertexAttribArray(loc);
|
||||
GLStateManager.glVertexAttribDivisor(loc, 1);
|
||||
@@ -188,6 +205,25 @@ final class InstancedTemplateRenderer {
|
||||
return mesh;
|
||||
}
|
||||
|
||||
private static int buildExt(TemplateBuffer template) {
|
||||
final ImmediateExtendedAttribHandler handler = GLSMHooks.immediateExtendedHandler;
|
||||
if (handler == null || !handler.wantsExtendedCapture()) return 0;
|
||||
final int extPrim = ImmediateExtendedAttribHandler.extPrimVerts(template.drawMode, template.vertexCount);
|
||||
if (extPrim == 0) return 0;
|
||||
|
||||
final int extStride = ImmediateExtendedAttribHandler.EXT_STRIDE;
|
||||
final ByteBuffer ext = memCalloc(template.vertexCount, extStride);
|
||||
handler.build(template.data, template.vertexCount, extPrim,
|
||||
ImmediateExtendedAttribHandler.RAW_NORMAL_INDEX, memAddress0(ext), extStride);
|
||||
|
||||
final int extVbo = GLStateManager.glGenBuffers();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, extVbo);
|
||||
ext.position(0).limit(template.vertexCount * extStride);
|
||||
GLStateManager.glBufferData(GL15.GL_ARRAY_BUFFER, ext, GL15.GL_STATIC_DRAW);
|
||||
memFree(ext);
|
||||
return extVbo;
|
||||
}
|
||||
|
||||
void endFrame() {
|
||||
if (ringUsedThisFrame && persistentRing != null) {
|
||||
persistentRing.postDraw();
|
||||
@@ -215,6 +251,7 @@ final class InstancedTemplateRenderer {
|
||||
|
||||
private static void delete(TemplateMesh mesh) {
|
||||
GLStateManager.glDeleteBuffers(mesh.vbo);
|
||||
if (mesh.extVbo != 0) GLStateManager.glDeleteBuffers(mesh.extVbo);
|
||||
GLStateManager.glDeleteVertexArrays(mesh.vao);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.gtnewhorizons.angelica.utils;
|
||||
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.init.Blocks;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet;
|
||||
|
||||
public class NaturalTextureUtils {
|
||||
|
||||
// Yes this is supposed to be volatile
|
||||
private volatile static Set<Block> cachedWhitelistedBlocks = null;
|
||||
|
||||
public static int getTopRotation(int x, int y, int z) {
|
||||
long hash = x * 0x9E3779B97F4A7C15L + z * 0xC2B2AE3D27D4EB4FL + y * 0x165667B19E3779F9L;
|
||||
// splitmix64 finalizer
|
||||
hash ^= hash >>> 30;
|
||||
hash *= 0xBF58476D1CE4E5B9L;
|
||||
hash ^= hash >>> 27;
|
||||
hash *= 0x94D049BB133111EBL;
|
||||
hash ^= hash >>> 31;
|
||||
return (int) (hash >>> 62);
|
||||
}
|
||||
|
||||
// This method gets called from worker threads
|
||||
public static boolean isNaturalBlock(Block block) {
|
||||
if (cachedWhitelistedBlocks == null) {
|
||||
// If list is empty add back all the stuff from config
|
||||
initCache();
|
||||
}
|
||||
return cachedWhitelistedBlocks.contains(block);
|
||||
}
|
||||
|
||||
// On reload clear the list
|
||||
public static void reloadCache() {
|
||||
cachedWhitelistedBlocks = null;
|
||||
}
|
||||
|
||||
private static void initCache() {
|
||||
Set<Block> set = new ReferenceOpenHashSet<>();
|
||||
if (AngelicaConfig.naturalTextureBlocks != null) {
|
||||
for (String name : AngelicaConfig.naturalTextureBlocks) {
|
||||
Block b = (Block) Block.blockRegistry.getObject(name);
|
||||
if (b != null && b != Blocks.air) {
|
||||
set.add(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
cachedWhitelistedBlocks = set;
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,7 @@ public class Zoom {
|
||||
private static long zoomLerpLastNano = 0L;
|
||||
|
||||
@Getter
|
||||
private static final KeyBinding zoomKey = new KeyBinding("Zoom", 0, "key.categories.misc");
|
||||
private static final KeyBinding zoomKey = new KeyBinding("angelica.keybind.zoom", 0, "key.categories.misc");
|
||||
|
||||
private static boolean zoomEnabled = false;
|
||||
|
||||
|
||||
@@ -250,8 +250,8 @@ public class ColorizeBlock {
|
||||
}
|
||||
colorMap = wrapBlockMap(colorMap);
|
||||
for (String idString : idList.split("\\s+")) {
|
||||
BlockStateMatcher blockMatcher = BlockAPI.createMatcher(new PropertiesFile(logger, resource), idString);
|
||||
if (blockMatcher != null) {
|
||||
for (BlockStateMatcher blockMatcher : BlockAPI
|
||||
.createMatchers(new PropertiesFile(logger, resource), idString)) {
|
||||
List<BlockStateMatcher> maps = blockColorMaps
|
||||
.computeIfAbsent(blockMatcher.getBlock(), k -> new ArrayList<>());
|
||||
blockMatcher.setData(colorMap);
|
||||
|
||||
@@ -13,6 +13,7 @@ import java.util.function.Consumer;
|
||||
|
||||
import com.github.bsideup.jabel.Desugar;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.client.resources.IResourcePack;
|
||||
import net.minecraft.util.IIcon;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
@@ -25,6 +26,7 @@ import com.prupe.mcpatcher.mal.block.RenderBlocksUtils;
|
||||
import com.prupe.mcpatcher.mal.block.RenderPassAPI;
|
||||
import com.prupe.mcpatcher.mal.resource.BlendMethod;
|
||||
import com.prupe.mcpatcher.mal.resource.ResourceList;
|
||||
import com.prupe.mcpatcher.mal.resource.ResourceLocationWithSource;
|
||||
import com.prupe.mcpatcher.mal.resource.TexturePackAPI;
|
||||
import com.prupe.mcpatcher.mal.resource.TexturePackChangeHandler;
|
||||
import com.prupe.mcpatcher.mal.tile.TileLoader;
|
||||
@@ -71,7 +73,7 @@ public class CTMUtils {
|
||||
}
|
||||
|
||||
public static void clearCurrentCompact() {
|
||||
CURRENT_COMPACT.remove();
|
||||
CURRENT_COMPACT.set(null);
|
||||
}
|
||||
|
||||
private static final StampedLock lock = new StampedLock();
|
||||
@@ -128,8 +130,14 @@ public class CTMUtils {
|
||||
logger.info("loading CTM overrides");
|
||||
newOverrides = new Overrides();
|
||||
if (MCPatcherForgeConfig.ConnectedTextures.standard || MCPatcherForgeConfig.ConnectedTextures.nonStandard) {
|
||||
for (ResourceLocation resource : ResourceList.getInstance()
|
||||
.listResources(TexturePackAPI.MCPATCHER_SUBDIR + "ctm", ".properties", true)) {
|
||||
final List<ResourceLocation> ctmResources = new ArrayList<>();
|
||||
for (String subdir : new String[] { TexturePackAPI.MCPATCHER_SUBDIR, TexturePackAPI.OPTIFINE_SUBDIR }) {
|
||||
ctmResources.addAll(ResourceList.getInstance()
|
||||
.listResources(subdir + "ctm", ".properties", true));
|
||||
}
|
||||
final Map<String, Map<IResourcePack, Integer>> packPriority = new HashMap<>();
|
||||
ctmResources.sort((a, b) -> packPriorityOf(packPriority, b) - packPriorityOf(packPriority, a));
|
||||
for (ResourceLocation resource : ctmResources) {
|
||||
registerOverrideWithoutLock(newOverrides, TileOverride.create(resource, tileLoader));
|
||||
}
|
||||
for (Consumer<Overrides> callback : ctmRegistrationCallbacks) {
|
||||
@@ -294,9 +302,7 @@ public class CTMUtils {
|
||||
Set<String> matchingTiles = override.getMatchingTiles();
|
||||
if (!MCPatcherUtils.isNullOrEmpty(matchingTiles)) {
|
||||
for (String name : matchingTiles) {
|
||||
List<TileOverride> list = overrides.tile.computeIfAbsent(name, k -> new ArrayList<>());
|
||||
list.add(override);
|
||||
logger.fine("using %s for tile %s", override, name);
|
||||
registerTileWithoutLock(overrides, name, override);
|
||||
registered = true;
|
||||
}
|
||||
}
|
||||
@@ -306,6 +312,30 @@ public class CTMUtils {
|
||||
}
|
||||
}
|
||||
|
||||
private static void registerTileWithoutLock(Overrides overrides, String name, TileOverride override) {
|
||||
overrides.tile.computeIfAbsent(name, k -> new ArrayList<>())
|
||||
.add(override);
|
||||
logger.fine("using %s for tile %s", override, name);
|
||||
}
|
||||
|
||||
private static Map<IResourcePack, Integer> getPackPriority(String namespace) {
|
||||
final List<IResourcePack> packs = TexturePackAPI.getResourcePacks(namespace);
|
||||
final Map<IResourcePack, Integer> priority = new IdentityHashMap<>(packs.size());
|
||||
for (int i = 0; i < packs.size(); i++) {
|
||||
priority.put(packs.get(i), i);
|
||||
}
|
||||
return priority;
|
||||
}
|
||||
|
||||
|
||||
private static int packPriorityOf(Map<String, Map<IResourcePack, Integer>> byDomain, ResourceLocation resource) {
|
||||
if (!(resource instanceof ResourceLocationWithSource source)) return -1;
|
||||
final Integer rank = byDomain
|
||||
.computeIfAbsent(resource.getResourceDomain(), CTMUtils::getPackPriority)
|
||||
.get(source.getSource());
|
||||
return rank == null ? -1 : rank;
|
||||
}
|
||||
|
||||
private static void setBlankResourceWithoutLock() {
|
||||
RenderBlocksUtils.blankIcon = tileLoader.getIcon(RenderPassAPI.instance.getBlankResource());
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ abstract public class RenderBlockState {
|
||||
public static final int CONNECT_BY_BLOCK = 0;
|
||||
public static final int CONNECT_BY_TILE = 1;
|
||||
public static final int CONNECT_BY_MATERIAL = 2;
|
||||
public static final int CONNECT_BY_LIST = 3;
|
||||
|
||||
protected static int[] add(int[] a, int[] b) {
|
||||
if (a.length != b.length) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.prupe.mcpatcher.ctm;
|
||||
|
||||
import static com.prupe.mcpatcher.ctm.RenderBlockState.CONNECT_BY_BLOCK;
|
||||
import static com.prupe.mcpatcher.ctm.RenderBlockState.CONNECT_BY_LIST;
|
||||
import static com.prupe.mcpatcher.ctm.RenderBlockState.CONNECT_BY_MATERIAL;
|
||||
import static com.prupe.mcpatcher.ctm.RenderBlockState.CONNECT_BY_TILE;
|
||||
import static com.prupe.mcpatcher.ctm.RenderBlockState.NORMALS;
|
||||
@@ -33,13 +34,18 @@ public abstract class TileOverride implements Comparable<TileOverride> {
|
||||
|
||||
private static final MCLogger logger = MCLogger.getLogger(MCLogger.Category.CONNECTED_TEXTURES, "CTM");
|
||||
|
||||
private static int nextRegistrationOrder;
|
||||
|
||||
public final PropertiesFile properties;
|
||||
private final String baseFilename;
|
||||
protected final TileLoader tileLoader;
|
||||
protected final int renderPass;
|
||||
private final int weight;
|
||||
private boolean lostScope;
|
||||
private final int registrationOrder = nextRegistrationOrder++;
|
||||
private final List<BlockStateMatcher> matchBlocks;
|
||||
private final Set<String> matchTiles;
|
||||
private final List<BlockStateMatcher> connectBlocks;
|
||||
private final BlockFaceMatcher faceMatcher;
|
||||
private final int connectType;
|
||||
private final boolean innerSeams;
|
||||
@@ -157,6 +163,7 @@ public abstract class TileOverride implements Comparable<TileOverride> {
|
||||
if (matchBlocks.isEmpty() && matchTiles.isEmpty()) {
|
||||
matchTiles.add(baseFilename);
|
||||
}
|
||||
connectBlocks = getBlockList(properties.getString("connectBlocks", ""), properties.getString("connectMetadata", ""));
|
||||
|
||||
faceMatcher = BlockFaceMatcher.create(properties.getString("faces", ""));
|
||||
|
||||
@@ -167,6 +174,14 @@ public abstract class TileOverride implements Comparable<TileOverride> {
|
||||
case "block" -> connectType = CONNECT_BY_BLOCK;
|
||||
case "tile" -> connectType = CONNECT_BY_TILE;
|
||||
case "material" -> connectType = CONNECT_BY_MATERIAL;
|
||||
case "list" -> {
|
||||
if (connectBlocks.isEmpty()) {
|
||||
properties.error("connect=list requires a non-empty connectBlocks= list");
|
||||
connectType = CONNECT_BY_BLOCK;
|
||||
} else {
|
||||
connectType = CONNECT_BY_LIST;
|
||||
}
|
||||
}
|
||||
default -> {
|
||||
properties.error("invalid connect type %s", connectType1);
|
||||
connectType = CONNECT_BY_BLOCK;
|
||||
@@ -274,19 +289,21 @@ public abstract class TileOverride implements Comparable<TileOverride> {
|
||||
// nothing
|
||||
} else if (token.matches("\\d+-\\d+")) {
|
||||
for (int id : MCPatcherUtils.parseIntegerList(token, 0, 65535)) {
|
||||
BlockStateMatcher matcher = BlockAPI.createMatcher(properties, id + defaultMetadata);
|
||||
if (matcher == null) {
|
||||
properties.warning("unknown block id %d", id);
|
||||
List<BlockStateMatcher> matchers = BlockAPI.createMatchers(properties, id + defaultMetadata);
|
||||
if (matchers.isEmpty()) {
|
||||
properties.fine("unknown block id %d", id);
|
||||
lostScope = true;
|
||||
} else {
|
||||
blocks.add(matcher);
|
||||
blocks.addAll(matchers);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
BlockStateMatcher matcher = BlockAPI.createMatcher(properties, token + defaultMetadata);
|
||||
if (matcher == null) {
|
||||
properties.warning("unknown block %s", token);
|
||||
List<BlockStateMatcher> matchers = BlockAPI.createMatchers(properties, token + defaultMetadata);
|
||||
if (matchers.isEmpty()) {
|
||||
properties.fine("unknown block %s", token);
|
||||
lostScope = true;
|
||||
} else {
|
||||
blocks.add(matcher);
|
||||
blocks.addAll(matchers);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -334,7 +351,11 @@ public abstract class TileOverride implements Comparable<TileOverride> {
|
||||
public final void registerIcons() {
|
||||
icons = new IIcon[tileNames.size()];
|
||||
for (int i = 0; i < icons.length; i++) {
|
||||
icons[i] = tileLoader.getIcon(tileNames.get(i));
|
||||
final ResourceLocation tileName = tileNames.get(i);
|
||||
icons[i] = tileLoader.getIcon(tileName);
|
||||
if (icons[i] == null && tileName != null) {
|
||||
properties.warning("tile %s did not resolve to an icon, tile %d will not render", tileName, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -368,11 +389,11 @@ public abstract class TileOverride implements Comparable<TileOverride> {
|
||||
if (result != 0) {
|
||||
return result;
|
||||
}
|
||||
if (o instanceof TileOverride) {
|
||||
return baseFilename.compareTo(((TileOverride) o).baseFilename);
|
||||
} else {
|
||||
return -1;
|
||||
result = Boolean.compare(lostScope, o.lostScope);
|
||||
if (result != 0) {
|
||||
return result;
|
||||
}
|
||||
return Integer.compare(registrationOrder, o.registrationOrder);
|
||||
}
|
||||
|
||||
final boolean shouldConnect(RenderBlockState renderBlockState, IIcon icon, int relativeDirection) {
|
||||
@@ -433,6 +454,16 @@ public abstract class TileOverride implements Comparable<TileOverride> {
|
||||
case CONNECT_BY_TILE -> renderBlockState.shouldConnectByTile(neighbor, icon, x, y, z);
|
||||
case CONNECT_BY_BLOCK -> renderBlockState.shouldConnectByBlock(neighbor, x, y, z);
|
||||
case CONNECT_BY_MATERIAL -> block.blockMaterial == neighbor.blockMaterial;
|
||||
case CONNECT_BY_LIST -> {
|
||||
boolean matched = false;
|
||||
for (BlockStateMatcher matcher : connectBlocks) {
|
||||
if (matcher.match(blockAccess, x, y, z)) {
|
||||
matched = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
yield matched;
|
||||
}
|
||||
default -> false;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -4,15 +4,20 @@ import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import net.coderbot.iris.shaderpack.materialmap.BlockEntry;
|
||||
import net.coderbot.iris.shaderpack.materialmap.FlatteningMap;
|
||||
import net.coderbot.iris.shaderpack.materialmap.NamespacedId;
|
||||
import net.minecraft.block.Block;
|
||||
|
||||
import com.prupe.mcpatcher.MCPatcherUtils;
|
||||
import com.prupe.mcpatcher.mal.resource.PropertiesFile;
|
||||
import com.prupe.mcpatcher.mal.resource.TexturePackAPI;
|
||||
|
||||
import cpw.mods.fml.common.registry.FMLControlledNamespacedRegistry;
|
||||
import cpw.mods.fml.common.registry.GameData;
|
||||
@@ -64,7 +69,7 @@ public class BlockAPI {
|
||||
return blocks;
|
||||
}
|
||||
|
||||
public static BlockStateMatcher createMatcher(PropertiesFile source, String matchString) {
|
||||
public static List<BlockStateMatcher> createMatchers(PropertiesFile source, String matchString) {
|
||||
Map<String, String> propertyMap = new HashMap<>();
|
||||
String namespace = null;
|
||||
String blockName = null;
|
||||
@@ -90,7 +95,7 @@ public class BlockAPI {
|
||||
appendThis = true;
|
||||
} else {
|
||||
source.warning("invalid token '%s' in %s", source, s, matchString);
|
||||
return null;
|
||||
return Collections.emptyList();
|
||||
}
|
||||
if (appendThis) {
|
||||
metaString.append(':');
|
||||
@@ -103,19 +108,73 @@ public class BlockAPI {
|
||||
}
|
||||
if (MCPatcherUtils.isNullOrEmpty(blockName)) {
|
||||
source.warning("cannot parse namespace/block name from %s", matchString);
|
||||
return null;
|
||||
return Collections.emptyList();
|
||||
}
|
||||
Block block = GameData.getBlockRegistry()
|
||||
.getObject(namespace + ':' + blockName);
|
||||
if (block == null) {
|
||||
source.warning("unknown block %s:%s", namespace, blockName);
|
||||
return null;
|
||||
final Block block = GameData.getBlockRegistry()
|
||||
.getRaw(namespace + ':' + blockName);
|
||||
if (block != null) {
|
||||
return Collections.singletonList(
|
||||
new BlockStateMatcher(
|
||||
metaString.toString(),
|
||||
block,
|
||||
metadata.toString()
|
||||
.trim()));
|
||||
}
|
||||
return new BlockStateMatcher(
|
||||
metaString.toString(),
|
||||
block,
|
||||
metadata.toString()
|
||||
.trim());
|
||||
if (!TexturePackAPI.DEFAULT_NAMESPACE.equals(namespace)) {
|
||||
source.fine("unknown block %s:%s", namespace, blockName);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
final List<BlockEntry> entries = FlatteningMap.toLegacy(blockName, propertyMap);
|
||||
if (entries == null) {
|
||||
source.fine("unknown block %s:%s", namespace, blockName);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
if (!propertyMap.isEmpty() && entries.equals(FlatteningMap.toLegacy(blockName, Collections.emptyMap()))) {
|
||||
source.fine("block %s has no 1.7.10 equivalent for %s, ignoring this match", blockName, propertyMap);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
final String explicitMetadata = metadata.toString()
|
||||
.trim();
|
||||
final List<BlockStateMatcher> matchers = new ArrayList<>(entries.size());
|
||||
for (BlockEntry entry : entries) {
|
||||
final NamespacedId id = entry.id();
|
||||
final Block legacy = GameData.getBlockRegistry()
|
||||
.getRaw(id.getNamespace() + ':' + id.getName());
|
||||
final String entryMetadata = intersectMetadata(explicitMetadata, entry.metas());
|
||||
if (entryMetadata == null) {
|
||||
continue;
|
||||
}
|
||||
matchers.add(new BlockStateMatcher(metaString.toString(), legacy, entryMetadata));
|
||||
}
|
||||
if (matchers.isEmpty()) {
|
||||
source.fine("unknown block %s:%s", namespace, blockName);
|
||||
}
|
||||
return matchers;
|
||||
}
|
||||
|
||||
private static String intersectMetadata(String explicitMetadata, Set<Integer> entryMetas) {
|
||||
if (MCPatcherUtils.isNullOrEmpty(explicitMetadata)) {
|
||||
if (entryMetas.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
for (int meta : entryMetas) {
|
||||
sb.append(' ')
|
||||
.append(meta);
|
||||
}
|
||||
return sb.toString()
|
||||
.trim();
|
||||
}
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
for (int meta : MCPatcherUtils.parseIntegerList(explicitMetadata, 0, 15)) {
|
||||
if (entryMetas.isEmpty() || entryMetas.contains(meta)) {
|
||||
sb.append(' ')
|
||||
.append(meta);
|
||||
}
|
||||
}
|
||||
return sb.length() == 0 ? null
|
||||
: sb.toString()
|
||||
.trim();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -15,14 +15,15 @@ public class ResourceLocationWithSource extends ResourceLocation {
|
||||
private final boolean isDirectory;
|
||||
|
||||
public ResourceLocationWithSource(IResourcePack source, ResourceLocation resource) {
|
||||
String resourcePath = resource.getResourcePath();
|
||||
boolean isDirectory = resourcePath.endsWith("/");
|
||||
|
||||
super(
|
||||
resource.getResourceDomain(),
|
||||
resource.getResourcePath()
|
||||
.replaceFirst("/$", ""));
|
||||
isDirectory ? resourcePath.substring(0, resourcePath.length() - 1) : resourcePath);
|
||||
this.source = source;
|
||||
order = ResourceList.getResourcePackOrder(source);
|
||||
isDirectory = resource.getResourcePath()
|
||||
.endsWith("/");
|
||||
this.order = ResourceList.getResourcePackOrder(source);
|
||||
this.isDirectory = isDirectory;
|
||||
}
|
||||
|
||||
public IResourcePack getSource() {
|
||||
|
||||
@@ -43,6 +43,7 @@ public class TexturePackAPI {
|
||||
public static final String DEFAULT_NAMESPACE = "minecraft";
|
||||
|
||||
public static final String MCPATCHER_SUBDIR = "mcpatcher/";
|
||||
public static final String OPTIFINE_SUBDIR = "optifine/";
|
||||
public static final ResourceLocation ITEMS_PNG = new ResourceLocation("textures/atlas/items.png");
|
||||
|
||||
private static final String ASSETS = "assets/";
|
||||
@@ -195,15 +196,23 @@ public class TexturePackAPI {
|
||||
}
|
||||
path = path.replace(File.separatorChar, '/');
|
||||
if (path.startsWith(ASSETS)) {
|
||||
path = path.substring(ASSETS.length());
|
||||
int slash = path.indexOf('/');
|
||||
if (slash > 0 && slash + 1 < path.length()) {
|
||||
return new ResourceLocation(path.substring(0, slash), path.substring(slash + 1));
|
||||
int namespaceStart = ASSETS.length();
|
||||
int slash = path.indexOf('/', namespaceStart);
|
||||
|
||||
if (slash > namespaceStart && slash + 1 < path.length()) {
|
||||
return new ResourceLocation(
|
||||
path.substring(namespaceStart, slash),
|
||||
path.substring(slash + 1));
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static String subdirOf(ResourceLocation baseResource) {
|
||||
return baseResource != null && baseResource.getResourcePath()
|
||||
.startsWith(OPTIFINE_SUBDIR) ? OPTIFINE_SUBDIR : MCPATCHER_SUBDIR;
|
||||
}
|
||||
|
||||
public static ResourceLocation parseResourceLocation(ResourceLocation baseResource, String path) {
|
||||
if (MCPatcherUtils.isNullOrEmpty(path)) {
|
||||
return null;
|
||||
@@ -232,8 +241,8 @@ public class TexturePackAPI {
|
||||
ResourceLocation resource;
|
||||
if (path.startsWith("~/")) {
|
||||
// Relative to namespace mcpatcher dir:
|
||||
// ~/path -> assets/(namespace of base file)/mcpatcher/path
|
||||
resource = new ResourceLocation(baseResource.getResourceDomain(), MCPATCHER_SUBDIR + path.substring(2));
|
||||
// ~/path -> assets/(namespace of base file)/(mcpatcher|optifine)/path
|
||||
resource = new ResourceLocation(baseResource.getResourceDomain(), subdirOf(baseResource) + path.substring(2));
|
||||
} else if (path.startsWith("./")) {
|
||||
// Relative to properties file:
|
||||
// ./path -> (dir of base file)/path
|
||||
|
||||
@@ -12,9 +12,7 @@ public class NotFineConfig {
|
||||
private static final String CATEGORY_TOGGLE = "toggle";
|
||||
private static final String CATEGORY_UNFINISHED = "unfinished";
|
||||
|
||||
public static boolean allowAdvancedOpenGL;
|
||||
public static boolean allowToggle3DAnaglyph;
|
||||
public static boolean allowToggleFBO;
|
||||
|
||||
public static boolean betterBlockFaceCulling;
|
||||
public static boolean renderPass;
|
||||
@@ -23,12 +21,7 @@ public class NotFineConfig {
|
||||
File configFile = new File(Launch.minecraftHome + File.separator + "config" + File.separator + NotFine.MODID + File.separator + "notfine.cfg");
|
||||
Configuration config = new Configuration(configFile);
|
||||
|
||||
allowAdvancedOpenGL = config.getBoolean("allowAdvancedOpenGL", CATEGORY_GENERAL, false,
|
||||
"Allow Advanced OpenGL to be enabled when it might be supported.");
|
||||
allowToggle3DAnaglyph = config.getBoolean("allowToggle3DAnaglyph", CATEGORY_GENERAL, true,
|
||||
"Allow 3D Anaglyph to be enabled.");
|
||||
allowToggleFBO = config.getBoolean("allowToggleFBO", CATEGORY_GENERAL, false,
|
||||
"Allow FBOs to be disabled.");
|
||||
allowToggle3DAnaglyph = config.getBoolean("allowToggle3DAnaglyph", CATEGORY_GENERAL, true, "Allow 3D Anaglyph to be enabled.");
|
||||
|
||||
config.setCategoryComment(CATEGORY_TOGGLE, "Toggle mod features.");
|
||||
betterBlockFaceCulling = config.getBoolean("betterBlockFaceCulling", CATEGORY_TOGGLE, true,
|
||||
@@ -43,10 +36,7 @@ public class NotFineConfig {
|
||||
config.save();
|
||||
}
|
||||
|
||||
//These will probably break hard with Angelica.
|
||||
//AdvancedOpenGL even breaks hard on its own.
|
||||
allowAdvancedOpenGL = false;
|
||||
allowToggleFBO = false;
|
||||
//This will probably break hard with Angelica.
|
||||
renderPass = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -243,21 +243,6 @@ public class NotFineGameOptionPages {
|
||||
List<OptionGroup> groups = new ArrayList<>();
|
||||
|
||||
groups.add(OptionGroup.createBuilder()
|
||||
.add(OptionImpl.createBuilder(boolean.class, vanillaOpts)
|
||||
.setName(I18n.format("options.advancedOpengl"))
|
||||
.setTooltip(I18n.format("sodium.options.advanced_opengl.tooltip"))
|
||||
.setControl(TickBoxControl::new)
|
||||
.setBinding((opts, value) -> opts.advancedOpengl = value, opts -> opts.advancedOpengl)
|
||||
.setImpact(OptionImpact.VARIES)
|
||||
.setEnabled(NotFineConfig.allowAdvancedOpenGL)
|
||||
.build())
|
||||
.add(OptionImpl.createBuilder(boolean.class, vanillaOpts)
|
||||
.setName(I18n.format("options.fboEnable"))
|
||||
.setTooltip(I18n.format("sodium.options.fbo.tooltip"))
|
||||
.setControl(TickBoxControl::new)
|
||||
.setBinding((opts, value) -> opts.fboEnable = value, opts -> opts.fboEnable)
|
||||
.setEnabled(NotFineConfig.allowToggleFBO)
|
||||
.build())
|
||||
.add(OptionImpl.createBuilder(boolean.class, vanillaOpts)
|
||||
.setName(I18n.format("options.anaglyph"))
|
||||
.setTooltip(I18n.format("sodium.options.anaglyph.tooltip"))
|
||||
|
||||
@@ -18,15 +18,9 @@ public class ClientProxy extends CommonProxy {
|
||||
FMLCommonHandler.instance().bus().register(this);
|
||||
MinecraftForge.EVENT_BUS.register(this);
|
||||
|
||||
if(!NotFineConfig.allowAdvancedOpenGL) {
|
||||
Minecraft.getMinecraft().gameSettings.advancedOpengl = false;
|
||||
}
|
||||
if(!NotFineConfig.allowToggle3DAnaglyph) {
|
||||
Minecraft.getMinecraft().gameSettings.anaglyph = false;
|
||||
}
|
||||
if(!NotFineConfig.allowToggleFBO) {
|
||||
Minecraft.getMinecraft().gameSettings.fboEnable = true;
|
||||
}
|
||||
|
||||
for(Settings setting : Settings.values()) {
|
||||
setting.ready();
|
||||
|
||||
+4
-13
@@ -105,9 +105,9 @@ public class ReeseSodiumVideoOptionsScreen extends SodiumOptionsGUI {
|
||||
final Dim2i applyButtonDim = new Dim2i(tabFrameDim.getLimitX() - 134, tabFrameDim.getLimitY() + 5, 65, 20);
|
||||
final Dim2i closeButtonDim = new Dim2i(tabFrameDim.getLimitX() - 65, tabFrameDim.getLimitY() + 5, 65, 20);
|
||||
|
||||
this.undoButton = new FlatButtonWidget(undoButtonDim, "Undo", this::undoChanges);
|
||||
this.applyButton = new FlatButtonWidget(applyButtonDim, "Apply", this::applyChanges);
|
||||
this.closeButton = new FlatButtonWidget(closeButtonDim, "Close", this::onClose);
|
||||
this.undoButton = new FlatButtonWidget(undoButtonDim, I18n.format("sodium.options.buttons.undo"), this::undoChanges);
|
||||
this.applyButton = new FlatButtonWidget(applyButtonDim, I18n.format("sodium.options.buttons.apply"), this::applyChanges);
|
||||
this.closeButton = new FlatButtonWidget(closeButtonDim, I18n.format("gui.done"), this::onClose);
|
||||
|
||||
// Pre-compute button text and widths to avoid duplicate calculations
|
||||
final String irisText = Iris.enabled ? I18n.format(IrisApi.getInstance().getMainScreenLanguageKey()) : null;
|
||||
@@ -202,16 +202,7 @@ public class ReeseSodiumVideoOptionsScreen extends SodiumOptionsGUI {
|
||||
}
|
||||
|
||||
private void updateControls() {
|
||||
boolean hasChanges = this.getAllOptions()
|
||||
.anyMatch(Option::hasChanged);
|
||||
|
||||
for (OptionPage page : this.pages) {
|
||||
for (Option<?> option : page.getOptions()) {
|
||||
if (option.hasChanged()) {
|
||||
hasChanges = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
final boolean hasChanges = this.getAllOptions().anyMatch(Option::hasChanged);
|
||||
|
||||
this.applyButton.setEnabled(hasChanges);
|
||||
this.undoButton.setVisible(hasChanges);
|
||||
|
||||
+1
-5
@@ -93,11 +93,7 @@ public class SearchTextFieldComponent extends AbstractWidget {
|
||||
return;
|
||||
}
|
||||
if (!this.isFocused() && this.text.isEmpty()) {
|
||||
final String key = "rso.search_bar_empty";
|
||||
String text = I18n.format(key);
|
||||
if (text.equals(key))
|
||||
text = "Search options...";
|
||||
this.textRenderer.drawString(text, this.dim.getOriginX() + 6, this.dim.getOriginY() + 6, 0xFFAAAAAA);
|
||||
this.textRenderer.drawString(I18n.format("rso.search_bar_empty"), this.dim.getOriginX() + 6, this.dim.getOriginY() + 6, 0xFFAAAAAA);
|
||||
}
|
||||
|
||||
drawRect(this.dim.getOriginX(), this.dim.getOriginY(), this.dim.getLimitX(), this.dim.getLimitY(), this.isFocused() ? 0xE0000000 : 0x90000000);
|
||||
|
||||
@@ -303,9 +303,10 @@ public class SodiumGameOptionPages {
|
||||
.add(OptionImpl.createBuilder(StreamingUploader.UploadStrategy.class, sodiumOpts)
|
||||
.setName(I18n.format("sodium.options.upload_method.name"))
|
||||
.setTooltip(I18n.format("sodium.options.upload_method.tooltip"))
|
||||
.setControl(o ->
|
||||
new CyclingControl<>(o, StreamingUploader.UploadStrategy.class, StreamingUploader.UploadStrategy.values())
|
||||
)
|
||||
.setControl(o -> new CyclingControl<>(o, StreamingUploader.UploadStrategy.class, new String[]{
|
||||
I18n.format("sodium.options.upload_method.buffer_data"),
|
||||
I18n.format("sodium.options.upload_method.buffer_subdata"),
|
||||
I18n.format("sodium.options.upload_method.map_buffer") }))
|
||||
.setBinding((opts, value) -> opts.advanced.streamingUploadStrategy = value, opts -> opts.advanced.streamingUploadStrategy)
|
||||
.setFlags(OptionFlag.REQUIRES_RENDERER_RELOAD)
|
||||
.setImpact(OptionImpact.VARIES)
|
||||
@@ -409,7 +410,10 @@ public class SodiumGameOptionPages {
|
||||
.add(OptionImpl.createBuilder(AsyncOcclusionMode.class, sodiumOpts)
|
||||
.setName(I18n.format("sodium.options.async_occlusion_mode.name"))
|
||||
.setTooltip(I18n.format("sodium.options.async_occlusion_mode.tooltip"))
|
||||
.setControl(o -> new CyclingControl<>(o, AsyncOcclusionMode.class, new String[]{"None", "Only Shadow", "Everything"}))
|
||||
.setControl(o -> new CyclingControl<>(o, AsyncOcclusionMode.class, new String[]{
|
||||
I18n.format("sodium.options.async_occlusion_mode.none"),
|
||||
I18n.format("sodium.options.async_occlusion_mode.only_shadow"),
|
||||
I18n.format("sodium.options.async_occlusion_mode.everything") }))
|
||||
.setImpact(OptionImpact.MEDIUM)
|
||||
.setBinding((opts, value) -> opts.performance.asyncOcclusionMode = value, opts -> opts.performance.asyncOcclusionMode)
|
||||
.setFlags(OptionFlag.REQUIRES_RENDERER_RELOAD)
|
||||
|
||||
@@ -54,7 +54,6 @@ public class SodiumOptionsGUI extends ScrollableGuiScreen {
|
||||
|
||||
protected OptionPage shaderPacks;
|
||||
|
||||
|
||||
public SodiumOptionsGUI(GuiScreen prevScreen) {
|
||||
this.prevScreen = prevScreen;
|
||||
|
||||
@@ -67,10 +66,7 @@ public class SodiumOptionsGUI extends ScrollableGuiScreen {
|
||||
|
||||
// Add Dynamic Lights page if enabled
|
||||
if (DynamicLights.configEnabled) {
|
||||
var dlPage = DynamicLightsOptionPages.dynamicLights();
|
||||
if (dlPage != null) {
|
||||
this.pages.add(dlPage);
|
||||
}
|
||||
this.pages.add(DynamicLightsOptionPages.dynamicLights());
|
||||
}
|
||||
|
||||
if (Iris.enabled) {
|
||||
@@ -194,15 +190,7 @@ public class SodiumOptionsGUI extends ScrollableGuiScreen {
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
|
||||
boolean hasChanges = this.getAllOptions().anyMatch(Option::hasChanged);
|
||||
|
||||
for (OptionPage page : this.pages) {
|
||||
for (Option<?> option : page.getOptions()) {
|
||||
if (option.hasChanged()) {
|
||||
hasChanges = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
final boolean hasChanges = this.getAllOptions().anyMatch(Option::hasChanged);
|
||||
|
||||
this.applyButton.setEnabled(hasChanges);
|
||||
this.undoButton.setVisible(hasChanges);
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
package me.jellysquid.mods.sodium.client.gui.options;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.function.Function;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiScreen;
|
||||
|
||||
import me.jellysquid.mods.sodium.client.gui.options.control.Control;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.control.ControlElement;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.control.element.ControlElementFactory;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.control.element.SodiumControlElement;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.storage.OptionStorage;
|
||||
import me.jellysquid.mods.sodium.client.util.Dim2i;
|
||||
|
||||
/** "Option" that opens a sub screen */
|
||||
public class SubScreenOption implements Option<Void> {
|
||||
|
||||
private static final int ARROW_HOVERED = 0xFF94E4D3;
|
||||
private static final int ARROW_IDLE = 0xFFAAAAAA;
|
||||
private static final String ARROW = ">";
|
||||
|
||||
private static final OptionStorage<Void> NO_STORAGE = new OptionStorage<Void>() {
|
||||
|
||||
@Override
|
||||
public Void getData() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save() {}
|
||||
};
|
||||
|
||||
private final String name;
|
||||
private final String tooltip;
|
||||
private final Function<GuiScreen, GuiScreen> destination;
|
||||
private final Control<Void> control = new SubScreenControl(this);
|
||||
|
||||
public SubScreenOption(String name, String tooltip, Function<GuiScreen, GuiScreen> destination) {
|
||||
this.name = name;
|
||||
this.tooltip = tooltip;
|
||||
this.destination = destination;
|
||||
}
|
||||
|
||||
private void open() {
|
||||
final var mc = Minecraft.getMinecraft();
|
||||
mc.displayGuiScreen(this.destination.apply(mc.currentScreen));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTooltip() {
|
||||
return this.tooltip;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OptionImpact getImpact() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Control<Void> getControl() {
|
||||
return this.control;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void getValue() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(Void value) {}
|
||||
|
||||
@Override
|
||||
public void reset() {}
|
||||
|
||||
@Override
|
||||
public OptionStorage<?> getStorage() {
|
||||
return NO_STORAGE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAvailable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasChanged() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyChanges() {}
|
||||
|
||||
@Override
|
||||
public Collection<OptionFlag> getFlags() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
private record SubScreenControl(SubScreenOption target) implements Control<Void> {
|
||||
|
||||
@Override
|
||||
public Option<Void> getOption() {
|
||||
return this.target;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ControlElement<Void> createElement(Dim2i dim, ControlElementFactory factory) {
|
||||
return new SubScreenElement(this.target, dim);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxWidth() {
|
||||
return 30;
|
||||
}
|
||||
}
|
||||
|
||||
private static class SubScreenElement extends SodiumControlElement<Void> {
|
||||
|
||||
private final SubScreenOption target;
|
||||
|
||||
SubScreenElement(SubScreenOption option, Dim2i dim) {
|
||||
super(option, dim);
|
||||
this.target = option;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(int mouseX, int mouseY, float delta) {
|
||||
super.render(mouseX, mouseY, delta);
|
||||
|
||||
final var width = this.font.getStringWidth(ARROW);
|
||||
this.drawString(ARROW, this.dim.getLimitX() - width - 6, this.dim.getCenterY() - 4, this.hovered ? ARROW_HOVERED : ARROW_IDLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mouseClicked(double mouseX, double mouseY, int button) {
|
||||
if (button != 0 || !this.dim.containsCursor(mouseX, mouseY)) return false;
|
||||
|
||||
this.playClickSound();
|
||||
this.target.open();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import com.gtnewhorizons.angelica.glsm.RenderSystem;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.BackendManager;
|
||||
import com.gtnewhorizons.angelica.glsm.shader.GlslVulkanPreprocess;
|
||||
import com.gtnewhorizons.angelica.glsm.shader.SpirvCompiler;
|
||||
import com.gtnewhorizons.angelica.iris.ImmediateExtendedAttribs;
|
||||
import com.gtnewhorizons.angelica.iris.IrisGLSMBridge;
|
||||
import com.gtnewhorizons.angelica.proxy.ClientProxy;
|
||||
import com.gtnewhorizons.angelica.rendering.StateAwareTessellator;
|
||||
@@ -343,11 +344,11 @@ public class Iris {
|
||||
final Minecraft mc = Minecraft.getMinecraft();
|
||||
try {
|
||||
reload();
|
||||
if (mc.thePlayer != null) mc.thePlayer.addChatMessage(new ChatComponentText("Shaders Reloaded!"));
|
||||
if (mc.thePlayer != null) mc.thePlayer.addChatMessage(new ChatComponentText(I18n.format("iris.shaders.reloaded")));
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("Error while reloading Shaders for Iris!", e);
|
||||
if (mc.thePlayer != null) mc.thePlayer.addChatMessage(new ChatComponentText( "Failed tgo reload shaders! Reason: " + Throwables.getRootCause(e).getMessage()));
|
||||
if (mc.thePlayer != null) mc.thePlayer.addChatMessage(new ChatComponentText(I18n.format("iris.shaders.reloaded.failure", Throwables.getRootCause(e).getMessage())));
|
||||
}
|
||||
} else if (toggleShadersKeybind.isPressed()) {
|
||||
final Minecraft mc = Minecraft.getMinecraft();
|
||||
@@ -356,7 +357,7 @@ public class Iris {
|
||||
} catch (Exception e) {
|
||||
logger.error("Error while toggling shaders!", e);
|
||||
|
||||
if (mc.thePlayer != null) mc.thePlayer.addChatMessage(new ChatComponentText( "Failed tgo toggle shaders! Reason: " + Throwables.getRootCause(e).getMessage()));
|
||||
if (mc.thePlayer != null) mc.thePlayer.addChatMessage(new ChatComponentText(I18n.format("iris.shaders.toggled.failure", Throwables.getRootCause(e).getMessage())));
|
||||
setShadersDisabled();
|
||||
fallback = true;
|
||||
}
|
||||
@@ -802,6 +803,9 @@ public class Iris {
|
||||
// Load the new shaderpack
|
||||
loadShaderpack();
|
||||
|
||||
// Drop the stale program probe and cached item geometry
|
||||
ImmediateExtendedAttribs.onShaderPackChanged();
|
||||
|
||||
// Very important - we need to re-create the pipeline straight away.
|
||||
// https://github.com/IrisShaders/Iris/issues/1330
|
||||
if (Minecraft.getMinecraft().theWorld != null) {
|
||||
@@ -974,9 +978,9 @@ public class Iris {
|
||||
|
||||
public void fmlInitEvent() {
|
||||
IRIS_VERSION = Tags.VERSION;
|
||||
reloadKeybind = new KeyBinding("Reload Shaders", 0, "Iris Keybinds");
|
||||
toggleShadersKeybind = new KeyBinding("Toggle Shaders", 0, "Iris Keybinds");
|
||||
shaderpackScreenKeybind = new KeyBinding("Shaderpack Selection Screen", 0, "Iris Keybinds");
|
||||
reloadKeybind = new KeyBinding("iris.keybind.reload", 0, "key.category.iris.keybinds");
|
||||
toggleShadersKeybind = new KeyBinding("iris.keybind.toggleShaders", 0, "key.category.iris.keybinds");
|
||||
shaderpackScreenKeybind = new KeyBinding("iris.keybind.shaderPackSelection", 0, "key.category.iris.keybinds");
|
||||
|
||||
ClientRegistry.registerKeyBinding(reloadKeybind);
|
||||
ClientRegistry.registerKeyBinding(toggleShadersKeybind);
|
||||
|
||||
@@ -5,6 +5,7 @@ package net.coderbot.iris.gl.shader;
|
||||
import com.gtnewhorizons.angelica.glsm.GLDebug;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.RenderSystem;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.ImmediateExtendedAttribHandler;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
@@ -15,10 +16,10 @@ public class ProgramCreator {
|
||||
|
||||
private static final Logger LOGGER = LogManager.getLogger("ProgramCreator");
|
||||
|
||||
public static final int MC_ENTITY = 11;
|
||||
public static final int MC_MID_TEX_COORD = 12;
|
||||
public static final int AT_TANGENT = 13;
|
||||
public static final int AT_MIDBLOCK = 14;
|
||||
public static final int MC_ENTITY = ImmediateExtendedAttribHandler.LOC_MC_ENTITY;
|
||||
public static final int MC_MID_TEX_COORD = ImmediateExtendedAttribHandler.LOC_MID_TEX;
|
||||
public static final int AT_TANGENT = ImmediateExtendedAttribHandler.LOC_TANGENT;
|
||||
public static final int AT_MIDBLOCK = ImmediateExtendedAttribHandler.LOC_MIDBLOCK;
|
||||
|
||||
public static int create(String name, GlShader... shaders) {
|
||||
int program = GLStateManager.glCreateProgram();
|
||||
|
||||
@@ -4,6 +4,7 @@ import lombok.Getter;
|
||||
import net.coderbot.iris.Iris;
|
||||
import net.coderbot.iris.gbuffer_overrides.matching.SpecialCondition;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.FfpExtendedAttribs;
|
||||
import com.gtnewhorizons.angelica.glsm.states.AlphaState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.BlendState;
|
||||
import net.coderbot.iris.gl.shader.ProgramCreator;
|
||||
@@ -240,8 +241,7 @@ public class GbufferPrograms {
|
||||
|
||||
public static void setBlockEntityDefaults() {
|
||||
GLStateManager.glVertexAttrib2s(ProgramCreator.MC_ENTITY, (short)-1, (short)-1);
|
||||
GLStateManager.glVertexAttrib2f(ProgramCreator.MC_MID_TEX_COORD, 0.5f, 0.5f);
|
||||
GLStateManager.glVertexAttrib4f(ProgramCreator.AT_TANGENT, 1.0f, 0.0f, 0.0f, 1.0f);
|
||||
FfpExtendedAttribs.setNeutralCurrentValues();
|
||||
GLStateManager.glVertexAttrib4f(ProgramCreator.AT_MIDBLOCK, 0.0f, 0.0f, 0.0f, 0.0f);
|
||||
}
|
||||
|
||||
|
||||
@@ -87,7 +87,6 @@ class AttributeTransformer {
|
||||
|
||||
if (instancedVertex) {
|
||||
foldConstantAttribute(transformer, "mc_Entity", "-1.0", "vec2(-1.0, -1.0)", "vec3(-1.0, -1.0, 0.0)", "vec4(-1.0, -1.0, 0.0, 1.0)");
|
||||
foldConstantAttribute(transformer, "mc_midTexCoord", "0.5", "vec2(0.5, 0.5)", "vec3(0.5, 0.5, 0.0)", "vec4(0.5, 0.5, 0.0, 1.0)");
|
||||
|
||||
final StringBuilder init = new StringBuilder(256);
|
||||
init.append("{ iris_ModelViewMatrix = mat4(iris_InstMat0, iris_InstMat1, iris_InstMat2, iris_InstMat3);");
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package net.coderbot.iris.pipeline.transform;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.InstancedAttribs;
|
||||
import org.taumc.glsl.Transformer;
|
||||
|
||||
import java.util.HashMap;
|
||||
@@ -20,12 +21,12 @@ class CoreTransformHelper {
|
||||
|
||||
static void injectMatrixUniforms(Transformer transformer, boolean instancedVertex) {
|
||||
if (instancedVertex) {
|
||||
transformer.injectVariable("layout(location = 7) in vec4 iris_InstMat0;");
|
||||
transformer.injectVariable("layout(location = 8) in vec4 iris_InstMat1;");
|
||||
transformer.injectVariable("layout(location = 9) in vec4 iris_InstMat2;");
|
||||
transformer.injectVariable("layout(location = 10) in vec4 iris_InstMat3;");
|
||||
transformer.injectVariable("layout(location = 11) in vec4 iris_InstColor;");
|
||||
transformer.injectVariable("layout(location = 12) in vec2 iris_InstLightmap;");
|
||||
transformer.injectVariable("layout(location = " + InstancedAttribs.LOC_MATRIX_COL0 + ") in vec4 iris_InstMat0;");
|
||||
transformer.injectVariable("layout(location = " + InstancedAttribs.LOC_MATRIX_COL1 + ") in vec4 iris_InstMat1;");
|
||||
transformer.injectVariable("layout(location = " + InstancedAttribs.LOC_MATRIX_COL2 + ") in vec4 iris_InstMat2;");
|
||||
transformer.injectVariable("layout(location = " + InstancedAttribs.LOC_MATRIX_COL3 + ") in vec4 iris_InstMat3;");
|
||||
transformer.injectVariable("layout(location = " + InstancedAttribs.LOC_COLOR + ") in vec4 iris_InstColor;");
|
||||
transformer.injectVariable("layout(location = " + InstancedAttribs.LOC_LIGHTMAP + ") in vec2 iris_InstLightmap;");
|
||||
transformer.injectVariable("mat4 iris_ModelViewMatrix;");
|
||||
transformer.injectVariable("mat4 iris_ModelViewMatrixInverse;");
|
||||
transformer.injectVariable("mat3 iris_NormalMatrix;");
|
||||
|
||||
@@ -89,7 +89,7 @@ public class PBRAtlasTexture extends AutoClosableAbstractTexture {
|
||||
}
|
||||
|
||||
if (AngelicaConfig.enablePBRDebug) {
|
||||
TextureExporter.exportTextures("pbr_debug/atlas", id.getResourceDomain() + "_" + id.getResourcePath().replaceAll("/", "_"), glId, mipLevel, atlasWidth, atlasHeight);
|
||||
TextureExporter.exportTextures("pbr_debug/atlas", id.getResourceDomain() + "_" + id.getResourcePath().replace("/", "_") + ".png", glId, mipLevel, atlasWidth, atlasHeight);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,10 +2,10 @@ package net.coderbot.iris.texture.pbr.loader;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.gtnewhorizons.angelica.compat.mojang.NativeImage;
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
import com.gtnewhorizons.angelica.glsm.texture.TextureInfo;
|
||||
import com.gtnewhorizons.angelica.glsm.texture.TextureInfoCache;
|
||||
import com.gtnewhorizons.angelica.mixins.interfaces.ISpriteExt;
|
||||
import me.jellysquid.mods.sodium.client.gui.SodiumGameOptions;
|
||||
import net.coderbot.iris.Iris;
|
||||
import net.coderbot.iris.texture.format.TextureFormat;
|
||||
import net.coderbot.iris.texture.format.TextureFormatLoader;
|
||||
@@ -17,10 +17,13 @@ import net.coderbot.iris.texture.pbr.PBRSpriteHolder;
|
||||
import net.coderbot.iris.texture.pbr.PBRType;
|
||||
import net.coderbot.iris.texture.pbr.TextureAtlasSpriteExtension;
|
||||
import net.coderbot.iris.texture.util.ImageManipulationUtil;
|
||||
import net.coderbot.iris.texture.util.TextureExporter;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.client.renderer.texture.TextureMap;
|
||||
import net.minecraft.client.renderer.texture.TextureUtil;
|
||||
import net.minecraft.client.resources.IResource;
|
||||
import net.minecraft.client.resources.IResourceManager;
|
||||
import net.minecraft.client.resources.data.AnimationFrame;
|
||||
import net.minecraft.client.resources.data.AnimationMetadataSection;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
@@ -28,7 +31,11 @@ import org.apache.commons.lang3.tuple.Pair;
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class AtlasPBRLoader implements PBRTextureLoader<TextureMap> {
|
||||
public static final ChannelMipmapGenerator LINEAR_MIPMAP_GENERATOR = new ChannelMipmapGenerator(
|
||||
@@ -84,6 +91,45 @@ public class AtlasPBRLoader implements PBRTextureLoader<TextureMap> {
|
||||
pbrTextureConsumer.acceptSpecularTexture(specularAtlas);
|
||||
}
|
||||
}
|
||||
|
||||
if (AngelicaConfig.enablePBRDebug && (normalAtlas != null || specularAtlas != null)) {
|
||||
TextureExporter.exportTextures("pbr_debug/atlas", "base_atlas.png", texMap.getGlTextureId(), mipLevel, atlasWidth, atlasHeight);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static NativeImage padAnisotropic(NativeImage image, int frameWidth, int frameHeight) {
|
||||
final int width = image.getWidth();
|
||||
final int height = image.getHeight();
|
||||
final int frames = (frameWidth == width && frameHeight > 0) ? height / frameHeight : 0;
|
||||
if (frames < 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final int paddedWidth = frameWidth + 16;
|
||||
final int paddedHeight = frameHeight + 16;
|
||||
final int framePixels = paddedWidth * paddedHeight;
|
||||
|
||||
final NativeImage padded = new NativeImage(paddedWidth, paddedHeight * frames, false);
|
||||
final int[] frame = new int[framePixels];
|
||||
for (int f = 0; f < frames; f++) {
|
||||
Arrays.fill(frame, 0);
|
||||
image.getRGB(0, f * frameHeight, frameWidth, frameHeight, frame, 0, frameWidth);
|
||||
TextureUtil.prepareAnisotropicData(frame, frameWidth, frameHeight, 8);
|
||||
padded.setRGB(0, f * paddedHeight, paddedWidth, paddedHeight, frame, 0, paddedWidth);
|
||||
}
|
||||
return padded;
|
||||
}
|
||||
|
||||
private static AnimationMetadataSection withFrameSize(AnimationMetadataSection src, int frameWidth, int frameHeight) {
|
||||
final List<AnimationFrame> frames = new ArrayList<>(src.getFrameCount());
|
||||
for (int f = 0; f < src.getFrameCount(); f++) {
|
||||
frames.add(new AnimationFrame(src.getFrameIndex(f), src.frameHasTime(f) ? src.getFrameTimeSingle(f) : -1));
|
||||
}
|
||||
return new AnimationMetadataSection(frames,
|
||||
src.getFrameWidth() != -1 ? frameWidth : -1,
|
||||
src.getFrameHeight() != -1 ? frameHeight : -1,
|
||||
src.getFrameTime());
|
||||
}
|
||||
|
||||
protected static int fetchAtlasMipLevel(TextureMap texMap) {
|
||||
@@ -104,18 +150,15 @@ public class AtlasPBRLoader implements PBRTextureLoader<TextureMap> {
|
||||
NativeImage nativeImage = NativeImage.read(resource.getInputStream());
|
||||
AnimationMetadataSection animationMetadata = (AnimationMetadataSection) resource.getMetadata("animation");
|
||||
if (animationMetadata == null) {
|
||||
final IResource resourceOriginal = resourceManager.getResource(imageLocation);
|
||||
animationMetadata = (AnimationMetadataSection) resourceOriginal.getMetadata("animation");
|
||||
if (animationMetadata == null) {
|
||||
animationMetadata = new AnimationMetadataSection(Lists.newArrayList(), -1, -1, -1);
|
||||
}
|
||||
animationMetadata = new AnimationMetadataSection(Lists.newArrayList(), -1, -1, -1);
|
||||
}
|
||||
|
||||
final Pair<Integer, Integer> frameSize = this.getFrameSize(nativeImage.getWidth(), nativeImage.getHeight(), animationMetadata);
|
||||
int frameWidth = frameSize.getLeft();
|
||||
int frameHeight = frameSize.getRight();
|
||||
final int targetFrameWidth = sprite.getIconWidth();
|
||||
final int targetFrameHeight = sprite.getIconHeight();
|
||||
final int spritePadding = sprite.useAnisotropicFiltering ? 16 : 0;
|
||||
final int targetFrameWidth = sprite.getIconWidth() - spritePadding;
|
||||
final int targetFrameHeight = sprite.getIconHeight() - spritePadding;
|
||||
if (frameWidth != targetFrameWidth || frameHeight != targetFrameHeight) {
|
||||
final int imageWidth = nativeImage.getWidth();
|
||||
final int imageHeight = nativeImage.getHeight();
|
||||
@@ -139,15 +182,8 @@ public class AtlasPBRLoader implements PBRTextureLoader<TextureMap> {
|
||||
frameWidth = targetFrameWidth;
|
||||
frameHeight = targetFrameHeight;
|
||||
|
||||
if (!animationMetadata.equals(new AnimationMetadataSection(Lists.newArrayList(), -1, -1, -1))) {
|
||||
final int internalFrameWidth = animationMetadata.frameHeight;
|
||||
final int internalFrameHeight = animationMetadata.frameWidth;
|
||||
if (internalFrameWidth != -1) {
|
||||
animationMetadata.frameWidth = frameWidth;
|
||||
}
|
||||
if (internalFrameHeight != -1) {
|
||||
animationMetadata.frameHeight = frameHeight;
|
||||
}
|
||||
if (animationMetadata.getFrameWidth() != -1 || animationMetadata.getFrameHeight() != -1) {
|
||||
animationMetadata = withFrameSize(animationMetadata, frameWidth, frameHeight);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,7 +192,15 @@ public class AtlasPBRLoader implements PBRTextureLoader<TextureMap> {
|
||||
|
||||
final int x = sprite.getOriginX();
|
||||
final int y = sprite.getOriginY();
|
||||
pbrSprite = new PBRTextureAtlasSprite(pbrSpriteInfo, animationMetadata, atlasWidth, atlasHeight, x, y, nativeImage, texMap, mipLevel);
|
||||
boolean padInLoadSprite = sprite.useAnisotropicFiltering;
|
||||
if (padInLoadSprite) {
|
||||
final NativeImage padded = padAnisotropic(nativeImage, frameWidth, frameHeight);
|
||||
if (padded != null) {
|
||||
nativeImage = padded;
|
||||
padInLoadSprite = false;
|
||||
}
|
||||
}
|
||||
pbrSprite = new PBRTextureAtlasSprite(pbrSpriteInfo, animationMetadata, atlasWidth, atlasHeight, x, y, nativeImage, padInLoadSprite, mipLevel);
|
||||
syncAnimation(sprite, pbrSprite);
|
||||
} catch (FileNotFoundException e) {
|
||||
//
|
||||
@@ -217,12 +261,10 @@ public class AtlasPBRLoader implements PBRTextureLoader<TextureMap> {
|
||||
|
||||
public static class PBRTextureAtlasSprite extends TextureAtlasSprite implements CustomMipmapGenerator.Provider {
|
||||
// This feels super janky
|
||||
protected PBRTextureAtlasSprite(TextureAtlasSpriteInfo info, AnimationMetadataSection animationMetaDataSection, int atlasWidth, int atlasHeight, int x, int y, NativeImage nativeImage, TextureMap texMap, int miplevel) {
|
||||
protected PBRTextureAtlasSprite(TextureAtlasSpriteInfo info, AnimationMetadataSection animationMetaDataSection, int atlasWidth, int atlasHeight, int x, int y, NativeImage nativeImage, boolean useAnisotropicFiltering, int miplevel) {
|
||||
super(info.name().toString());
|
||||
super.loadSprite(Objects.requireNonNull(getMipmapGenerator(info, atlasWidth, atlasHeight)).generateMipLevels(nativeImage, miplevel), animationMetaDataSection, useAnisotropicFiltering);
|
||||
super.initSprite(atlasWidth, atlasHeight, x, y, false);
|
||||
final boolean useAnisotropicFiltering = texMap.anisotropicFiltering > 1
|
||||
|| SodiumGameOptions.needsForcedSpritePadding();
|
||||
super.loadSprite(getMipmapGenerator(info, atlasWidth, atlasHeight).generateMipLevels(nativeImage, miplevel), animationMetaDataSection, useAnisotropicFiltering);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -101,13 +101,18 @@ sodium.options.use_no_error_context.name=No Error Context
|
||||
sodium.options.use_no_error_context.tooltip=If enabled, the OpenGL context will be created with error checking disabled. This may slightly improve performance, but it also increases the risk that the game will crash instead of gracefully handling OpenGL errors. You should disable this option if you are experiencing sudden unexplained crashes.
|
||||
sodium.options.async_occlusion_mode.name=Async Occlusion
|
||||
sodium.options.async_occlusion_mode.tooltip=Controls which render passes use asynchronous occlusion culling. ONLY_SHADOW (default) runs async occlusion only for the shadow pass, keeping the main render list synchronous. EVERYTHING runs all passes async, which may cause 1-frame visibility lag during fast camera movement. NONE disables async occlusion entirely.
|
||||
sodium.options.async_occlusion_mode.none=None
|
||||
sodium.options.async_occlusion_mode.only_shadow=Only Shadow
|
||||
sodium.options.async_occlusion_mode.everything=Everything
|
||||
sodium.options.use_occlusion_culling.name=Occlusion Culling
|
||||
sodium.options.use_occlusion_culling.tooltip=If enabled, chunks that are hidden behind other chunks will not be rendered. Disabling this will significantly reduce performance but can be useful for debugging.
|
||||
sodium.options.threads.value=%s thread(s)
|
||||
sodium.options.default=Default
|
||||
sodium.options.buttons.undo=Undo
|
||||
sodium.options.buttons.apply=Apply
|
||||
sodium.options.buttons.donate=Buy coffee for JellySquid!
|
||||
sodium.options.anaglyph.tooltip=If enabled, the world will be rendered twice with red/cyan color masks.
|
||||
sodium.options.show_cape.tooltip=If enabled, your cape will be shown.
|
||||
rso.search_bar_empty=Search options...
|
||||
|
||||
options.dynamic_fov=Dynamic FOV
|
||||
options.dynamic_fov.tooltip=If enabled, the game's field of view will change based on the player's speed.
|
||||
@@ -115,6 +120,15 @@ options.framerate=%s FPS
|
||||
|
||||
iris.shaders.toggled=Toggled shaders to %s!
|
||||
iris.shaders.disabled=Shaders disabled!
|
||||
iris.shaders.reloaded=Shaders Reloaded!
|
||||
iris.shaders.reloaded.failure=Failed to reload shaders! Reason: %s
|
||||
iris.shaders.toggled.failure=Failed to toggle shaders! Reason: %s
|
||||
|
||||
key.category.iris.keybinds=Iris Keybinds
|
||||
iris.keybind.reload=Reload Shaders
|
||||
iris.keybind.toggleShaders=Toggle Shaders
|
||||
iris.keybind.shaderPackSelection=Shaderpack Selection Screen
|
||||
angelica.keybind.zoom=Zoom
|
||||
|
||||
options.iris.apply=Apply
|
||||
options.iris.refresh=Refresh
|
||||
@@ -176,10 +190,14 @@ options.dynamic_lights_cull_timeout=Culling Timeout (ticks)
|
||||
options.dynamic_lights_cull_timeout.tooltip=Maximum ticks a hidden chunk can wait before being forced to rebuild. Lower = more responsive but more GPU work.
|
||||
options.dynamiclights.page=Dynamic Lights
|
||||
options.dynamiclights.entities=Configure Entities...
|
||||
options.dynamiclights.entities.tooltip=Choose which entity types emit dynamic light.
|
||||
options.dynamiclights.entities.title=Dynamic Lights - Entities
|
||||
options.dynamiclights.entities.info=Toggle which entity types emit dynamic light. Some entries are for mod compatibility.
|
||||
options.dynamiclights.entities.search=Search (@mod, $on, $off)
|
||||
options.dynamiclights.entity.tooltip=Toggle dynamic lighting for %s entities. Disable to improve performance if this entity type emits unwanted lights.
|
||||
options.dynamiclights.entities.enableall=Enable All
|
||||
options.dynamiclights.entities.disableall=Disable All
|
||||
options.dynamiclights.entities.enableshown=Enable Shown
|
||||
options.dynamiclights.entities.disableshown=Disable Shown
|
||||
|
||||
options.angelica.search.no_results=No matching options
|
||||
options.angelica.fontconfig=Font Configuration...
|
||||
@@ -282,4 +300,4 @@ sodium.options.upload_method.buffer_data=BufferData
|
||||
sodium.options.upload_method.buffer_subdata=BufferSubData
|
||||
sodium.options.upload_method.map_buffer=MapBufferRange
|
||||
sodium.options.enable_deferred_batching.name=Deferred Particle Batching
|
||||
sodium.options.enable_deferred_batching.tooltip=Batches particle draw calls into fewer merged draws per layer. Disable if particles render incorrectly.
|
||||
sodium.options.enable_deferred_batching.tooltip=Batches particle draw calls into fewer merged draws per layer. Disable if particles render incorrectly.
|
||||
@@ -161,7 +161,6 @@ options.dynamiclights.entities=エンティティの設定...
|
||||
options.dynamiclights.entities.title=ダイナミックライト- エンティティ
|
||||
options.dynamiclights.entities.info=ダイナミックライトを適用するエンティティの種類を設定します。一部の項目はMODとの互換性のために設定されています。
|
||||
options.dynamiclights.entities.search=検索 (@mod, $on, $off)
|
||||
options.dynamiclights.entity.tooltip=%sのエンティティのダイナミックライトを設定します。そのエンティティタイプが不要な光を発する場合は、パフォーマンス向上のために無効にしてください。
|
||||
|
||||
options.angelica.search.no_results=該当する設定が見つかりません
|
||||
options.angelica.fontconfig=フォント設定...
|
||||
|
||||
@@ -0,0 +1,303 @@
|
||||
options.button.shader=Шейдери...
|
||||
|
||||
options.chunks=%s чанків
|
||||
options.entityShadows=Тіні сутностей
|
||||
options.droppedItems=%s предметів
|
||||
options.droppedItems.max=Необмежено
|
||||
|
||||
sodium.option_impact.low=Низький
|
||||
sodium.option_impact.medium=Середній
|
||||
sodium.option_impact.high=Високий
|
||||
sodium.option_impact.extreme=Екстремальний
|
||||
sodium.option_impact.varies=Варіюється
|
||||
sodium.options.pages.quality=Якість
|
||||
sodium.options.pages.performance=Продуктивність
|
||||
sodium.options.pages.advanced=Додатково
|
||||
sodium.options.pages.appearance=Вигляд
|
||||
sodium.options.pages.text=Текстові ефекти
|
||||
sodium.options.view_distance.tooltip=Дальність промальовування контролює, як далеко відображатиметься ландшафт. Менша дальність означає, що відображатиметься менше ландшафту, що в підсумку покращує частоту кадрів.
|
||||
sodium.options.simulation_distance.tooltip=Дальність симуляції контролює, як далеко завантажуватимуться та оновлюватимуться ландшафт та сутності. Менша дальність може зменшити навантаження на внутрішній сервер і, можливо, підвищити частоту кадрів.
|
||||
sodium.options.cc.vertical_view_distance.name=Вертикальна дальність огляду
|
||||
sodium.options.cc.vertical_view_distance.tooltip=Вертикальна дальність огляду керує діапазоном по вертикалі, у межах якого завантажуватимуться блоки навколо гравця. Менша відстань означає, що відображатиметься менше ландшафту, що підвищує частоту кадрів.
|
||||
sodium.options.brightness.tooltip=Регулює яскравість (гамму) гри.
|
||||
sodium.options.clouds.name=Хмари
|
||||
sodium.options.clouds.tooltip=Контролює, чи будуть відображатися хмари в ігровому світі.
|
||||
sodium.options.gui_scale.tooltip=Встановлює максимальний масштаб інтерфейсу користувача. Якщо вибрано «Авто», завжди використовуватиметься найбільший можливий масштаб.
|
||||
sodium.options.fullscreen.tooltip=Якщо увімкнено, гра відображатиметься у повноекранному режимі (якщо це підтримується системою).
|
||||
sodium.options.v_sync.tooltip=«Вимк.» відображає кожен кадр одразу: найменша затримка, але можливі розриви зображення. «Увімк.» очікує на vblank і ставить новий кадр у чергу: без розривів, але з більшою затримкою. «Mailbox» очікує на vblank, але замінює кадр у черзі: без розривів, із затримкою, близькою до режиму «Вимк.».
|
||||
sodium.options.vsync_mode.off=Вимк.
|
||||
sodium.options.vsync_mode.on=Увімк.
|
||||
sodium.options.vsync_mode.mailbox=Mailbox
|
||||
sodium.options.fps_limit.tooltip=Обмежує максимальну частоту кадрів (FPS). Це допомагає зменшити споживання енергії акумулятора та загальне навантаження на систему під час багатозадачності. Застосовується в усіх режимах VSync.
|
||||
sodium.options.view_bobbing.tooltip=Якщо увімкнено, камера гравця буде погойдуватися під час руху. Гравцям, які схильні до захитування, рекомендується вимкнути цю опцію.
|
||||
sodium.options.attack_indicator.tooltip=Керує розташуванням індикатора атаки на екрані.
|
||||
sodium.options.autosave_indicator.tooltip=Якщо увімкнено, під час збереження світу на диск на екрані відображатиметься відповідний індикатор.
|
||||
sodium.options.graphics_quality.tooltip=Стандартна якість графіки керує деякими застарілими параметрами й необхідна для сумісності з модами. Якщо для наведених нижче опцій залишити значення «За замовчуванням», вони використовуватимуть це налаштування.
|
||||
sodium.options.clouds_quality.tooltip=Керує якістю візуалізації хмар у небі.
|
||||
sodium.options.weather_quality.tooltip=Керує якістю ефектів дощу та снігу.
|
||||
sodium.options.leaves_quality.name=Якість листя
|
||||
sodium.options.leaves_quality.tooltip=Керує якістю відображення листя.
|
||||
sodium.options.grass_quality.name=Якість трави
|
||||
sodium.options.grass_quality.tooltip=Керує якістю відображення бічних граней блоків трави.
|
||||
sodium.options.celeritas_smooth_lighting.name=М’яке освітлення Celeritas
|
||||
sodium.options.celeritas_smooth_lighting.tooltip=Якщо увімкнено, для м’якого освітлення використовуватиметься конвеєр обробки світла Celeritas. Ця опція працюватиме лише тоді, коли увімкнено загальне м’яке освітлення.
|
||||
sodium.options.texture_filtering.name=Фільтрація текстур
|
||||
sodium.options.texture_filtering.tooltip=Визначає, як відбувається вибірка текстур блоків на відстані.
|
||||
sodium.options.texture_filtering.rgss=RGSS
|
||||
sodium.options.texture_filtering.anisotropic=Анізотропна
|
||||
sodium.options.anisotropic_filtering.name=Анізотропна фільтрація
|
||||
sodium.options.anisotropic_filtering.tooltip=Покращує чіткість текстур, що розглядаються під гострими кутами.
|
||||
sodium.options.particle_quality.name=Якість частинок
|
||||
sodium.options.particle_quality.tooltip=Керує максимальною кількістю частинок, які можуть одночасно відображатися на екрані.
|
||||
sodium.options.smooth_lighting.tooltip=Визначає, чи будуть блоки мати м’яке освітлення та затінення. Це дещо збільшує час, необхідний для перебудови чанків, але не впливає на частоту кадрів.
|
||||
sodium.options.biome_blend.value=%s блок(ів)
|
||||
sodium.options.biome_blend.tooltip=Керує радіусом вибірки біомів для забарвлення блоків. Вищі значення значно збільшують час побудови чанків, натомість подальший приріст якості стає дедалі менш помітним.
|
||||
sodium.options.entity_distance.tooltip=Керує відстанню, на якій відображаються сутності від гравця. Вищі значення збільшують дальність відображення ціною частоти кадрів.
|
||||
sodium.options.entity_shadows.tooltip=Якщо увімкнено, під мобами та іншими сутностями відображатимуться прості тіні.
|
||||
sodium.options.vignette.name=Віньєтка
|
||||
sodium.options.vignette.tooltip=Якщо увімкнено, на екрані відображатиметься ефект затемнення по краях. Це навряд чи вплине на частоту кадрів, за винятком випадків, коли продуктивність обмежена швидкістю заповнення вашої відеокарти.
|
||||
sodium.options.mipmap_levels.tooltip=Керує кількістю рівнів MIP-текстурування для моделей блоків. Вищі значення покращують вигляд віддалених блоків, але можуть негативно вплинути на продуктивність, якщо у грі багато анімованих текстур.
|
||||
sodium.options.use_chunk_multidraw.name=Мультипромальовування чанків
|
||||
sodium.options.use_chunk_multidraw.tooltip=Мультипромальовування дозволяє рендерити кілька чанків із меншою кількістю викликів промальовування. Це суттєво знижує навантаження на процесор під час відтворення світу, а також дозволяє ефективніше використовувати відеокарту. Ця оптимізація може викликати проблеми з деякими графічними драйверами, тому її варто вимкнути, якщо ви помітили збої чи артефакти.
|
||||
sodium.options.use_vertex_objects.name=Об’єкти буферів вершин
|
||||
sodium.options.use_vertex_objects.tooltip=Допомагає підвищити продуктивність, передаючи інформацію про спосіб рендерингу вершин безпосередньо драйверу. Це дозволяє краще оптимізувати повторний рендеринг тих самих об’єктів. Зазвичай немає причин вимикати цей параметр, якщо ви не використовуєте несумісні моди.
|
||||
sodium.options.use_block_face_culling.name=Відсікання граней блоків
|
||||
sodium.options.use_block_face_culling.tooltip=Якщо увімкнено, для рендерингу передаватимуться лише ті грані блоків, які повернуті до камери. Це дозволяє відсікти величезну кількість невидимих поверхонь на ранньому етапі, заощаджуючи пропускну здатність пам’яті та ресурси відеокарти. Деякі ресурспаки можуть некоректно працювати з цим параметром, тому спробуйте вимкнути його, якщо бачите «дірки» в блоках.
|
||||
sodium.options.use_compact_vertex_format.name=Компактний формат вершин
|
||||
sodium.options.use_compact_vertex_format.tooltip=Якщо увімкнено, для рендерингу чанків використовуватиметься компактніший формат вершин. Це суттєво зменшує споживання відеопам’яті та вимоги до пропускної здатності (особливо актуально для інтегрованих відеокарт), проте через зниження точності координат може викликати миготіння текстур з деякими ресурспаками.
|
||||
sodium.options.use_fog_occlusion.name=Оклюзія туманом
|
||||
sodium.options.use_fog_occlusion.tooltip=Якщо увімкнено, чанки, які повністю приховані ефектом туману, не будуть рендеритися, що допомагає підвищити продуктивність. Приріст може бути помітнішим, коли туман стає густішим (наприклад, під водою), але в деяких сценаріях це може викликати небажані візуальні артефакти між небом та туманом.
|
||||
sodium.options.use_entity_culling.name=Відсікання сутностей
|
||||
sodium.options.use_entity_culling.tooltip=Якщо увімкнено, сутності, які не потрапляють у жоден із видимих чанків, пропускатимуться під час рендерингу. Це допомагає підвищити продуктивність, уникаючи промальовування сутностей, що перебувають під землею або за стінами.
|
||||
sodium.options.section_gated_tesr_culling.name=Відсікання TESR за секціями
|
||||
sodium.options.section_gated_tesr_culling.tooltip=Якщо увімкнено, перевірка видимості блоків-сутностей (скринь, механізмів) виконується одразу для всієї секції чанку, а не для кожного блоку окремо.
|
||||
sodium.options.use_particle_culling.name=Відсікання частинок
|
||||
sodium.options.use_particle_culling.tooltip=Якщо увімкнено, рендеритися будуть лише ті частинки, які є безпосередньо видимими. Це дозволяє суттєво підвищити частоту кадрів, коли поруч є велика кількість частинок.
|
||||
sodium.options.animate_only_visible_textures.name=Анімувати лише видимі текстури
|
||||
sodium.options.animate_only_visible_textures.tooltip=Якщо увімкнено, оновлюватимуться лише ті анімовані текстури, які є видимими для гравця. Це може суттєво підвищити частоту кадрів на деякому залізі, особливо з важкими ресурспаками. Якщо ви помітили, що деякі текстури перестали анімуватися, спробуйте вимкнути цю опцію.
|
||||
sodium.options.translucency_sorting.name=Сортування напівпрозорості
|
||||
sodium.options.translucency_sorting.tooltip=Якщо увімкнено, напівпрозорі блоки сортуватимуться у правильному порядку (від дальніх до ближніх) для коректнішого рендерингу. Це особливо важливо, коли кілька таких блоків накладаються один на одного шарами. Ця операція є досить ресурсомісткою.
|
||||
sodium.options.cpu_render_ahead_limit.name=Ліміт заздалегідь підготовлених кадрів CPU
|
||||
sodium.options.cpu_render_ahead_limit.tooltip=Визначає, скільки кадрів процесор може підготувати наперед для відеокарти. Нижчі значення зменшують затримку введення, а вищі — згладжують нерівномірний час кадру. Занадто низькі або високі значення можуть викликати нестабільність частоти кадрів. Значення 0 вимикає цей параметр; бекенд SDL-GPU ігнорує його та застосовує власний механізм зворотного тиску.
|
||||
sodium.options.cpu_render_ahead_limit.value=%s кадр(ів)
|
||||
sodium.options.allow_direct_memory_access.name=Дозволити прямий доступ до пам’яті
|
||||
sodium.options.allow_direct_memory_access.tooltip=Якщо увімкнено, деякі критично важливі ділянки коду зможуть використовувати прямий доступ до пам’яті для підвищення швидкодії. Це часто суттєво знижує навантаження на процесор під час рендерингу чанків та сутностей, але може ускладнити діагностику деяких багів та вильотів. Вимикайте цей параметр лише за рекомендацією або якщо точно знаєте, що робите.
|
||||
sodium.options.ignore_driver_blacklist.name=Ігнорувати чорний список драйверів
|
||||
sodium.options.ignore_driver_blacklist.tooltip=Якщо увімкнено, відомі проблеми несумісності з вашою конфігурацією заліза чи драйвера будуть ігноруватися, що дозволить увімкнути опції, які можуть викликати збої в грі. Зазвичай не варто чіпати цей параметр, якщо ви точно не знаєте, що робите. Після зміни цієї опції необхідно зберегти налаштування, закрити й знову відкрити меню графіки.
|
||||
sodium.options.enable_memory_tracing.name=Увімкнути трасування пам’яті
|
||||
sodium.options.enable_memory_tracing.tooltip=Функція налагодження. Якщо увімкнено, під час виділення пам’яті також збиратимуться трасування стека. Це допомагає отримати точнішу діагностичну інформацію у разі виявлення витоків пам’яті.
|
||||
sodium.options.performance_impact_string=Вплив на продуктивність: %s
|
||||
sodium.options.use_persistent_mapping.name=Постійне відображення пам’яті
|
||||
sodium.options.use_persistent_mapping.tooltip=Якщо увімкнено, гра постійно утримуватиме відкритим прямий доступ до пам’яті, що прискорить оновлення чанків та рендерингу сутностей.\n\nПотрібен OpenGL 4.4 або розширення ARB_buffer_storage.
|
||||
sodium.options.chunk_memory_allocator.name=Алгоритм розподілу пам’яті чанків
|
||||
sodium.options.chunk_memory_allocator.tooltip=Вибирає алгоритм розподілу пам’яті, який буде використовуватися для рендерингу чанків.\n- ASYNC (Асинхронний): Найшвидший варіант, добре працює з більшістю сучасних графічних драйверів.\n- SWAP (Обмінний): Запасний варіант для старих графічних драйверів. Може значно збільшити використання пам’яті.
|
||||
sodium.options.chunk_memory_allocator.async=Async
|
||||
sodium.options.chunk_memory_allocator.swap=Swap
|
||||
sodium.options.chunk_update_threads.name=Потоки оновлення чанків
|
||||
sodium.options.chunk_update_threads.tooltip=Регулює кількість потоків процесора, які використовуються для побудови чанків. Використання більшої кількості потоків може прискорити завантаження та оновлення світу, але може негативно вплинути на плавність кадру (викликати мікрофризи).
|
||||
sodium.options.always_defer_chunk_updates.name=Завжди відкладати оновлення чанків
|
||||
sodium.options.always_defer_chunk_updates.tooltip=Якщо увімкнено, рендеринг ніколи не чекатиме завершення оновлення чанків, навіть якщо вони важливі. Це може значно підвищити частоту кадрів у деяких сценаріях, але може спричинити помітні візуальні затримки (блоки з’являтимуться із запізненням).
|
||||
sodium.options.use_no_error_context.name=Контекст без перевірки помилок
|
||||
sodium.options.use_no_error_context.tooltip=Якщо увімкнено, контекст OpenGL створюватиметься із вимкненою перевіркою помилок. Це може дещо покращити продуктивність, але також збільшує ризик того, що гра просто вилетить замість коректної обробки графічних помилок. Вимкніть цей параметр, якщо ви стикаєтеся з раптовими незрозумілими вильотами.
|
||||
sodium.options.async_occlusion_mode.name=Асинхронна оклюзія
|
||||
sodium.options.async_occlusion_mode.tooltip=Керує тим, які проходи рендерингу використовують асинхронне відсікання невидимих об’єктів.\n- ONLY_SHADOW (стандартно): запускає асинхронну оклюзію лише для проходу тіней, залишаючи основний список рендерингу синхронним.\n- EVERYTHING: запускає всі проходи асинхронно, що може викликати затримку видимості в 1 кадр під час швидкого руху камери.\n- NONE: повністю вимикає асинхронну оклюзію.
|
||||
sodium.options.async_occlusion_mode.none=Вимк.
|
||||
sodium.options.async_occlusion_mode.only_shadow=Тільки тіні
|
||||
sodium.options.async_occlusion_mode.everything=Усе
|
||||
sodium.options.use_occlusion_culling.name=Оклюзійне відсікання
|
||||
sodium.options.use_occlusion_culling.tooltip=Якщо увімкнено, чанки, які повністю сховані за іншими чанками, не будуть рендеритися. Вимкнення цього параметра суттєво знизить продуктивність, але може бути корисним для налагодження.
|
||||
sodium.options.threads.value=Потоків: %s
|
||||
sodium.options.default=За замовчуванням
|
||||
sodium.options.buttons.undo=Скасувати
|
||||
sodium.options.buttons.apply=Застосувати
|
||||
sodium.options.anaglyph.tooltip=Якщо увімкнено, світ відображатиметься двічі з червоною та блакитною колірними масками.
|
||||
sodium.options.show_cape.tooltip=Якщо увімкнено, ваш плащ буде відображатися.
|
||||
rso.search_bar_empty=Пошук налаштувань...
|
||||
|
||||
options.dynamic_fov=Динамічне поле зору
|
||||
options.dynamic_fov.tooltip=Якщо увімкнено, поле зору гри змінюватиметься залежно від швидкості руху гравця (наприклад, під час бігу, польоту або ефекту швидкості).
|
||||
options.framerate=%s FPS
|
||||
|
||||
iris.shaders.toggled=Шейдери перемкнуто на %s!
|
||||
iris.shaders.disabled=Шейдери вимкнено!
|
||||
iris.shaders.reloaded=Шейдери перезавантажено!
|
||||
iris.shaders.reloaded.failure=Не вдалося перезавантажити шейдери! Причина: %s
|
||||
iris.shaders.toggled.failure=Не вдалося перемкнути шейдери! Причина: %s
|
||||
|
||||
key.category.iris.keybinds=Прив’язка клавіш Iris
|
||||
iris.keybind.reload=Перезавантажити шейдери
|
||||
iris.keybind.toggleShaders=Перемкнути шейдери
|
||||
iris.keybind.shaderPackSelection=Екран вибору шейдерів
|
||||
angelica.keybind.zoom=Наближення
|
||||
|
||||
options.iris.apply=Застосувати
|
||||
options.iris.refresh=Оновити
|
||||
options.iris.openShaderPackFolder=Відкрити папку шейдерів...
|
||||
options.iris.shaderPackSettings=Налаштування шейдерів...
|
||||
options.iris.shaderPackList=Список шейдерів...
|
||||
options.iris.refreshShaderPacks=Оновити шейдери
|
||||
options.iris.shaderPackSelection=Шейдери...
|
||||
options.iris.shaderPackSelection.title=Шейдери
|
||||
options.iris.shaderPackSelection.addedPack=Додано шейдер "%s"
|
||||
options.iris.shaderPackSelection.addedPacks=Додано %s шейдерів
|
||||
options.iris.shaderPackSelection.failedAdd=Файли не є дійсними шейдерами
|
||||
options.iris.shaderPackSelection.failedAddSingle="%s" не є дійсним шейдером
|
||||
options.iris.shaderPackSelection.copyError=Не вдалося додати шейдер "%s"
|
||||
options.iris.shaderPackSelection.copyErrorAlreadyExists="%s" вже є в папці шейдерів!
|
||||
options.iris.shaderPackOptions.noOptions="%s" не має доступних налаштувань
|
||||
options.iris.shaderPackOptions.tooManyFiles=Неможливо імпортувати кілька файлів налаштувань шейдерів!
|
||||
options.iris.shaderPackOptions.failedImport=Не вдалося імпортувати файл налаштувань шейдерів "%s"
|
||||
options.iris.shaderPackOptions.importedSettings=Налаштування шейдерів імпортовано із "%s"
|
||||
options.iris.shaders.disabled=Шейдери: Вимкнено
|
||||
options.iris.shaders.enabled=Шейдери: Увімкнено
|
||||
options.iris.shaders.nonePresent=Шейдери: Відсутні
|
||||
options.iris.back=Назад
|
||||
options.iris.reset=Скинути
|
||||
options.iris.reset.tooltip=Скинути ВСІ налаштування та застосувати?
|
||||
options.iris.reset.tooltip.holdShift=Утримуйте SHIFT і клацніть, щоб скинути
|
||||
options.iris.importSettings.tooltip=Імпортувати налаштування з файлу
|
||||
options.iris.exportSettings.tooltip=Експортувати застосовані налаштування у файл
|
||||
options.iris.mustDisableFullscreen=Будь ласка, спочатку вимкніть повноекранний режим!
|
||||
options.iris.setToDefault=Скинути параметр до значення за замовчуванням?
|
||||
options.iris.profile=Профіль
|
||||
options.iris.profile.custom=Власний
|
||||
options.iris.shadowDistance=Максимальна дальність тіней
|
||||
options.iris.shadowDistance.sodium_tooltip=Максимальна дальність тіней
|
||||
options.iris.shadowDistance.enabled=Дозволяє змінювати максимальну дальність тіней. Ландшафт та сутності за межами цієї відстані не відкидатимуть тіні. Зменшення дальності тіней може значно підвищити продуктивність.
|
||||
options.iris.shadowDistance.disabled=У поточному шейдері вже встановлено дальність тіней; ви не можете її змінити.
|
||||
options.iris.gui.hide=Приховати інтерфейс
|
||||
options.iris.gui.show=Показати інтерфейс
|
||||
options.iris.search.button=Пошук
|
||||
options.iris.clear.button=Очистити
|
||||
options.iris.search.tooltip=Пошук налаштувань шейдерів (Ctrl + F)
|
||||
options.iris.clear.tooltip=Вийти з пошуку шейдерів (Esc)
|
||||
|
||||
options.dynamic_lights=Динамічне освітлення
|
||||
options.dynamic_lights.tooltip=Вмикає динамічне освітлення. Режими: OFF — вимкнено, FASTEST — затримка 500 мс, FAST — затримка 250 мс, FANCY — затримка 50 мс, REALTIME — без затримки.
|
||||
options.dynamic_lights_shader_force=Динамічне освітлення — примусово із шейдерами
|
||||
options.dynamic_lights_shader_force.tooltip=Увімкнено — динамічне освітлення відображається навіть тоді, коли активні шейдери; Вимкнено — динамічне освітлення вимикається, якщо активні шейдери.
|
||||
options.dynamic_lights_frustum_culling=Відсікання за Пірамідою Огляду
|
||||
options.dynamic_lights_frustum_culling.tooltip=Перебудовувати чанки лише для джерел світла, які видно на екрані. Зменшує навантаження на відеокарту від освітлення позаду камери.
|
||||
options.dynamic_lights_adaptive_ticking=Адаптивне оновлення
|
||||
options.dynamic_lights_adaptive_ticking.tooltip=Оновлювати віддалені та позаекранні джерела світла рідше. Працює разом із глобальним налаштуванням режиму.
|
||||
options.dynamic_lights_slow_dist=Дальність повільного оновлення
|
||||
options.dynamic_lights_slow_dist.tooltip=Відстань у блоках, на якій освітлення оновлюється з 1/5 частоти. Рекомендовано: 32 блоки (~2 чанки).
|
||||
options.dynamic_lights_slower_dist=Дальність повільнішого оновлення
|
||||
options.dynamic_lights_slower_dist.tooltip=Відстань у блоках, на якій освітлення оновлюється з 1/10 частоти. Рекомендовано: 64 блоки (~4 чанки).
|
||||
options.dynamic_lights_background_dist=Дальність фонового оновлення
|
||||
options.dynamic_lights_background_dist.tooltip=Відстань у блоках, на якій освітлення оновлюється з 1/20 частоти (раз на секунду). Також використовується для освітлення позаду камери.
|
||||
options.dynamic_lights_cull_timeout=Час до примусового оновлення (в тактах)
|
||||
options.dynamic_lights_cull_timeout.tooltip=Максимальна кількість тактів, яку прихований чанк може чекати перед примусовою перебудовою. Менше значення = кращий відгук, але більше навантаження на відеокарту.
|
||||
options.dynamiclights.page=Динамічне освітлення
|
||||
options.dynamiclights.entities=Налаштування сутностей...
|
||||
options.dynamiclights.entities.tooltip=Виберіть типи сутностей, які випромінюватимуть динамічне світло.
|
||||
options.dynamiclights.entities.title=Динамічне освітлення — Сутностей
|
||||
options.dynamiclights.entities.info=Виберіть типи сутностей, які випромінюватимуть динамічне світло. Деякі пункти потрібні для сумісності з модами.
|
||||
options.dynamiclights.entities.search=Пошук (@mod, $on, $off)
|
||||
options.dynamiclights.entities.enableall=Увімкнути всі
|
||||
options.dynamiclights.entities.disableall=Вимкнути всі
|
||||
options.dynamiclights.entities.enableshown=Увімкнути відображені
|
||||
options.dynamiclights.entities.disableshown=Вимкнути відображені
|
||||
|
||||
options.angelica.search.no_results=Відповідних параметрів не знайдено
|
||||
options.angelica.fontconfig=Налаштування шрифтів...
|
||||
options.angelica.fontconfig.title=Налаштування шрифтів
|
||||
options.angelica.fontconfig.currentfonts=Основний шрифт: §e%s§r Альтернативний шрифт: §b%s
|
||||
options.angelica.fontconfig.searchprompt=§7§oПошук шрифтів...
|
||||
options.angelica.fontconfig.testareaprompt=§7§oЗона тестування...
|
||||
options.angelica.fontconfig.testareainfo=Введіть текст нижче, щоб побачити, як він відображатиметься. Використовуйте & для форматування.
|
||||
options.angelica.fontconfig.testareaexample=&d&lГеробріна&r тут &7&m&oНЕ&r &nбуло
|
||||
options.angelica.fontconfig.disable_custom_font=Вимкнути власний шрифт
|
||||
options.angelica.fontconfig.enable_custom_font=Увімкнути власний шрифт
|
||||
options.angelica.fontconfig.reset_values=Скинути значення
|
||||
options.angelica.fontconfig.no_fonts_warning=Власні шрифти недоступні у вашій системі. Перевірте логи для детальної інформації.
|
||||
options.angelica.fontconfig.font_quality=Якість власного шрифту: %s
|
||||
options.angelica.fontconfig.shadow_offset=Зсув тіні: %s
|
||||
options.angelica.fontconfig.shadow_copies=Копії тіней: %s
|
||||
options.angelica.fontconfig.bold_copies=Жирний шрифт: %s
|
||||
options.angelica.fontconfig.glyph_scale=Масштаб символів: %s
|
||||
options.angelica.fontconfig.glyph_aspect=Вигляд символу: %s
|
||||
options.angelica.fontconfig.whitespace_scale=Розмір пробілу: %s
|
||||
options.angelica.fontconfig.glyph_spacing=Відстань між символами: %s
|
||||
options.angelica.fontconfig.font_aa_strength=Сила згладжування шрифту: %s
|
||||
options.angelica.fontconfig.aamode.aa_none=Згладжування шрифту: ВИМК
|
||||
options.angelica.fontconfig.aamode.aa_4x=Згладжування шрифту: 4x MSAA
|
||||
options.angelica.fontconfig.aamode.aa_16x=Згладжування шрифту: 16x MSAA
|
||||
options.angelica.fontconfig.custom_font_scale=Масштаб власного шрифту: %s
|
||||
options.angelica.fontconfig.shadow_offset_unicode=Зсув тіні Unicode: %s
|
||||
options.angelica.fontconfig.font_quality.tooltip=Якість, з якою відображаються власні шрифти. Робить їх менш піксельними ціною певного зниження продуктивності та збільшення споживання пам’яті.
|
||||
options.angelica.fontconfig.shadow_offset.tooltip=Відстань, на якій відображаються тіні шрифтів. Також впливає на те, як відображається §lжирний§r текст.
|
||||
options.angelica.fontconfig.shadow_copies.tooltip=Кількість тіней, що відображаються позаду тексту; всі вони зміщуються для створення ефекту псевдо-3D.
|
||||
options.angelica.fontconfig.bold_copies.tooltip=Кількість копій тексту, що накладаються одна на одну для створення жирного тексту. Повзунок допомагає покращити вигляд жирного тексту, якщо власний шрифт занадто тонкий.
|
||||
options.angelica.fontconfig.glyph_scale.tooltip=Масштаб кожного окремого символу. Екстремальні значення можуть зробити гру нечитабельною.
|
||||
options.angelica.fontconfig.glyph_aspect.tooltip=Співвідношення сторін кожного окремого символу, що безпосередньо змінює його вигляд.
|
||||
options.angelica.fontconfig.whitespace_scale.tooltip=Коефіцієнт масштабування для розміру пробілу між всіма словами в тексті.
|
||||
options.angelica.fontconfig.glyph_spacing.tooltip=Інтервал між кожним окремим символом.
|
||||
options.angelica.fontconfig.font_aa_strength.tooltip=Відстань між вибірками, що використовуються для згладжування шрифту. Високі значення можуть зробити шрифт розмитим, низькі — призвести до появи «сходинок» на літерах. Виберіть те, що найкраще підходить для ваших налаштувань.
|
||||
options.angelica.fontconfig.aamode.tooltip=Режим згладжування шрифту. Може значно покращити вигляд і читабельність тексту під час масштабування або перегляду під кутом, ціною певного зниження продуктивності.
|
||||
options.angelica.fontconfig.custom_font_scale.tooltip=Множник, який впливає на загальний масштаб власних шрифтів. Налаштуйте його, якщо текст обраного шрифту виглядає занадто малим або великим відносно стандартного тексту гри.
|
||||
options.angelica.fontconfig.shadow_offset_unicode.tooltip=Множник зсуву тіні, який застосовується лише до шрифту Unicode. Встановіть значення 1.0x, якщо, наприклад, ви використовуєте текстурпак, який замінює частину Unicode-шрифту на стандартні літери у стилі Minecraft.
|
||||
options.angelica.hudCaching=Кешування HUD
|
||||
options.angelica.hudCaching.tooltip=Кешує HUD у буфер кадру та використовує його повторно, щоб зменшити навантаження на відеокарту. Функція має бути увімкнена під час запуску гри. [Експериментальна]
|
||||
options.angelica.droppedItemLimit=Ліміт відображення викинутих предметів
|
||||
options.angelica.droppedItemLimit.tooltip=Максимальна кількість викинутих предметів, яка відображатиметься на екрані. Менші значення можуть підвищити продуктивність.
|
||||
options.angelica.mobSpawnerRenderDistance=Дальність відображення мобів у спавнерах
|
||||
options.angelica.mobSpawnerRenderDistance.tooltip=Дальність відображення для моба, що обертається всередині спавнерів.
|
||||
options.angelica.shadowGraphAngleDelta=Поріг оновлення карт тіней
|
||||
options.angelica.shadowGraphAngleDelta.tooltip=Визначає, наскільки небесне тіло може зміститися, перш ніж оновиться карта тіней. Майте на увазі, що цей параметр вимикається, якщо шейдерпак використовує функції вокселізації.
|
||||
options.angelica.shadowGraphAngleDelta.always=Завжди
|
||||
options.angelica.shadowGraphHorizonScale=Ущільнення оновлень карт тіней біля горизонту
|
||||
options.angelica.shadowGraphHorizonScale.tooltip=Регулює частоту оновлення карт тіней, коли небесне тіло перебуває поблизу горизонту. Зміна частоти є синусоїдальною.
|
||||
options.angelica.shadowGraphHorizonScale.off=Вимк.
|
||||
options.angelica.itemdisplaylistcount=Ліміт кешу відображення предметів
|
||||
options.angelica.itemdisplaylistcount.tooltip=Максимальна кількість моделей предметів, які гра може одночасно тримати в пам’яті відеокарти. Більші значення значно підвищують FPS, але споживають більше відеопам’яті.
|
||||
options.angelica.threadedChunkBuilding=Багатопотокова побудова чанків
|
||||
options.angelica.threadedChunkBuilding.tooltip=Якщо увімкнено, побудова сітки чанків виконуватиметься в кількох потоках паралельно, що значно прискорить швидкість їхнього завантаження. Вимкніть, якщо виникають графічні артефакти з модами, які мають власні рендерери блоків. Потребує перезавантаження рендерера для застосування змін.
|
||||
options.angelica.gpuCullingMode=Режим відсікання GPU
|
||||
options.angelica.gpuCullingMode.tooltip=Вибирає конвеєр відсікання чанків. «Лише CPU» використовує емітер CPU. «Обчислювальний» запускає відсікання за пірамідою видимості на GPU. Потребує GL 4.3 / ARB_compute_shader + SSBO + Indirect Multi-Draw.
|
||||
options.angelica.gpuCullingMode.cpu_only=Лише CPU
|
||||
options.angelica.gpuCullingMode.compute=Обчислювальний
|
||||
|
||||
pack.iris.select.title=Вибрати
|
||||
pack.iris.configure.title=Налаштувати
|
||||
pack.iris.list.label=+ Перетягніть сюди шейдери для додавання
|
||||
pack.iris.list.label.lwjgl2=Встановіть lwjgl3ify, щоб перетягувати шейдери
|
||||
label.iris.true=Увімк.
|
||||
label.iris.false=Вимк.
|
||||
|
||||
sodium.options.use_render_pass_optimization.name=Оптимізація проходів рендерингу
|
||||
sodium.options.use_render_pass_optimization.tooltip=Аналізує прозорість текстур (спрайтів), щоб автоматично спрощувати проходи рендерингу (наприклад, з напівпрозорого до вирізаного, з вирізаного до суцільного), коли об’єкт не потребує ресурсомісткої обробки. Зменшує кількість викликів малювання та надлишкового зафарбовування.
|
||||
|
||||
options.angelica.aggressiveChunkLoading=Агресивне завантаження чанків
|
||||
options.angelica.aggressiveChunkLoading.tooltip=Візуалізує чанки відразу після їх завантаження, навіть якщо сусідні чанки ще не готові. Покращує видиму швидкість завантаження на межах дистанції промальовування. Чанки автоматично перебудовуються, коли завантажуються сусідні. Корисно для серверів із низькою дистанцією рендерингу.
|
||||
options.angelica.disablef3=Вимкнути доповнення F3
|
||||
options.angelica.disablef3.tooltip=Вимкнути додаткову інформацію від Angelica на екрані налагодження F3.
|
||||
options.angelica.texteffects.rgb_colors=Кольори RGB
|
||||
options.angelica.texteffects.rgb_colors.tooltip=Увімкнути hex-кольори через синтаксис %s
|
||||
options.angelica.texteffects.gradients=Градієнти
|
||||
options.angelica.texteffects.gradients.tooltip=Плавні переходи кольорів за допомогою:
|
||||
options.angelica.texteffects.rainbow=Веселка
|
||||
options.angelica.texteffects.rainbow.tooltip=Анімоване переливання кольорів за допомогою:
|
||||
options.angelica.texteffects.wave_text=Хвилястий текст
|
||||
options.angelica.texteffects.wave_text.tooltip=Анімований текст, що підстрибує, за допомогою:
|
||||
options.angelica.texteffects.wave_amplitude=Амплітуда хвилі
|
||||
options.angelica.texteffects.wave_amplitude.tooltip=Наскільки сильно підстрибує хвилястий текст (за замовчуванням 2.0)
|
||||
options.angelica.texteffects.dinnerbone_text=Текст Dinnerbone
|
||||
options.angelica.texteffects.dinnerbone_text.tooltip=Перевернутий догори дном текст за допомогою:
|
||||
options.angelica.texteffects.drop_shadow=Тінь тексту
|
||||
options.angelica.texteffects.drop_shadow.tooltip=Окрема тінь для сегментів через %s, кольорова тінь за допомогою:
|
||||
options.angelica.texteffects.ampersand_conversion=Конвертація амперсанда
|
||||
options.angelica.texteffects.ampersand_conversion.tooltip=Перетворювати префіксні коди %s на коди форматування під час рендерингу
|
||||
|
||||
sodium.options.multidraw_mode.name=Режим Multi-Draw
|
||||
sodium.options.multidraw_mode.tooltip=Керує тим, як команди малювання надсилаються для рендерингу ландшафту. §lПрямий§r використовує glMultiDrawElementsBaseVertex (типово, GL 3.2+). §lНепрямий§r використовує непрямий рендеринг GPU (GL 4.3+), що може знизити навантаження на CPU та є необхідним для GPU / Hi-Z відсікання. §lІндивідуальний§r надсилає команди малювання поштучно.
|
||||
sodium.options.multidraw_mode.direct=Прямий
|
||||
sodium.options.multidraw_mode.indirect=Непрямий
|
||||
sodium.options.multidraw_mode.individual=Індивідуальний
|
||||
sodium.options.upload_method.name=Метод потокового завантаження
|
||||
sodium.options.upload_method.tooltip=Змінює спосіб передачі графічних даних до відеокарти. Вибір певного методу може суттєво вплинути на продуктивність та плавність гри залежно від вашого графічного драйвера.
|
||||
sodium.options.upload_method.buffer_data=BufferData
|
||||
sodium.options.upload_method.buffer_subdata=BufferSubData
|
||||
sodium.options.upload_method.map_buffer=MapBufferRange
|
||||
sodium.options.enable_deferred_batching.name=Відкладене групування частинок
|
||||
sodium.options.enable_deferred_batching.tooltip=Групує виклики малювання частинок, об’єднуючи їх у значно меншу кількість викликів для кожного шару рендерингу. Вимкніть, якщо частинки відображаються некоректно.
|
||||
@@ -164,7 +164,6 @@ options.dynamiclights.entities=配置实体……
|
||||
options.dynamiclights.entities.title=动态光源 - 实体
|
||||
options.dynamiclights.entities.info=切换哪些实体类型发射动态光。部分条目用于模组兼容性。
|
||||
options.dynamiclights.entities.search=搜索(@模组, $开启, $关闭)
|
||||
options.dynamiclights.entity.tooltip=切换 %s 实体的动态光照。如果此类型实体产生不必要光照,禁用以提升性能。
|
||||
|
||||
options.angelica.search.no_results=无匹配选项
|
||||
options.angelica.fontconfig=字体配置...
|
||||
|
||||
@@ -77,8 +77,9 @@ options.total_stars.tooltip=Star Density
|
||||
options.void_fog=Void Fog
|
||||
options.void_fog.tooltip=Void Fog
|
||||
options.hurt_shake=Hurt Screen Shake
|
||||
options.hurt_shake.tooltip=How strongly the camera tilts when you take damage.
|
||||
options.background.random=Random
|
||||
options.zoom_smooth=Smooth Zoom
|
||||
options.zoom_smooth.tooltip=Smoothly interpolates the camera toward the zoom level instead of snapping instantly.
|
||||
options.zoom_smooth_speed=Smooth Zoom Speed
|
||||
options.zoom_smooth_speed.tooltip=How fast smooth zoom converges to the target zoom. Higher is snappier, lower is floatier. No effect when Smooth Zoom is off.
|
||||
options.zoom_smooth_speed.tooltip=How fast smooth zoom converges to the target zoom. Higher is snappier, lower is floatier. No effect when Smooth Zoom is off.
|
||||
@@ -0,0 +1,85 @@
|
||||
options.button.detail=Деталі...
|
||||
options.button.other=Інше...
|
||||
options.button.particle=Частинки...
|
||||
options.button.sky=Атмосфера...
|
||||
options.chunks=%s чанків
|
||||
options.framerate=%s FPS
|
||||
options.graphics.shelledfancy=Гібридна красива
|
||||
options.graphics.shelledfast=Гібридна швидка
|
||||
options.graphics.smart=Розумна
|
||||
options.graphics.ultra=Ультра
|
||||
options.off=Вимк.
|
||||
options.on=Увімк.
|
||||
options.cloud_height=Висота хмар
|
||||
options.cloud_height.tooltip=Висота відображення хмар у світах, де вона не задана власноруч. Світи з власною висотою зберігають її зі зсувом на різницю цього параметра відносно 128.
|
||||
options.cloud_scale=Масштаб хмар
|
||||
options.cloud_scale.tooltip=Масштаб хмар
|
||||
options.gui_background=Тло
|
||||
options.gui_background.tooltip=Тло меню
|
||||
options.downfall_distance=Ефекти погоди
|
||||
options.downfall_distance.tooltip=Ефекти погоди
|
||||
options.dynamic_fov=Динамічне поле зору
|
||||
options.dynamic_fov.tooltip=Залежно від режиму, поле зору гри змінюватиметься на основі швидкості руху гравця або через ефекти певних модів (зокрема наближення інструментів чи прицілу зброї).
|
||||
options.horizon=Горизонт
|
||||
options.horizon.tooltip=Горизонт
|
||||
options.terrain_fog=Туман ландшафту
|
||||
options.terrain_fog.tooltip=Туман ландшафту (також відомий як туман дальності огляду)
|
||||
options.fog_near_distance=Відстань до появи туману
|
||||
options.fog_near_distance.tooltip=Відстань від гравця, з якої починається туман (0%% непрозорості з подальшим лінійним збільшенням до межі дальності промальовування).
|
||||
options.bobview_mode=Режим погойдування камери
|
||||
options.bobview_mode.tooltip=Змінює поведінку погойдування під час руху: дозволяє погойдувати лише руку або лише камеру.
|
||||
options.bobview.hand=Тільки рука
|
||||
options.bobview.camera=Тільки камера
|
||||
options.fov.all=Увімкнено
|
||||
options.fov.mods=Тільки моди
|
||||
options.fov.none=Вимкнено
|
||||
options.mode_cloud_translucency=Напівпрозорість хмар
|
||||
options.mode_cloud_translucency.tooltip=Напівпрозорість хмар
|
||||
options.mode_clouds=Хмари
|
||||
options.mode_clouds.tooltip=Хмари
|
||||
options.mode_dropped_items=Викинуті предмети
|
||||
options.mode_dropped_items.tooltip=Якість відображення викинутих предметів
|
||||
options.mode_glint_inv=Ефект чарів в інтерфейсі
|
||||
options.mode_glint_inv.tooltip=Ефект чарів в інтерфейсі
|
||||
options.mode_glint_world=Ефект чарів у світі
|
||||
options.mode_glint_world.tooltip=Ефект чарів у світі
|
||||
options.mode_gui_background=Тло ігрового меню
|
||||
options.mode_gui_background.tooltip=Тло ігрового меню
|
||||
options.mode_leaves=Рослинність
|
||||
options.mode_leaves.tooltip=Рослинність
|
||||
options.mode_light_flicker=Миготіння світла
|
||||
options.mode_light_flicker.tooltip=Миготіння світла
|
||||
options.mode_shadows=Тіні сутностей
|
||||
options.mode_shadows.tooltip=Тіні сутностей
|
||||
options.mode_sky=Небо
|
||||
options.mode_sky.tooltip=Візуалізація неба
|
||||
options.mode_sun_moon=Сонце та Місяць
|
||||
options.mode_sun_moon.tooltip=Візуалізація Сонця та Місяця
|
||||
options.mode_stars=Зірки
|
||||
options.mode_stars.tooltip=Візуалізація зірок
|
||||
options.mode_water=Вода
|
||||
options.mode_water.tooltip=Вода
|
||||
options.mode_vignette=Віньєтка
|
||||
options.mode_vignette.tooltip=Віньєтка
|
||||
options.particles_enc_table=Частинки зачарування
|
||||
options.particles_enc_table.tooltip=Частинки зачарування
|
||||
options.particles_void=Частинки порожнечі
|
||||
options.particles_void.tooltip=Частинки порожнечі
|
||||
options.render_distance_clouds=Дальність відображення хмар
|
||||
options.render_distance_clouds.tooltip=Дальність відображення хмар
|
||||
options.title.detail=Налаштування деталей
|
||||
options.title.other=Інші налаштування
|
||||
options.title.particle=Налаштування частинок
|
||||
options.title.sky=Налаштування атмосфери
|
||||
options.title.video=Налаштування графіки
|
||||
options.total_stars=Щільність зірок
|
||||
options.total_stars.tooltip=Щільність зірок
|
||||
options.void_fog=Туман порожнечі
|
||||
options.void_fog.tooltip=Туман порожнечі
|
||||
options.hurt_shake=Погойдування камери від шкоди
|
||||
options.hurt_shake.tooltip=Визначає, наскільки сильно нахиляється камера під час отримання шкоди.
|
||||
options.background.random=Випадково
|
||||
options.zoom_smooth=Плавне наближення
|
||||
options.zoom_smooth.tooltip=Плавно зміщує камеру до рівня наближення замість миттєвого перемикання.
|
||||
options.zoom_smooth_speed=Швидкість плавного наближення
|
||||
options.zoom_smooth_speed.tooltip=Швидкість, з якою плавне наближення досягає цільового рівня. Вищі значення роблять наближення різкішим, нижчі — м’якшим. Не має ефекту, якщо плавне наближення вимкнено.
|
||||
+3
-5
@@ -3,7 +3,6 @@ package com.gtnewhorizons.angelica.mixins.early.angelica.animation;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.mixins.interfaces.IPatchedTextureAtlasSprite;
|
||||
import com.gtnewhorizons.angelica.proxy.ClientProxy;
|
||||
import com.gtnewhorizons.angelica.utils.AnimationMode;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.texture.AbstractTexture;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
@@ -30,9 +29,8 @@ public abstract class MixinTextureMap extends AbstractTexture {
|
||||
*/
|
||||
@Overwrite
|
||||
public void updateAnimations() {
|
||||
final boolean renderAll = ClientProxy.animationsMode.is(AnimationMode.ALL);
|
||||
final boolean renderVisible = ClientProxy.animationsMode.is(AnimationMode.VISIBLE_ONLY);
|
||||
|
||||
final boolean renderVisible = ClientProxy.options().performance.animateOnlyVisibleTextures;
|
||||
|
||||
Minecraft.getMinecraft().mcProfiler.startSection("updateAnimations");
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, this.getGlTextureId());
|
||||
|
||||
@@ -42,7 +40,7 @@ public abstract class MixinTextureMap extends AbstractTexture {
|
||||
final IPatchedTextureAtlasSprite patched = (IPatchedTextureAtlasSprite) sprite;
|
||||
|
||||
// needsAnimationUpdate() is one-shot: returns true if marked, then auto-resets
|
||||
if (renderAll || (renderVisible && patched.needsAnimationUpdate())) {
|
||||
if (!renderVisible || patched.needsAnimationUpdate()) {
|
||||
sprite.updateAnimation();
|
||||
} else {
|
||||
// Keep frame counters in sync for sprites not being updated
|
||||
|
||||
Loaded 100 of 110 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user