mirror of
https://github.com/GTNewHorizons/Angelica.git
synced 2026-11-15 01:34:48 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
73f5556595 | ||
|
|
19bc7cbaed | ||
|
|
d189a644e2 | ||
|
|
94ba40ae4d | ||
|
|
961b23fd2d | ||
|
|
61b5301f1a | ||
|
|
0e539ae034 | ||
|
|
e581326641 | ||
|
|
c707cc51f1 | ||
|
|
7d890bf3e7 | ||
|
|
ffc324bab6 | ||
|
|
0dfffca88e |
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
|
||||
|
||||
@@ -846,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()) {
|
||||
|
||||
@@ -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;
|
||||
@@ -180,6 +184,10 @@ 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];
|
||||
|
||||
@@ -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;
|
||||
@@ -2577,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);
|
||||
}
|
||||
@@ -2692,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) {
|
||||
@@ -5886,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 {
|
||||
@@ -5897,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();
|
||||
@@ -5989,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);
|
||||
@@ -6022,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);
|
||||
@@ -6056,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); }
|
||||
@@ -6109,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); }
|
||||
@@ -6136,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); }
|
||||
@@ -6169,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)); }
|
||||
@@ -6201,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) {
|
||||
@@ -6373,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); }
|
||||
@@ -6411,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);
|
||||
}
|
||||
@@ -7728,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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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++;
|
||||
|
||||
@@ -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,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) {
|
||||
|
||||
@@ -517,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")
|
||||
@@ -722,6 +723,7 @@ public class GLSMRedirector {
|
||||
);
|
||||
final var uniformBufferObject = RedirectMap.newMap()
|
||||
.add("glBindBufferBase")
|
||||
.add("glBindBufferRange")
|
||||
.add("glGetUniformBlockIndex")
|
||||
.add("glUniformBlockBinding");
|
||||
methodRedirects.put(ARBUniformBufferObject, uniformBufferObject);
|
||||
|
||||
+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;
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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"));
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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 +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); }
|
||||
}
|
||||
}
|
||||
@@ -67,6 +67,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() {
|
||||
@@ -298,6 +302,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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
+2
@@ -45,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;
|
||||
@@ -168,6 +169,7 @@ 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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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/";
|
||||
@@ -204,6 +205,11 @@ public class TexturePackAPI {
|
||||
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 +238,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
|
||||
|
||||
@@ -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;
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -300,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.
|
||||
@@ -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=Групує виклики малювання частинок, об’єднуючи їх у значно меншу кількість викликів для кожного шару рендерингу. Вимкніть, якщо частинки відображаються некоректно.
|
||||
@@ -82,4 +82,4 @@ 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
|
||||
|
||||
+3
@@ -9,6 +9,7 @@ import com.gtnewhorizons.angelica.glsm.streaming.TessellatorStreamingDrawer;
|
||||
import com.gtnewhorizons.angelica.compat.DriverCompatabilityCheck;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.BackendManager;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMConfig;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMHooks;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMInitConfig;
|
||||
import com.gtnewhorizons.angelica.proxy.ClientProxy;
|
||||
@@ -51,6 +52,8 @@ public class MixinInitGLStateManager {
|
||||
.postInitCallback(SelectionBoxRenderer::init)
|
||||
.build());
|
||||
|
||||
GLSMConfig.extendedAttribsExpected = AngelicaConfig.enableIris;
|
||||
|
||||
GLSMHooks.LIGHTMAP_COORDS.addListener(event -> {
|
||||
OpenGlHelper.lastBrightnessX = event.x;
|
||||
OpenGlHelper.lastBrightnessY = event.y;
|
||||
|
||||
+14
-14
@@ -2,6 +2,7 @@ package com.gtnewhorizons.angelica.mixins.early.angelica.tesr;
|
||||
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.VanillaModelMeshes;
|
||||
import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
@@ -10,21 +11,22 @@ import net.minecraft.client.model.ModelLargeChest;
|
||||
import net.minecraft.client.renderer.tileentity.TileEntityChestRenderer;
|
||||
import net.minecraft.tileentity.TileEntityChest;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Dynamic;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
|
||||
@Mixin(TileEntityChestRenderer.class)
|
||||
public abstract class MixinTileEntityChestRenderer {
|
||||
@Unique
|
||||
private ResourceLocation angelica$lastBoundTexture = null;
|
||||
|
||||
@Shadow @Final private static ResourceLocation field_147507_b; // trapped_double
|
||||
@Shadow @Final private static ResourceLocation field_147508_c; // christmas_double
|
||||
@Shadow @Final private static ResourceLocation field_147505_d; // normal_double
|
||||
@Shadow @Final private static ResourceLocation field_147506_e; // trapped
|
||||
@Shadow @Final private static ResourceLocation field_147503_f; // christmas
|
||||
@Shadow @Final private static ResourceLocation field_147504_g; // normal
|
||||
@Shadow private boolean field_147509_j; // Christmas season flag
|
||||
@Dynamic(mixin = MixinTileEntityChestRenderer_BindTexture.class)
|
||||
@WrapMethod(method = "bindTexture(Lnet/minecraft/util/ResourceLocation;)V", require = 1)
|
||||
private void angelica$storeBoundTexture(ResourceLocation resourceLocation, Operation<Void> original) {
|
||||
this.angelica$lastBoundTexture = resourceLocation;
|
||||
original.call(resourceLocation);
|
||||
}
|
||||
|
||||
@WrapOperation(method = "renderTileEntityAt(Lnet/minecraft/tileentity/TileEntityChest;DDDF)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/model/ModelChest;renderAll()V"))
|
||||
private void angelica$cachedRenderAll(ModelChest model, Operation<Void> original, @Local(argsOnly = true) TileEntityChest chest) {
|
||||
@@ -33,10 +35,8 @@ public abstract class MixinTileEntityChestRenderer {
|
||||
return;
|
||||
}
|
||||
final boolean isDouble = model instanceof ModelLargeChest;
|
||||
final boolean trapped = chest.func_145980_j() == 1;
|
||||
final ResourceLocation texture = isDouble
|
||||
? (trapped ? field_147507_b : field_147509_j ? field_147508_c : field_147505_d)
|
||||
: (trapped ? field_147506_e : field_147509_j ? field_147503_f : field_147504_g);
|
||||
VanillaModelMeshes.renderChest(model, texture, isDouble);
|
||||
if (angelica$lastBoundTexture != null) {
|
||||
VanillaModelMeshes.renderChest(model, angelica$lastBoundTexture, isDouble);
|
||||
}
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package com.gtnewhorizons.angelica.mixins.early.angelica.tesr;
|
||||
|
||||
import net.minecraft.client.renderer.tileentity.TileEntityChestRenderer;
|
||||
import net.minecraft.client.renderer.tileentity.TileEntityEnderChestRenderer;
|
||||
import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
|
||||
@Mixin(value = { TileEntityChestRenderer.class, TileEntityEnderChestRenderer.class }, priority = 100)
|
||||
public abstract class MixinTileEntityChestRenderer_BindTexture extends TileEntitySpecialRenderer {
|
||||
// this just provides a default override of bindTexture that other mixins can mixin into
|
||||
// in a way that makes it compatible, even if a mixin like this is applied multiple times
|
||||
@Unique
|
||||
@Override
|
||||
protected void bindTexture(ResourceLocation resourceLocation) {
|
||||
super.bindTexture(resourceLocation);
|
||||
}
|
||||
}
|
||||
+14
-4
@@ -2,6 +2,7 @@ package com.gtnewhorizons.angelica.mixins.early.angelica.tesr;
|
||||
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.VanillaModelMeshes;
|
||||
import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
@@ -9,15 +10,22 @@ import net.minecraft.client.model.ModelChest;
|
||||
import net.minecraft.client.renderer.tileentity.TileEntityEnderChestRenderer;
|
||||
import net.minecraft.tileentity.TileEntityEnderChest;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Dynamic;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
|
||||
@Mixin(TileEntityEnderChestRenderer.class)
|
||||
public abstract class MixinTileEntityEnderChestRenderer {
|
||||
@Unique
|
||||
private ResourceLocation angelica$lastBoundTexture = null;
|
||||
|
||||
@Shadow @Final private static ResourceLocation field_147520_b; // ender
|
||||
@Dynamic(mixin = MixinTileEntityChestRenderer_BindTexture.class)
|
||||
@WrapMethod(method = "bindTexture(Lnet/minecraft/util/ResourceLocation;)V", require = 1)
|
||||
private void angelica$storeBoundTexture(ResourceLocation resourceLocation, Operation<Void> original) {
|
||||
this.angelica$lastBoundTexture = resourceLocation;
|
||||
original.call(resourceLocation);
|
||||
}
|
||||
|
||||
@WrapOperation(method = "renderTileEntityAt(Lnet/minecraft/tileentity/TileEntityEnderChest;DDDF)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/model/ModelChest;renderAll()V"))
|
||||
private void angelica$cachedRenderAll(ModelChest model, Operation<Void> original, @Local(argsOnly = true) TileEntityEnderChest chest) {
|
||||
@@ -25,6 +33,8 @@ public abstract class MixinTileEntityEnderChestRenderer {
|
||||
original.call(model);
|
||||
return;
|
||||
}
|
||||
VanillaModelMeshes.renderChest(model, field_147520_b, false);
|
||||
if (angelica$lastBoundTexture != null) {
|
||||
VanillaModelMeshes.renderChest(model, angelica$lastBoundTexture, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package com.gtnewhorizons.angelica.mixins.early.celeritas.terrain;
|
||||
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.world.chunk.Chunk;
|
||||
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Pseudo;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
import com.gtnewhorizons.angelica.mixins.interfaces.IChunkTileEntityMapHolder;
|
||||
import com.gtnewhorizons.angelica.utils.ConcurrentTileEntityMap;
|
||||
|
||||
import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
|
||||
@Pseudo
|
||||
@Mixin(targets = "com.cardinalstar.cubicchunks.world.cube.Cube", remap = false)
|
||||
public abstract class MixinCubicChunksCube {
|
||||
|
||||
@Final
|
||||
@Shadow(remap = false)
|
||||
private Chunk column;
|
||||
|
||||
@WrapMethod(method = "setBlockTileEntityInChunk")
|
||||
private void angelica$lockTileEntityMutation(int x, int y, int z, TileEntity tileEntity, Operation<Void> original) {
|
||||
ConcurrentTileEntityMap tileEntities = ((IChunkTileEntityMapHolder) column).angelica$getConcurrentTEMap();
|
||||
tileEntities.withWriteLock(() -> original.call(x, y, z, tileEntity));
|
||||
}
|
||||
}
|
||||
+5
-1
@@ -209,7 +209,11 @@ public abstract class MixinRenderBlocks implements ExtCeleritasRenderBlocks {
|
||||
return;
|
||||
}
|
||||
|
||||
if (processor.processFace((RenderBlocks)(Object)this, renderBlockState, icon, direction.ordinal())) {
|
||||
RenderBlocks rb = (RenderBlocks) (Object) this;
|
||||
if (rb.hasOverrideBlockTexture()) {
|
||||
return;
|
||||
}
|
||||
if (processor.processFace(rb, renderBlockState, icon, direction.ordinal())) {
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
+10
-1
@@ -1,5 +1,8 @@
|
||||
package com.gtnewhorizons.angelica.mixins.early.shaders;
|
||||
|
||||
import net.coderbot.iris.Iris;
|
||||
import net.coderbot.iris.texture.format.TextureFormatLoader;
|
||||
import net.coderbot.iris.texture.pbr.PBRTextureManager;
|
||||
import net.coderbot.iris.uniforms.CapturedRenderingState;
|
||||
import net.minecraft.client.renderer.texture.TextureManager;
|
||||
import net.minecraft.client.resources.IResourceManager;
|
||||
@@ -9,13 +12,19 @@ import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
/**
|
||||
* Hook into TextureManger to increment the reload count.
|
||||
* Hook into TextureManger to reload the PBR texture format and increment the reload count.
|
||||
*/
|
||||
@Mixin(TextureManager.class)
|
||||
public class MixinTextureManager_ReloadCount {
|
||||
|
||||
@Inject(method = "onResourceManagerReload", at = @At("TAIL"))
|
||||
private void angelica$bumpTextureReloadCount(IResourceManager resourceManager, CallbackInfo ci) {
|
||||
try {
|
||||
TextureFormatLoader.reload(resourceManager);
|
||||
} catch (Exception e) {
|
||||
Iris.logger.error("Failed to reload the shader pipeline for a texture format change", e);
|
||||
}
|
||||
PBRTextureManager.INSTANCE.clear();
|
||||
CapturedRenderingState.INSTANCE.incrementTextureReloadCount();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package com.gtnewhorizons.angelica.rendering;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormatElement;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.InstancedAttribs;
|
||||
import net.coderbot.iris.gl.shader.ProgramCreator;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
/**
|
||||
* Make sure we don't ever have collisions again, that was annoying.
|
||||
*/
|
||||
class VertexAttributeLayoutTest {
|
||||
|
||||
private static final int MAX_VERTEX_ATTRIBS = 16;
|
||||
|
||||
private final Map<Integer, String> claimed = new HashMap<>();
|
||||
|
||||
private void claim(int location, String owner) {
|
||||
assertTrue(location >= 0 && location < MAX_VERTEX_ATTRIBS,
|
||||
owner + " uses location " + location + ", outside 0.." + (MAX_VERTEX_ATTRIBS - 1));
|
||||
final String previous = claimed.put(location, owner);
|
||||
assertNull(previous, "location " + location + " claimed by both " + previous + " and " + owner);
|
||||
}
|
||||
|
||||
@Test
|
||||
void reservedAttributeLocationsDoNotOverlap() {
|
||||
for (VertexFormatElement.Usage usage : VertexFormatElement.Usage.values()) {
|
||||
final int loc = usage.getAttributeLocation();
|
||||
if (loc < 0) continue;
|
||||
claim(loc, "vertex format " + usage.name());
|
||||
}
|
||||
|
||||
claim(InstancedAttribs.LOC_MATRIX_COL0, "InstancedAttribs.LOC_MATRIX_COL0");
|
||||
claim(InstancedAttribs.LOC_MATRIX_COL1, "InstancedAttribs.LOC_MATRIX_COL1");
|
||||
claim(InstancedAttribs.LOC_MATRIX_COL2, "InstancedAttribs.LOC_MATRIX_COL2");
|
||||
claim(InstancedAttribs.LOC_MATRIX_COL3, "InstancedAttribs.LOC_MATRIX_COL3");
|
||||
claim(InstancedAttribs.LOC_COLOR, "InstancedAttribs.LOC_COLOR");
|
||||
claim(InstancedAttribs.LOC_LIGHTMAP, "InstancedAttribs.LOC_LIGHTMAP");
|
||||
|
||||
claim(ProgramCreator.MC_ENTITY, "ProgramCreator.MC_ENTITY");
|
||||
claim(ProgramCreator.MC_MID_TEX_COORD, "ProgramCreator.MC_MID_TEX_COORD");
|
||||
claim(ProgramCreator.AT_TANGENT, "ProgramCreator.AT_TANGENT");
|
||||
claim(ProgramCreator.AT_MIDBLOCK, "ProgramCreator.AT_MIDBLOCK");
|
||||
}
|
||||
|
||||
@Test
|
||||
void instancedMatrixColumnsAreContiguous() {
|
||||
final int[] columns = {
|
||||
InstancedAttribs.LOC_MATRIX_COL0, InstancedAttribs.LOC_MATRIX_COL1,
|
||||
InstancedAttribs.LOC_MATRIX_COL2, InstancedAttribs.LOC_MATRIX_COL3
|
||||
};
|
||||
for (int c = 1; c < columns.length; c++) {
|
||||
assertEquals(columns[0] + c, columns[c],
|
||||
"instance matrix column " + c + " must sit at LOC_MATRIX_COL0 + " + c);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void reportsRemainingFreeLocations() {
|
||||
reservedAttributeLocationsDoNotOverlap();
|
||||
final TreeSet<Integer> free = new TreeSet<>();
|
||||
for (int i = 0; i < MAX_VERTEX_ATTRIBS; i++) {
|
||||
if (!claimed.containsKey(i)) free.add(i);
|
||||
}
|
||||
System.out.println("free vertex attribute locations: " + free);
|
||||
}
|
||||
}
|
||||
+29
-9
@@ -1,5 +1,11 @@
|
||||
package net.coderbot.iris.pipeline.transform;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import java.util.HashMap;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.regex.Matcher;
|
||||
import net.coderbot.iris.gl.shader.ProgramCreator;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.InstancedAttribs;
|
||||
import com.gtnewhorizons.angelica.glsm.RenderSystem;
|
||||
import com.gtnewhorizons.angelica.glsm.testutil.Reflect;
|
||||
import net.coderbot.iris.gbuffer_overrides.matching.InputAvailability;
|
||||
@@ -93,15 +99,29 @@ class AttributeTransformerInstancedTest {
|
||||
return squash(shader).indexOf(squash(snippet));
|
||||
}
|
||||
|
||||
@Test
|
||||
void generatedInstancedShaderHasNoOverlappingAttributeLocations() {
|
||||
final String v = instancedVertex(VERTEX);
|
||||
final Matcher m = Pattern.compile("layout\\s*\\(\\s*location\\s*=\\s*(\\d+)\\s*\\)\\s*in\\s+\\w+\\s+(\\w+)").matcher(v);
|
||||
final Map<Integer, String> byLocation = new HashMap<>();
|
||||
while (m.find()) {
|
||||
final int loc = Integer.parseInt(m.group(1));
|
||||
final String name = m.group(2);
|
||||
final String previous = byLocation.put(loc, name);
|
||||
assertNull(previous, "location " + loc + " declared twice: " + previous + " and " + name);
|
||||
}
|
||||
assertFalse(byLocation.isEmpty(), "expected the instanced variant to declare attribute locations");
|
||||
}
|
||||
|
||||
@Test
|
||||
void declaresInstanceAttribsAndGlobalMatrices() {
|
||||
final String v = instancedVertex(VERTEX);
|
||||
assertHas(v, "layout(location = 7) in vec4 iris_InstMat0");
|
||||
assertHas(v, "layout(location = 8) in vec4 iris_InstMat1");
|
||||
assertHas(v, "layout(location = 9) in vec4 iris_InstMat2");
|
||||
assertHas(v, "layout(location = 10) in vec4 iris_InstMat3");
|
||||
assertHas(v, "layout(location = 11) in vec4 iris_InstColor");
|
||||
assertHas(v, "layout(location = 12) in vec2 iris_InstLightmap");
|
||||
assertHas(v, "layout(location = " + InstancedAttribs.LOC_MATRIX_COL0 + ") in vec4 iris_InstMat0");
|
||||
assertHas(v, "layout(location = " + InstancedAttribs.LOC_MATRIX_COL1 + ") in vec4 iris_InstMat1");
|
||||
assertHas(v, "layout(location = " + InstancedAttribs.LOC_MATRIX_COL2 + ") in vec4 iris_InstMat2");
|
||||
assertHas(v, "layout(location = " + InstancedAttribs.LOC_MATRIX_COL3 + ") in vec4 iris_InstMat3");
|
||||
assertHas(v, "layout(location = " + InstancedAttribs.LOC_COLOR + ") in vec4 iris_InstColor");
|
||||
assertHas(v, "layout(location = " + InstancedAttribs.LOC_LIGHTMAP + ") in vec2 iris_InstLightmap");
|
||||
assertLacks(v, "uniform mat4 iris_ModelViewMatrix;");
|
||||
assertLacks(v, "uniform mat3 iris_NormalMatrix;");
|
||||
assertHas(v, "mat4 iris_ModelViewMatrix;");
|
||||
@@ -123,12 +143,12 @@ class AttributeTransformerInstancedTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void foldsEntityAndMidTexCoordAttributes() {
|
||||
void foldsEntityAttributeButKeepsMidTexCoord() {
|
||||
final String v = instancedVertex(VERTEX);
|
||||
assertLacks(v, "mc_Entity");
|
||||
assertLacks(v, "mc_midTexCoord");
|
||||
assertHas(v, "vec4(-1.0, -1.0, 0.0, 1.0)");
|
||||
assertHas(v, "vec4(0.5, 0.5, 0.0, 1.0)");
|
||||
assertHas(v, "mc_midTexCoord");
|
||||
assertLacks(v, "vec4(0.5, 0.5, 0.0, 1.0)");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in new issue
Block a user