mirror of
https://github.com/GTNewHorizons/Angelica.git
synced 2026-11-15 01:34:48 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b7d439e6b1 | ||
|
|
c36647af34 | ||
|
|
f787801a0e | ||
|
|
f192793395 | ||
|
|
3876f7e693 | ||
|
|
94ee37bfed | ||
|
|
26c161f0e8 | ||
|
|
75639d3b73 | ||
|
|
a07cecb879 | ||
|
|
81b82748ce | ||
|
|
8d0c90c423 | ||
|
|
8ce6a2f8c6 | ||
|
|
2aa4ac7509 | ||
|
|
8f98151049 | ||
|
|
2b70318327 | ||
|
|
3b4ebcf611 | ||
|
|
2845c9e078 | ||
|
|
6f054a09a9 | ||
|
|
37a10eee3f | ||
|
|
744c1f971f | ||
|
|
32d1c27b9c | ||
|
|
f180894da7 | ||
|
|
bb67f82701 | ||
|
|
73f5556595 | ||
|
|
19bc7cbaed | ||
|
|
d189a644e2 | ||
|
|
94ba40ae4d | ||
|
|
961b23fd2d | ||
|
|
61b5301f1a | ||
|
|
0e539ae034 | ||
|
|
e581326641 | ||
|
|
c707cc51f1 | ||
|
|
7d890bf3e7 | ||
|
|
ffc324bab6 | ||
|
|
0dfffca88e |
No files matched your search
@@ -20,3 +20,11 @@ indent_size = 2
|
||||
|
||||
[*.lang]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[*.java]
|
||||
max_line_length = 240
|
||||
ij_java_wrap_long_lines = false
|
||||
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
|
||||
Executable
+13
@@ -0,0 +1,13 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
cd "$REPO_ROOT/umbra"
|
||||
|
||||
gradle_args=(:compileJava :compileMixinJava :test --no-daemon --console=plain --stacktrace)
|
||||
if [[ -n "${UMBRA_GRADLE_ARGS:-}" ]]; then
|
||||
read -ra extra_args <<< "$UMBRA_GRADLE_ARGS"
|
||||
gradle_args+=("${extra_args[@]}")
|
||||
fi
|
||||
|
||||
sh ./gradlew "${gradle_args[@]}"
|
||||
@@ -0,0 +1,25 @@
|
||||
name: Umbra build
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [ master, main ]
|
||||
push:
|
||||
branches: [ master, main ]
|
||||
|
||||
jobs:
|
||||
umbra-build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-java@v5
|
||||
with:
|
||||
distribution: zulu
|
||||
java-version: |
|
||||
21
|
||||
25
|
||||
- uses: gradle/actions/setup-gradle@v5
|
||||
with:
|
||||
validate-wrappers: true
|
||||
- run: .github/scripts/umbra-build.sh
|
||||
@@ -1,5 +1,6 @@
|
||||
.gradle
|
||||
.kotlin/
|
||||
.lwjgl/
|
||||
.settings
|
||||
/.idea/
|
||||
/.vs/
|
||||
@@ -43,5 +44,6 @@ tracy-client/src/main/resources/natives/
|
||||
tracy-client/angelica/
|
||||
dist/tracy-natives/
|
||||
*.tracy
|
||||
*.jfr
|
||||
|
||||
tracy-client/angelica/
|
||||
@@ -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
|
||||
@@ -99,3 +101,5 @@ Targets GL 3.3+ core profile
|
||||
* FFP vertex shader generation inspired by ffvertex_prog.c (Copyright 2007 VMware, Inc.)
|
||||
* FFP fragment shader generation inspired by ff_fragment_shader.c (Copyright 2007, 2009 VMware, Inc. / 2010-2011 Intel Corporation)
|
||||
* Fog parameter optimization inspired by st_nir_lower_fog.c (Copyright 2023 Google LLC) and prog_statevars.c (Copyright 1999-2007 Brian Paul)
|
||||
* RGSS Terrain Texture Filtering
|
||||
* Indiesoft for [defold-sharp-sprite](https://github.com/indiesoftby/defold-sharp-sprite), used as the reference implementation under the MIT license
|
||||
+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)
|
||||
+7
-82
@@ -1,19 +1,16 @@
|
||||
import com.modrinth.minotaur.TaskModrinthUpload
|
||||
import net.darkhax.curseforgegradle.TaskPublishCurseForge
|
||||
import xyz.wagyourtail.jvmdg.gradle.flags.DowngradeFlags
|
||||
import xyz.wagyourtail.jvmdg.gradle.task.DowngradeJar
|
||||
import xyz.wagyourtail.jvmdg.gradle.task.files.DowngradeFiles
|
||||
|
||||
plugins {
|
||||
id("com.gtnewhorizons.gtnhconvention")
|
||||
}
|
||||
|
||||
tasks.withType<DowngradeJar>().configureEach { logLevel.set("FATAL") }
|
||||
tasks.withType<DowngradeFiles>().configureEach { logLevel.set("FATAL") }
|
||||
val lwjglDebug = false
|
||||
val gpuHud = false
|
||||
val renderdoc = false
|
||||
val rgp = false
|
||||
extra["lwjglDebug"] = lwjglDebug
|
||||
|
||||
minecraft {
|
||||
extraRunJvmArguments.add("-Dangelica.debug.testBlocks=true")
|
||||
@@ -24,24 +21,13 @@ minecraft {
|
||||
|
||||
extraRunJvmArguments.add("-Dsun.net.client.defaultConnectTimeout=5000")
|
||||
extraRunJvmArguments.add("-Dsun.net.client.defaultReadTimeout=5000")
|
||||
// TODO: Remove once GTNHGradle includes shaderc/spvc in the default lwjgl3Bindings list
|
||||
lwjgl3Bindings.addAll("shaderc", "spvc")
|
||||
lwjgl3Version = libs.versions.lwjgl3.get()
|
||||
// extraRunJvmArguments.addAll("-Dlegacy.debugClassLoadingSave=true")
|
||||
// extraRunJvmArguments.addAll("-Drfb.dumpLoadedClasses=true", "-Drfb.dumpLoadedClassesPerTransformer=true")
|
||||
//extraRunJvmArguments.add("-Dangelica.debug.redirectorLogspam=true")
|
||||
|
||||
}
|
||||
|
||||
// Forward -Dangelica.* from the gradle invocation to the client
|
||||
tasks.withType<JavaExec>().matching { it.name.startsWith("runClient") }.configureEach {
|
||||
for ((key, value) in System.getProperties()) {
|
||||
val name = key.toString()
|
||||
if (name.startsWith("angelica.")) {
|
||||
jvmArgs("-D$name=$value")
|
||||
}
|
||||
}
|
||||
}
|
||||
apply(from = "gradle/angelica-run-common.gradle.kts")
|
||||
|
||||
if (gpuHud) {
|
||||
tasks.withType<JavaExec>().matching { it.name.startsWith("runClient") }.configureEach {
|
||||
@@ -57,47 +43,12 @@ if (gpuHud) {
|
||||
|
||||
configurations.all {
|
||||
exclude(group = "com.github.GTNewHorizons", module = "Angelica")
|
||||
resolutionStrategy.dependencySubstitution {
|
||||
substitute(module("com.github.GTNewHorizons:lwjgl3ify"))
|
||||
.using(module("com.github.GTNewHorizons:lwjgl3ify:${libs.versions.lwjgl3ify.get()}"))
|
||||
}
|
||||
}
|
||||
|
||||
// lwjgl3ify pulls a pinned lwjgl 3.4.2 snapshot transitively from here.
|
||||
repositories {
|
||||
maven {
|
||||
name = "Maven Central Snapshots"
|
||||
url = uri("https://central.sonatype.com/repository/maven-snapshots/")
|
||||
content { includeGroup("org.lwjgl") }
|
||||
}
|
||||
}
|
||||
|
||||
val osName = System.getProperty("os.name").lowercase()
|
||||
val isMacOs = org.gradle.internal.os.OperatingSystem.current().isMacOsX
|
||||
val isMacOsArm64 = isMacOs && System.getProperty("os.arch") == "aarch64"
|
||||
|
||||
tasks.withType<JavaExec>().configureEach {
|
||||
if (name.startsWith("runClient") && name != "runClient") {
|
||||
if (isMacOs) {
|
||||
// SDL3 / Cocoa / Metal must initialize on the JVM main thread.
|
||||
jvmArgs("-XstartOnFirstThread")
|
||||
jvmArgs("-Dangelica.sdlgpu.encoderAssertions=" + if (lwjglDebug) "fatal" else "warn")
|
||||
if (lwjglDebug) {
|
||||
environment("METAL_DEVICE_WRAPPER_TYPE", "1")
|
||||
environment("METAL_DEBUG_ERROR_MODE", "0")
|
||||
environment("MTL_SHADER_VALIDATION", "1")
|
||||
environment("MTL_SHADER_VALIDATION_REPORT_TO_STDERR", "1")
|
||||
environment("MTL_DEBUG_LAYER", "1")
|
||||
environment("MallocScribble", "1")
|
||||
environment("MallocPreScribble", "1")
|
||||
environment("MallocGuardEdges", "1")
|
||||
environment("MallocStackLogging", "1")
|
||||
environment("MallocStackLoggingNoCompact", "1")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Linux runClient: RenderDoc frame capture (F12) OR RADV RGP/SQTT capture. Mutually exclusive; RGP wins
|
||||
tasks.withType<JavaExec>().configureEach {
|
||||
if (name.startsWith("runClient") && !isMacOs && !osName.contains("windows")) {
|
||||
@@ -126,14 +77,6 @@ tasks.withType<JavaExec>().configureEach {
|
||||
}
|
||||
}
|
||||
|
||||
tasks.processResources {
|
||||
val projectVersion = project.version.toString()
|
||||
inputs.property("version", projectVersion)
|
||||
filesMatching("META-INF/rfb-plugin/*") {
|
||||
expand("version" to projectVersion)
|
||||
}
|
||||
}
|
||||
|
||||
tasks.register<Copy>("copyDependencies") {
|
||||
group = "Angelica"
|
||||
description = "Collect dependencies into the testDependencies folder"
|
||||
@@ -187,6 +130,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(
|
||||
@@ -203,6 +149,7 @@ fun Test.configureAngelicaJava8() {
|
||||
tasks.test {
|
||||
useJUnitPlatform { excludeTags = setOf("gl-core") }
|
||||
configureAngelicaJava8()
|
||||
jvmArgs("-Dceleritas.lwjglService=org.taumc.celeritas.lwjgl.HeadlessTestLWJGLService")
|
||||
dependsOn(":glsm:classes", ":lwjgl3-backend:classes", ":sdl-gpu:classes")
|
||||
}
|
||||
|
||||
@@ -230,29 +177,7 @@ val glCoreTest by tasks.registering(Test::class) {
|
||||
|
||||
tasks.test { finalizedBy(glCoreTest) }
|
||||
|
||||
tasks.shadowJar {
|
||||
dependsOn(embedOnly)
|
||||
from(embedOnly.map(::zipTree))
|
||||
|
||||
duplicatesStrategy = DuplicatesStrategy.INCLUDE
|
||||
mergeServiceFiles()
|
||||
|
||||
minimize {
|
||||
exclude(project(rootProject.path))
|
||||
exclude(project(":glsm"))
|
||||
exclude(project(":lwjgl3-backend"))
|
||||
exclude(project(":sdl-gpu"))
|
||||
exclude(dependency("org.taumc:.*:.*"))
|
||||
exclude(dependency("org.antlr:.*:.*"))
|
||||
}
|
||||
|
||||
relocate("com.mitchej123", "com.mitchej123")
|
||||
relocate("org.embeddedt", "org.embeddedt")
|
||||
relocate("com.gtnewhorizons.angelica.config", "com.gtnewhorizons.angelica.config")
|
||||
relocate("com.gtnewhorizons.angelica.glsm", "com.gtnewhorizons.angelica.glsm")
|
||||
relocate("com.gtnewhorizons.angelica.lwjgl3", "com.gtnewhorizons.angelica.lwjgl3")
|
||||
relocate("com.gtnewhorizons.angelica.sdlgpu", "com.gtnewhorizons.angelica.sdlgpu")
|
||||
}
|
||||
apply(from = "gradle/angelica-shadow-common.gradle.kts")
|
||||
|
||||
tasks.withType<TaskPublishCurseForge>().configureEach {
|
||||
uploadArtifacts.forEach { it.addGameVersion("Client") }
|
||||
|
||||
@@ -52,8 +52,8 @@ configurations.configureEach {
|
||||
}
|
||||
|
||||
DependencyHandlerScope.of(dependencies).apply {
|
||||
compileOnly("com.github.GTNewHorizons:Hodgepodge:2.7.187:dev")
|
||||
runtimeOnlyNonPublishable("com.github.GTNewHorizons:Hodgepodge:2.7.187:dev")
|
||||
compileOnly("com.github.GTNewHorizons:Hodgepodge:2.7.197:dev")
|
||||
runtimeOnlyNonPublishable("com.github.GTNewHorizons:Hodgepodge:2.7.197:dev")
|
||||
|
||||
runtimeOnlyNonPublishable(rfg.deobf("curse.maven:biomes-o-plenty-220318:2499612"))
|
||||
|
||||
@@ -104,7 +104,6 @@ DependencyHandlerScope.of(dependencies).apply {
|
||||
|
||||
// Celeritas (isTransitive=false: deps provided by GTNHLib/runtime)
|
||||
embedOnly(libs.celeritas.common) { isTransitive = false }
|
||||
embedOnly(libs.celeritas.lwjgl2.service) { isTransitive = false }
|
||||
|
||||
// Because who doesn't want NEI
|
||||
devOnlyNonPublishable("com.github.GTNewHorizons:NotEnoughItems:2.8.109-GTNH:dev")
|
||||
@@ -177,7 +176,7 @@ DependencyHandlerScope.of(dependencies).apply {
|
||||
compileOnly("com.github.GTNewHorizons:BetterCrashes:1.4.5-GTNH:dev")
|
||||
|
||||
// Distant Horizons
|
||||
compileOnly("maven.modrinth:DistantHorizonsApi:5.1.0")
|
||||
compileOnly(libs.distant.horizons.api)
|
||||
|
||||
compileOnly("mega:fluidlogged-mc1.7.10:0.1.2")
|
||||
|
||||
@@ -208,5 +207,6 @@ afterEvaluate {
|
||||
|
||||
testRuntimeOnly(libs.fastutil)
|
||||
testRuntimeOnly(libs.joml)
|
||||
testImplementation(libs.distant.horizons.api)
|
||||
}
|
||||
}
|
||||
+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); втім, його можна використовувати для модифікованих блоків, яким потрібні кілька різних значень відтінку в одному блоці.
|
||||
+32
-2
@@ -46,6 +46,27 @@ tasks.withType<Jar>().configureEach {
|
||||
exclude("net/minecraft/**")
|
||||
}
|
||||
|
||||
val stubsElements by configurations.creating {
|
||||
isCanBeConsumed = true
|
||||
isCanBeResolved = false
|
||||
attributes {
|
||||
attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage::class.java, Usage.JAVA_RUNTIME))
|
||||
attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category::class.java, Category.LIBRARY))
|
||||
attribute(rfgObfAttr, "mcp")
|
||||
attribute(rfgTransformedAttr, true)
|
||||
}
|
||||
outgoing {
|
||||
capability("${project.group}:glsm-stubs:${project.version}")
|
||||
}
|
||||
}
|
||||
|
||||
artifacts {
|
||||
add(stubsElements.name, sourceSets["stubs"].java.classesDirectory) {
|
||||
type = "java-classes-directory"
|
||||
builtBy(tasks.named("stubsClasses"))
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven {
|
||||
name = "Mojang"
|
||||
@@ -82,6 +103,11 @@ repositories {
|
||||
mavenLocal()
|
||||
}
|
||||
|
||||
val spirvTestRuntime by configurations.creating {
|
||||
isCanBeConsumed = false
|
||||
isCanBeResolved = true
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// MC compile stubs
|
||||
compileOnly(sourceSets["stubs"].output)
|
||||
@@ -112,8 +138,10 @@ dependencies {
|
||||
testRuntimeOnly(libs.lwjgl3.spvc) { artifact { classifier = lwjglNatives } }
|
||||
testRuntimeOnly(libs.lwjgl3)
|
||||
testRuntimeOnly(libs.lwjgl3) { artifact { classifier = lwjglNatives } }
|
||||
spirvTestRuntime(libs.lwjgl3.opengl)
|
||||
// @Lwjgl3Aware annotation
|
||||
compileOnly(libs.lwjgl3ify) { artifact { classifier = "dev" }; isTransitive = false }
|
||||
compileOnly(libs.retrofuturabootstrap) { isTransitive = false }
|
||||
|
||||
compileOnly(libs.lombok) { isTransitive = false }
|
||||
annotationProcessor(libs.lombok)
|
||||
@@ -140,7 +168,6 @@ dependencies {
|
||||
testRuntimeOnly(libs.junit.platform.launcher)
|
||||
testRuntimeOnly(libs.log4j.core)
|
||||
testImplementation(libs.celeritas.common) { isTransitive = false }
|
||||
testRuntimeOnly(libs.celeritas.lwjgl2.service) { isTransitive = false }
|
||||
testRuntimeOnly(libs.jvmdowngrader.java.api) { artifact { classifier = "downgraded-8" } }
|
||||
testRuntimeOnly(libs.commons.lang3)
|
||||
}
|
||||
@@ -224,6 +251,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 {
|
||||
@@ -285,7 +315,7 @@ val spirvTest by tasks.registering(Test::class) {
|
||||
classpath = sourceSets["test"].runtimeClasspath.filter { file ->
|
||||
// Strip the LWJGL2 jars - they collide with LWJGL3's sealed org.lwjgl package.
|
||||
!file.name.startsWith("lwjgl-2.") && !file.name.startsWith("lwjgl_util-") && !file.name.startsWith("lwjgl-platform-")
|
||||
}
|
||||
}.plus(spirvTestRuntime)
|
||||
|
||||
filter {
|
||||
includeTestsMatching("com.gtnewhorizons.angelica.glsm.shader.SpirvShaderTranslator*Test")
|
||||
|
||||
@@ -47,10 +47,15 @@ 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
|
||||
public static final boolean FLYBY_EXIT_WHEN_DONE = Boolean.getBoolean("angelica.flyby.exitWhenDone");
|
||||
public static final long FLYBY_TIME_OF_DAY = Math.floorMod(Long.getLong("angelica.flyby.timeOfDay", 6000L), 24000L);
|
||||
public static final String FLYBY_COMMANDS = System.getProperty("angelica.flyby.commands", "");
|
||||
public static final String FLYBY_ORIGIN = System.getProperty("angelica.flyby.origin", "");
|
||||
public static final FlybyPacing FLYBY_PACING = parseEnum("angelica.flyby.pacing", FlybyPacing.UNCAPPED, FlybyPacing.UNCAPPED, FlybyPacing.class);
|
||||
|
||||
// Debug
|
||||
public static final boolean LWJGL_DEBUG = Boolean.getBoolean("org.lwjgl.util.Debug");
|
||||
@@ -59,19 +64,26 @@ public final class SystemProperties {
|
||||
public static final boolean ENABLE_TEST_BLOCKS = Boolean.getBoolean("angelica.debug.testBlocks");
|
||||
public static final boolean DUMP_CLASS = Boolean.getBoolean("angelica.debug.dumpClass");
|
||||
private static final boolean DUMP_SHADERS_PROP = Boolean.getBoolean("angelica.debug.dumpShaders");
|
||||
private static final boolean DEBUG_TOOLING_PROP = Boolean.getBoolean("angelica.debug.tooling");
|
||||
public static final boolean REDIRECTOR_LOGSPAM = Boolean.getBoolean("angelica.debug.redirectorLogspam");
|
||||
public static final boolean DEBUG_DISPLAY_LISTS = Boolean.getBoolean("angelica.debug.displayLists");
|
||||
public static final boolean FFP_TRACE = Boolean.getBoolean("angelica.debug.ffpTrace");
|
||||
public static final boolean LOG_DISPLAY_LIST_COMPILATION = Boolean.getBoolean("angelica.debug.displayLists.compilation");
|
||||
public static final boolean FORCE_ORPHAN_STREAMING = Boolean.getBoolean("angelica.debug.forceOrphanStreaming");
|
||||
public static final String SHADER_DUMP_ROOT = "angelica_dumps";
|
||||
|
||||
// Set by us, read by celeritas
|
||||
public static final String KEY_CELERITAS_ENABLE_GL_DEBUG = "celeritas.enableGLDebug";
|
||||
public static final String KEY_CELERITAS_LWJGL_SERVICE = "celeritas.lwjglService";
|
||||
|
||||
public static boolean dumpShaders() {
|
||||
return DUMP_SHADERS_PROP || isDeobf();
|
||||
}
|
||||
|
||||
public static boolean debugTooling() {
|
||||
return DEBUG_TOOLING_PROP || isDeobf();
|
||||
}
|
||||
|
||||
public static Path shaderDumpDir(String phase) {
|
||||
return dumpShaders() ? Paths.get(SHADER_DUMP_ROOT, phase) : null;
|
||||
}
|
||||
@@ -101,6 +113,11 @@ public final class SystemProperties {
|
||||
FATAL
|
||||
}
|
||||
|
||||
public enum FlybyPacing {
|
||||
UNCAPPED,
|
||||
CONFIGURED
|
||||
}
|
||||
|
||||
private static <E extends Enum<E>> E parseEnum(String key, E whenAbsent, E whenInvalid, Class<E> type) {
|
||||
final String raw = System.getProperty(key);
|
||||
if (raw == null || raw.isEmpty()) return whenAbsent;
|
||||
|
||||
@@ -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;
|
||||
@@ -23,6 +27,7 @@ import com.gtnewhorizons.angelica.glsm.states.ClipPlaneState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.Color4;
|
||||
import com.gtnewhorizons.angelica.glsm.states.PixelUnpackState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.ImageUnitArray;
|
||||
import com.gtnewhorizons.angelica.glsm.states.SamplerUnitArray;
|
||||
import com.gtnewhorizons.angelica.glsm.states.TextureUnitArray;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.ints.IntStack;
|
||||
@@ -97,6 +102,7 @@ class GLContextState {
|
||||
public final IntegerStateStack shadeModelState = new IntegerStateStack(GL11.GL_SMOOTH);
|
||||
public final TextureUnitArray textures = new TextureUnitArray();
|
||||
public final ImageUnitArray imageUnits = new ImageUnitArray();
|
||||
public final SamplerUnitArray samplerUnits = new SamplerUnitArray();
|
||||
public final BlendStateStack blendState = new BlendStateStack();
|
||||
public final BooleanStateStack blendMode = track(new BooleanStateStack(GL11.GL_BLEND));
|
||||
public final BooleanStateStack scissorTest = track(new BooleanStateStack(GL11.GL_SCISSOR_TEST));
|
||||
@@ -180,6 +186,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];
|
||||
|
||||
@@ -564,7 +564,7 @@ public final class GLDebug {
|
||||
/**
|
||||
* Overload that lets you gate the String concat behind the debugState
|
||||
*/
|
||||
public static void pushGroup(String prefix, int value) {
|
||||
public static void pushGroup(String prefix, long value) {
|
||||
if (debugState != null && Thread.currentThread() == GLStateManager.getMainThread()) {
|
||||
debugState.pushGroup(prefix + value);
|
||||
}
|
||||
|
||||
@@ -10,7 +10,10 @@ import com.gtnewhorizons.angelica.config.SystemProperties;
|
||||
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.RenderBackend;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.VSyncMode;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.FfpExtendedAttribs;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.Instancing;
|
||||
import com.gtnewhorizons.angelica.glsm.profiling.Tracy;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.ShaderManager;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.VAOManager;
|
||||
@@ -51,11 +54,13 @@ import com.gtnewhorizons.angelica.glsm.states.ClipPlaneState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.Color4;
|
||||
import com.gtnewhorizons.angelica.glsm.states.PixelUnpackState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.PolygonState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.SamplerUnitArray;
|
||||
import com.gtnewhorizons.angelica.glsm.states.ImageUnitBinding;
|
||||
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;
|
||||
@@ -89,6 +94,7 @@ import org.lwjgl.opengl.GL21;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
import org.lwjgl.opengl.GL31;
|
||||
import org.lwjgl.opengl.GL32;
|
||||
import org.lwjgl.opengl.GL33;
|
||||
import org.lwjgl.opengl.GLContext;
|
||||
import org.lwjgl.opengl.KHRDebug;
|
||||
import org.lwjgl.opengl.KHRDebugCallback;
|
||||
@@ -422,11 +428,7 @@ public class GLStateManager {
|
||||
public static boolean wideLineEmulationActive = false;
|
||||
public static boolean lineStippleActive = false;
|
||||
|
||||
public static boolean instancedFfpDrawActive = false;
|
||||
|
||||
|
||||
|
||||
|
||||
public static Instancing ffpInstancing = Instancing.NONE;
|
||||
|
||||
private static final MethodHandle MAT4_STACK_CURR_DEPTH;
|
||||
|
||||
@@ -491,7 +493,9 @@ public class GLStateManager {
|
||||
|
||||
public static void initialize(GLSMInitConfig config) {
|
||||
initConfig = config;
|
||||
preInit(config.getDisplayWidth(), config.getDisplayHeight());
|
||||
final int width = config.getDisplayWidth() > 0 ? config.getDisplayWidth() : Display.getWidth();
|
||||
final int height = config.getDisplayHeight() > 0 ? config.getDisplayHeight() : Display.getHeight();
|
||||
preInit(width, height);
|
||||
init(config.getPostInitCallback());
|
||||
}
|
||||
|
||||
@@ -1050,6 +1054,8 @@ public class GLStateManager {
|
||||
case GL30.GL_DRAW_FRAMEBUFFER_BINDING -> glCtx.drawFramebuffer;
|
||||
case GL30.GL_READ_FRAMEBUFFER_BINDING -> glCtx.readFramebuffer;
|
||||
|
||||
case GL33.GL_SAMPLER_BINDING -> glCtx.samplerUnits.get(glCtx.activeTextureUnit.getValue());
|
||||
|
||||
default -> switch (pname) {
|
||||
case GL11.GL_FOG_MODE -> glCtx.fogState.getFogMode();
|
||||
case GL11.GL_LINE_STIPPLE_PATTERN -> glCtx.lineState.getStipplePattern() & 0xFFFF;
|
||||
@@ -1302,6 +1308,29 @@ public class GLStateManager {
|
||||
}
|
||||
}
|
||||
|
||||
public static void glEnablei(int cap, int index) {
|
||||
RENDER_BACKEND.enablei(cap, index);
|
||||
markIndexedCapUnknown(cap);
|
||||
}
|
||||
|
||||
public static void glDisablei(int cap, int index) {
|
||||
RENDER_BACKEND.disablei(cap, index);
|
||||
markIndexedCapUnknown(cap);
|
||||
}
|
||||
|
||||
private static void markIndexedCapUnknown(int cap) {
|
||||
if (cap == GL11.GL_BLEND) {
|
||||
ctx().blendMode.setUnknownState();
|
||||
} else if (cap == GL11.GL_SCISSOR_TEST) {
|
||||
ctx().scissorTest.setUnknownState();
|
||||
}
|
||||
}
|
||||
|
||||
public static void glBlendFuncSeparatei(int buf, int srcRGB, int dstRGB, int srcAlpha, int dstAlpha) {
|
||||
RENDER_BACKEND.blendFuncSeparatei(buf, srcRGB, dstRGB, srcAlpha, dstAlpha);
|
||||
ctx().blendState.setFuncUnknownState();
|
||||
}
|
||||
|
||||
public static void enableBlend() {
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
@@ -1397,8 +1426,11 @@ public class GLStateManager {
|
||||
}
|
||||
|
||||
public static void endForeignDraw() {
|
||||
if (foreignDrawDepth > 0 && --foreignDrawDepth == 0 && GLSMHooks.FOREIGN_DRAW_END.hasListeners()) {
|
||||
GLSMHooks.FOREIGN_DRAW_END.post(GLSMHooks.foreignDrawEndEvent);
|
||||
if (foreignDrawDepth > 0 && --foreignDrawDepth == 0) {
|
||||
GLSMHooks.resolvePendingProgram();
|
||||
if (GLSMHooks.FOREIGN_DRAW_END.hasListeners()) {
|
||||
GLSMHooks.FOREIGN_DRAW_END.post(GLSMHooks.foreignDrawEndEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1447,6 +1479,7 @@ public class GLStateManager {
|
||||
final boolean caching = isCachingEnabled();
|
||||
if (GLSMConfig.hudCacheOverride) {
|
||||
glCtx.blendState.setAll(srcFactor, dstFactor, GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
glCtx.blendState.clearFuncUnknownState();
|
||||
RENDER_BACKEND.blendFuncSeparate(srcFactor, dstFactor, GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
if (GLSMHooks.BLEND_FUNC_CHANGE.hasListeners()) {
|
||||
GLSMHooks.blendFuncChangeEvent.srcRgb = srcFactor;
|
||||
@@ -1463,8 +1496,10 @@ public class GLStateManager {
|
||||
|| glCtx.blendState.getSrcRgb() != srcFactor
|
||||
|| glCtx.blendState.getDstRgb() != dstFactor
|
||||
|| glCtx.blendState.getSrcAlpha() != srcFactor
|
||||
|| glCtx.blendState.getDstAlpha() != dstFactor) {
|
||||
|| glCtx.blendState.getDstAlpha() != dstFactor
|
||||
|| glCtx.blendState.isFuncUnknown()) {
|
||||
glCtx.blendState.setAll(srcFactor, dstFactor, srcFactor, dstFactor);
|
||||
glCtx.blendState.clearFuncUnknownState();
|
||||
RENDER_BACKEND.blendFunc(srcFactor, dstFactor);
|
||||
if (GLSMHooks.BLEND_FUNC_CHANGE.hasListeners()) {
|
||||
GLSMHooks.blendFuncChangeEvent.srcRgb = srcFactor;
|
||||
@@ -1527,8 +1562,9 @@ public class GLStateManager {
|
||||
}
|
||||
final boolean caching = isCachingEnabled();
|
||||
final boolean bypass = !caching;
|
||||
if (bypass || glCtx.blendState.getSrcRgb() != srcRgb || glCtx.blendState.getDstRgb() != dstRgb || glCtx.blendState.getSrcAlpha() != srcAlpha || glCtx.blendState.getDstAlpha() != dstAlpha) {
|
||||
if (bypass || glCtx.blendState.getSrcRgb() != srcRgb || glCtx.blendState.getDstRgb() != dstRgb || glCtx.blendState.getSrcAlpha() != srcAlpha || glCtx.blendState.getDstAlpha() != dstAlpha || glCtx.blendState.isFuncUnknown()) {
|
||||
glCtx.blendState.setAll(srcRgb, dstRgb, srcAlpha, dstAlpha);
|
||||
glCtx.blendState.clearFuncUnknownState();
|
||||
RENDER_BACKEND.blendFuncSeparate(srcRgb, dstRgb, srcAlpha, dstAlpha);
|
||||
if (GLSMHooks.BLEND_FUNC_CHANGE.hasListeners()) {
|
||||
GLSMHooks.blendFuncChangeEvent.srcRgb = srcRgb;
|
||||
@@ -2001,6 +2037,7 @@ public class GLStateManager {
|
||||
}
|
||||
|
||||
public static void glBindTexture(int target, int texture) {
|
||||
if (TextureInfoCache.isProxyTarget(target)) return;
|
||||
final GLContextState glCtx = ctx();
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
@@ -2577,12 +2614,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 +2734,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) {
|
||||
@@ -3522,24 +3569,28 @@ public class GLStateManager {
|
||||
Display.swapBuffers();
|
||||
}
|
||||
|
||||
private static boolean firstClearPending;
|
||||
private static long firstClearBlockNanos;
|
||||
|
||||
public static void updateDisplay() throws LWJGLException {
|
||||
updateDisplay(true);
|
||||
}
|
||||
|
||||
public static void updateDisplay(boolean processMessages) throws LWJGLException {
|
||||
if (Thread.currentThread() != MainThread) {
|
||||
swapBuffers();
|
||||
if (processMessages) Display.processMessages();
|
||||
RENDER_BACKEND.updateDisplayFromWorkerThread(processMessages);
|
||||
return;
|
||||
}
|
||||
if (processMessages) pumpDisplayMessages();
|
||||
if (!RENDER_BACKEND.wantsDisplayUpdateGateTiming()) {
|
||||
Display.update(processMessages);
|
||||
Display.update(false);
|
||||
return;
|
||||
}
|
||||
final long start = System.nanoTime();
|
||||
Display.update(processMessages);
|
||||
Display.update(false);
|
||||
final long end = System.nanoTime();
|
||||
RENDER_BACKEND.recordFrameGate(end - start, end);
|
||||
RENDER_BACKEND.recordGate(end - start, end);
|
||||
firstClearPending = true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3736,6 +3787,7 @@ public class GLStateManager {
|
||||
RENDER_BACKEND.clearDepth(glCtx.depthState.getClearValue());
|
||||
|
||||
RENDER_BACKEND.blendFuncSeparate(glCtx.blendState.getSrcRgb(), glCtx.blendState.getDstRgb(), glCtx.blendState.getSrcAlpha(), glCtx.blendState.getDstAlpha());
|
||||
glCtx.blendState.clearFuncUnknownState();
|
||||
RENDER_BACKEND.blendEquationSeparate(glCtx.blendState.getEquationRgb(), glCtx.blendState.getEquationAlpha());
|
||||
RENDER_BACKEND.blendColor(glCtx.blendState.getBlendColorR(), glCtx.blendState.getBlendColorG(), glCtx.blendState.getBlendColorB(), glCtx.blendState.getBlendColorA());
|
||||
RENDER_BACKEND.colorMask(glCtx.colorMask.red, glCtx.colorMask.green, glCtx.colorMask.blue, glCtx.colorMask.alpha);
|
||||
@@ -3759,6 +3811,12 @@ public class GLStateManager {
|
||||
applyPolygonOffset(glCtx);
|
||||
RENDER_BACKEND.cullFace(glCtx.polygonState.getCullFaceMode());
|
||||
RENDER_BACKEND.frontFace(glCtx.polygonState.getFrontFace());
|
||||
|
||||
final SamplerUnitArray samplerUnits = glCtx.samplerUnits;
|
||||
for (int i = 0; i < samplerUnits.size(); i++) {
|
||||
final int sampler = samplerUnits.get(i);
|
||||
if (sampler != 0) RENDER_BACKEND.bindSampler(i, sampler);
|
||||
}
|
||||
}
|
||||
|
||||
private static void applyPolygonOffset(GLContextState glCtx) {
|
||||
@@ -3783,13 +3841,18 @@ public class GLStateManager {
|
||||
RENDER_BACKEND.clearDepth(glCtx.depthState.getClearValue());
|
||||
}
|
||||
if ((mask & GL11.GL_COLOR_BUFFER_BIT) != 0) {
|
||||
if (glCtx.restoreBlendChanged) {
|
||||
final BlendStateStack blend = glCtx.blendState;
|
||||
if (glCtx.restoreBlendChanged || blend.isFuncUnknown()) {
|
||||
final DeferredBlendHandler restoreBlendHandler = GLSMHooks.blendHandler;
|
||||
if (restoreBlendHandler == null || !restoreBlendHandler.isOverrideHeld()) {
|
||||
RENDER_BACKEND.blendFuncSeparate(glCtx.blendState.getSrcRgb(), glCtx.blendState.getDstRgb(), glCtx.blendState.getSrcAlpha(), glCtx.blendState.getDstAlpha());
|
||||
RENDER_BACKEND.blendFuncSeparate(blend.getSrcRgb(), blend.getDstRgb(), blend.getSrcAlpha(), blend.getDstAlpha());
|
||||
blend.clearFuncUnknownState();
|
||||
}
|
||||
if (glCtx.restoreBlendChanged) {
|
||||
RENDER_BACKEND.blendEquationSeparate(blend.getEquationRgb(), blend.getEquationAlpha());
|
||||
RENDER_BACKEND.blendColor(
|
||||
blend.getBlendColorR(), blend.getBlendColorG(), blend.getBlendColorB(), blend.getBlendColorA());
|
||||
}
|
||||
RENDER_BACKEND.blendEquationSeparate(glCtx.blendState.getEquationRgb(), glCtx.blendState.getEquationAlpha());
|
||||
RENDER_BACKEND.blendColor(glCtx.blendState.getBlendColorR(), glCtx.blendState.getBlendColorG(), glCtx.blendState.getBlendColorB(), glCtx.blendState.getBlendColorA());
|
||||
}
|
||||
if (glCtx.restoreColorMaskChanged && !isDepthColorOverridden()) {
|
||||
RENDER_BACKEND.colorMask(glCtx.colorMask.red, glCtx.colorMask.green, glCtx.colorMask.blue, glCtx.colorMask.alpha);
|
||||
@@ -3890,9 +3953,26 @@ public class GLStateManager {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (firstClearPending && Thread.currentThread() == MainThread && getDrawFramebuffer() == 0) {
|
||||
firstClearPending = false;
|
||||
final long start = System.nanoTime();
|
||||
RENDER_BACKEND.clear(mask);
|
||||
final long end = System.nanoTime();
|
||||
if (end - start > 0) {
|
||||
firstClearBlockNanos += end - start;
|
||||
RENDER_BACKEND.recordGate(end - start, end);
|
||||
}
|
||||
return;
|
||||
}
|
||||
RENDER_BACKEND.clear(mask);
|
||||
}
|
||||
|
||||
public static long takeFirstClearBlockNanos() {
|
||||
final long value = firstClearBlockNanos;
|
||||
firstClearBlockNanos = 0L;
|
||||
return value;
|
||||
}
|
||||
|
||||
public static void glPushAttrib(int mask) {
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
@@ -3982,6 +4062,14 @@ public class GLStateManager {
|
||||
}
|
||||
}
|
||||
|
||||
public static void setTextureMatrix(int unit, Matrix4fc m) {
|
||||
if (isCachingEnabled()) {
|
||||
final GLContextState glCtx = ctx();
|
||||
glCtx.textures.getTextureUnitMatrix(unit).set(m);
|
||||
glCtx.texMatrixGeneration++;
|
||||
}
|
||||
}
|
||||
|
||||
public static void setProjectionMatrix(Matrix4fc m) {
|
||||
final GLContextState glCtx = ctx();
|
||||
if (isCachingEnabled()) {
|
||||
@@ -4542,6 +4630,11 @@ public class GLStateManager {
|
||||
};
|
||||
}
|
||||
|
||||
private static int mipLevelSize(int base, int level) {
|
||||
if (base <= 0 || level < 0 || level >= Integer.SIZE - 1) return 1;
|
||||
return Math.max(base >> level, 1);
|
||||
}
|
||||
|
||||
public static int glGetTexLevelParameteri(int target, int level, int pname) {
|
||||
if (target != GL11.GL_TEXTURE_2D || !isCachingEnabled()) {
|
||||
return RENDER_BACKEND.getTexLevelParameteri(target, level, pname);
|
||||
@@ -4556,8 +4649,8 @@ public class GLStateManager {
|
||||
return RENDER_BACKEND.getTexLevelParameteri(target, level, pname);
|
||||
}
|
||||
return switch (pname) {
|
||||
case GL11.GL_TEXTURE_WIDTH -> Math.max(info.getWidth() >> level, 1);
|
||||
case GL11.GL_TEXTURE_HEIGHT -> Math.max(info.getHeight() >> level, 1);
|
||||
case GL11.GL_TEXTURE_WIDTH -> mipLevelSize(info.getWidth(), level);
|
||||
case GL11.GL_TEXTURE_HEIGHT -> mipLevelSize(info.getHeight(), level);
|
||||
case GL11.GL_TEXTURE_INTERNAL_FORMAT -> {
|
||||
if (info.needsInternalFormatResolve() && isRecordingDisplayList()) {
|
||||
throw new IllegalStateException(String.format("glGetTexLevelParameteri(GL_TEXTURE_INTERNAL_FORMAT) needs to resolve a generic compressed format for texture %d during display list recording", getBoundTextureForServerState()));
|
||||
@@ -4679,8 +4772,61 @@ public class GLStateManager {
|
||||
public static void glClearTexImage(int texture, int level, int format, int type, ByteBuffer data) { RENDER_BACKEND.clearTexImage(texture, level, format, type); }
|
||||
|
||||
public static int glGenSamplers() { return RENDER_BACKEND.genSamplers(); }
|
||||
public static void glDeleteSamplers(int sampler) { RENDER_BACKEND.deleteSamplers(sampler); }
|
||||
public static void glBindSampler(int unit, int sampler) { RENDER_BACKEND.bindSampler(unit, sampler); }
|
||||
|
||||
public static void glGenSamplers(IntBuffer samplers) {
|
||||
for (int i = samplers.position(); i < samplers.limit(); i++) samplers.put(i, RENDER_BACKEND.genSamplers());
|
||||
}
|
||||
|
||||
public static void glGenSamplers(int[] samplers) {
|
||||
for (int i = 0; i < samplers.length; i++) samplers[i] = RENDER_BACKEND.genSamplers();
|
||||
}
|
||||
|
||||
public static void glDeleteSamplers(int sampler) {
|
||||
if (sampler == 0) return;
|
||||
ctx().samplerUnits.unbindEverywhere(sampler);
|
||||
RENDER_BACKEND.deleteSamplers(sampler);
|
||||
}
|
||||
|
||||
public static void glDeleteSamplers(IntBuffer samplers) {
|
||||
for (int i = samplers.position(); i < samplers.limit(); i++) glDeleteSamplers(samplers.get(i));
|
||||
}
|
||||
|
||||
public static void glDeleteSamplers(int[] samplers) {
|
||||
for (int sampler : samplers) glDeleteSamplers(sampler);
|
||||
}
|
||||
|
||||
public static void glBindSampler(int unit, int sampler) {
|
||||
final SamplerUnitArray units = ctx().samplerUnits;
|
||||
if (isCachingEnabled() && unit >= 0 && unit < units.size() && units.get(unit) == sampler) return;
|
||||
if (!units.set(unit, sampler)) {
|
||||
warnOnce("samplerUnitRange", "glBindSampler unit {} exceeds GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS {}", unit, units.size());
|
||||
}
|
||||
RENDER_BACKEND.bindSampler(unit, sampler);
|
||||
}
|
||||
|
||||
public static void glBindSamplers(int first, int count, IntBuffer samplers) {
|
||||
if (samplers == null) {
|
||||
for (int i = 0; i < count; i++) glBindSampler(first + i, 0);
|
||||
return;
|
||||
}
|
||||
if (samplers.remaining() < count) {
|
||||
throw new IllegalArgumentException("Number of remaining buffer elements is " + samplers.remaining() + ", must be at least " + count);
|
||||
}
|
||||
final int base = samplers.position();
|
||||
for (int i = 0; i < count; i++) glBindSampler(first + i, samplers.get(base + i));
|
||||
}
|
||||
|
||||
public static void glBindSamplers(int first, IntBuffer samplers) {
|
||||
if (samplers != null) glBindSamplers(first, samplers.remaining(), samplers);
|
||||
}
|
||||
|
||||
public static void glBindSamplers(int first, int[] samplers) {
|
||||
if (samplers == null) return;
|
||||
for (int i = 0; i < samplers.length; i++) glBindSampler(first + i, samplers[i]);
|
||||
}
|
||||
|
||||
public static int getSamplerBinding(int unit) { return ctx().samplerUnits.get(unit); }
|
||||
public static int getSamplerUnitCount() { return ctx().samplerUnits.size(); }
|
||||
public static void glSamplerParameteri(int sampler, int pname, int param) { RENDER_BACKEND.samplerParameteri(sampler, pname, param); }
|
||||
public static void glSamplerParameterf(int sampler, int pname, float param) { RENDER_BACKEND.samplerParameterf(sampler, pname, param); }
|
||||
|
||||
@@ -5234,20 +5380,8 @@ public class GLStateManager {
|
||||
return RENDER_BACKEND.getEffectiveVSyncMode();
|
||||
}
|
||||
|
||||
public static int getDisplayRefreshRateHz() {
|
||||
return RENDER_BACKEND.getDisplayRefreshRateHz();
|
||||
}
|
||||
|
||||
public static boolean gateAnchorsNextFrameStart() {
|
||||
return RENDER_BACKEND.gateAnchorsNextFrameStart();
|
||||
}
|
||||
|
||||
public static long lastFrameGateNanos() {
|
||||
return RENDER_BACKEND.lastFrameGateNanos();
|
||||
}
|
||||
|
||||
public static long lastFrameGateEndNanos() {
|
||||
return RENDER_BACKEND.lastFrameGateEndNanos();
|
||||
public static void setPresentSuppressed(boolean suppressed) {
|
||||
RENDER_BACKEND.setPresentSuppressed(suppressed);
|
||||
}
|
||||
|
||||
public static boolean hasSwapchainBackpressure() {
|
||||
@@ -5255,7 +5389,7 @@ public class GLStateManager {
|
||||
}
|
||||
|
||||
public static void pumpDisplayMessages() {
|
||||
Display.processMessages();
|
||||
RENDER_BACKEND.pumpDisplayMessages();
|
||||
}
|
||||
|
||||
public static int glGetError() {
|
||||
@@ -5886,10 +6020,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 +6040,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 +6142,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 +6238,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 +6272,17 @@ 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 long glMapBufferRangeAddress(int target, long offset, long length, int access) {
|
||||
onBufferMapRange(target, access);
|
||||
return RENDER_BACKEND.mapBufferRangeAddress(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 +6329,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 +6356,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 +6389,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 +6421,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 +6593,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 +6650,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 +7968,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;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import com.gtnewhorizons.angelica.glsm.dsa.DSAEXT;
|
||||
import com.gtnewhorizons.angelica.glsm.dsa.DSAUnsupported;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.ShaderManager;
|
||||
import com.gtnewhorizons.angelica.glsm.texture.TextureInfoCache;
|
||||
import com.mitchej123.lwjgl.LWJGLServiceProvider;
|
||||
import org.taumc.celeritas.lwjgl.LWJGLServiceProvider;
|
||||
import lombok.Getter;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.joml.Matrix4f;
|
||||
@@ -49,6 +49,7 @@ public class RenderSystem {
|
||||
private static boolean supportsTesselation;
|
||||
private static boolean supportsSamplerObjects;
|
||||
private static int maxImageUnits;
|
||||
@Getter private static int maxCombinedTextureImageUnits;
|
||||
private static int maxSSBOBindings;
|
||||
private static int maxGlslVersion;
|
||||
private static boolean supportsGpuShader4;
|
||||
@@ -64,9 +65,6 @@ public class RenderSystem {
|
||||
private static volatile boolean isLTW;
|
||||
private static volatile boolean ltwDetected;
|
||||
|
||||
// Sampler object state tracking (null if unsupported)
|
||||
private static int[] samplers;
|
||||
|
||||
private static final Pattern SEMVER_PATTERN = Pattern.compile("(?<major>\\d+)\\.(?<minor>\\d+)\\.*(?<bugfix>\\d*)(.*)");
|
||||
|
||||
private RenderSystem() {}
|
||||
@@ -158,7 +156,7 @@ public class RenderSystem {
|
||||
// Check for sampler objects support (GL 3.3+ or ARB extension)
|
||||
supportsSamplerObjects = GLStateManager.capabilities.OpenGL33 || GLStateManager.capabilities.GL_ARB_sampler_objects;
|
||||
if (supportsSamplerObjects) {
|
||||
samplers = new int[RENDER_BACKEND.getInteger(GL20.GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS)];
|
||||
maxCombinedTextureImageUnits = RENDER_BACKEND.getInteger(GL20.GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS);
|
||||
}
|
||||
|
||||
maxGlslVersion = Integer.parseInt(parseGlVersionString(RENDER_BACKEND.getString(GL20.GL_SHADING_LANGUAGE_VERSION)));
|
||||
@@ -486,17 +484,15 @@ public class RenderSystem {
|
||||
}
|
||||
|
||||
public static void disableBufferBlend(int buffer) {
|
||||
RENDER_BACKEND.disablei(GL11.GL_BLEND, buffer);
|
||||
GLStateManager.getBlendMode().setUnknownState();
|
||||
GLStateManager.glDisablei(GL11.GL_BLEND, buffer);
|
||||
}
|
||||
|
||||
public static void enableBufferBlend(int buffer) {
|
||||
RENDER_BACKEND.enablei(GL11.GL_BLEND, buffer);
|
||||
GLStateManager.getBlendMode().setUnknownState();
|
||||
GLStateManager.glEnablei(GL11.GL_BLEND, buffer);
|
||||
}
|
||||
|
||||
public static void blendFuncSeparatei(int buffer, int srcRGB, int dstRGB, int srcAlpha, int dstAlpha) {
|
||||
RENDER_BACKEND.blendFuncSeparatei(buffer, srcRGB, dstRGB, srcAlpha, dstAlpha);
|
||||
GLStateManager.glBlendFuncSeparatei(buffer, srcRGB, dstRGB, srcAlpha, dstAlpha);
|
||||
}
|
||||
|
||||
public static void bindTextureToUnit(int unit, int texture) {
|
||||
@@ -559,7 +555,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) {
|
||||
@@ -628,7 +624,7 @@ public class RenderSystem {
|
||||
|
||||
public static int genSampler() {
|
||||
if (!supportsSamplerObjects) return 0;
|
||||
return RENDER_BACKEND.genSamplers();
|
||||
return GLStateManager.glGenSamplers();
|
||||
}
|
||||
|
||||
public static void destroySampler(int sampler) {
|
||||
@@ -638,22 +634,20 @@ public class RenderSystem {
|
||||
|
||||
public static void samplerParameteri(int sampler, int pname, int param) {
|
||||
if (!supportsSamplerObjects || sampler == 0) return;
|
||||
RENDER_BACKEND.samplerParameteri(sampler, pname, param);
|
||||
GLStateManager.glSamplerParameteri(sampler, pname, param);
|
||||
}
|
||||
|
||||
public static void bindSamplerToUnit(int unit, int sampler) {
|
||||
if (!supportsSamplerObjects) return;
|
||||
if (samplers[unit] == sampler) return;
|
||||
RENDER_BACKEND.bindSampler(unit, sampler);
|
||||
samplers[unit] = sampler;
|
||||
GLStateManager.glBindSampler(unit, sampler);
|
||||
}
|
||||
|
||||
public static void unbindAllSamplers() {
|
||||
if (!supportsSamplerObjects) return;
|
||||
for (int i = 0; i < samplers.length; i++) {
|
||||
if (samplers[i] != 0) {
|
||||
RENDER_BACKEND.bindSampler(i, 0);
|
||||
samplers[i] = 0;
|
||||
final int count = GLStateManager.getSamplerUnitCount();
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (GLStateManager.getSamplerBinding(i) != 0) {
|
||||
GLStateManager.glBindSampler(i, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+10
-4
@@ -40,7 +40,7 @@ import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.nio.ShortBuffer;
|
||||
|
||||
import static com.mitchej123.lwjgl.LWJGLServiceProvider.LWJGL;
|
||||
import static org.taumc.celeritas.lwjgl.LWJGLServiceProvider.LWJGL;
|
||||
|
||||
/**
|
||||
* LWJGL2 GL implementation of {@link RenderBackend}.
|
||||
@@ -122,11 +122,11 @@ public final class Lwjgl2GLRenderBackend extends RenderBackend {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int queryDisplayRefreshRateHz() {
|
||||
protected long queryRefreshPeriodNanos() {
|
||||
final DisplayMode mode = Display.getDisplayMode();
|
||||
if (mode != null && mode.getFrequency() > 0) return mode.getFrequency();
|
||||
if (mode != null && mode.getFrequency() > 0) return periodFromIntegerHz(mode.getFrequency());
|
||||
final DisplayMode desktop = Display.getDesktopDisplayMode();
|
||||
return desktop == null ? 0 : desktop.getFrequency();
|
||||
return desktop == null ? 0L : periodFromIntegerHz(desktop.getFrequency());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -134,6 +134,12 @@ public final class Lwjgl2GLRenderBackend extends RenderBackend {
|
||||
return caps != null && caps.GL_EXT_texture_filter_anisotropic;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateDisplayFromWorkerThread(boolean processMessages) {
|
||||
if (processMessages) pumpDisplayMessages();
|
||||
Display.update(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() {GL11.glFlush();}
|
||||
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
package com.gtnewhorizons.angelica.glsm.backend;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.profiling.Tracy;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.lwjgl.input.Keyboard;
|
||||
import org.lwjgl.input.Mouse;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.function.BooleanSupplier;
|
||||
|
||||
public final class MainThreadPump {
|
||||
|
||||
private static final Logger LOGGER = LogManager.getLogger("MainThreadPump");
|
||||
|
||||
private static final Tracy.ZoneId Z_DISPLAY_MESSAGES = Tracy.zoneId("displayMessages", Tracy.COLOR_CLIENT);
|
||||
|
||||
private static final long PARK_MILLIS = 20L;
|
||||
private static final long STALL_WARN_NANOS = 5_000_000_000L;
|
||||
|
||||
public static void pollInput() {
|
||||
Keyboard.poll();
|
||||
Mouse.poll();
|
||||
}
|
||||
|
||||
private final Executor mainExecutor;
|
||||
private final BooleanSupplier isMainThread;
|
||||
private final Runnable pumpBody;
|
||||
private final Runnable pollBody;
|
||||
private final Runnable task = this::runPump;
|
||||
private final Object lock = new Object();
|
||||
|
||||
private boolean done;
|
||||
private Throwable failure;
|
||||
private ClassLoader callerLoader;
|
||||
|
||||
public MainThreadPump(Executor mainExecutor, BooleanSupplier isMainThread, Runnable pumpBody, Runnable pollBody) {
|
||||
this.mainExecutor = mainExecutor;
|
||||
this.isMainThread = isMainThread;
|
||||
this.pumpBody = pumpBody;
|
||||
this.pollBody = pollBody;
|
||||
}
|
||||
|
||||
public void pumpMessages() {
|
||||
Tracy.beginZone(Z_DISPLAY_MESSAGES);
|
||||
try {
|
||||
if (isMainThread.getAsBoolean()) {
|
||||
pumpBody.run();
|
||||
} else {
|
||||
synchronized (lock) {
|
||||
runPumpOnMainThread();
|
||||
}
|
||||
}
|
||||
pollBody.run();
|
||||
} finally {
|
||||
Tracy.endZone();
|
||||
}
|
||||
}
|
||||
|
||||
private void runPumpOnMainThread() {
|
||||
final Thread self = Thread.currentThread();
|
||||
callerLoader = self.getContextClassLoader();
|
||||
failure = null;
|
||||
done = false;
|
||||
mainExecutor.execute(task);
|
||||
boolean interrupted = false;
|
||||
boolean stallReported = false;
|
||||
final long stallAt = System.nanoTime() + STALL_WARN_NANOS;
|
||||
while (!done) {
|
||||
try {
|
||||
lock.wait(PARK_MILLIS);
|
||||
} catch (InterruptedException e) {
|
||||
interrupted = true;
|
||||
}
|
||||
if (!done && !stallReported && System.nanoTime() - stallAt >= 0L) {
|
||||
stallReported = true;
|
||||
LOGGER.warn("Display message pump has been waiting on the RFB main thread for over {} ms", STALL_WARN_NANOS / 1_000_000L);
|
||||
}
|
||||
}
|
||||
if (interrupted) self.interrupt();
|
||||
final Throwable t = failure;
|
||||
if (t != null) {
|
||||
throw asUnchecked(t);
|
||||
}
|
||||
}
|
||||
|
||||
private void runPump() {
|
||||
final Thread self = Thread.currentThread();
|
||||
final ClassLoader saved = self.getContextClassLoader();
|
||||
try {
|
||||
self.setContextClassLoader(callerLoader);
|
||||
pumpBody.run();
|
||||
} catch (Throwable t) {
|
||||
failure = t;
|
||||
} finally {
|
||||
try {
|
||||
self.setContextClassLoader(saved);
|
||||
} finally {
|
||||
synchronized (lock) {
|
||||
done = true;
|
||||
lock.notifyAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static RuntimeException asUnchecked(Throwable t) {
|
||||
if (t instanceof RuntimeException runtime) return runtime;
|
||||
if (t instanceof Error error) throw error;
|
||||
return new RuntimeException(t);
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,8 @@ package com.gtnewhorizons.angelica.glsm.backend;
|
||||
import com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.RenderSystem;
|
||||
import org.lwjgl.LWJGLException;
|
||||
import org.lwjgl.opengl.Display;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
@@ -12,6 +14,7 @@ import java.nio.IntBuffer;
|
||||
import java.nio.ShortBuffer;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.locks.LockSupport;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
@@ -72,7 +75,9 @@ public abstract class RenderBackend {
|
||||
|
||||
public final void setVSyncMode(VSyncMode preferred) {
|
||||
setVSyncPreference(preferred);
|
||||
publishVSyncMode(applyVSyncMode(preferred));
|
||||
final VSyncMode mode = applyVSyncMode(preferred);
|
||||
vsyncHonored = applyHonored();
|
||||
publishVSyncMode(mode);
|
||||
}
|
||||
|
||||
public final void setVSyncPreference(VSyncMode preferred) {
|
||||
@@ -90,50 +95,146 @@ public abstract class RenderBackend {
|
||||
|
||||
protected abstract VSyncMode applyVSyncMode(VSyncMode preferred);
|
||||
|
||||
protected boolean applyHonored() { return true; }
|
||||
|
||||
private boolean vsyncHonored = true;
|
||||
|
||||
public final boolean vsyncHonored() { return vsyncHonored; }
|
||||
|
||||
public boolean supportsVSyncMode(VSyncMode mode) { return mode != VSyncMode.MAILBOX; }
|
||||
|
||||
protected final void publishVSyncMode(VSyncMode mode) { effectiveVSyncMode = mode; }
|
||||
protected final void publishVSyncMode(VSyncMode mode) {
|
||||
effectiveVSyncMode = mode;
|
||||
bumpDisplayGeneration();
|
||||
}
|
||||
|
||||
public final VSyncMode getEffectiveVSyncMode() { return effectiveVSyncMode; }
|
||||
|
||||
private int displayGeneration;
|
||||
|
||||
public final int displayGeneration() { return displayGeneration; }
|
||||
|
||||
protected final void bumpDisplayGeneration() { displayGeneration++; }
|
||||
|
||||
private static final long REFRESH_POLL_NANOS = 1_000_000_000L;
|
||||
private static final long MIN_PLAUSIBLE_PERIOD_NANOS = 1_000_000L;
|
||||
private static final long MAX_PLAUSIBLE_PERIOD_NANOS = 50_000_000L;
|
||||
|
||||
private long refreshPollNanos;
|
||||
private int cachedRefreshHz;
|
||||
private long cachedRefreshPeriodNanos;
|
||||
|
||||
public final int getDisplayRefreshRateHz() {
|
||||
public final long refreshPeriodNanos() {
|
||||
final long now = System.nanoTime();
|
||||
if (refreshPollNanos == 0L || now - refreshPollNanos >= REFRESH_POLL_NANOS) {
|
||||
refreshPollNanos = now;
|
||||
cachedRefreshHz = queryDisplayRefreshRateHz();
|
||||
final long fresh = plausiblePeriodNanos(queryRefreshPeriodNanos());
|
||||
if (fresh != cachedRefreshPeriodNanos) bumpDisplayGeneration();
|
||||
cachedRefreshPeriodNanos = fresh;
|
||||
}
|
||||
return cachedRefreshHz;
|
||||
return cachedRefreshPeriodNanos;
|
||||
}
|
||||
|
||||
protected int queryDisplayRefreshRateHz() { return 0; }
|
||||
protected long queryRefreshPeriodNanos() { return 0L; }
|
||||
|
||||
public static int refreshHzFrom(int numerator, int denominator, float fallbackHz) {
|
||||
if (numerator > 0 && denominator > 0) return (numerator + denominator - 1) / denominator;
|
||||
return fallbackHz > 0.0f ? (int) Math.ceil(fallbackHz) : 0;
|
||||
static long plausiblePeriodNanos(long periodNanos) {
|
||||
return (periodNanos >= MIN_PLAUSIBLE_PERIOD_NANOS && periodNanos <= MAX_PLAUSIBLE_PERIOD_NANOS) ? periodNanos : 0L;
|
||||
}
|
||||
|
||||
public static long periodFromRational(int numerator, int denominator, float fallbackHz) {
|
||||
if (numerator > 0 && denominator > 0) {
|
||||
final long scaledDenominator = 1_000_000_000L * denominator;
|
||||
return (scaledDenominator + numerator / 2) / numerator;
|
||||
}
|
||||
return fallbackHz > 0.0f ? Math.round(1_000_000_000.0 / fallbackHz) : 0L;
|
||||
}
|
||||
|
||||
private static final int[] TRUNCATED_NTSC_HZ = {23, 29, 59, 71, 119, 143, 239};
|
||||
|
||||
private static boolean isTruncatedNtscRate(int hz) {
|
||||
for (int candidate : TRUNCATED_NTSC_HZ) {
|
||||
if (candidate == hz) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static long periodFromIntegerHz(int hz) {
|
||||
if (hz <= 0) return 0L;
|
||||
if (isTruncatedNtscRate(hz)) {
|
||||
final long denominator = (hz + 1) * 1000L;
|
||||
return Math.round(1_000_000_000.0 * 1001 / denominator);
|
||||
}
|
||||
return Math.round(1_000_000_000.0 / hz);
|
||||
}
|
||||
|
||||
public static int hzFromPeriod(long periodNanos) {
|
||||
return periodNanos <= 0L ? 0 : (int) Math.round(1_000_000_000.0 / periodNanos);
|
||||
}
|
||||
|
||||
public boolean hasSwapchainBackpressure() { return false; }
|
||||
|
||||
private long frameGateNanos;
|
||||
private long frameGateEndNanos;
|
||||
public static final class GateSample {
|
||||
public long durationNanos;
|
||||
public long endNanos;
|
||||
public long gpuWaitNanos;
|
||||
}
|
||||
|
||||
public boolean gateAnchorsNextFrameStart() { return false; }
|
||||
static final class GateRecorder {
|
||||
|
||||
private long gateDurationNanos;
|
||||
private long gateEndNanos;
|
||||
private long gpuWaitNanos;
|
||||
|
||||
synchronized void recordGate(long durationNanos, long endNanos) {
|
||||
if (gateEndNanos != 0L && gateDurationNanos >= durationNanos) return;
|
||||
gateDurationNanos = durationNanos;
|
||||
gateEndNanos = endNanos;
|
||||
}
|
||||
|
||||
synchronized void recordGpuWait(long nanos) {
|
||||
gpuWaitNanos += nanos;
|
||||
}
|
||||
|
||||
synchronized void readGateSample(GateSample out) {
|
||||
final long end = gateEndNanos;
|
||||
out.durationNanos = end == 0L ? 0L : gateDurationNanos;
|
||||
out.endNanos = end;
|
||||
out.gpuWaitNanos = gpuWaitNanos;
|
||||
gpuWaitNanos = 0L;
|
||||
gateEndNanos = 0L;
|
||||
}
|
||||
}
|
||||
|
||||
private final GateRecorder gateRecorder = new GateRecorder();
|
||||
|
||||
public boolean wantsDisplayUpdateGateTiming() { return true; }
|
||||
|
||||
public void recordFrameGate(long durationNanos, long endNanos) {
|
||||
frameGateNanos = durationNanos;
|
||||
frameGateEndNanos = endNanos;
|
||||
public final void recordGate(long durationNanos, long endNanos) {
|
||||
gateRecorder.recordGate(durationNanos, endNanos);
|
||||
}
|
||||
|
||||
public long lastFrameGateNanos() { return frameGateNanos; }
|
||||
public final void recordGpuWait(long nanos) {
|
||||
gateRecorder.recordGpuWait(nanos);
|
||||
}
|
||||
|
||||
public long lastFrameGateEndNanos() { return frameGateEndNanos; }
|
||||
public final void readGateSample(GateSample out) {
|
||||
gateRecorder.readGateSample(out);
|
||||
}
|
||||
|
||||
public void awaitPresent() {}
|
||||
|
||||
public void parkNanos(long nanos) {
|
||||
LockSupport.parkNanos(nanos);
|
||||
}
|
||||
|
||||
public void pumpDisplayMessages() {
|
||||
Display.processMessages();
|
||||
}
|
||||
|
||||
private boolean presentSuppressed;
|
||||
|
||||
public final void setPresentSuppressed(boolean suppressed) { presentSuppressed = suppressed; }
|
||||
|
||||
public final boolean isPresentSuppressed() { return presentSuppressed; }
|
||||
|
||||
public void onFrameBegin() {
|
||||
if (GLStateManager.takeStateSeedPending()) {
|
||||
@@ -144,16 +245,23 @@ public abstract class RenderBackend {
|
||||
public void onFrameEnd() {}
|
||||
|
||||
/** Fired by lwjgl3ify Display before a mutation that recreates the swapchain. */
|
||||
public void onPreSwapchainInvalidatingChange(Object change) {}
|
||||
public void onPreSwapchainInvalidatingChange(Object change) { bumpDisplayGeneration(); }
|
||||
|
||||
public boolean handleMakeCurrent(Object drawable) { return false; }
|
||||
public boolean handleReleaseContext(Object drawable) { return false; }
|
||||
public boolean handleSwapBuffers() { return false; }
|
||||
public void onRenderThreadReleased(Thread thread) {}
|
||||
|
||||
public void updateDisplayFromWorkerThread(boolean processMessages) throws LWJGLException {
|
||||
GLStateManager.swapBuffers();
|
||||
if (processMessages) {
|
||||
pumpDisplayMessages();
|
||||
}
|
||||
}
|
||||
|
||||
public String getTransferDebugInfo() { return null; }
|
||||
|
||||
public boolean bindVoxelizationRegion(int ssboBinding, int vertexBufferGlId, long openPass, float x, float y, float z) { return false; }
|
||||
public boolean bindVoxelizationRegion(int ssboBinding, long openPass, float x, float y, float z) { return false; }
|
||||
public long beginVoxelizationBatch(int ssboBinding) { return 0L; }
|
||||
public void voxelizeRange(long pass, int vertexOffset, int vertexCount) {}
|
||||
public void endVoxelizationBatch(long pass) {}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
public final class CubeInstancedAttribs {
|
||||
|
||||
public static final int LOC_LIGHTMAP_SCALE = InstancedAttribs.LOC_LIGHTMAP;
|
||||
public static final int LOC_CUBE_TEX = 15;
|
||||
|
||||
public static final int OFFSET_LIGHTMAP_SCALE = InstancedAttribs.HEAD_SIZE;
|
||||
public static final int OFFSET_CUBE_TEX = OFFSET_LIGHTMAP_SCALE + 16;
|
||||
public static final int STRIDE = OFFSET_CUBE_TEX + 16;
|
||||
|
||||
public static void pointInstanceAttribs(long base) {
|
||||
InstancedAttribs.pointHead(base, STRIDE);
|
||||
GLStateManager.glVertexAttribPointer(LOC_LIGHTMAP_SCALE, 4, GL11.GL_FLOAT, false, STRIDE, base + OFFSET_LIGHTMAP_SCALE);
|
||||
GLStateManager.glVertexAttribPointer(LOC_CUBE_TEX, 4, GL11.GL_FLOAT, false, STRIDE, base + OFFSET_CUBE_TEX);
|
||||
}
|
||||
|
||||
static void enableInstanceArrays() {
|
||||
InstancedAttribs.enableHeadArrays();
|
||||
InstancedAttribs.enableInstanced(LOC_CUBE_TEX);
|
||||
}
|
||||
|
||||
private CubeInstancedAttribs() {}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memPutFloat;
|
||||
|
||||
public record CubeParams(float minX, float minY, float minZ, float spanX, float spanY, float spanZ, float u0, float v0, float uScaleX, float vScaleY, float uScaleZ, float vScaleZ) {
|
||||
|
||||
private static final float MIN_SPAN = 1.0e-4f;
|
||||
|
||||
public CubeParams {
|
||||
if (!(spanX > MIN_SPAN && spanY > MIN_SPAN && spanZ > MIN_SPAN)) throw new IllegalArgumentException("degenerate cube span");
|
||||
}
|
||||
|
||||
public static CubeParams of(float texWidth, float texHeight, boolean mirror, int texU, int texV, float x, float y, float z, int sizeX, int sizeY, int sizeZ, float inflate) {
|
||||
if (texWidth <= 0.0f || texHeight <= 0.0f) return null;
|
||||
if (sizeX < 0 || sizeY < 0 || sizeZ < 0) return null;
|
||||
if (mirror && sizeX == 0) return null;
|
||||
final float minX = x - inflate;
|
||||
final float minY = y - inflate;
|
||||
final float minZ = z - inflate;
|
||||
final float spanX = ((x + sizeX) + inflate) - minX;
|
||||
final float spanY = ((y + sizeY) + inflate) - minY;
|
||||
final float spanZ = ((z + sizeZ) + inflate) - minZ;
|
||||
if (spanX <= MIN_SPAN || spanY <= MIN_SPAN || spanZ <= MIN_SPAN) return null;
|
||||
return new CubeParams(minX, minY, minZ, spanX, spanY, spanZ, texU / texWidth, texV / texHeight, (mirror ? -sizeX : sizeX) / texWidth, sizeY / texHeight, sizeZ / texWidth, sizeZ / texHeight);
|
||||
}
|
||||
|
||||
public void writeRows(long ptr, float[] mv, int off, float scale) {
|
||||
final float tx = minX * scale;
|
||||
final float ty = minY * scale;
|
||||
final float tz = minZ * scale;
|
||||
final float sx = spanX * scale;
|
||||
final float sy = spanY * scale;
|
||||
final float sz = spanZ * scale;
|
||||
for (int r = 0; r < 3; r++) {
|
||||
final float c0 = mv[off + r];
|
||||
final float c1 = mv[off + 4 + r];
|
||||
final float c2 = mv[off + 8 + r];
|
||||
final long row = ptr + InstancedAttribs.OFFSET_ROWS + r * 16L;
|
||||
memPutFloat(row, c0 * sx);
|
||||
memPutFloat(row + 4, c1 * sy);
|
||||
memPutFloat(row + 8, c2 * sz);
|
||||
memPutFloat(row + 12, mv[off + 12 + r] + c0 * tx + c1 * ty + c2 * tz);
|
||||
}
|
||||
}
|
||||
|
||||
public void writeTexture(long ptr) {
|
||||
memPutFloat(ptr + CubeInstancedAttribs.OFFSET_LIGHTMAP_SCALE + 8, uScaleZ);
|
||||
memPutFloat(ptr + CubeInstancedAttribs.OFFSET_LIGHTMAP_SCALE + 12, vScaleZ);
|
||||
memPutFloat(ptr + CubeInstancedAttribs.OFFSET_CUBE_TEX, u0);
|
||||
memPutFloat(ptr + CubeInstancedAttribs.OFFSET_CUBE_TEX + 4, v0);
|
||||
memPutFloat(ptr + CubeInstancedAttribs.OFFSET_CUBE_TEX + 8, uScaleX);
|
||||
memPutFloat(ptr + CubeInstancedAttribs.OFFSET_CUBE_TEX + 12, vScaleY);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,583 @@
|
||||
package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.cel.api.util.NormI8;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormatElement.Usage;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMHooks;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.ImmediateExtendedAttribHandler;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.ints.IntSet;
|
||||
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectSet;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
import org.lwjgl.opengl.GL31;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.HashMap;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAddress0;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAlloc;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memCalloc;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memFree;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memGetByte;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memGetInt;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memGetShort;
|
||||
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
|
||||
|
||||
/**
|
||||
* Supplies {@code mc_midTexCoord}/{@code at_tangent} for mod geometry drawn through the FFPclient-array path
|
||||
* ({@code glVertexPointer}/{@code glTexCoordPointer} + {@code glDrawArrays}) out of a VBO, which bypasses the
|
||||
* Tessellator and display-list ext-injection paths.
|
||||
*
|
||||
* <p>Tangents are reconstructed from the flat face normal (handler's {@code normalOffset < 0} path).
|
||||
*/
|
||||
public final class FfpExtendedAttribs {
|
||||
|
||||
private FfpExtendedAttribs() {}
|
||||
|
||||
private static final int POSITION_LOC = Usage.POSITION.getAttributeLocation();
|
||||
private static final int UV_LOC = Usage.PRIMARY_UV.getAttributeLocation();
|
||||
private static final int NORMAL_LOC = Usage.NORMAL.getAttributeLocation();
|
||||
private static final int SRC_TEX_OFFSET = 12;
|
||||
private static final int SRC_NORMAL_OFFSET = 20;
|
||||
private static final int SRC_STRIDE_FLAT = 20;
|
||||
private static final int SRC_STRIDE_SMOOTH = 24;
|
||||
private static final int MAX_CACHE = 1024;
|
||||
private static final int MAX_EXT_VERTS = 1 << 20;
|
||||
private static final int MAX_RESPECS = 8;
|
||||
private static final int RESPEC_DECAY_FRAMES = 2;
|
||||
private static final int MIN_STREAM_SPAN_FRAMES = 4;
|
||||
private static final int STREAM_EXPIRE_FRAMES = 60;
|
||||
|
||||
private static final HashMap<Key, ExtBuffer> cache = new HashMap<>();
|
||||
private static final Key lookup = new Key();
|
||||
private static int internalGlDepth = 0;
|
||||
|
||||
private static final HashMap<EboKey, Boolean> eboPatternCache = new HashMap<>();
|
||||
private static final EboKey eboLookup = new EboKey();
|
||||
|
||||
private static final Int2ObjectMap<ObjectSet<Key>> keysBySource = new Int2ObjectOpenHashMap<>();
|
||||
private static final Int2ObjectMap<ObjectSet<EboKey>> eboKeysBySource = new Int2ObjectOpenHashMap<>();
|
||||
private static final Int2IntMap respecCounts = new Int2IntOpenHashMap();
|
||||
private static final Int2IntMap respecFrames = new Int2IntOpenHashMap();
|
||||
private static final Int2IntMap respecRunStart = new Int2IntOpenHashMap();
|
||||
private static final IntSet streamingSources = new IntOpenHashSet();
|
||||
private static final IntSet persistentSources = new IntOpenHashSet();
|
||||
private static int frame;
|
||||
|
||||
public static void endFrame() { frame++; }
|
||||
|
||||
public static boolean isEmpty() {
|
||||
return cache.isEmpty() && eboPatternCache.isEmpty();
|
||||
}
|
||||
|
||||
private static int internalDrawDepth = 0;
|
||||
|
||||
public static void beginInternalDraw() { internalDrawDepth++; }
|
||||
|
||||
public static void endInternalDraw() { if (internalDrawDepth > 0) internalDrawDepth--; }
|
||||
|
||||
private static void beginInternalGl() { internalGlDepth++; }
|
||||
|
||||
private static void endInternalGl() { if (internalGlDepth > 0) internalGlDepth--; }
|
||||
|
||||
private static boolean isNotConventionalTexturedArray(VAOManager.Attrib a) {
|
||||
return a == null || !a.enabled || a.genericPointer || a.vboId == 0 || a.size < 2;
|
||||
}
|
||||
|
||||
private static boolean isStreaming(int vboId) {
|
||||
if (vboId == 0 || !streamingSources.contains(vboId)) return false;
|
||||
if (persistentSources.contains(vboId)) return true;
|
||||
if (frame - respecFrames.get(vboId) > STREAM_EXPIRE_FRAMES) {
|
||||
streamingSources.remove(vboId);
|
||||
respecFrames.remove(vboId);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean maybeBind(int mode, int first, int count) {
|
||||
final ImmediateExtendedAttribHandler h = GLSMHooks.immediateExtendedHandler;
|
||||
if (h == null) return false;
|
||||
if (internalDrawDepth > 0) return false;
|
||||
final int extPrim = ImmediateExtendedAttribHandler.extPrimVerts(mode, count);
|
||||
if (extPrim == 0 || first < 0) return false;
|
||||
if (!h.wantsExtended()) return false;
|
||||
|
||||
final VAOManager.Attrib pos = VAOManager.get(POSITION_LOC);
|
||||
final VAOManager.Attrib uv = VAOManager.get(UV_LOC);
|
||||
if (isNotConventionalTexturedArray(pos) || isNotConventionalTexturedArray(uv)) return false;
|
||||
if (isStreaming(pos.vboId) || isStreaming(uv.vboId)) return false;
|
||||
|
||||
VAOManager.Attrib normal = extPrim == 3 ? VAOManager.get(NORMAL_LOC) : null;
|
||||
if (normal != null && (!normal.enabled || normal.genericPointer || normal.vboId == 0 || normal.size < 3
|
||||
|| isStreaming(normal.vboId))) {
|
||||
normal = null;
|
||||
}
|
||||
|
||||
return bindExt(h, mode, first, count, extPrim, pos, uv, normal);
|
||||
}
|
||||
|
||||
public static boolean maybeBindIndexed(int mode, int indexCount, int indexType, long indicesOffset) {
|
||||
final ImmediateExtendedAttribHandler h = GLSMHooks.immediateExtendedHandler;
|
||||
if (h == null) return false;
|
||||
if (internalDrawDepth > 0) return false;
|
||||
if (mode != GL11.GL_TRIANGLES || indexCount <= 0 || indexCount % 6 != 0) return false;
|
||||
if (!h.wantsExtended()) return false;
|
||||
|
||||
final VAOManager.Attrib pos = VAOManager.get(POSITION_LOC);
|
||||
final VAOManager.Attrib uv = VAOManager.get(UV_LOC);
|
||||
if (isNotConventionalTexturedArray(pos) || isNotConventionalTexturedArray(uv)) return false;
|
||||
if (isStreaming(pos.vboId) || isStreaming(uv.vboId) || isStreaming(VAOManager.boundEBO)) return false;
|
||||
|
||||
final int indexSize = indexTypeBytes(indexType);
|
||||
if (indexSize == 0) return false;
|
||||
if (indicesOffset % (6L * indexSize) != 0) return false;
|
||||
final int first = (int) (indicesOffset / indexSize) / 6 * 4;
|
||||
final int count = indexCount / 6 * 4;
|
||||
|
||||
if (!isQuadPatternEbo(VAOManager.boundEBO, indicesOffset, indexCount, indexType, indexSize, first)) return false;
|
||||
|
||||
// Build/bind as GL_QUADS (the real primitive): face-normal tangents, 4-vertex tiles, no per-vertex normal.
|
||||
return bindExt(h, GL11.GL_QUADS, first, count, 4, pos, uv, null);
|
||||
}
|
||||
|
||||
private static boolean isQuadPatternEbo(int ebo, long offset, int indexCount, int indexType, int indexSize, int firstVertex) {
|
||||
if (ebo == 0) return false;
|
||||
eboLookup.set(ebo, offset, indexCount, indexType);
|
||||
final Boolean cached = eboPatternCache.get(eboLookup);
|
||||
if (cached != null) return cached;
|
||||
if (eboPatternCache.size() >= MAX_CACHE) {
|
||||
eboPatternCache.clear();
|
||||
eboKeysBySource.clear();
|
||||
}
|
||||
|
||||
final ByteBuffer buf = memCalloc(indexCount, indexSize);
|
||||
RENDER_BACKEND.getBufferSubData(GL15.GL_ELEMENT_ARRAY_BUFFER, offset, buf);
|
||||
final long addr = memAddress0(buf);
|
||||
boolean match = true;
|
||||
for (int q = 0, quadCount = indexCount / 6; q < quadCount; q++) {
|
||||
final int base = firstVertex + q * 4;
|
||||
final long p = addr + (long) q * 6 * indexSize;
|
||||
if (readIndex(p, 0, indexSize) != base
|
||||
|| readIndex(p, 1, indexSize) != base + 1
|
||||
|| readIndex(p, 2, indexSize) != base + 2
|
||||
|| readIndex(p, 3, indexSize) != base + 2
|
||||
|| readIndex(p, 4, indexSize) != base + 3
|
||||
|| readIndex(p, 5, indexSize) != base) {
|
||||
match = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
memFree(buf);
|
||||
final EboKey stored = eboLookup.copy();
|
||||
eboPatternCache.put(stored, match);
|
||||
ObjectSet<EboKey> ebos = eboKeysBySource.get(stored.ebo);
|
||||
if (ebos == null) eboKeysBySource.put(stored.ebo, ebos = new ObjectOpenHashSet<>());
|
||||
ebos.add(stored);
|
||||
return match;
|
||||
}
|
||||
|
||||
private static long readIndex(long base, int i, int indexSize) {
|
||||
return switch (indexSize) {
|
||||
case 1 -> memGetByte(base + i) & 0xFFL;
|
||||
case 2 -> memGetShort(base + (long) i * 2) & 0xFFFFL;
|
||||
default -> memGetInt(base + (long) i * 4) & 0xFFFFFFFFL;
|
||||
};
|
||||
}
|
||||
|
||||
private static int indexTypeBytes(int indexType) {
|
||||
return switch (indexType) {
|
||||
case GL11.GL_UNSIGNED_BYTE -> 1;
|
||||
case GL11.GL_UNSIGNED_SHORT -> 2;
|
||||
case GL11.GL_UNSIGNED_INT -> 4;
|
||||
default -> 0;
|
||||
};
|
||||
}
|
||||
|
||||
private static boolean bindExt(ImmediateExtendedAttribHandler h, int mode, int first, int count, int extPrim,
|
||||
VAOManager.Attrib pos, VAOManager.Attrib uv, VAOManager.Attrib normal) {
|
||||
final int extVbo = getOrBuild(h, pos, uv, normal, first, count, extPrim, mode);
|
||||
if (extVbo == 0) return false;
|
||||
|
||||
final int savedVBO = GLStateManager.getBoundVBO();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, extVbo);
|
||||
ImmediateExtendedAttribHandler.setupExtAttribPointers(0L, ImmediateExtendedAttribHandler.EXT_STRIDE);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, savedVBO);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void unbind() {
|
||||
GLStateManager.glDisableVertexAttribArray(ImmediateExtendedAttribHandler.LOC_MID_TEX);
|
||||
GLStateManager.glDisableVertexAttribArray(ImmediateExtendedAttribHandler.LOC_TANGENT);
|
||||
setNeutralCurrentValues();
|
||||
}
|
||||
|
||||
public static void setNeutralCurrentValues() {
|
||||
GLStateManager.glVertexAttrib2f(ImmediateExtendedAttribHandler.LOC_MID_TEX, 0.5f, 0.5f);
|
||||
GLStateManager.glVertexAttrib4f(ImmediateExtendedAttribHandler.LOC_TANGENT, 1.0f, 0.0f, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
public static void onDeleteBuffer(int vboId) {
|
||||
if (internalGlDepth > 0 || vboId == 0) return;
|
||||
dropDerivedFrom(vboId);
|
||||
respecCounts.remove(vboId);
|
||||
respecFrames.remove(vboId);
|
||||
respecRunStart.remove(vboId);
|
||||
streamingSources.remove(vboId);
|
||||
persistentSources.remove(vboId);
|
||||
}
|
||||
|
||||
public static void onBufferRespecified(int vboId) {
|
||||
if (internalGlDepth > 0 || vboId == 0) return;
|
||||
if (streamingSources.contains(vboId)) {
|
||||
respecFrames.put(vboId, frame);
|
||||
return;
|
||||
}
|
||||
if (!dropDerivedFrom(vboId)) return;
|
||||
final boolean sameRun = respecCounts.containsKey(vboId) && frame - respecFrames.get(vboId) <= RESPEC_DECAY_FRAMES;
|
||||
if (!sameRun) {
|
||||
startRespecRun(vboId);
|
||||
return;
|
||||
}
|
||||
final int respecs = respecCounts.get(vboId) + 1;
|
||||
final int span = frame - respecRunStart.get(vboId);
|
||||
if (respecs >= MAX_RESPECS && span >= MIN_STREAM_SPAN_FRAMES && span <= respecs) {
|
||||
markStreaming(vboId);
|
||||
return;
|
||||
}
|
||||
if (span > respecs) {
|
||||
startRespecRun(vboId);
|
||||
return;
|
||||
}
|
||||
respecCounts.put(vboId, respecs);
|
||||
respecFrames.put(vboId, frame);
|
||||
}
|
||||
|
||||
private static void startRespecRun(int vboId) {
|
||||
respecCounts.put(vboId, 1);
|
||||
respecRunStart.put(vboId, frame);
|
||||
respecFrames.put(vboId, frame);
|
||||
}
|
||||
|
||||
public static void markStreaming(int vboId) {
|
||||
if (internalGlDepth > 0 || vboId == 0) return;
|
||||
respecCounts.remove(vboId);
|
||||
respecRunStart.remove(vboId);
|
||||
respecFrames.put(vboId, frame);
|
||||
if (streamingSources.add(vboId)) dropDerivedFrom(vboId);
|
||||
}
|
||||
|
||||
public static void markPersistent(int vboId) {
|
||||
if (internalGlDepth > 0 || vboId == 0) return;
|
||||
markStreaming(vboId);
|
||||
persistentSources.add(vboId);
|
||||
}
|
||||
|
||||
public static void reset() {
|
||||
invalidateAll();
|
||||
respecCounts.clear();
|
||||
respecFrames.clear();
|
||||
respecRunStart.clear();
|
||||
streamingSources.clear();
|
||||
persistentSources.clear();
|
||||
}
|
||||
|
||||
private static boolean dropDerivedFrom(int srcId) {
|
||||
final ObjectSet<EboKey> ebos = eboKeysBySource.remove(srcId);
|
||||
if (ebos != null) {
|
||||
for (EboKey k : ebos) eboPatternCache.remove(k);
|
||||
}
|
||||
final ObjectSet<Key> keys = keysBySource.get(srcId);
|
||||
if (keys == null || keys.isEmpty()) return ebos != null;
|
||||
IntArrayList toFree = null;
|
||||
for (Key k : keys.toArray(new Key[0])) {
|
||||
final ExtBuffer buf = cache.remove(k);
|
||||
if (buf != null && buf.vbo != 0) {
|
||||
if (toFree == null) toFree = new IntArrayList();
|
||||
toFree.add(buf.vbo);
|
||||
}
|
||||
unindexKey(k);
|
||||
}
|
||||
freeExtVbos(toFree);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void indexKey(Key k) {
|
||||
addToBucket(k.posVBO, k);
|
||||
if (k.uvVBO != k.posVBO) addToBucket(k.uvVBO, k);
|
||||
if (k.normalVBO != k.posVBO && k.normalVBO != k.uvVBO) addToBucket(k.normalVBO, k);
|
||||
}
|
||||
|
||||
private static void unindexKey(Key k) {
|
||||
removeFromBucket(k.posVBO, k);
|
||||
if (k.uvVBO != k.posVBO) removeFromBucket(k.uvVBO, k);
|
||||
if (k.normalVBO != k.posVBO && k.normalVBO != k.uvVBO) removeFromBucket(k.normalVBO, k);
|
||||
}
|
||||
|
||||
private static void addToBucket(int src, Key k) {
|
||||
if (src == 0) return;
|
||||
ObjectSet<Key> set = keysBySource.get(src);
|
||||
if (set == null) keysBySource.put(src, set = new ObjectOpenHashSet<>());
|
||||
set.add(k);
|
||||
}
|
||||
|
||||
private static void removeFromBucket(int src, Key k) {
|
||||
if (src == 0) return;
|
||||
final ObjectSet<Key> set = keysBySource.get(src);
|
||||
if (set != null && set.remove(k) && set.isEmpty()) keysBySource.remove(src);
|
||||
}
|
||||
|
||||
private static int getOrBuild(ImmediateExtendedAttribHandler h, VAOManager.Attrib pos, VAOManager.Attrib uv,
|
||||
VAOManager.Attrib normal, int first, int count, int extPrim, int mode) {
|
||||
if (first + count > MAX_EXT_VERTS) return 0;
|
||||
lookup.set(pos, uv, normal, mode, first % extPrim);
|
||||
ExtBuffer buf = cache.get(lookup);
|
||||
if (buf == null) {
|
||||
if (cache.size() >= MAX_CACHE) invalidateExtBuffers();
|
||||
buf = new ExtBuffer();
|
||||
final Key stored = lookup.copy();
|
||||
cache.put(stored, buf);
|
||||
indexKey(stored);
|
||||
}
|
||||
|
||||
final long range = (long) first << 32 | (count & 0xFFFFFFFFL);
|
||||
if (buf.vbo != 0 && buf.builtRanges.contains(range)) return buf.vbo;
|
||||
if (!ensureCapacity(buf, first + count)) return 0;
|
||||
buildRange(h, buf.vbo, pos, uv, normal, first, count, extPrim);
|
||||
buf.builtRanges.add(range);
|
||||
return buf.vbo;
|
||||
}
|
||||
|
||||
private static boolean ensureCapacity(ExtBuffer buf, int neededVerts) {
|
||||
if (buf.vbo != 0 && buf.capacityVerts >= neededVerts) return true;
|
||||
final int extStride = ImmediateExtendedAttribHandler.EXT_STRIDE;
|
||||
final int newCap = Math.min(MAX_EXT_VERTS, Math.max(neededVerts, buf.capacityVerts * 2));
|
||||
beginInternalGl();
|
||||
try {
|
||||
final int vbo = GLStateManager.glGenBuffers();
|
||||
if (vbo == 0) return false;
|
||||
final int savedVBO = GLStateManager.getBoundVBO();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
|
||||
GLStateManager.glBufferData(GL15.GL_ARRAY_BUFFER, (long) newCap * extStride, GL15.GL_STATIC_DRAW);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, savedVBO);
|
||||
if (buf.vbo != 0) {
|
||||
final int savedRead = GLStateManager.getBoundCopyReadBuffer();
|
||||
final int savedWrite = GLStateManager.getBoundCopyWriteBuffer();
|
||||
GLStateManager.glBindBuffer(GL31.GL_COPY_READ_BUFFER, buf.vbo);
|
||||
GLStateManager.glBindBuffer(GL31.GL_COPY_WRITE_BUFFER, vbo);
|
||||
GLStateManager.glCopyBufferSubData(GL31.GL_COPY_READ_BUFFER, GL31.GL_COPY_WRITE_BUFFER, 0L, 0L,
|
||||
(long) buf.capacityVerts * extStride);
|
||||
GLStateManager.glBindBuffer(GL31.GL_COPY_READ_BUFFER, savedRead);
|
||||
GLStateManager.glBindBuffer(GL31.GL_COPY_WRITE_BUFFER, savedWrite);
|
||||
GLStateManager.glDeleteBuffers(buf.vbo);
|
||||
}
|
||||
buf.vbo = vbo;
|
||||
buf.capacityVerts = newCap;
|
||||
return true;
|
||||
} finally {
|
||||
endInternalGl();
|
||||
}
|
||||
}
|
||||
|
||||
private static void buildRange(ImmediateExtendedAttribHandler h, int extVbo, VAOManager.Attrib pos,
|
||||
VAOManager.Attrib uv, VAOManager.Attrib normal, int first, int count, int extPrim) {
|
||||
final int posStride = pos.effectiveStride();
|
||||
final int uvStride = uv.effectiveStride();
|
||||
final boolean smooth = normal != null;
|
||||
final int normalStride = smooth ? normal.effectiveStride() : 0;
|
||||
final int srcStride = smooth ? SRC_STRIDE_SMOOTH : SRC_STRIDE_FLAT;
|
||||
final int srcNormalOffset = smooth ? SRC_NORMAL_OFFSET : -1;
|
||||
|
||||
final ByteBuffer posBuf = readRange(pos, first, count, posStride);
|
||||
final ByteBuffer uvBuf = readRange(uv, first, count, uvStride);
|
||||
final ByteBuffer normalBuf = smooth ? readRange(normal, first, count, normalStride) : null;
|
||||
|
||||
final ByteBuffer src = memAlloc(count * srcStride).order(ByteOrder.nativeOrder());
|
||||
for (int i = 0; i < count; i++) {
|
||||
final int pb = i * posStride, ub = i * uvStride, d = i * srcStride;
|
||||
src.putFloat(d, pos.readComponent(posBuf, pb, 0));
|
||||
src.putFloat(d + 4, pos.readComponent(posBuf, pb, 1));
|
||||
src.putFloat(d + 8, pos.size >= 3 ? pos.readComponent(posBuf, pb, 2) : 0f);
|
||||
src.putFloat(d + SRC_TEX_OFFSET, uv.readComponent(uvBuf, ub, 0));
|
||||
src.putFloat(d + SRC_TEX_OFFSET + 4, uv.readComponent(uvBuf, ub, 1));
|
||||
if (smooth) {
|
||||
final int nb = i * normalStride;
|
||||
src.putInt(d + SRC_NORMAL_OFFSET, NormI8.pack(
|
||||
normal.readComponent(normalBuf, nb, 0),
|
||||
normal.readComponent(normalBuf, nb, 1),
|
||||
normal.readComponent(normalBuf, nb, 2)));
|
||||
}
|
||||
}
|
||||
|
||||
final int extStride = ImmediateExtendedAttribHandler.EXT_STRIDE;
|
||||
final ByteBuffer ext = memCalloc(count, extStride);
|
||||
h.buildPacked(memAddress0(src), srcStride, 0, SRC_TEX_OFFSET, srcNormalOffset, count, extPrim, memAddress0(ext), extStride);
|
||||
|
||||
beginInternalGl();
|
||||
try {
|
||||
final int savedVBO = GLStateManager.getBoundVBO();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, extVbo);
|
||||
ext.position(0).limit(count * extStride);
|
||||
GLStateManager.glBufferSubData(GL15.GL_ARRAY_BUFFER, (long) first * extStride, ext);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, savedVBO);
|
||||
} finally {
|
||||
endInternalGl();
|
||||
}
|
||||
|
||||
memFree(posBuf);
|
||||
memFree(uvBuf);
|
||||
if (normalBuf != null) memFree(normalBuf);
|
||||
memFree(src);
|
||||
memFree(ext);
|
||||
}
|
||||
|
||||
private static ByteBuffer readRange(VAOManager.Attrib a, int first, int count, int stride) {
|
||||
final int elementBytes = a.size * a.typeSizeBytes();
|
||||
final int bytes = (count - 1) * stride + elementBytes;
|
||||
final ByteBuffer dst = memAlloc(bytes).order(ByteOrder.nativeOrder());
|
||||
dst.position(0).limit(bytes);
|
||||
final int savedVBO = GLStateManager.getBoundVBO();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, a.vboId);
|
||||
RENDER_BACKEND.getBufferSubData(GL15.GL_ARRAY_BUFFER, a.offset + (long) first * stride, dst);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, savedVBO);
|
||||
dst.position(0).limit(bytes);
|
||||
return dst;
|
||||
}
|
||||
|
||||
private static void invalidateAll() {
|
||||
eboPatternCache.clear();
|
||||
eboKeysBySource.clear();
|
||||
invalidateExtBuffers();
|
||||
}
|
||||
|
||||
private static void invalidateExtBuffers() {
|
||||
if (cache.isEmpty()) return;
|
||||
final IntArrayList toFree = new IntArrayList(cache.size());
|
||||
for (ExtBuffer buf : cache.values()) {
|
||||
if (buf.vbo != 0) toFree.add(buf.vbo);
|
||||
}
|
||||
cache.clear();
|
||||
keysBySource.clear();
|
||||
freeExtVbos(toFree);
|
||||
}
|
||||
|
||||
private static void freeExtVbos(IntArrayList vbos) {
|
||||
if (vbos == null || vbos.isEmpty()) return;
|
||||
beginInternalGl();
|
||||
try {
|
||||
for (int i = 0; i < vbos.size(); i++) {
|
||||
GLStateManager.glDeleteBuffers(vbos.getInt(i));
|
||||
}
|
||||
} finally {
|
||||
endInternalGl();
|
||||
}
|
||||
}
|
||||
|
||||
private static final class ExtBuffer {
|
||||
int vbo;
|
||||
int capacityVerts;
|
||||
final LongOpenHashSet builtRanges = new LongOpenHashSet();
|
||||
}
|
||||
|
||||
private static final class EboKey {
|
||||
int ebo, count, type;
|
||||
long offset;
|
||||
|
||||
void set(int ebo, long offset, int count, int type) {
|
||||
this.ebo = ebo;
|
||||
this.offset = offset;
|
||||
this.count = count;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
EboKey copy() {
|
||||
final EboKey k = new EboKey();
|
||||
k.set(ebo, offset, count, type);
|
||||
return k;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof EboKey k)) return false;
|
||||
return ebo == k.ebo && count == k.count && type == k.type && offset == k.offset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int h = ebo;
|
||||
h = h * 31 + count;
|
||||
h = h * 31 + type;
|
||||
h = h * 31 + Long.hashCode(offset);
|
||||
return h;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class Key {
|
||||
int posVBO, uvVBO, normalVBO, posStride, uvStride, normalStride, posFmt, uvFmt, normalFmt, mode, phase;
|
||||
long posOffset, uvOffset, normalOffset;
|
||||
|
||||
void set(VAOManager.Attrib pos, VAOManager.Attrib uv, VAOManager.Attrib normal, int mode, int phase) {
|
||||
posVBO = pos.vboId; posOffset = pos.offset; posStride = pos.effectiveStride(); posFmt = fmt(pos);
|
||||
uvVBO = uv.vboId; uvOffset = uv.offset; uvStride = uv.effectiveStride(); uvFmt = fmt(uv);
|
||||
if (normal != null) {
|
||||
normalVBO = normal.vboId; normalOffset = normal.offset;
|
||||
normalStride = normal.effectiveStride(); normalFmt = fmt(normal);
|
||||
} else {
|
||||
normalVBO = 0; normalOffset = 0; normalStride = 0; normalFmt = 0;
|
||||
}
|
||||
this.mode = mode;
|
||||
this.phase = phase;
|
||||
}
|
||||
|
||||
private static int fmt(VAOManager.Attrib a) {
|
||||
return (a.size & 0x7F) | (a.normalized ? 0x80 : 0) | (a.type << 8);
|
||||
}
|
||||
|
||||
Key copy() {
|
||||
final Key k = new Key();
|
||||
k.posVBO = posVBO; k.posOffset = posOffset; k.posStride = posStride; k.posFmt = posFmt;
|
||||
k.uvVBO = uvVBO; k.uvOffset = uvOffset; k.uvStride = uvStride; k.uvFmt = uvFmt;
|
||||
k.normalVBO = normalVBO; k.normalOffset = normalOffset; k.normalStride = normalStride; k.normalFmt = normalFmt;
|
||||
k.mode = mode; k.phase = phase;
|
||||
return k;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof Key k)) return false;
|
||||
return posVBO == k.posVBO && uvVBO == k.uvVBO && normalVBO == k.normalVBO
|
||||
&& posStride == k.posStride && uvStride == k.uvStride && normalStride == k.normalStride
|
||||
&& posFmt == k.posFmt && uvFmt == k.uvFmt && normalFmt == k.normalFmt
|
||||
&& mode == k.mode && phase == k.phase
|
||||
&& posOffset == k.posOffset && uvOffset == k.uvOffset && normalOffset == k.normalOffset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int h = posVBO;
|
||||
h = h * 31 + uvVBO;
|
||||
h = h * 31 + normalVBO;
|
||||
h = h * 31 + posStride;
|
||||
h = h * 31 + uvStride;
|
||||
h = h * 31 + normalStride;
|
||||
h = h * 31 + posFmt;
|
||||
h = h * 31 + uvFmt;
|
||||
h = h * 31 + normalFmt;
|
||||
h = h * 31 + mode;
|
||||
h = h * 31 + phase;
|
||||
h = h * 31 + Long.hashCode(posOffset);
|
||||
h = h * 31 + Long.hashCode(uvOffset);
|
||||
h = h * 31 + Long.hashCode(normalOffset);
|
||||
return h;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,7 @@ import org.lwjgl.opengl.GL13;
|
||||
* Packed-long fragment shader permutation key for FFP emulation.
|
||||
*
|
||||
* Layout:
|
||||
* long[0]: global bits (13) + unit 0 (47 bits at offset 13)
|
||||
* long[0]: global bits (14) + unit 0 (47 bits at offset 14)
|
||||
* long[1..3]: unit 1..3 (47 bits each, low-aligned)
|
||||
* Only max(1, nrEnabledUnits) longs are significant.
|
||||
*
|
||||
@@ -63,7 +63,7 @@ public final class FragmentKey {
|
||||
public static final int FOG_EXP = 2;
|
||||
public static final int FOG_EXP2 = 3;
|
||||
|
||||
private static final int GLOBAL_BITS = 13;
|
||||
private static final int GLOBAL_BITS = 14;
|
||||
private static final int BIT_FOG_MODE = 0; // 2 bits
|
||||
private static final int BIT_ALPHA_TEST = 2; // 1 bit
|
||||
private static final int BIT_ALPHA_FUNC = 3; // 3 bits
|
||||
@@ -72,6 +72,7 @@ public final class FragmentKey {
|
||||
private static final int BIT_OVERLAY_ENABLED = 10; // 1 bit
|
||||
private static final int BIT_LINE_STIPPLE = 11; // 1 bit
|
||||
private static final int BIT_COLOR_SUM = 12; // 1 bit
|
||||
private static final int BIT_OVERLAY_INSTANCED = 13;
|
||||
|
||||
private static final int U_ENABLED = 0;
|
||||
private static final int U_MODE = 1; // 3 bits
|
||||
@@ -123,7 +124,9 @@ public final class FragmentKey {
|
||||
}
|
||||
|
||||
// Damage overlay
|
||||
if (GLStateManager.getOverlayA() != 0.0f) {
|
||||
if (GLStateManager.ffpInstancing.hasInstanceHead()) {
|
||||
global |= (1L << BIT_OVERLAY_INSTANCED);
|
||||
} else if (GLStateManager.getOverlayA() != 0.0f) {
|
||||
global |= (1L << BIT_OVERLAY_ENABLED);
|
||||
}
|
||||
|
||||
@@ -245,6 +248,7 @@ public final class FragmentKey {
|
||||
public boolean lineStipple() { return ((packed[0] >> BIT_LINE_STIPPLE) & 1) != 0; }
|
||||
public int nrEnabledUnits() { return (int) ((packed[0] >> BIT_NR_ENABLED_UNITS) & 0x7); }
|
||||
public boolean overlayEnabled() { return ((packed[0] >> BIT_OVERLAY_ENABLED) & 1) != 0; }
|
||||
public boolean overlayInstanced() { return ((packed[0] >> BIT_OVERLAY_INSTANCED) & 1) != 0; }
|
||||
|
||||
private long unitBits(int i) {
|
||||
return (i == 0) ? (packed[0] >>> GLOBAL_BITS) : packed[i];
|
||||
@@ -378,10 +382,7 @@ public final class FragmentKey {
|
||||
default -> "?";
|
||||
};
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append(String.format("FFPFragmentKey[fog=%s alpha=%b(%s) specSep=%b colorSum=%b overlay=%b units=%d",
|
||||
fogName, alphaTestEnabled(),
|
||||
alphaTestEnabled() ? String.format("0x%04X", decodeAlphaFunc(alphaTestFunc())) : "-",
|
||||
separateSpecular(), colorSum(), overlayEnabled(), nrEnabledUnits()));
|
||||
sb.append(String.format("FFPFragmentKey[fog=%s alpha=%b(%s) specSep=%b colorSum=%b overlay=%b units=%d", fogName, alphaTestEnabled(), alphaTestEnabled() ? String.format("0x%04X", decodeAlphaFunc(alphaTestFunc())) : "-", separateSpecular(), colorSum(), overlayEnabled() || overlayInstanced(), nrEnabledUnits()));
|
||||
for (int i = 0; i < nrEnabledUnits(); i++) {
|
||||
if (!unitEnabled(i)) {
|
||||
sb.append(String.format(" u%d=OFF", i));
|
||||
|
||||
@@ -35,6 +35,9 @@ public final class FragmentShaderGenerator {
|
||||
private static void emitInputs(StringBuilder sb, FragmentKey key) {
|
||||
sb.append("// Inputs from vertex shader\n");
|
||||
sb.append("in vec4 v_Color;\n");
|
||||
if (key.overlayInstanced()) {
|
||||
sb.append("in vec4 v_Overlay;\n");
|
||||
}
|
||||
if (key.separateSpecular()) {
|
||||
sb.append("in vec3 v_SpecularColor;\n");
|
||||
}
|
||||
@@ -116,8 +119,11 @@ public final class FragmentShaderGenerator {
|
||||
* Emit the modern-style damage overlay mix.
|
||||
*/
|
||||
private static void emitOverlay(StringBuilder sb, FragmentKey key) {
|
||||
if (!key.overlayEnabled()) return;
|
||||
sb.append(" color.rgb = mix(color.rgb, u_OverlayColor.rgb, u_OverlayColor.a);\n");
|
||||
if (key.overlayInstanced()) {
|
||||
sb.append(" color.rgb = mix(color.rgb, v_Overlay.rgb, v_Overlay.a);\n");
|
||||
} else if (key.overlayEnabled()) {
|
||||
sb.append(" color.rgb = mix(color.rgb, u_OverlayColor.rgb, u_OverlayColor.a);\n");
|
||||
}
|
||||
}
|
||||
|
||||
private static void emitSimpleUnit(StringBuilder sb, FragmentKey key, int unit, String texVar, String envColorVar, String prevVar, String assign) {
|
||||
|
||||
@@ -1,18 +1,84 @@
|
||||
package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memPutFloat;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memPutInt;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memPutLong;
|
||||
|
||||
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_ROW0 = 5;
|
||||
public static final int LOC_ROW1 = 6;
|
||||
public static final int LOC_ROW2 = 7;
|
||||
public static final int LOC_COLOR = 8;
|
||||
public static final int LOC_OVERLAY = 9;
|
||||
public static final int LOC_LIGHTMAP = 10;
|
||||
public static final int LOC_ENTITY = 11;
|
||||
|
||||
public static final int OFFSET_MATRIX = 0;
|
||||
public static final int OFFSET_COLOR = 64;
|
||||
public static final int OFFSET_LIGHTMAP = 68;
|
||||
public static final int STRIDE = 80;
|
||||
public static final int OFFSET_ROWS = 0;
|
||||
public static final int OFFSET_COLOR = 48;
|
||||
public static final int OFFSET_OVERLAY = 52;
|
||||
public static final int OFFSET_ENTITY = 56;
|
||||
public static final int HEAD_SIZE = 64;
|
||||
public static final int OFFSET_LIGHTMAP = HEAD_SIZE;
|
||||
public static final int STRIDE = 72;
|
||||
|
||||
public static void writeHead(long ptr, float[] colMajor, int off, int colorABGR, int overlayABGR, long entityInfo) {
|
||||
writeRows(ptr, colMajor, off);
|
||||
writeTail(ptr, colorABGR, overlayABGR, entityInfo);
|
||||
}
|
||||
|
||||
private static void writeRows(long ptr, float[] colMajor, int off) {
|
||||
for (int r = 0; r < 3; r++) {
|
||||
final long row = ptr + OFFSET_ROWS + r * 16L;
|
||||
memPutFloat(row, colMajor[off + r]);
|
||||
memPutFloat(row + 4, colMajor[off + 4 + r]);
|
||||
memPutFloat(row + 8, colMajor[off + 8 + r]);
|
||||
memPutFloat(row + 12, colMajor[off + 12 + r]);
|
||||
}
|
||||
}
|
||||
|
||||
public static void writeTail(long ptr, int colorABGR, int overlayABGR, long entityInfo) {
|
||||
memPutInt(ptr + OFFSET_COLOR, colorABGR);
|
||||
memPutInt(ptr + OFFSET_OVERLAY, overlayABGR);
|
||||
memPutLong(ptr + OFFSET_ENTITY, entityInfo);
|
||||
}
|
||||
|
||||
public static void writeLightmap(long ptr, int packedLight) {
|
||||
memPutFloat(ptr, packedLight & 0xFFFF);
|
||||
memPutFloat(ptr + 4, (packedLight >>> 16) & 0xFFFF);
|
||||
}
|
||||
|
||||
public static long packEntityInfo(int entity, int blockEntity, int item) {
|
||||
return (entity & 0xFFFFL) | ((blockEntity & 0xFFFFL) << 16) | ((item & 0xFFFFL) << 32);
|
||||
}
|
||||
|
||||
public static void pointHead(long base, int stride) {
|
||||
for (int r = 0; r < 3; r++) {
|
||||
GLStateManager.glVertexAttribPointer(LOC_ROW0 + r, 4, GL11.GL_FLOAT, false, stride, base + OFFSET_ROWS + r * 16L);
|
||||
}
|
||||
GLStateManager.glVertexAttribPointer(LOC_COLOR, 4, GL11.GL_UNSIGNED_BYTE, true, stride, base + OFFSET_COLOR);
|
||||
GLStateManager.glVertexAttribPointer(LOC_OVERLAY, 4, GL11.GL_UNSIGNED_BYTE, true, stride, base + OFFSET_OVERLAY);
|
||||
GLStateManager.glVertexAttribIPointer(LOC_ENTITY, 4, GL11.GL_SHORT, stride, base + OFFSET_ENTITY);
|
||||
}
|
||||
|
||||
public static void pointTemplate(long base) {
|
||||
pointHead(base, STRIDE);
|
||||
GLStateManager.glVertexAttribPointer(LOC_LIGHTMAP, 2, GL11.GL_FLOAT, false, STRIDE, base + OFFSET_LIGHTMAP);
|
||||
}
|
||||
|
||||
public static void enableHeadArrays() {
|
||||
for (int loc = LOC_ROW0; loc <= LOC_ENTITY; loc++) {
|
||||
enableInstanced(loc);
|
||||
}
|
||||
}
|
||||
|
||||
static void enableInstanced(int loc) {
|
||||
GLStateManager.glEnableVertexAttribArray(loc);
|
||||
GLStateManager.glVertexAttribDivisor(loc, 1);
|
||||
}
|
||||
|
||||
private InstancedAttribs() {}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormatElement;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public final class InstancedGlslHelpers {
|
||||
|
||||
private InstancedGlslHelpers() {}
|
||||
|
||||
public static String mat4FromRows(String row0, String row1, String row2) {
|
||||
return "mat4("
|
||||
+ "vec4(" + row0 + ".x, " + row1 + ".x, " + row2 + ".x, 0.0), "
|
||||
+ "vec4(" + row0 + ".y, " + row1 + ".y, " + row2 + ".y, 0.0), "
|
||||
+ "vec4(" + row0 + ".z, " + row1 + ".z, " + row2 + ".z, 0.0), "
|
||||
+ "vec4(" + row0 + ".w, " + row1 + ".w, " + row2 + ".w, 1.0))";
|
||||
}
|
||||
|
||||
public static String mat3FromRows(String row0, String row1, String row2) {
|
||||
return "mat3("
|
||||
+ "normalize(vec3(" + row0 + ".x, " + row1 + ".x, " + row2 + ".x)), "
|
||||
+ "normalize(vec3(" + row0 + ".y, " + row1 + ".y, " + row2 + ".y)), "
|
||||
+ "normalize(vec3(" + row0 + ".z, " + row1 + ".z, " + row2 + ".z)))";
|
||||
}
|
||||
|
||||
public static String particlePos(String centerHalf, String offset) {
|
||||
return "vec4(" + centerHalf + ".xyz + " + offset + " * " + centerHalf + ".w, 1.0)";
|
||||
}
|
||||
|
||||
public static String particleUv(String uv, String corner) {
|
||||
return "vec4("
|
||||
+ "mix(" + uv + ".x, " + uv + ".z, " + corner + ".x), "
|
||||
+ "mix(" + uv + ".y, " + uv + ".w, " + corner + ".y), "
|
||||
+ "0.0, 1.0)";
|
||||
}
|
||||
|
||||
public static String cubePrelude(String prefix, String mid, String delta, String normal, String tex, String sep) {
|
||||
return "float " + prefix + "cubeMirror = " + tex + ".z < 0.0 ? 1.0 : 0.0;" + sep
|
||||
+ "vec2 " + prefix + "cubeMidU = " + mid + ".xy - " + prefix + "cubeMirror * " + normal + ".x;" + sep
|
||||
+ "vec2 " + prefix + "cubeU = " + prefix + "cubeMidU + (1.0 - 2.0 * " + prefix + "cubeMirror) * " + delta + ".xy;" + sep
|
||||
+ "vec2 " + prefix + "cubeV = " + mid + ".zw + " + delta + ".zw;";
|
||||
}
|
||||
|
||||
public static String cubeUv(String tex, String lightScale, String uA, String uZ, String vA, String vZ) {
|
||||
return "vec4("
|
||||
+ tex + ".x + " + uA + " * abs(" + tex + ".z) + " + uZ + " * " + lightScale + ".z, "
|
||||
+ tex + ".y + " + vA + " * " + tex + ".w + " + vZ + " * " + lightScale + ".w, "
|
||||
+ "0.0, 1.0)";
|
||||
}
|
||||
|
||||
public static String[] attributeDecls(String prefix, Instancing kind) {
|
||||
final ArrayList<String> decls = new ArrayList<>(9);
|
||||
switch (kind) {
|
||||
case NONE -> {}
|
||||
case TEMPLATE -> addHead(decls, prefix, "vec2", "InstLightmap");
|
||||
case CUBE -> {
|
||||
decls.add(decl(VertexFormatElement.Usage.PRIMARY_UV.getAttributeLocation(), "vec4", prefix + "CubeMid"));
|
||||
decls.add(decl(VertexFormatElement.Usage.SECONDARY_UV.getAttributeLocation(), "vec4", prefix + "CubeDelta"));
|
||||
addHead(decls, prefix, "vec4", "InstLightmapScale");
|
||||
decls.add(decl(CubeInstancedAttribs.LOC_CUBE_TEX, "vec4", prefix + "CubeTex"));
|
||||
}
|
||||
case PARTICLE -> {
|
||||
decls.add(decl(ParticleInstancedAttribs.LOC_CENTER_HALF, "vec4", prefix + "InstCenterHalf"));
|
||||
decls.add(decl(ParticleInstancedAttribs.LOC_UV, "vec4", prefix + "InstUv"));
|
||||
decls.add(decl(ParticleInstancedAttribs.LOC_COLOR, "vec4", prefix + "InstColor"));
|
||||
decls.add(decl(ParticleInstancedAttribs.LOC_LIGHTMAP, "vec2", prefix + "InstLightmap"));
|
||||
}
|
||||
}
|
||||
return decls.toArray(String[]::new);
|
||||
}
|
||||
|
||||
private static void addHead(ArrayList<String> decls, String prefix, String lightmapType, String lightmapName) {
|
||||
decls.add(decl(InstancedAttribs.LOC_ROW0, "vec4", prefix + "InstRow0"));
|
||||
decls.add(decl(InstancedAttribs.LOC_ROW1, "vec4", prefix + "InstRow1"));
|
||||
decls.add(decl(InstancedAttribs.LOC_ROW2, "vec4", prefix + "InstRow2"));
|
||||
decls.add(decl(InstancedAttribs.LOC_COLOR, "vec4", prefix + "InstColor"));
|
||||
decls.add(decl(InstancedAttribs.LOC_OVERLAY, "vec4", prefix + "InstOverlay"));
|
||||
decls.add(decl(InstancedAttribs.LOC_LIGHTMAP, lightmapType, prefix + lightmapName));
|
||||
}
|
||||
|
||||
private static String decl(int loc, String type, String name) {
|
||||
return "layout(location = " + loc + ") in " + type + " " + name + ";";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
public enum Instancing {
|
||||
NONE(""),
|
||||
TEMPLATE("_instanced"),
|
||||
CUBE("_cubeinstanced"),
|
||||
PARTICLE("_particleinstanced");
|
||||
|
||||
public static final Instancing[] VALUES = values();
|
||||
|
||||
public final String variantSuffix;
|
||||
|
||||
Instancing(String variantSuffix) {
|
||||
this.variantSuffix = variantSuffix;
|
||||
}
|
||||
|
||||
public boolean hasInstanceHead() {
|
||||
return this == TEMPLATE || this == CUBE;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memPutFloat;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memPutInt;
|
||||
|
||||
public final class ParticleInstancedAttribs {
|
||||
|
||||
public static final int LOC_CENTER_HALF = 5;
|
||||
public static final int LOC_UV = 6;
|
||||
public static final int LOC_COLOR = InstancedAttribs.LOC_COLOR;
|
||||
public static final int LOC_LIGHTMAP = InstancedAttribs.LOC_LIGHTMAP;
|
||||
|
||||
public static final int OFFSET_CENTER_HALF = 0;
|
||||
public static final int OFFSET_UV = 16;
|
||||
public static final int OFFSET_COLOR = 32;
|
||||
public static final int OFFSET_LIGHTMAP = 36;
|
||||
public static final int STRIDE = 48;
|
||||
|
||||
public static void pointInstanceAttribs(long base) {
|
||||
GLStateManager.glVertexAttribPointer(LOC_CENTER_HALF, 4, GL11.GL_FLOAT, false, STRIDE, base + OFFSET_CENTER_HALF);
|
||||
GLStateManager.glVertexAttribPointer(LOC_UV, 4, GL11.GL_FLOAT, false, STRIDE, base + OFFSET_UV);
|
||||
GLStateManager.glVertexAttribPointer(LOC_COLOR, 4, GL11.GL_UNSIGNED_BYTE, true, STRIDE, base + OFFSET_COLOR);
|
||||
GLStateManager.glVertexAttribPointer(LOC_LIGHTMAP, 2, GL11.GL_FLOAT, false, STRIDE, base + OFFSET_LIGHTMAP);
|
||||
}
|
||||
|
||||
static void enableInstanceArrays() {
|
||||
InstancedAttribs.enableInstanced(LOC_CENTER_HALF);
|
||||
InstancedAttribs.enableInstanced(LOC_UV);
|
||||
InstancedAttribs.enableInstanced(LOC_COLOR);
|
||||
InstancedAttribs.enableInstanced(LOC_LIGHTMAP);
|
||||
}
|
||||
|
||||
public static void writeInstance(long ptr, float cx, float cy, float cz, float half, float u0, float v0, float u1, float v1, int colorABGR, int packedLight) {
|
||||
memPutFloat(ptr + OFFSET_CENTER_HALF, cx);
|
||||
memPutFloat(ptr + OFFSET_CENTER_HALF + 4, cy);
|
||||
memPutFloat(ptr + OFFSET_CENTER_HALF + 8, cz);
|
||||
memPutFloat(ptr + OFFSET_CENTER_HALF + 12, half);
|
||||
memPutFloat(ptr + OFFSET_UV, u0);
|
||||
memPutFloat(ptr + OFFSET_UV + 4, v0);
|
||||
memPutFloat(ptr + OFFSET_UV + 8, u1);
|
||||
memPutFloat(ptr + OFFSET_UV + 12, v1);
|
||||
memPutInt(ptr + OFFSET_COLOR, colorABGR);
|
||||
InstancedAttribs.writeLightmap(ptr + OFFSET_LIGHTMAP, packedLight);
|
||||
}
|
||||
|
||||
private ParticleInstancedAttribs() {}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.cel.api.util.NormI8;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFlags;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormatElement;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.ImmediateExtendedAttribHandler;
|
||||
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.memAlloc;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memFree;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memPutFloat;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memPutInt;
|
||||
|
||||
public final class ParticleQuadMesh {
|
||||
|
||||
public static final int VERTEX_FLAGS = VertexFlags.TEXTURE_BIT;
|
||||
|
||||
public static final int VERTEX_COUNT = 4;
|
||||
private static final int VERTEX_STRIDE = 24;
|
||||
|
||||
private static final int OFFSET_OFFSET = 0;
|
||||
private static final int OFFSET_CORNER = 12;
|
||||
private static final int OFFSET_TANGENT = 20;
|
||||
|
||||
private static final float[] CORNER_A = { -1.0f, -1.0f, 1.0f, 1.0f };
|
||||
private static final float[] CORNER_B = { -1.0f, 1.0f, 1.0f, -1.0f };
|
||||
|
||||
private static int vao;
|
||||
private static int vbo;
|
||||
private static ByteBuffer staging;
|
||||
private static boolean written;
|
||||
private static float lastRotX, lastRotXZ, lastRotZ, lastRotYZ, lastRotXY;
|
||||
|
||||
private ParticleQuadMesh() {}
|
||||
|
||||
public static float cornerA(int corner) {
|
||||
return CORNER_A[corner];
|
||||
}
|
||||
|
||||
public static float cornerB(int corner) {
|
||||
return CORNER_B[corner];
|
||||
}
|
||||
|
||||
public static float offsetX(float a, float b, float rotX, float rotYZ) {
|
||||
return a * rotX + b * rotYZ;
|
||||
}
|
||||
|
||||
public static float offsetY(float b, float rotXZ) {
|
||||
return b * rotXZ;
|
||||
}
|
||||
|
||||
public static float offsetZ(float a, float b, float rotZ, float rotXY) {
|
||||
return a * rotZ + b * rotXY;
|
||||
}
|
||||
|
||||
public static int vao() {
|
||||
if (vao == 0) build();
|
||||
return vao;
|
||||
}
|
||||
|
||||
public static void update(float rotX, float rotXZ, float rotZ, float rotYZ, float rotXY) {
|
||||
if (vao == 0) build();
|
||||
if (written && rotX == lastRotX && rotXZ == lastRotXZ && rotZ == lastRotZ && rotYZ == lastRotYZ && rotXY == lastRotXY) {
|
||||
return;
|
||||
}
|
||||
lastRotX = rotX;
|
||||
lastRotXZ = rotXZ;
|
||||
lastRotZ = rotZ;
|
||||
lastRotYZ = rotYZ;
|
||||
lastRotXY = rotXY;
|
||||
written = true;
|
||||
|
||||
write(memAddress0(staging), rotX, rotXZ, rotZ, rotYZ, rotXY);
|
||||
final int savedVBO = GLStateManager.getBoundVBO();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
|
||||
staging.position(0).limit(VERTEX_COUNT * VERTEX_STRIDE);
|
||||
GLStateManager.glBufferSubData(GL15.GL_ARRAY_BUFFER, 0L, staging);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, savedVBO);
|
||||
}
|
||||
|
||||
private static void write(long dst, float rotX, float rotXZ, float rotZ, float rotYZ, float rotXY) {
|
||||
final int tangent = packTangent(rotX, 0.0f, rotZ);
|
||||
long ptr = dst;
|
||||
for (int i = 0; i < VERTEX_COUNT; i++) {
|
||||
final float a = cornerA(i);
|
||||
final float b = cornerB(i);
|
||||
memPutFloat(ptr + OFFSET_OFFSET, offsetX(a, b, rotX, rotYZ));
|
||||
memPutFloat(ptr + OFFSET_OFFSET + 4, offsetY(b, rotXZ));
|
||||
memPutFloat(ptr + OFFSET_OFFSET + 8, offsetZ(a, b, rotZ, rotXY));
|
||||
memPutFloat(ptr + OFFSET_CORNER, (a + 1.0f) * 0.5f);
|
||||
memPutFloat(ptr + OFFSET_CORNER + 4, (b + 1.0f) * 0.5f);
|
||||
memPutInt(ptr + OFFSET_TANGENT, tangent);
|
||||
ptr += VERTEX_STRIDE;
|
||||
}
|
||||
}
|
||||
|
||||
private static int packTangent(float x, float y, float z) {
|
||||
final float lenSq = x * x + y * y + z * z;
|
||||
final float nx, ny, nz;
|
||||
if (lenSq < 1.0e-12f) {
|
||||
nx = 1.0f;
|
||||
ny = 0.0f;
|
||||
nz = 0.0f;
|
||||
} else {
|
||||
final float inv = (float) (1.0 / Math.sqrt(lenSq));
|
||||
nx = x * inv;
|
||||
ny = y * inv;
|
||||
nz = z * inv;
|
||||
}
|
||||
return NormI8.pack(nx, ny, nz) | (127 << 24);
|
||||
}
|
||||
|
||||
private static void build() {
|
||||
final int savedVAO = GLStateManager.getBoundVAO();
|
||||
final int savedVBO = GLStateManager.getBoundVBO();
|
||||
vao = GLStateManager.glGenVertexArrays();
|
||||
GLStateManager.glBindVertexArray(vao);
|
||||
vbo = GLStateManager.glGenBuffers();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
|
||||
GLStateManager.glBufferData(GL15.GL_ARRAY_BUFFER, (long) VERTEX_COUNT * VERTEX_STRIDE, GL15.GL_DYNAMIC_DRAW);
|
||||
|
||||
attrib(VertexFormatElement.Usage.POSITION.getAttributeLocation(), 3, GL11.GL_FLOAT, false, OFFSET_OFFSET);
|
||||
attrib(VertexFormatElement.Usage.PRIMARY_UV.getAttributeLocation(), 2, GL11.GL_FLOAT, false, OFFSET_CORNER);
|
||||
attrib(ImmediateExtendedAttribHandler.LOC_TANGENT, 4, GL11.GL_BYTE, true, OFFSET_TANGENT);
|
||||
|
||||
ParticleInstancedAttribs.enableInstanceArrays();
|
||||
|
||||
GLStateManager.glBindVertexArray(savedVAO);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, savedVBO);
|
||||
|
||||
if (staging == null) staging = memAlloc(VERTEX_COUNT * VERTEX_STRIDE);
|
||||
written = false;
|
||||
}
|
||||
|
||||
private static void attrib(int loc, int size, int type, boolean normalized, int offset) {
|
||||
GLStateManager.glEnableVertexAttribArray(loc);
|
||||
GLStateManager.glVertexAttribPointer(loc, size, type, normalized, VERTEX_STRIDE, offset);
|
||||
}
|
||||
|
||||
public static void delete() {
|
||||
if (vbo != 0) {
|
||||
GLStateManager.glDeleteBuffers(vbo);
|
||||
vbo = 0;
|
||||
}
|
||||
if (vao != 0) {
|
||||
GLStateManager.glDeleteVertexArrays(vao);
|
||||
vao = 0;
|
||||
}
|
||||
if (staging != null) {
|
||||
memFree(staging);
|
||||
staging = null;
|
||||
}
|
||||
written = false;
|
||||
}
|
||||
}
|
||||
@@ -80,6 +80,10 @@ public final class ShaderManager {
|
||||
warmUp();
|
||||
enabled = true;
|
||||
|
||||
if (GLStateManager.getActiveProgram() == 0) {
|
||||
activate();
|
||||
}
|
||||
|
||||
GLStateManager.LOGGER.info("FFP shader emulation enabled");
|
||||
}
|
||||
|
||||
@@ -93,6 +97,8 @@ public final class ShaderManager {
|
||||
final long vkPacked = VertexKey.packFromState(true, true, true, true, fragMask);
|
||||
final VertexKey vk = VertexKey.fromPacked(vkPacked);
|
||||
VertexShaderGenerator.generate(vk);
|
||||
VertexShaderGenerator.generate(VertexKey.fromPacked(VertexKey.withInstancing(vkPacked, Instancing.CUBE)));
|
||||
VertexShaderGenerator.generate(VertexKey.fromPacked(VertexKey.withInstancing(vkPacked, Instancing.PARTICLE)));
|
||||
FragmentShaderGenerator.generate(FragmentKey.fromPacked(fkScratch, fkLen));
|
||||
GeometryShaderGenerator.generate(vk);
|
||||
final Class<?>[] touched = { Program.class, ShaderCache.class, TessellatorStreamingDrawer.class, QuadConverter.class };
|
||||
@@ -123,6 +129,7 @@ public final class ShaderManager {
|
||||
}
|
||||
|
||||
public void preDraw() {
|
||||
GLSMHooks.resolvePendingProgram();
|
||||
final DeferredBlendHandler bh = GLSMHooks.blendHandler;
|
||||
if (bh != null) bh.flushDeferredBlend();
|
||||
|
||||
@@ -179,6 +186,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++;
|
||||
|
||||
@@ -0,0 +1,161 @@
|
||||
package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.cel.api.util.NormI8;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFlags;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormatElement;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.ImmediateExtendedAttribHandler;
|
||||
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.memAlloc;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memFree;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memPutFloat;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memPutInt;
|
||||
|
||||
public final class UnitCubeMesh {
|
||||
|
||||
public static final int VERTEX_FLAGS = VertexFlags.COLOR_BIT | VertexFlags.TEXTURE_BIT | VertexFlags.NORMAL_BIT;
|
||||
|
||||
public static final int FACE_COUNT = 6;
|
||||
public static final int VERTEX_COUNT = 24;
|
||||
public static final int VERTEX_STRIDE = 64;
|
||||
|
||||
public static final int OFFSET_POSITION = 0;
|
||||
private static final int OFFSET_COLOR = 12;
|
||||
public static final int OFFSET_MID = 16;
|
||||
public static final int OFFSET_DELTA = 32;
|
||||
public static final int OFFSET_NORMAL = 48;
|
||||
public static final int OFFSET_TANGENT = 60;
|
||||
|
||||
public static final float[][] CORNERS = {
|
||||
{ 0, 0, 0 }, { 1, 0, 0 }, { 1, 1, 0 }, { 0, 1, 0 },
|
||||
{ 0, 0, 1 }, { 1, 0, 1 }, { 1, 1, 1 }, { 0, 1, 1 },
|
||||
};
|
||||
|
||||
public record Span(int a, int z) {
|
||||
public int texels(int sizeA, int sizeZ) {
|
||||
return a * sizeA + z * sizeZ;
|
||||
}
|
||||
}
|
||||
|
||||
public record Net(Span uLow, Span uHigh, Span vLow, Span vHigh) {
|
||||
public Span u(int vertex) {
|
||||
return vertex == 0 || vertex == 3 ? uHigh : uLow;
|
||||
}
|
||||
|
||||
public Span v(int vertex) {
|
||||
return vertex >= 2 ? vHigh : vLow;
|
||||
}
|
||||
}
|
||||
|
||||
public record Face(int[] corners, float[] normal, float[] tangent, Net net) {}
|
||||
|
||||
public static final Face[] FACES = {
|
||||
new Face(new int[] { 5, 1, 2, 6 }, new float[] { 1, 0, 0 }, new float[] { 0, 0, 1, 1 }, new Net(new Span(1, 1), new Span(1, 2), new Span(0, 1), new Span(1, 1))),
|
||||
new Face(new int[] { 0, 4, 7, 3 }, new float[] { -1, 0, 0 }, new float[] { 0, 0, -1, 1 }, new Net(new Span(0, 0), new Span(0, 1), new Span(0, 1), new Span(1, 1))),
|
||||
new Face(new int[] { 5, 4, 0, 1 }, new float[] { 0, -1, 0 }, new float[] { 1, 0, 0, 1 }, new Net(new Span(0, 1), new Span(1, 1), new Span(0, 0), new Span(0, 1))),
|
||||
new Face(new int[] { 2, 3, 7, 6 }, new float[] { 0, 1, 0 }, new float[] { 1, 0, 0, -1 }, new Net(new Span(1, 1), new Span(2, 1), new Span(0, 1), new Span(0, 0))),
|
||||
new Face(new int[] { 1, 0, 3, 2 }, new float[] { 0, 0, -1 }, new float[] { 1, 0, 0, 1 }, new Net(new Span(0, 1), new Span(1, 1), new Span(0, 1), new Span(1, 1))),
|
||||
new Face(new int[] { 4, 5, 6, 7 }, new float[] { 0, 0, 1 }, new float[] { -1, 0, 0, 1 }, new Net(new Span(1, 2), new Span(2, 2), new Span(0, 1), new Span(1, 1))),
|
||||
};
|
||||
|
||||
private static int vao;
|
||||
private static int vbo;
|
||||
|
||||
private UnitCubeMesh() {}
|
||||
|
||||
public static void write(long dst) {
|
||||
long ptr = dst;
|
||||
for (int f = 0; f < FACE_COUNT; f++) {
|
||||
final Face face = FACES[f];
|
||||
final Net net = face.net();
|
||||
final float midUA = (net.uLow().a() + net.uHigh().a()) * 0.5f;
|
||||
final float midUZ = (net.uLow().z() + net.uHigh().z()) * 0.5f;
|
||||
final float midVA = (net.vLow().a() + net.vHigh().a()) * 0.5f;
|
||||
final float midVZ = (net.vLow().z() + net.vHigh().z()) * 0.5f;
|
||||
for (int c = 0; c < 4; c++) {
|
||||
final float[] corner = CORNERS[face.corners()[c]];
|
||||
final Span u = net.u(c);
|
||||
final Span v = net.v(c);
|
||||
memPutFloat(ptr + OFFSET_POSITION, corner[0]);
|
||||
memPutFloat(ptr + OFFSET_POSITION + 4, corner[1]);
|
||||
memPutFloat(ptr + OFFSET_POSITION + 8, corner[2]);
|
||||
memPutInt(ptr + OFFSET_COLOR, 0xFFFFFFFF);
|
||||
memPutFloat(ptr + OFFSET_MID, midUA);
|
||||
memPutFloat(ptr + OFFSET_MID + 4, midUZ);
|
||||
memPutFloat(ptr + OFFSET_MID + 8, midVA);
|
||||
memPutFloat(ptr + OFFSET_MID + 12, midVZ);
|
||||
memPutFloat(ptr + OFFSET_DELTA, u.a() - midUA);
|
||||
memPutFloat(ptr + OFFSET_DELTA + 4, u.z() - midUZ);
|
||||
memPutFloat(ptr + OFFSET_DELTA + 8, v.a() - midVA);
|
||||
memPutFloat(ptr + OFFSET_DELTA + 12, v.z() - midVZ);
|
||||
memPutFloat(ptr + OFFSET_NORMAL, face.normal()[0]);
|
||||
memPutFloat(ptr + OFFSET_NORMAL + 4, face.normal()[1]);
|
||||
memPutFloat(ptr + OFFSET_NORMAL + 8, face.normal()[2]);
|
||||
final float[] tangent = face.tangent();
|
||||
memPutInt(ptr + OFFSET_TANGENT, packTangent(tangent[0], tangent[1], tangent[2], tangent[3]));
|
||||
ptr += VERTEX_STRIDE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int packTangent(float x, float y, float z, float w) {
|
||||
return NormI8.pack(x, y, z) | (((int) (w * 127.0f) & 0xFF) << 24);
|
||||
}
|
||||
|
||||
public static int vao() {
|
||||
if (vao == 0) build();
|
||||
return vao;
|
||||
}
|
||||
|
||||
private static void build() {
|
||||
final int savedVAO = GLStateManager.getBoundVAO();
|
||||
final int savedVBO = GLStateManager.getBoundVBO();
|
||||
vao = GLStateManager.glGenVertexArrays();
|
||||
GLStateManager.glBindVertexArray(vao);
|
||||
vbo = GLStateManager.glGenBuffers();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
|
||||
|
||||
final int bytes = VERTEX_COUNT * VERTEX_STRIDE;
|
||||
final ByteBuffer data = memAlloc(bytes);
|
||||
try {
|
||||
write(memAddress0(data));
|
||||
data.position(0).limit(bytes);
|
||||
GLStateManager.glBufferData(GL15.GL_ARRAY_BUFFER, data, GL15.GL_STATIC_DRAW);
|
||||
} finally {
|
||||
memFree(data);
|
||||
}
|
||||
|
||||
attrib(VertexFormatElement.Usage.POSITION.getAttributeLocation(), 3, GL11.GL_FLOAT, false, OFFSET_POSITION);
|
||||
attrib(VertexFormatElement.Usage.COLOR.getAttributeLocation(), 4, GL11.GL_UNSIGNED_BYTE, true, OFFSET_COLOR);
|
||||
attrib(VertexFormatElement.Usage.PRIMARY_UV.getAttributeLocation(), 4, GL11.GL_FLOAT, false, OFFSET_MID);
|
||||
attrib(VertexFormatElement.Usage.SECONDARY_UV.getAttributeLocation(), 4, GL11.GL_FLOAT, false, OFFSET_DELTA);
|
||||
attrib(VertexFormatElement.Usage.NORMAL.getAttributeLocation(), 3, GL11.GL_FLOAT, false, OFFSET_NORMAL);
|
||||
attrib(ImmediateExtendedAttribHandler.LOC_TANGENT, 4, GL11.GL_BYTE, true, OFFSET_TANGENT);
|
||||
|
||||
CubeInstancedAttribs.enableInstanceArrays();
|
||||
|
||||
GLStateManager.glBindVertexArray(savedVAO);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, savedVBO);
|
||||
}
|
||||
|
||||
private static void attrib(int loc, int size, int type, boolean normalized, int offset) {
|
||||
GLStateManager.glEnableVertexAttribArray(loc);
|
||||
GLStateManager.glVertexAttribPointer(loc, size, type, normalized, VERTEX_STRIDE, offset);
|
||||
}
|
||||
|
||||
public static void delete() {
|
||||
if (vbo != 0) {
|
||||
GLStateManager.glDeleteBuffers(vbo);
|
||||
vbo = 0;
|
||||
}
|
||||
if (vao != 0) {
|
||||
GLStateManager.glDeleteVertexArrays(vao);
|
||||
vao = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -49,7 +49,7 @@ public final class VertexKey {
|
||||
private static final int BIT_UNIT_TEXMAT_BASE = BIT_WIDE_LINE + 1;
|
||||
private static final int BIT_UNIT23_UV_FROM_UNIT0 = BIT_UNIT_TEXMAT_BASE + MAX_UNITS;
|
||||
private static final int BIT_LINE_STIPPLE = BIT_UNIT23_UV_FROM_UNIT0 + 1;
|
||||
private static final int BIT_INSTANCED = BIT_LINE_STIPPLE + 1;
|
||||
private static final int BIT_INSTANCING = BIT_LINE_STIPPLE + 1;
|
||||
|
||||
public static final int TG_NONE = 0;
|
||||
public static final int TG_OBJ_LINEAR = 1;
|
||||
@@ -97,7 +97,7 @@ public final class VertexKey {
|
||||
public boolean clipPlanesEnabled() { return bit(BIT_CLIP_PLANES); }
|
||||
public boolean wideLineEmulation() { return bit(BIT_WIDE_LINE); }
|
||||
public boolean lineStipple() { return bit(BIT_LINE_STIPPLE); }
|
||||
public boolean instancedDraw() { return bit(BIT_INSTANCED); }
|
||||
public Instancing instancing() { return Instancing.VALUES[(int) ((packed >> BIT_INSTANCING) & 0x3)]; }
|
||||
|
||||
public boolean cmReplacesAmbient() { final int m = colorMaterialMode(); return m == CM_AMBIENT || m == CM_AMBIENT_AND_DIFFUSE; }
|
||||
public boolean cmReplacesDiffuse() { final int m = colorMaterialMode(); return m == CM_DIFFUSE || m == CM_AMBIENT_AND_DIFFUSE; }
|
||||
@@ -226,13 +226,15 @@ public final class VertexKey {
|
||||
bits |= (1L << BIT_LINE_STIPPLE);
|
||||
}
|
||||
|
||||
if (GLStateManager.instancedFfpDrawActive) {
|
||||
bits |= (1L << BIT_INSTANCED);
|
||||
}
|
||||
bits |= (long) GLStateManager.ffpInstancing.ordinal() << BIT_INSTANCING;
|
||||
|
||||
return bits;
|
||||
}
|
||||
|
||||
static long withInstancing(long packed, Instancing kind) {
|
||||
return (packed & ~(0x3L << BIT_INSTANCING)) | ((long) kind.ordinal() << BIT_INSTANCING);
|
||||
}
|
||||
|
||||
public static VertexKey fromState(boolean hasColor, boolean hasNormal, boolean hasTexCoord, boolean hasLightmap, int fragUnitMask) {
|
||||
return new VertexKey(packFromState(hasColor, hasNormal, hasTexCoord, hasLightmap, fragUnitMask));
|
||||
}
|
||||
@@ -255,12 +257,6 @@ public final class VertexKey {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("FFPVertexKey[0x%011X: lit=%b l0=%b l1=%b cm=%b fog=%b tex=%d%d%d%d texmat=%d%d%d%d col=%b nrm=%b vtex=%b vlm=%b tg=%d/%d/%d/%d clip=%b wline=%b inst=%b]",
|
||||
packed, lightingEnabled(), lightEnabled(0), lightEnabled(1),
|
||||
colorMaterialEnabled(), fogEnabled(),
|
||||
unitTexCoordEnabled(0)?1:0, unitTexCoordEnabled(1)?1:0, unitTexCoordEnabled(2)?1:0, unitTexCoordEnabled(3)?1:0,
|
||||
unitTexMatEnabled(0)?1:0, unitTexMatEnabled(1)?1:0, unitTexMatEnabled(2)?1:0, unitTexMatEnabled(3)?1:0,
|
||||
hasVertexColor(), hasVertexNormal(), hasVertexTexCoord(), hasVertexLightmap(),
|
||||
texGenModeS(), texGenModeT(), texGenModeR(), texGenModeQ(), clipPlanesEnabled(), wideLineEmulation(), instancedDraw());
|
||||
return String.format("FFPVertexKey[0x%011X: lit=%b l0=%b l1=%b cm=%b fog=%b tex=%d%d%d%d texmat=%d%d%d%d col=%b nrm=%b vtex=%b vlm=%b tg=%d/%d/%d/%d clip=%b wline=%b inst=%s]", packed,lightingEnabled(), lightEnabled(0), lightEnabled(1), colorMaterialEnabled(), fogEnabled(), unitTexCoordEnabled(0)?1:0, unitTexCoordEnabled(1)?1:0, unitTexCoordEnabled(2)?1:0, unitTexCoordEnabled(3)?1:0, unitTexMatEnabled(0)?1:0, unitTexMatEnabled(1)?1:0, unitTexMatEnabled(2)?1:0, unitTexMatEnabled(3)?1:0, hasVertexColor(), hasVertexNormal(), hasVertexTexCoord(), hasVertexLightmap(), texGenModeS(), texGenModeT(), texGenModeR(), texGenModeQ(), clipPlanesEnabled(), wideLineEmulation(), instancing());
|
||||
}
|
||||
}
|
||||
@@ -55,31 +55,43 @@ public final class VertexShaderGenerator {
|
||||
}
|
||||
|
||||
private static void emitAttributes(StringBuilder sb, VertexKey key) {
|
||||
final Instancing kind = key.instancing();
|
||||
sb.append("// Vertex attributes\n");
|
||||
sb.append("layout(location = ").append(VertexFormatElement.Usage.POSITION.getAttributeLocation()).append(") in vec3 a_Position;\n");
|
||||
if (key.hasVertexColor()) {
|
||||
sb.append("layout(location = ").append(VertexFormatElement.Usage.COLOR.getAttributeLocation()).append(") in vec4 a_Color;\n");
|
||||
}
|
||||
if (key.hasVertexTexCoord()) {
|
||||
if (kind == Instancing.PARTICLE) {
|
||||
sb.append("layout(location = ").append(VertexFormatElement.Usage.PRIMARY_UV.getAttributeLocation()).append(") in vec2 a_TexCoord0;\n");
|
||||
} else {
|
||||
if (key.hasVertexColor()) {
|
||||
sb.append("layout(location = ").append(VertexFormatElement.Usage.COLOR.getAttributeLocation()).append(") in vec4 a_Color;\n");
|
||||
}
|
||||
if (kind != Instancing.CUBE) {
|
||||
if (key.hasVertexTexCoord()) {
|
||||
sb.append("layout(location = ").append(VertexFormatElement.Usage.PRIMARY_UV.getAttributeLocation()).append(") in vec2 a_TexCoord0;\n");
|
||||
}
|
||||
if (key.hasVertexLightmap()) {
|
||||
sb.append("layout(location = ").append(VertexFormatElement.Usage.SECONDARY_UV.getAttributeLocation()).append(") in vec2 a_TexCoord1;\n");
|
||||
}
|
||||
}
|
||||
if (key.hasVertexNormal() || kind == Instancing.CUBE) {
|
||||
sb.append("layout(location = ").append(VertexFormatElement.Usage.NORMAL.getAttributeLocation()).append(") in vec3 a_Normal;\n");
|
||||
}
|
||||
}
|
||||
if (key.hasVertexLightmap()) {
|
||||
sb.append("layout(location = ").append(VertexFormatElement.Usage.SECONDARY_UV.getAttributeLocation()).append(") in vec2 a_TexCoord1;\n");
|
||||
}
|
||||
if (key.hasVertexNormal()) {
|
||||
sb.append("layout(location = ").append(VertexFormatElement.Usage.NORMAL.getAttributeLocation()).append(") in vec3 a_Normal;\n");
|
||||
}
|
||||
if (key.instancedDraw()) {
|
||||
sb.append("layout(location = ").append(InstancedAttribs.LOC_MATRIX_COL0).append(") in vec4 a_InstCol0;\n");
|
||||
sb.append("layout(location = ").append(InstancedAttribs.LOC_MATRIX_COL1).append(") in vec4 a_InstCol1;\n");
|
||||
sb.append("layout(location = ").append(InstancedAttribs.LOC_MATRIX_COL2).append(") in vec4 a_InstCol2;\n");
|
||||
sb.append("layout(location = ").append(InstancedAttribs.LOC_MATRIX_COL3).append(") in vec4 a_InstCol3;\n");
|
||||
sb.append("layout(location = ").append(InstancedAttribs.LOC_COLOR).append(") in vec4 a_InstColor;\n");
|
||||
sb.append("layout(location = ").append(InstancedAttribs.LOC_LIGHTMAP).append(") in vec2 a_InstLightmap;\n");
|
||||
for (String d : InstancedGlslHelpers.attributeDecls("a_", kind)) {
|
||||
sb.append(d).append('\n');
|
||||
}
|
||||
sb.append('\n');
|
||||
}
|
||||
|
||||
private static final String PARTICLE_UV = InstancedGlslHelpers.particleUv("a_InstUv", "a_TexCoord0");
|
||||
|
||||
private static String unit0Source(VertexKey key) {
|
||||
return switch (key.instancing()) {
|
||||
case CUBE -> "cubeUv";
|
||||
case PARTICLE -> PARTICLE_UV;
|
||||
case NONE, TEMPLATE -> key.hasVertexTexCoord() ? "vec4(a_TexCoord0, 0.0, 1.0)" : "u_CurrentTexCoord0";
|
||||
};
|
||||
}
|
||||
|
||||
private static void emitLineStart(StringBuilder sb) {
|
||||
sb.append(" vec2 ndc = gl_Position.xy / gl_Position.w;\n");
|
||||
sb.append(" v_LineStart = u_Viewport.xy + (ndc * 0.5 + 0.5) * u_Viewport.zw;\n\n");
|
||||
@@ -93,6 +105,10 @@ public final class VertexShaderGenerator {
|
||||
private static void emitOutputs(StringBuilder sb, VertexKey key) {
|
||||
sb.append("// Outputs\n");
|
||||
sb.append("out vec4 v_Color;\n");
|
||||
if (key.instancing().hasInstanceHead()) {
|
||||
sb.append("out vec4 v_Overlay;\n");
|
||||
sb.append("invariant gl_Position;\n");
|
||||
}
|
||||
if (key.lineStipple()) {
|
||||
sb.append("flat out vec2 v_LineStart;\n");
|
||||
}
|
||||
@@ -116,11 +132,16 @@ public final class VertexShaderGenerator {
|
||||
|
||||
private static void emitPositionTransform(StringBuilder sb, VertexKey key) {
|
||||
sb.append(" // Position transform\n");
|
||||
sb.append(" vec4 pos4 = vec4(a_Position, 1.0);\n");
|
||||
if (key.instancedDraw()) {
|
||||
sb.append(" mat4 instMV = mat4(a_InstCol0, a_InstCol1, a_InstCol2, a_InstCol3);\n");
|
||||
if (key.instancing() == Instancing.PARTICLE) {
|
||||
sb.append(" vec4 pos4 = ").append(InstancedGlslHelpers.particlePos("a_InstCenterHalf", "a_Position")).append(";\n");
|
||||
} else {
|
||||
sb.append(" vec4 pos4 = vec4(a_Position, 1.0);\n");
|
||||
}
|
||||
if (key.instancing().hasInstanceHead()) {
|
||||
sb.append(" mat4 instMV = ").append(InstancedGlslHelpers.mat4FromRows("a_InstRow0", "a_InstRow1", "a_InstRow2")).append(";\n");
|
||||
sb.append(" vec4 eyePos = instMV * pos4;\n");
|
||||
sb.append(" gl_Position = u_ProjectionMatrix * eyePos;\n");
|
||||
sb.append(" v_Overlay = a_InstOverlay;\n");
|
||||
} else {
|
||||
sb.append(" gl_Position = u_MVPMatrix * pos4;\n");
|
||||
|
||||
@@ -134,8 +155,8 @@ public final class VertexShaderGenerator {
|
||||
|
||||
private static void emitNormalTransform(StringBuilder sb, VertexKey key) {
|
||||
sb.append(" // Normal transform\n");
|
||||
final String srcNormal = key.hasVertexNormal() ? "a_Normal" : "u_CurrentNormal";
|
||||
if (key.instancedDraw()) {
|
||||
final String srcNormal = key.hasVertexNormal() && key.instancing() != Instancing.PARTICLE ? "a_Normal" : "u_CurrentNormal";
|
||||
if (key.instancing().hasInstanceHead()) {
|
||||
sb.append(" vec3 normal = normalize(mat3(instMV) * ").append(srcNormal).append(");\n");
|
||||
sb.append('\n');
|
||||
return;
|
||||
@@ -156,9 +177,13 @@ public final class VertexShaderGenerator {
|
||||
if (key.colorMaterialEnabled()) {
|
||||
// Color material: vertex color replaces specific material properties per glColorMaterial mode (Mesa ffvertex_prog.c get_material() bitmask approach)
|
||||
sb.append(" vec4 matColor = ");
|
||||
sb.append(key.hasVertexColor() ? "a_Color" : "u_CurrentColor");
|
||||
if (key.instancedDraw()) {
|
||||
sb.append(" * a_InstColor");
|
||||
if (key.instancing() == Instancing.PARTICLE) {
|
||||
sb.append("a_InstColor");
|
||||
} else {
|
||||
sb.append(key.hasVertexColor() ? "a_Color" : "u_CurrentColor");
|
||||
if (key.instancing().hasInstanceHead()) {
|
||||
sb.append(" * a_InstColor");
|
||||
}
|
||||
}
|
||||
sb.append(";\n");
|
||||
|
||||
@@ -255,22 +280,27 @@ public final class VertexShaderGenerator {
|
||||
private static void emitColorPassthrough(StringBuilder sb, VertexKey key) {
|
||||
sb.append(" // Color passthrough (no lighting)\n");
|
||||
final String src = key.hasVertexColor() ? "a_Color" : "u_CurrentColor";
|
||||
if (key.instancedDraw()) {
|
||||
sb.append(" v_Color = ").append(src).append(" * a_InstColor;\n");
|
||||
} else {
|
||||
sb.append(" v_Color = ").append(src).append(";\n");
|
||||
switch (key.instancing()) {
|
||||
case PARTICLE -> sb.append(" v_Color = a_InstColor;\n");
|
||||
case TEMPLATE, CUBE -> sb.append(" v_Color = ").append(src).append(" * a_InstColor;\n");
|
||||
case NONE -> sb.append(" v_Color = ").append(src).append(";\n");
|
||||
}
|
||||
sb.append('\n');
|
||||
}
|
||||
|
||||
private static void emitTexCoordPassthrough(StringBuilder sb, VertexKey key) {
|
||||
final boolean cube = key.instancing() == Instancing.CUBE;
|
||||
final boolean unit0FromCube = cube && key.unitTexCoordEnabled(0) && !key.texGenEnabled();
|
||||
final boolean unit23FromCube = cube && key.unit23UvFromUnit0() && (key.unitTexCoordEnabled(2) || key.unitTexCoordEnabled(3));
|
||||
if (unit0FromCube || unit23FromCube) {
|
||||
emitCubeTexCoord(sb);
|
||||
}
|
||||
|
||||
if (key.texGenEnabled()) {
|
||||
emitTexGenCoordGeneration(sb, key);
|
||||
} else if (key.unitTexCoordEnabled(0)) {
|
||||
sb.append(" // Texture coordinates - unit 0\n");
|
||||
final String src = key.hasVertexTexCoord()
|
||||
? "vec4(a_TexCoord0, 0.0, 1.0)"
|
||||
: "u_CurrentTexCoord0";
|
||||
final String src = unit0Source(key);
|
||||
if (key.unitTexMatEnabled(0)) {
|
||||
sb.append(" v_TexCoord0 = u_TextureMatrix0 * ").append(src).append(";\n");
|
||||
} else {
|
||||
@@ -279,20 +309,17 @@ public final class VertexShaderGenerator {
|
||||
}
|
||||
|
||||
if (key.lightmapEnabled()) {
|
||||
if (key.instancedDraw()) {
|
||||
sb.append(" v_TexCoord1 = u_LightmapTextureMatrix * vec4(a_InstLightmap, 0.0, 1.0);\n");
|
||||
} else if (key.hasVertexLightmap()) {
|
||||
sb.append(" v_TexCoord1 = u_LightmapTextureMatrix * vec4(a_TexCoord1, 0.0, 1.0);\n");
|
||||
} else {
|
||||
sb.append(" v_TexCoord1 = u_LightmapTextureMatrix * vec4(u_CurrentLightmapCoord, 0.0, 1.0);\n");
|
||||
}
|
||||
final String lightmap = switch (key.instancing()) {
|
||||
case PARTICLE, TEMPLATE -> "a_InstLightmap";
|
||||
case CUBE -> "a_InstLightmapScale.xy";
|
||||
case NONE -> key.hasVertexLightmap() ? "a_TexCoord1" : "u_CurrentLightmapCoord";
|
||||
};
|
||||
sb.append(" v_TexCoord1 = u_LightmapTextureMatrix * vec4(").append(lightmap).append(", 0.0, 1.0);\n");
|
||||
}
|
||||
|
||||
for (int i = 2; i < VertexKey.MAX_UNITS; i++) {
|
||||
if (!key.unitTexCoordEnabled(i)) continue;
|
||||
final String src = key.unit23UvFromUnit0()
|
||||
? "vec4(a_TexCoord0, 0.0, 1.0)"
|
||||
: "u_CurrentTexCoord" + i;
|
||||
final String src = key.unit23UvFromUnit0() ? unit0Source(key) : "u_CurrentTexCoord" + i;
|
||||
if (!key.unit23UvFromUnit0() && key.unitTexMatEnabled(i)) {
|
||||
sb.append(" v_TexCoord").append(i).append(" = u_TextureMatrix").append(i).append(" * ").append(src).append(";\n");
|
||||
} else {
|
||||
@@ -301,6 +328,12 @@ public final class VertexShaderGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
private static void emitCubeTexCoord(StringBuilder sb) {
|
||||
sb.append(" // Cube texture net\n");
|
||||
sb.append(" ").append(InstancedGlslHelpers.cubePrelude("", "a_CubeMid", "a_CubeDelta", "a_Normal", "a_CubeTex", "\n ")).append('\n');
|
||||
sb.append(" vec4 cubeUv = ").append(InstancedGlslHelpers.cubeUv("a_CubeTex", "a_InstLightmapScale", "cubeU.x", "cubeU.y", "cubeV.x", "cubeV.y")).append(";\n\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit per-coordinate texgen code following Mesa's ffvertex_prog.c build order.
|
||||
* OBJ_LINEAR: dot(pos4, objPlane), EYE_LINEAR: dot(eyePos, eyePlane).
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
package com.gtnewhorizons.angelica.glsm.hooks;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.CaptureGate;
|
||||
import com.gtnewhorizons.angelica.glsm.DisplayListManager;
|
||||
import com.gtnewhorizons.angelica.glsm.GLDebug;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.BackendManager;
|
||||
|
||||
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
|
||||
|
||||
public final class FrameHooks {
|
||||
|
||||
private FrameHooks() {}
|
||||
|
||||
private static boolean gameLoopStarted;
|
||||
private static long frameCounter;
|
||||
private static boolean frameGroupActive;
|
||||
|
||||
public static void bootstrapFirstFrame() {
|
||||
DisplayListManager.abortIfLeaked();
|
||||
if (!gameLoopStarted) {
|
||||
gameLoopStarted = true;
|
||||
RENDER_BACKEND.onFrameBegin();
|
||||
pushFrameGroup();
|
||||
}
|
||||
}
|
||||
|
||||
public static void frameEnd() {
|
||||
popFrameGroup();
|
||||
RENDER_BACKEND.onFrameEnd();
|
||||
}
|
||||
|
||||
public static void frameBegin() {
|
||||
if (gameLoopStarted) {
|
||||
RENDER_BACKEND.onFrameBegin();
|
||||
pushFrameGroup();
|
||||
}
|
||||
}
|
||||
|
||||
public static void shutdown() {
|
||||
BackendManager.shutdown();
|
||||
}
|
||||
|
||||
public static void bindSplashVao() {
|
||||
GLStateManager.glBindVertexArray(GLStateManager.glGenVertexArrays());
|
||||
}
|
||||
|
||||
public static void splashFinished() {
|
||||
GLStateManager.markSplashComplete("SplashProgress.finish");
|
||||
}
|
||||
|
||||
private static void pushFrameGroup() {
|
||||
CaptureGate.refresh();
|
||||
GLDebug.pushGroup("frame:", frameCounter);
|
||||
frameGroupActive = true;
|
||||
}
|
||||
|
||||
private static void popFrameGroup() {
|
||||
if (frameGroupActive) {
|
||||
GLDebug.popGroup();
|
||||
frameGroupActive = false;
|
||||
frameCounter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@ public final class GLSMConfig {
|
||||
|
||||
public static boolean hudCacheOverride;
|
||||
|
||||
public static boolean extendedAttribsExpected;
|
||||
public static volatile boolean expandVertexFormats;
|
||||
|
||||
private GLSMConfig() {}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.gtnewhorizons.angelica.glsm.hooks;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.DisplayListManager;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.events.*;
|
||||
import net.minecraftforge.eventbus.api.bus.EventBus;
|
||||
|
||||
@@ -9,10 +10,17 @@ 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;
|
||||
public static PerFrameUniformBlock perPassUniformBlock;
|
||||
public static PendingProgramSelection pendingProgramSelection;
|
||||
|
||||
public static void resolvePendingProgram() {
|
||||
final PendingProgramSelection p = pendingProgramSelection;
|
||||
if (p != null && !DisplayListManager.isRecording()) p.resolvePendingProgram();
|
||||
}
|
||||
|
||||
// EventBus instances
|
||||
public static final EventBus<TextureBindEvent> TEXTURE_BIND = EventBus.create(TextureBindEvent.class);
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package com.gtnewhorizons.angelica.glsm.hooks;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.DirectTessellator;
|
||||
import com.gtnewhorizons.angelica.config.SystemProperties;
|
||||
import com.gtnewhorizons.angelica.glsm.streaming.StreamingUploader;
|
||||
import com.gtnewhorizons.angelica.glsm.streaming.TessellatorStreamingDrawer;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
@@ -14,7 +16,7 @@ public final class GLSMInitConfig {
|
||||
@Getter private final int displayWidth;
|
||||
@Getter private final int displayHeight;
|
||||
@Getter private final Runnable postInitCallback;
|
||||
@Getter private final boolean DSAEnabled;
|
||||
private final boolean dsaEnabled;
|
||||
@Getter private final boolean noErrorChecks;
|
||||
|
||||
private GLSMInitConfig(Builder builder) {
|
||||
@@ -26,7 +28,11 @@ public final class GLSMInitConfig {
|
||||
this.displayWidth = builder.displayWidth;
|
||||
this.displayHeight = builder.displayHeight;
|
||||
this.postInitCallback = builder.postInitCallback;
|
||||
this.DSAEnabled = builder.enableDSA;
|
||||
this.dsaEnabled = builder.dsaEnabled;
|
||||
}
|
||||
|
||||
public boolean isDSAEnabled() {
|
||||
return dsaEnabled;
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
@@ -34,14 +40,14 @@ public final class GLSMInitConfig {
|
||||
}
|
||||
|
||||
public static final class Builder {
|
||||
private boolean lwjglDebug = false;
|
||||
private boolean lwjglDebug = SystemProperties.LWJGL_DEBUG;
|
||||
private StreamingUploader.UploadStrategy streamingUploadStrategy = StreamingUploader.UploadStrategy.BUFFER_DATA;
|
||||
private Consumer<DirectTessellator> directDrawer = null;
|
||||
private Runnable streamingDrawerDestroy = null;
|
||||
private Consumer<DirectTessellator> directDrawer = TessellatorStreamingDrawer::drawDirect;
|
||||
private Runnable streamingDrawerDestroy = TessellatorStreamingDrawer::destroy;
|
||||
private int displayWidth = 0;
|
||||
private int displayHeight = 0;
|
||||
private Runnable postInitCallback = null;
|
||||
private boolean enableDSA = false;
|
||||
private boolean dsaEnabled = true;
|
||||
private boolean noErrorChecks;
|
||||
|
||||
private Builder() {}
|
||||
@@ -83,7 +89,7 @@ public final class GLSMInitConfig {
|
||||
}
|
||||
|
||||
public Builder enableDSA(boolean enableDSA) {
|
||||
this.enableDSA = enableDSA;
|
||||
this.dsaEnabled = enableDSA;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
+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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.gtnewhorizons.angelica.glsm.hooks;
|
||||
|
||||
public interface PendingProgramSelection {
|
||||
void resolvePendingProgram();
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.gtnewhorizons.angelica.glsm.hooks;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.DisplayListManager;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.RenderSystem;
|
||||
import org.lwjgl.opengl.EXTTextureFilterAnisotropic;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
|
||||
public final class TextureUtilHooks {
|
||||
|
||||
private TextureUtilHooks() {}
|
||||
|
||||
private static int savedFilterTextureId;
|
||||
|
||||
public static void setMaxLevel(int mipmapLevels) {
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL12.GL_TEXTURE_MAX_LEVEL, mipmapLevels);
|
||||
}
|
||||
|
||||
public static void captureBoundFilterTexture() {
|
||||
savedFilterTextureId = GLStateManager.getBoundTextureForServerState();
|
||||
}
|
||||
|
||||
public static void clearSavedFilterTexture() {
|
||||
savedFilterTextureId = 0;
|
||||
}
|
||||
|
||||
public static void restoreFilter(int min, int mag) {
|
||||
final int id = savedFilterTextureId;
|
||||
if (id != 0 && DisplayListManager.getRecordMode() == DisplayListManager.RecordMode.NONE) {
|
||||
RenderSystem.texParameteri(id, GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, min);
|
||||
RenderSystem.texParameteri(id, GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, mag);
|
||||
} else {
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, min);
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, mag);
|
||||
}
|
||||
}
|
||||
|
||||
public static void restoreAniso(float aniso) {
|
||||
final int id = savedFilterTextureId;
|
||||
if (id != 0 && DisplayListManager.getRecordMode() == DisplayListManager.RecordMode.NONE) {
|
||||
RenderSystem.texParameterf(id, GL11.GL_TEXTURE_2D, EXTTextureFilterAnisotropic.GL_TEXTURE_MAX_ANISOTROPY_EXT, aniso);
|
||||
} else {
|
||||
GLStateManager.glTexParameterf(GL11.GL_TEXTURE_2D, EXTTextureFilterAnisotropic.GL_TEXTURE_MAX_ANISOTROPY_EXT, aniso);
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
@@ -4,5 +4,6 @@ import net.minecraftforge.eventbus.api.event.MutableEvent;
|
||||
|
||||
public final class TextureUnitStateEvent extends MutableEvent {
|
||||
public int unit;
|
||||
public int cap;
|
||||
public boolean enabled;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.gtnewhorizons.angelica.glsm.loading;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.asm.ASMUtil;
|
||||
import com.gtnewhorizons.retrofuturabootstrap.api.ClassNodeHandle;
|
||||
|
||||
public final class ClassDump {
|
||||
|
||||
public static final ClassDump DISABLED = new ClassDump(false);
|
||||
|
||||
private final boolean enabled;
|
||||
|
||||
public ClassDump(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public void dumpClass(String className, byte[] originalBytes, byte[] transformedBytes, Object transformer) {
|
||||
if (!enabled) return;
|
||||
ASMUtil.saveAsRawClassFile(originalBytes, className + "_PRE", transformer);
|
||||
ASMUtil.saveAsRawClassFile(transformedBytes, className + "_POST", transformer);
|
||||
}
|
||||
|
||||
public void dumpRFBClass(String className, ClassNodeHandle classNode, Object transformer) {
|
||||
if (!enabled) return;
|
||||
dumpClass(className, classNode.getOriginalBytes(), classNode.computeBytes(), transformer);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,10 @@
|
||||
package com.gtnewhorizons.angelica.glsm.loading;
|
||||
|
||||
import net.minecraft.launchwrapper.Launch;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import javax.swing.JOptionPane;
|
||||
import java.awt.GraphicsEnvironment;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -15,6 +20,10 @@ public final class DependencyVerifier {
|
||||
*/
|
||||
public record Check(String resourcePath, String errorMessage) {}
|
||||
|
||||
public interface FatalDialog {
|
||||
void show(String title, String message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify all checks pass. Throws RuntimeException on first failure.
|
||||
* @param anchor Class whose classloader to use for resource lookups
|
||||
@@ -28,5 +37,39 @@ public final class DependencyVerifier {
|
||||
}
|
||||
}
|
||||
|
||||
public static List<Check> gtnhLibChecks(String productName) {
|
||||
return List.of(
|
||||
new Check("/it/unimi/dsi/fastutil/ints/Int2ObjectMap.class", "Missing dependency: " + productName + " requires GTNHLib! Download: https://modrinth.com/mod/gtnhlib"),
|
||||
new Check("/com/gtnewhorizon/gtnhlib/client/renderer/VertexCallbackManager.class", "GTNHLib is outdated: " + productName + " requires GTNHLib 0.10.0 or newer! Download: https://modrinth.com/mod/gtnhlib"),
|
||||
new Check("/it/unimi/dsi/fastutil/objects/ObjectBooleanBiConsumer.class", "GTNHLib is outdated: " + productName + " requires GTNHLib 0.11.19 or newer (fastutil 8.5.18+)! Download: https://modrinth.com/mod/gtnhlib"));
|
||||
}
|
||||
|
||||
public static void verifyOrHalt(Class<?> anchor, String productName, List<Check> checks, Logger logger, FatalDialog sdlDialog) {
|
||||
final String message;
|
||||
try {
|
||||
verify(anchor, checks);
|
||||
return;
|
||||
} catch (RuntimeException ex) {
|
||||
message = ex.getMessage();
|
||||
}
|
||||
|
||||
final String title = productName + " - Missing Dependency";
|
||||
System.err.println("FATAL: " + message);
|
||||
try {
|
||||
logger.fatal(message);
|
||||
} catch (Throwable ignored) {}
|
||||
|
||||
final int lwjgl3ifyMajor = ((Integer) Launch.blackboard.getOrDefault("lwjgl3ify:major-version", Integer.MIN_VALUE));
|
||||
try {
|
||||
if (lwjgl3ifyMajor >= 3) {
|
||||
sdlDialog.show(title, message);
|
||||
} else if (!GraphicsEnvironment.isHeadless()) {
|
||||
JOptionPane.showMessageDialog(null, message, title, JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
} catch (Throwable ignored) {}
|
||||
|
||||
Runtime.getRuntime().halt(1);
|
||||
}
|
||||
|
||||
private DependencyVerifier() {}
|
||||
}
|
||||
+2
-48
@@ -1,54 +1,8 @@
|
||||
package com.gtnewhorizons.angelica.glsm.loading;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.redirect.GLSMRedirector;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public final class EarlyRedirectorCore {
|
||||
|
||||
private final GLSMRedirector core;
|
||||
private final String[] targets;
|
||||
private final String[] exclusions;
|
||||
public final class EarlyRedirectorCore extends ModRedirector {
|
||||
|
||||
public EarlyRedirectorCore(String... extraExclusions) {
|
||||
this.core = new GLSMRedirector();
|
||||
this.targets = EcosystemNarrowRules.EARLY_REDIRECTOR_TARGETS.clone();
|
||||
|
||||
final List<String> excl = new ArrayList<>(Arrays.asList(core.getCoreExclusions()));
|
||||
if (extraExclusions != null && extraExclusions.length > 0) {
|
||||
Collections.addAll(excl, extraExclusions);
|
||||
}
|
||||
this.exclusions = excl.toArray(new String[0]);
|
||||
}
|
||||
|
||||
public byte[] transform(String transformedName, byte[] basicClass) {
|
||||
if (basicClass == null) return null;
|
||||
|
||||
if (!startsWithAny(transformedName, targets)) return basicClass;
|
||||
if (startsWithAny(transformedName, exclusions)) return basicClass;
|
||||
if (!core.shouldTransform(basicClass)) return basicClass;
|
||||
|
||||
final ClassReader cr = new ClassReader(basicClass);
|
||||
final ClassNode cn = new ClassNode();
|
||||
cr.accept(cn, 0);
|
||||
if (core.transformClassNode(transformedName, cn)) {
|
||||
final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
|
||||
cn.accept(cw);
|
||||
return cw.toByteArray();
|
||||
}
|
||||
return basicClass;
|
||||
}
|
||||
|
||||
private static boolean startsWithAny(String name, String[] prefixes) {
|
||||
for (String p : prefixes) {
|
||||
if (name.startsWith(p)) return true;
|
||||
}
|
||||
return false;
|
||||
super(ClassDump.DISABLED, EcosystemNarrowRules.EARLY_REDIRECTOR_TARGETS.clone(), extraExclusions);
|
||||
}
|
||||
}
|
||||
+1
-2
@@ -91,9 +91,8 @@ public final class EcosystemNarrowRules {
|
||||
public static final String[] LWJGL3IFY_EXCLUSIONS_SHARED = {
|
||||
"org.embeddedt.embeddium",
|
||||
"org.taumc.celeritas",
|
||||
"com.mitchej123.lwjgl.lwjgl3",
|
||||
"com.mitchej123.glsm",
|
||||
"com.gtnewhorizons.angelica.lwjgl3",
|
||||
"com.gtnewhorizons.angelica.sdlgpu",
|
||||
};
|
||||
|
||||
public static final String[] EARLY_REDIRECTOR_TARGETS = {
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.gtnewhorizons.angelica.glsm.loading;
|
||||
|
||||
import com.gtnewhorizons.retrofuturabootstrap.SharedConfig;
|
||||
|
||||
public final class Lwjgl3ifyExclusions {
|
||||
|
||||
public static void apply() {
|
||||
final var handle = SharedConfig.getRfbTransformers().stream()
|
||||
.filter(transformer -> transformer.id().equals("lwjgl3ify:redirect"))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
if (handle == null) return;
|
||||
for (String exclusion : EcosystemNarrowRules.LWJGL3IFY_EXCLUSIONS_SHARED) {
|
||||
handle.exclusions().add(exclusion);
|
||||
}
|
||||
}
|
||||
|
||||
private Lwjgl3ifyExclusions() {}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package com.gtnewhorizons.angelica.glsm.loading;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.redirect.GLSMRedirector;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class ModRedirector {
|
||||
|
||||
private final GLSMRedirector core = new GLSMRedirector();
|
||||
private final ClassDump dump;
|
||||
private final String[] targets;
|
||||
private final String[] allExclusions;
|
||||
|
||||
public ModRedirector(ClassDump dump, String... extraExclusions) {
|
||||
this(dump, (String[]) null, extraExclusions);
|
||||
}
|
||||
|
||||
public ModRedirector(ClassDump dump, String[] targets, String[] extraExclusions) {
|
||||
this.dump = dump;
|
||||
this.targets = targets;
|
||||
final List<String> exclusions = new ArrayList<>(Arrays.asList(core.getCoreExclusions()));
|
||||
exclusions.add("com.gtnewhorizons.angelica.lwjgl3.");
|
||||
Collections.addAll(exclusions, extraExclusions);
|
||||
this.allExclusions = exclusions.toArray(new String[0]);
|
||||
}
|
||||
|
||||
public ClassDump getDump() {
|
||||
return dump;
|
||||
}
|
||||
|
||||
public String[] getTransformerExclusions() {
|
||||
return allExclusions;
|
||||
}
|
||||
|
||||
public boolean shouldTransform(byte[] basicClass) {
|
||||
return core.shouldTransform(basicClass);
|
||||
}
|
||||
|
||||
public boolean transformClassNode(String transformedName, ClassNode cn, boolean lwjgl3Aware) {
|
||||
return core.transformClassNode(transformedName, cn, lwjgl3Aware);
|
||||
}
|
||||
|
||||
public byte[] transform(String transformedName, byte[] basicClass, Object transformer) {
|
||||
if (basicClass == null) return null;
|
||||
|
||||
if (targets != null && !startsWithAny(transformedName, targets)) return basicClass;
|
||||
if (startsWithAny(transformedName, allExclusions)) return basicClass;
|
||||
|
||||
if (!core.shouldTransform(basicClass)) {
|
||||
return basicClass;
|
||||
}
|
||||
|
||||
final ClassReader cr = new ClassReader(basicClass);
|
||||
final ClassNode cn = new ClassNode();
|
||||
cr.accept(cn, 0);
|
||||
if (!core.transformClassNode(transformedName, cn)) {
|
||||
return basicClass;
|
||||
}
|
||||
final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
|
||||
cn.accept(cw);
|
||||
final byte[] bytes = cw.toByteArray();
|
||||
dump.dumpClass(transformedName, basicClass, bytes, transformer);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
private static boolean startsWithAny(String name, String[] prefixes) {
|
||||
for (String prefix : prefixes) {
|
||||
if (name.startsWith(prefix)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package com.gtnewhorizons.angelica.glsm.loading;
|
||||
|
||||
import com.gtnewhorizons.retrofuturabootstrap.api.ClassNodeHandle;
|
||||
import com.gtnewhorizons.retrofuturabootstrap.api.ExtensibleClassLoader;
|
||||
import com.gtnewhorizons.retrofuturabootstrap.api.RfbClassTransformer;
|
||||
import org.intellij.lang.annotations.Pattern;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.jar.Attributes;
|
||||
import java.util.jar.Manifest;
|
||||
|
||||
public abstract class RfbModRedirector implements RfbClassTransformer {
|
||||
|
||||
private static final Attributes.Name LWJGL3_AWARE = new Attributes.Name("Lwjgl3ify-Aware");
|
||||
|
||||
private final String id;
|
||||
private final ModRedirector inner;
|
||||
|
||||
protected RfbModRedirector(@Pattern("[a-z0-9-]+") String id, ModRedirector inner) {
|
||||
this.id = id;
|
||||
this.inner = inner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String id() {
|
||||
return id;
|
||||
}
|
||||
|
||||
private static boolean isLwjgl3Aware(@Nullable Manifest manifest) {
|
||||
return manifest != null && "true".equals(manifest.getMainAttributes().getValue(LWJGL3_AWARE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String @Nullable [] sortAfter() {
|
||||
return new String[] {"*", "mixin:mixin"};
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String @Nullable [] sortBefore() {
|
||||
return new String[] {"lwjgl3ify:redirect"};
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String @Nullable [] additionalExclusions() {
|
||||
return inner.getTransformerExclusions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldTransformClass(@NotNull ExtensibleClassLoader classLoader,
|
||||
@NotNull RfbClassTransformer.Context context, @Nullable Manifest manifest, @NotNull String className,
|
||||
@NotNull ClassNodeHandle classNode) {
|
||||
if (!classNode.isPresent()) {
|
||||
return false;
|
||||
}
|
||||
return inner.shouldTransform(classNode.getOriginalBytes());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean transformClassIfNeeded(@NotNull ExtensibleClassLoader classLoader,
|
||||
@NotNull RfbClassTransformer.Context context, @Nullable Manifest manifest, @NotNull String className,
|
||||
@NotNull ClassNodeHandle classNode) {
|
||||
final boolean changed = inner.transformClassNode(className, classNode.getNode(), isLwjgl3Aware(manifest));
|
||||
if (changed) {
|
||||
classNode.computeMaxs();
|
||||
inner.getDump().dumpRFBClass(className, classNode, this);
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transformClass(@NotNull ExtensibleClassLoader classLoader, @NotNull RfbClassTransformer.Context context,
|
||||
@Nullable Manifest manifest, @NotNull String className, @NotNull ClassNodeHandle classNode) {
|
||||
transformClassIfNeeded(classLoader, context, manifest, className, classNode);
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -164,6 +164,8 @@ public class GLSMRedirector {
|
||||
private static final String EXTGPUShader4 = "org/lwjgl/opengl/EXTGPUShader4";
|
||||
private static final String ARBTimerQuery = "org/lwjgl/opengl/ARBTimerQuery";
|
||||
private static final String EXTTimerQuery = "org/lwjgl/opengl/EXTTimerQuery";
|
||||
private static final String ARBSamplerObjects = "org/lwjgl/opengl/ARBSamplerObjects";
|
||||
private static final String ARBMultiBind = "org/lwjgl/opengl/ARBMultiBind";
|
||||
|
||||
// Redirect VAO related calls from NHLib
|
||||
private static final String UniversalVAO = "com/gtnewhorizon/gtnhlib/client/opengl/UniversalVAO";
|
||||
@@ -517,7 +519,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")
|
||||
@@ -554,6 +557,7 @@ public class GLSMRedirector {
|
||||
.add("glMultiDrawElementsIndirect");
|
||||
final var gl44 = RedirectMap.newMap()
|
||||
.add("glBufferStorage")
|
||||
.add("glBindSamplers")
|
||||
.add("glClearTexImage");
|
||||
final var gl45 = RedirectMap.newMap()
|
||||
.add("glCreateBuffers")
|
||||
@@ -722,6 +726,7 @@ public class GLSMRedirector {
|
||||
);
|
||||
final var uniformBufferObject = RedirectMap.newMap()
|
||||
.add("glBindBufferBase")
|
||||
.add("glBindBufferRange")
|
||||
.add("glGetUniformBlockIndex")
|
||||
.add("glUniformBlockBinding");
|
||||
methodRedirects.put(ARBUniformBufferObject, uniformBufferObject);
|
||||
@@ -765,6 +770,17 @@ public class GLSMRedirector {
|
||||
.add("glMapBufferRange")
|
||||
.add("glFlushMappedBufferRange")
|
||||
);
|
||||
methodRedirects.put(ARBSamplerObjects, RedirectMap.newMap()
|
||||
.add("glGenSamplers")
|
||||
.add("glDeleteSamplers")
|
||||
.add("glIsSampler")
|
||||
.add("glBindSampler")
|
||||
.add("glSamplerParameteri")
|
||||
.add("glSamplerParameterf")
|
||||
);
|
||||
methodRedirects.put(ARBMultiBind, RedirectMap.newMap()
|
||||
.add("glBindSamplers")
|
||||
);
|
||||
|
||||
// APPLE
|
||||
methodRedirects.put(APPLEVertexArrayObject, RedirectMap.newMap()
|
||||
|
||||
@@ -12,6 +12,8 @@ public class BlendStateStack extends BlendState implements IStateStack<BlendStat
|
||||
|
||||
protected int pointer;
|
||||
|
||||
private boolean funcUnknown;
|
||||
|
||||
@Setter private VanillaStateLayer<BlendState> vanillaLayer;
|
||||
|
||||
public BlendStateStack() {
|
||||
@@ -44,6 +46,18 @@ public class BlendStateStack extends BlendState implements IStateStack<BlendStat
|
||||
return this;
|
||||
}
|
||||
|
||||
public void setFuncUnknownState() {
|
||||
funcUnknown = true;
|
||||
}
|
||||
|
||||
public boolean isFuncUnknown() {
|
||||
return funcUnknown;
|
||||
}
|
||||
|
||||
public void clearFuncUnknownState() {
|
||||
funcUnknown = false;
|
||||
}
|
||||
|
||||
public BlendState readEffective(BlendState out) {
|
||||
out.set(this);
|
||||
VanillaStateLayer.capture(vanillaLayer, out);
|
||||
|
||||
+1
@@ -44,6 +44,7 @@ public class TextureUnitBooleanStateStack extends BooleanStateStack {
|
||||
|
||||
if (GLSMHooks.TEXTURE_UNIT_STATE.hasListeners()) {
|
||||
GLSMHooks.textureUnitStateEvent.unit = unitIndex;
|
||||
GLSMHooks.textureUnitStateEvent.cap = glCap;
|
||||
GLSMHooks.textureUnitStateEvent.enabled = enabled;
|
||||
GLSMHooks.TEXTURE_UNIT_STATE.post(GLSMHooks.textureUnitStateEvent);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.gtnewhorizons.angelica.glsm.states;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.RenderSystem;
|
||||
|
||||
public class SamplerUnitArray {
|
||||
|
||||
private static final int MIN_UNITS = 16;
|
||||
|
||||
private int[] samplers;
|
||||
|
||||
public int get(int unit) {
|
||||
final int[] local = samplers;
|
||||
return local != null && unit >= 0 && unit < local.length ? local[unit] : 0;
|
||||
}
|
||||
|
||||
public boolean set(int unit, int sampler) {
|
||||
if (unit < 0) return false;
|
||||
int[] local = samplers;
|
||||
if (local == null) {
|
||||
local = new int[Math.max(MIN_UNITS, RenderSystem.getMaxCombinedTextureImageUnits())];
|
||||
samplers = local;
|
||||
}
|
||||
if (unit >= local.length) return false;
|
||||
local[unit] = sampler;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void unbindEverywhere(int sampler) {
|
||||
final int[] local = samplers;
|
||||
if (local == null) return;
|
||||
for (int i = 0; i < local.length; i++) {
|
||||
if (local[i] == sampler) local[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public int size() {
|
||||
final int[] local = samplers;
|
||||
return local == null ? 0 : local.length;
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,8 @@ package com.gtnewhorizons.angelica.glsm.streaming;
|
||||
import com.gtnewhorizons.angelica.config.SystemProperties;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.RenderSystem;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayFIFOQueue;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayFIFOQueue;
|
||||
import it.unimi.dsi.fastutil.longs.LongArrayFIFOQueue;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
import org.lwjgl.opengl.GL32;
|
||||
import org.lwjgl.opengl.GL44;
|
||||
@@ -11,7 +12,6 @@ import org.lwjgl.opengl.GL44;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAddress0;
|
||||
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
|
||||
|
||||
/**
|
||||
* Persistent-mapped fence-reclaimed ring over any buffer target. Callers reserve a range and
|
||||
@@ -28,7 +28,8 @@ final class GlStreamingRing {
|
||||
private static final long SYNC_WAIT_SLICE_NANOS = 10_000_000L;
|
||||
private static final long SYNC_WAIT_TOTAL_NANOS = 5_000_000_000L;
|
||||
|
||||
private final ObjectArrayFIFOQueue<FencedRegion> fenceQueue = new ObjectArrayFIFOQueue<>();
|
||||
private final LongArrayFIFOQueue fenceIds = new LongArrayFIFOQueue();
|
||||
private final IntArrayFIFOQueue fenceBytes = new IntArrayFIFOQueue();
|
||||
private int writePos;
|
||||
private int remaining;
|
||||
private int pendingBytes;
|
||||
@@ -51,13 +52,13 @@ final class GlStreamingRing {
|
||||
|
||||
static GlStreamingRing create(int target, int capacity, boolean forceOrphan) {
|
||||
if (forceOrphan || !RenderSystem.supportsBufferStorage()) return null;
|
||||
final int id = RENDER_BACKEND.genBuffers();
|
||||
RENDER_BACKEND.bindBuffer(target, id);
|
||||
final int id = GLStateManager.glGenBuffers();
|
||||
GLStateManager.glBindBuffer(target, id);
|
||||
final int storageFlags = GL44.GL_MAP_PERSISTENT_BIT | GL30.GL_MAP_WRITE_BIT | GL44.GL_MAP_COHERENT_BIT | GL44.GL_CLIENT_STORAGE_BIT;
|
||||
RenderSystem.bufferStorage(target, capacity, storageFlags);
|
||||
final int mapFlags = GL44.GL_MAP_PERSISTENT_BIT | GL30.GL_MAP_WRITE_BIT | GL44.GL_MAP_COHERENT_BIT;
|
||||
final ByteBuffer mapped = RENDER_BACKEND.mapBufferRange(target, 0, capacity, mapFlags);
|
||||
RENDER_BACKEND.bindBuffer(target, 0);
|
||||
final ByteBuffer mapped = GLStateManager.glMapBufferRange(target, 0, capacity, mapFlags);
|
||||
GLStateManager.glBindBuffer(target, 0);
|
||||
if (mapped == null) {
|
||||
GLStateManager.glDeleteBuffers(id);
|
||||
GLStateManager.LOGGER.warn("Persistent map failed for streaming ring (target=0x{}, {} bytes)", Integer.toHexString(target), capacity);
|
||||
@@ -92,7 +93,7 @@ final class GlStreamingRing {
|
||||
|
||||
private boolean ensureRemaining(int needed) {
|
||||
while (remaining < needed) {
|
||||
if (fenceQueue.isEmpty()) {
|
||||
if (fenceIds.isEmpty()) {
|
||||
if (pendingBytes == 0) return false;
|
||||
fence();
|
||||
}
|
||||
@@ -103,7 +104,8 @@ final class GlStreamingRing {
|
||||
|
||||
private void fence() {
|
||||
if (pendingBytes > 0) {
|
||||
fenceQueue.enqueue(new FencedRegion(RENDER_BACKEND.fenceSync(GL32.GL_SYNC_GPU_COMMANDS_COMPLETE, 0), pendingBytes));
|
||||
fenceIds.enqueue(GLStateManager.glFenceSync(GL32.GL_SYNC_GPU_COMMANDS_COMPLETE, 0));
|
||||
fenceBytes.enqueue(pendingBytes);
|
||||
pendingBytes = 0;
|
||||
fencesIssued++;
|
||||
}
|
||||
@@ -114,30 +116,32 @@ final class GlStreamingRing {
|
||||
}
|
||||
|
||||
private void reclaim() {
|
||||
while (!fenceQueue.isEmpty()) {
|
||||
final FencedRegion region = fenceQueue.first();
|
||||
if (!signaled(RENDER_BACKEND.clientWaitSync(region.fenceId, 0, 0L))) break;
|
||||
GLStateManager.glDeleteSync(region.fenceId);
|
||||
fenceQueue.dequeue();
|
||||
remaining += region.bytes;
|
||||
while (!fenceIds.isEmpty()) {
|
||||
final long fenceId = fenceIds.firstLong();
|
||||
if (!signaled(GLStateManager.glClientWaitSync(fenceId, 0, 0L))) break;
|
||||
GLStateManager.glDeleteSync(fenceId);
|
||||
fenceIds.dequeueLong();
|
||||
remaining += fenceBytes.dequeueInt();
|
||||
}
|
||||
}
|
||||
|
||||
private void syncOldest() {
|
||||
final FencedRegion region = fenceQueue.dequeue();
|
||||
final long fenceId = fenceIds.dequeueLong();
|
||||
final int bytes = fenceBytes.dequeueInt();
|
||||
forcedReclaims++;
|
||||
final long deadline = System.nanoTime() + SYNC_WAIT_TOTAL_NANOS;
|
||||
long remainingNanos = SYNC_WAIT_TOTAL_NANOS;
|
||||
int status;
|
||||
do {
|
||||
status = RENDER_BACKEND.clientWaitSync(region.fenceId, GL32.GL_SYNC_FLUSH_COMMANDS_BIT, Math.min(SYNC_WAIT_SLICE_NANOS, remainingNanos));
|
||||
status = GLStateManager.glClientWaitSync(fenceId, GL32.GL_SYNC_FLUSH_COMMANDS_BIT,
|
||||
Math.min(SYNC_WAIT_SLICE_NANOS, remainingNanos));
|
||||
remainingNanos = deadline - System.nanoTime();
|
||||
} while (status == GL32.GL_TIMEOUT_EXPIRED && remainingNanos > 0);
|
||||
if (!signaled(status)) {
|
||||
GLStateManager.LOGGER.warn("Streaming ring fence wait did not signal (status=0x{}); reclaiming anyway", Integer.toHexString(status));
|
||||
}
|
||||
GLStateManager.glDeleteSync(region.fenceId);
|
||||
remaining += region.bytes;
|
||||
GLStateManager.glDeleteSync(fenceId);
|
||||
remaining += bytes;
|
||||
reclaim();
|
||||
}
|
||||
|
||||
@@ -156,17 +160,16 @@ final class GlStreamingRing {
|
||||
int forcedReclaims() { return forcedReclaims; }
|
||||
|
||||
void destroy() {
|
||||
while (!fenceQueue.isEmpty()) {
|
||||
GLStateManager.glDeleteSync(fenceQueue.dequeue().fenceId);
|
||||
while (!fenceIds.isEmpty()) {
|
||||
GLStateManager.glDeleteSync(fenceIds.dequeueLong());
|
||||
}
|
||||
fenceBytes.clear();
|
||||
if (bufferId != 0) {
|
||||
RENDER_BACKEND.bindBuffer(target, bufferId);
|
||||
RENDER_BACKEND.unmapBuffer(target);
|
||||
RENDER_BACKEND.bindBuffer(target, 0);
|
||||
GLStateManager.glBindBuffer(target, bufferId);
|
||||
GLStateManager.glUnmapBuffer(target);
|
||||
GLStateManager.glBindBuffer(target, 0);
|
||||
GLStateManager.glDeleteBuffers(bufferId);
|
||||
bufferId = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private record FencedRegion(long fenceId, int bytes) {}
|
||||
}
|
||||
+5
-7
@@ -5,8 +5,6 @@ import org.lwjgl.opengl.GL15;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
|
||||
|
||||
/**
|
||||
* Streaming buffer using the classic orphan pattern.
|
||||
*/
|
||||
@@ -16,19 +14,19 @@ public final class OrphanStreamingBuffer {
|
||||
private int capacity;
|
||||
|
||||
public OrphanStreamingBuffer() {
|
||||
this.bufferId = RENDER_BACKEND.genBuffers();
|
||||
this.bufferId = GLStateManager.glGenBuffers();
|
||||
}
|
||||
|
||||
public void upload(ByteBuffer data) {
|
||||
RENDER_BACKEND.bindBuffer(GL15.GL_ARRAY_BUFFER, bufferId);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, bufferId);
|
||||
capacity = StreamingUploader.upload(data, capacity);
|
||||
RENDER_BACKEND.bindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
}
|
||||
|
||||
void upload(StreamingUploader.UploadStrategy strategy, ByteBuffer data) {
|
||||
RENDER_BACKEND.bindBuffer(GL15.GL_ARRAY_BUFFER, bufferId);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, bufferId);
|
||||
capacity = StreamingUploader.upload(strategy, data, capacity);
|
||||
RENDER_BACKEND.bindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
}
|
||||
|
||||
public int getCapacity() {
|
||||
|
||||
@@ -7,7 +7,6 @@ import org.lwjgl.opengl.GL30;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAddress0;
|
||||
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memCopy;
|
||||
|
||||
|
||||
@@ -28,30 +27,30 @@ public final class StreamingUploader {
|
||||
public static int upload(UploadStrategy strategy, ByteBuffer data, int capacity) {
|
||||
switch (strategy) {
|
||||
case BUFFER_DATA -> {
|
||||
RENDER_BACKEND.bufferData(GL15.GL_ARRAY_BUFFER, data, GL15.GL_STREAM_DRAW);
|
||||
GLStateManager.glBufferData(GL15.GL_ARRAY_BUFFER, data, GL15.GL_STREAM_DRAW);
|
||||
return data.remaining();
|
||||
}
|
||||
case BUFFER_SUB_DATA -> {
|
||||
if (data.remaining() > capacity) {
|
||||
RENDER_BACKEND.bufferData(GL15.GL_ARRAY_BUFFER, data, GL15.GL_STREAM_DRAW);
|
||||
GLStateManager.glBufferData(GL15.GL_ARRAY_BUFFER, data, GL15.GL_STREAM_DRAW);
|
||||
return data.remaining();
|
||||
}
|
||||
RENDER_BACKEND.bufferSubData(GL15.GL_ARRAY_BUFFER, 0, data);
|
||||
GLStateManager.glBufferSubData(GL15.GL_ARRAY_BUFFER, 0, data);
|
||||
return capacity;
|
||||
}
|
||||
case MAP_BUFFER_RANGE -> {
|
||||
if (data.remaining() > capacity) {
|
||||
RENDER_BACKEND.bufferData(GL15.GL_ARRAY_BUFFER, data, GL15.GL_STREAM_DRAW);
|
||||
GLStateManager.glBufferData(GL15.GL_ARRAY_BUFFER, data, GL15.GL_STREAM_DRAW);
|
||||
return data.remaining();
|
||||
}
|
||||
final int dataSize = data.remaining();
|
||||
final long dst = RENDER_BACKEND.mapBufferRangeAddress(GL15.GL_ARRAY_BUFFER, 0, dataSize, MAP_WRITE_INVALIDATE_BUFFER);
|
||||
final long dst = GLStateManager.glMapBufferRangeAddress(GL15.GL_ARRAY_BUFFER, 0, dataSize, MAP_WRITE_INVALIDATE_BUFFER);
|
||||
if (dst == 0L) {
|
||||
RENDER_BACKEND.bufferData(GL15.GL_ARRAY_BUFFER, data, GL15.GL_STREAM_DRAW);
|
||||
GLStateManager.glBufferData(GL15.GL_ARRAY_BUFFER, data, GL15.GL_STREAM_DRAW);
|
||||
return data.remaining();
|
||||
}
|
||||
memCopy(memAddress0(data), dst, dataSize);
|
||||
RENDER_BACKEND.unmapBuffer(GL15.GL_ARRAY_BUFFER);
|
||||
GLStateManager.glUnmapBuffer(GL15.GL_ARRAY_BUFFER);
|
||||
return capacity;
|
||||
}
|
||||
default -> throw new UnsupportedOperationException();
|
||||
|
||||
+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;
|
||||
|
||||
+9
-10
@@ -10,7 +10,6 @@ import java.nio.ByteBuffer;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAddress0;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memCopy;
|
||||
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
|
||||
|
||||
public final class UniformRingBuffer {
|
||||
|
||||
@@ -35,10 +34,10 @@ public final class UniformRingBuffer {
|
||||
this.stride = (blockSize + OFFSET_ALIGNMENT - 1) & -OFFSET_ALIGNMENT;
|
||||
this.ring = GlStreamingRing.create(GL31.GL_UNIFORM_BUFFER, capacity, forceOrphan);
|
||||
if (ring == null) {
|
||||
orphanBufferId = RENDER_BACKEND.genBuffers();
|
||||
RENDER_BACKEND.bindBuffer(GL31.GL_UNIFORM_BUFFER, orphanBufferId);
|
||||
RENDER_BACKEND.bufferData(GL31.GL_UNIFORM_BUFFER, capacity, GL15.GL_STREAM_DRAW);
|
||||
RENDER_BACKEND.bindBuffer(GL31.GL_UNIFORM_BUFFER, 0);
|
||||
orphanBufferId = GLStateManager.glGenBuffers();
|
||||
GLStateManager.glBindBuffer(GL31.GL_UNIFORM_BUFFER, orphanBufferId);
|
||||
GLStateManager.glBufferData(GL31.GL_UNIFORM_BUFFER, capacity, GL15.GL_STREAM_DRAW);
|
||||
GLStateManager.glBindBuffer(GL31.GL_UNIFORM_BUFFER, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,20 +55,20 @@ public final class UniformRingBuffer {
|
||||
}
|
||||
|
||||
private int writeOrphan(long srcAddress) {
|
||||
RENDER_BACKEND.bindBuffer(GL31.GL_UNIFORM_BUFFER, orphanBufferId);
|
||||
GLStateManager.glBindBuffer(GL31.GL_UNIFORM_BUFFER, orphanBufferId);
|
||||
int offset = writePos;
|
||||
if (offset + stride > capacity) {
|
||||
RENDER_BACKEND.bufferData(GL31.GL_UNIFORM_BUFFER, capacity, GL15.GL_STREAM_DRAW);
|
||||
GLStateManager.glBufferData(GL31.GL_UNIFORM_BUFFER, capacity, GL15.GL_STREAM_DRAW);
|
||||
offset = 0;
|
||||
orphanWraps++;
|
||||
}
|
||||
final long dst = RENDER_BACKEND.mapBufferRangeAddress(GL31.GL_UNIFORM_BUFFER, offset, blockSize, GL30.GL_MAP_WRITE_BIT | GL30.GL_MAP_INVALIDATE_RANGE_BIT | GL30.GL_MAP_UNSYNCHRONIZED_BIT);
|
||||
final long dst = GLStateManager.glMapBufferRangeAddress(GL31.GL_UNIFORM_BUFFER, offset, blockSize, GL30.GL_MAP_WRITE_BIT | GL30.GL_MAP_INVALIDATE_RANGE_BIT | GL30.GL_MAP_UNSYNCHRONIZED_BIT);
|
||||
if (dst == 0L) {
|
||||
throw new IllegalStateException("mapBufferRange failed on uniform ring (offset=" + offset + ")");
|
||||
}
|
||||
memCopy(srcAddress, dst, blockSize);
|
||||
RENDER_BACKEND.unmapBuffer(GL31.GL_UNIFORM_BUFFER);
|
||||
RENDER_BACKEND.bindBuffer(GL31.GL_UNIFORM_BUFFER, 0);
|
||||
GLStateManager.glUnmapBuffer(GL31.GL_UNIFORM_BUFFER);
|
||||
GLStateManager.glBindBuffer(GL31.GL_UNIFORM_BUFFER, 0);
|
||||
writePos = offset + stride;
|
||||
return offset;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,9 @@ import org.lwjgl.opengl.GL12;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
import org.lwjgl.opengl.GL21;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
import org.lwjgl.opengl.GL31;
|
||||
import org.lwjgl.opengl.GL32;
|
||||
import org.lwjgl.opengl.GL40;
|
||||
|
||||
import java.nio.Buffer;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
@@ -101,6 +104,22 @@ public class TextureInfoCache {
|
||||
};
|
||||
}
|
||||
|
||||
public static boolean isProxyTarget(int target) {
|
||||
return switch (target) {
|
||||
case GL11.GL_PROXY_TEXTURE_1D,
|
||||
GL11.GL_PROXY_TEXTURE_2D,
|
||||
GL12.GL_PROXY_TEXTURE_3D,
|
||||
GL13.GL_PROXY_TEXTURE_CUBE_MAP,
|
||||
GL30.GL_PROXY_TEXTURE_1D_ARRAY,
|
||||
GL30.GL_PROXY_TEXTURE_2D_ARRAY,
|
||||
GL31.GL_PROXY_TEXTURE_RECTANGLE,
|
||||
GL32.GL_PROXY_TEXTURE_2D_MULTISAMPLE,
|
||||
GL32.GL_PROXY_TEXTURE_2D_MULTISAMPLE_ARRAY,
|
||||
GL40.GL_PROXY_TEXTURE_CUBE_MAP_ARRAY -> true;
|
||||
default -> false;
|
||||
};
|
||||
}
|
||||
|
||||
private static boolean isTrackedTarget(int target) {
|
||||
return target == GL11.GL_TEXTURE_2D
|
||||
|| target == GL11.GL_TEXTURE_1D
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.gtnewhorizons.angelica.glsm.texture;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
import org.lwjgl.opengl.GL31;
|
||||
import org.lwjgl.opengl.GL32;
|
||||
import org.lwjgl.opengl.GL40;
|
||||
|
||||
public final class TextureTargets {
|
||||
|
||||
private TextureTargets() {}
|
||||
|
||||
public static boolean isProxyTarget(int target) {
|
||||
return switch (target) {
|
||||
case GL11.GL_PROXY_TEXTURE_1D,
|
||||
GL11.GL_PROXY_TEXTURE_2D,
|
||||
GL12.GL_PROXY_TEXTURE_3D,
|
||||
GL13.GL_PROXY_TEXTURE_CUBE_MAP,
|
||||
GL30.GL_PROXY_TEXTURE_1D_ARRAY,
|
||||
GL30.GL_PROXY_TEXTURE_2D_ARRAY,
|
||||
GL31.GL_PROXY_TEXTURE_RECTANGLE,
|
||||
GL32.GL_PROXY_TEXTURE_2D_MULTISAMPLE,
|
||||
GL32.GL_PROXY_TEXTURE_2D_MULTISAMPLE_ARRAY,
|
||||
GL40.GL_PROXY_TEXTURE_CUBE_MAP_ARRAY -> true;
|
||||
default -> false;
|
||||
};
|
||||
}
|
||||
}
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL14;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assumptions.assumeTrue;
|
||||
|
||||
@GLCoreTest
|
||||
class GLSM_BlendFuncUnknownState_GLTest {
|
||||
|
||||
private static final int VANILLA_SRC_RGB = GL11.GL_SRC_ALPHA;
|
||||
private static final int VANILLA_DST_RGB = GL11.GL_ONE_MINUS_SRC_ALPHA;
|
||||
private static final int VANILLA_SRC_ALPHA = GL11.GL_ONE;
|
||||
private static final int VANILLA_DST_ALPHA = GL11.GL_ZERO;
|
||||
|
||||
@BeforeEach
|
||||
void requireBufferBlending() {
|
||||
assumeTrue(RenderSystem.supportsBufferBlending());
|
||||
drainGlErrors();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void restoreDefaultBlendFunc() {
|
||||
if (!RenderSystem.supportsBufferBlending()) return;
|
||||
GLStateManager.tryBlendFuncSeparate(GL11.GL_ZERO, GL11.GL_ZERO, GL11.GL_ZERO, GL11.GL_ZERO);
|
||||
GLStateManager.tryBlendFuncSeparate(GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
drainGlErrors();
|
||||
}
|
||||
|
||||
private static void drainGlErrors() {
|
||||
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
|
||||
}
|
||||
|
||||
private static void seedVanillaSeparate() {
|
||||
GLStateManager.tryBlendFuncSeparate(GL11.GL_ONE, GL11.GL_ONE, GL11.GL_ONE, GL11.GL_ONE);
|
||||
GLStateManager.tryBlendFuncSeparate(VANILLA_SRC_RGB, VANILLA_DST_RGB, VANILLA_SRC_ALPHA, VANILLA_DST_ALPHA);
|
||||
}
|
||||
|
||||
private static void overrideBufferZero() {
|
||||
RenderSystem.blendFuncSeparatei(0, GL11.GL_ONE, GL11.GL_ONE, GL11.GL_ONE, GL11.GL_ONE);
|
||||
assertBufferBlend(0, GL11.GL_ONE, GL11.GL_ONE, GL11.GL_ONE, GL11.GL_ONE);
|
||||
}
|
||||
|
||||
private static void assertBufferBlend(int buffer, int srcRgb, int dstRgb, int srcAlpha, int dstAlpha) {
|
||||
assertEquals(srcRgb, GL30.glGetInteger(GL14.GL_BLEND_SRC_RGB, buffer), "buffer " + buffer + " src rgb");
|
||||
assertEquals(dstRgb, GL30.glGetInteger(GL14.GL_BLEND_DST_RGB, buffer), "buffer " + buffer + " dst rgb");
|
||||
assertEquals(srcAlpha, GL30.glGetInteger(GL14.GL_BLEND_SRC_ALPHA, buffer), "buffer " + buffer + " src alpha");
|
||||
assertEquals(dstAlpha, GL30.glGetInteger(GL14.GL_BLEND_DST_ALPHA, buffer), "buffer " + buffer + " dst alpha");
|
||||
}
|
||||
|
||||
private static void assertVanillaOnBothBuffers() {
|
||||
for (int buffer = 0; buffer < 2; buffer++) {
|
||||
assertBufferBlend(buffer, VANILLA_SRC_RGB, VANILLA_DST_RGB, VANILLA_SRC_ALPHA, VANILLA_DST_ALPHA);
|
||||
}
|
||||
assertFalse(GLStateManager.getBlendState().isFuncUnknown(), "forward must clear the unknown flag");
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
|
||||
}
|
||||
|
||||
@Test
|
||||
void tryBlendFuncSeparateForwardsAfterIndexedOverride() {
|
||||
seedVanillaSeparate();
|
||||
overrideBufferZero();
|
||||
|
||||
GLStateManager.tryBlendFuncSeparate(VANILLA_SRC_RGB, VANILLA_DST_RGB, VANILLA_SRC_ALPHA, VANILLA_DST_ALPHA);
|
||||
|
||||
assertVanillaOnBothBuffers();
|
||||
}
|
||||
|
||||
@Test
|
||||
void glBlendFuncForwardsAfterIndexedOverride() {
|
||||
GLStateManager.glBlendFunc(GL11.GL_ONE, GL11.GL_ONE);
|
||||
GLStateManager.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
overrideBufferZero();
|
||||
|
||||
GLStateManager.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
for (int buffer = 0; buffer < 2; buffer++) {
|
||||
assertBufferBlend(buffer, GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
}
|
||||
assertFalse(GLStateManager.getBlendState().isFuncUnknown(), "forward must clear the unknown flag");
|
||||
}
|
||||
|
||||
@Test
|
||||
void popAttribForwardsAfterIndexedOverride() {
|
||||
seedVanillaSeparate();
|
||||
|
||||
GLStateManager.glPushAttrib(GL11.GL_COLOR_BUFFER_BIT);
|
||||
try {
|
||||
overrideBufferZero();
|
||||
} finally {
|
||||
GLStateManager.glPopAttrib();
|
||||
}
|
||||
|
||||
assertVanillaOnBothBuffers();
|
||||
}
|
||||
|
||||
@Test
|
||||
void displayListReplayForwardsAfterIndexedOverride() {
|
||||
final int list = GLStateManager.glGenLists(1);
|
||||
try {
|
||||
GLStateManager.glNewList(list, GL11.GL_COMPILE);
|
||||
GLStateManager.tryBlendFuncSeparate(VANILLA_SRC_RGB, VANILLA_DST_RGB, VANILLA_SRC_ALPHA, VANILLA_DST_ALPHA);
|
||||
GLStateManager.glEndList();
|
||||
|
||||
seedVanillaSeparate();
|
||||
overrideBufferZero();
|
||||
|
||||
GLStateManager.glCallList(list);
|
||||
|
||||
assertVanillaOnBothBuffers();
|
||||
} finally {
|
||||
GLStateManager.glDeleteLists(list, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,235 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
import org.lwjgl.opengl.GL33;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.IntBuffer;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assumptions.assumeTrue;
|
||||
|
||||
@GLCoreTest
|
||||
class GLSM_SamplerBinding_GLTest {
|
||||
|
||||
private static final int UNITS = 4;
|
||||
|
||||
@BeforeEach
|
||||
void requireSamplerObjects() {
|
||||
assumeTrue(RenderSystem.supportsSamplerObjects());
|
||||
drainGlErrors();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void unbindTestUnits() {
|
||||
if (!RenderSystem.supportsSamplerObjects()) return;
|
||||
for (int unit = 0; unit < UNITS; unit++) {
|
||||
GLStateManager.glBindSampler(unit, 0);
|
||||
GL33.glBindSampler(unit, 0);
|
||||
}
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
drainGlErrors();
|
||||
}
|
||||
|
||||
private static void drainGlErrors() {
|
||||
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
|
||||
}
|
||||
|
||||
private static int driverBinding(int unit) {
|
||||
final int saved = GL11.glGetInteger(GL13.GL_ACTIVE_TEXTURE);
|
||||
GL13.glActiveTexture(GL13.GL_TEXTURE0 + unit);
|
||||
final int binding = GL11.glGetInteger(GL33.GL_SAMPLER_BINDING);
|
||||
GL13.glActiveTexture(saved);
|
||||
return binding;
|
||||
}
|
||||
|
||||
private static void assertUnits(String context, int... expected) {
|
||||
for (int unit = 0; unit < expected.length; unit++) {
|
||||
assertEquals(expected[unit], GLStateManager.getSamplerBinding(unit), context + ": cache unit " + unit);
|
||||
assertEquals(expected[unit], driverBinding(unit), context + ": driver unit " + unit);
|
||||
}
|
||||
}
|
||||
|
||||
private static IntBuffer ints(int... values) {
|
||||
final IntBuffer buf = ByteBuffer.allocateDirect(values.length * 4).order(ByteOrder.nativeOrder()).asIntBuffer();
|
||||
buf.put(values).flip();
|
||||
return buf;
|
||||
}
|
||||
|
||||
@Test
|
||||
void renderSystemRebindAfterGlsmUnbindReachesDriver() {
|
||||
final int sampler = RenderSystem.genSampler();
|
||||
try {
|
||||
RenderSystem.bindSamplerToUnit(0, sampler);
|
||||
GLStateManager.glBindSampler(0, 0);
|
||||
assertEquals(0, driverBinding(0), "setup: GLSM unbind must reach the driver");
|
||||
|
||||
RenderSystem.bindSamplerToUnit(0, sampler);
|
||||
|
||||
assertEquals(sampler, driverBinding(0), "RenderSystem skipped a bind the driver no longer holds");
|
||||
} finally {
|
||||
RenderSystem.destroySampler(sampler);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void deleteClearsEveryUnitAndTheNextBindReachesDriver() {
|
||||
final int sampler = RenderSystem.genSampler();
|
||||
GLStateManager.glBindSampler(1, sampler);
|
||||
GLStateManager.glBindSampler(3, sampler);
|
||||
|
||||
GLStateManager.glDeleteSamplers(sampler);
|
||||
|
||||
assertUnits("after delete", 0, 0, 0, 0);
|
||||
|
||||
final int next = RenderSystem.genSampler();
|
||||
try {
|
||||
GLStateManager.glBindSampler(1, next);
|
||||
assertEquals(next, driverBinding(1), "bind after delete never reached the driver");
|
||||
} finally {
|
||||
RenderSystem.destroySampler(next);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void samplerBindingQueryAnswersForTheActiveUnit() {
|
||||
final int sampler = RenderSystem.genSampler();
|
||||
try {
|
||||
GLStateManager.glBindSampler(1, sampler);
|
||||
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE1);
|
||||
assertEquals(GL11.glGetInteger(GL33.GL_SAMPLER_BINDING),
|
||||
GLStateManager.glGetInteger(GL33.GL_SAMPLER_BINDING));
|
||||
assertEquals(sampler, GLStateManager.glGetInteger(GL33.GL_SAMPLER_BINDING));
|
||||
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
assertEquals(GL11.glGetInteger(GL33.GL_SAMPLER_BINDING),
|
||||
GLStateManager.glGetInteger(GL33.GL_SAMPLER_BINDING));
|
||||
assertEquals(0, GLStateManager.glGetInteger(GL33.GL_SAMPLER_BINDING));
|
||||
} finally {
|
||||
RenderSystem.destroySampler(sampler);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void unbindAllSamplersClearsUnitsBoundThroughGlsm() {
|
||||
final int sampler = RenderSystem.genSampler();
|
||||
try {
|
||||
GLStateManager.glBindSampler(1, sampler);
|
||||
GLStateManager.glBindSampler(3, sampler);
|
||||
|
||||
RenderSystem.unbindAllSamplers();
|
||||
|
||||
assertEquals(0, driverBinding(1), "unit 1 still bound on the driver");
|
||||
assertEquals(0, driverBinding(3), "unit 3 still bound on the driver");
|
||||
assertEquals(0, GLStateManager.getSamplerBinding(1));
|
||||
assertEquals(0, GLStateManager.getSamplerBinding(3));
|
||||
} finally {
|
||||
RenderSystem.destroySampler(sampler);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void replayForwardsCachedSamplerBindings() {
|
||||
final int sampler = RenderSystem.genSampler();
|
||||
try {
|
||||
GLStateManager.glBindSampler(2, sampler);
|
||||
GL33.glBindSampler(2, 0);
|
||||
assertEquals(0, driverBinding(2), "setup failed to diverge the driver");
|
||||
|
||||
GLStateManager.replayStateToBackend();
|
||||
|
||||
assertEquals(sampler, driverBinding(2), "replay dropped a cached sampler binding");
|
||||
} finally {
|
||||
RenderSystem.destroySampler(sampler);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void intBufferGenFillsFromPositionWithBindableSamplers() {
|
||||
final IntBuffer buf = ints(-1, 0, 0);
|
||||
buf.position(1);
|
||||
GLStateManager.glGenSamplers(buf);
|
||||
final int a = buf.get(1);
|
||||
final int b = buf.get(2);
|
||||
try {
|
||||
assertEquals(-1, buf.get(0), "slot before position was overwritten");
|
||||
assertEquals(1, buf.position(), "gen must not move the buffer position");
|
||||
assertNotEquals(0, a);
|
||||
assertNotEquals(0, b);
|
||||
assertNotEquals(a, b);
|
||||
|
||||
GLStateManager.glBindSampler(0, a);
|
||||
GLStateManager.glBindSampler(1, b);
|
||||
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError(), "generated name rejected by the driver");
|
||||
assertEquals(a, driverBinding(0));
|
||||
assertEquals(b, driverBinding(1));
|
||||
} finally {
|
||||
GLStateManager.glDeleteSamplers(a);
|
||||
GLStateManager.glDeleteSamplers(b);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void bindSamplersFormsBindFromPosition() {
|
||||
final int a = RenderSystem.genSampler();
|
||||
final int b = RenderSystem.genSampler();
|
||||
try {
|
||||
final IntBuffer buf = ints(-1, a, b);
|
||||
buf.position(1);
|
||||
|
||||
GLStateManager.glBindSamplers(1, 2, buf);
|
||||
assertEquals(1, buf.position(), "count form must not move the buffer position");
|
||||
assertUnits("count form", 0, a, b, 0);
|
||||
|
||||
GLStateManager.glBindSampler(1, 0);
|
||||
GLStateManager.glBindSampler(2, 0);
|
||||
buf.position(1);
|
||||
|
||||
GLStateManager.glBindSamplers(1, buf);
|
||||
assertEquals(1, buf.position(), "remaining form must not move the buffer position");
|
||||
assertUnits("remaining form", 0, a, b, 0);
|
||||
|
||||
GLStateManager.glBindSamplers(1, (IntBuffer) null);
|
||||
assertUnits("null remaining form", 0, a, b, 0);
|
||||
|
||||
GLStateManager.glBindSamplers(2, new int[]{a, b});
|
||||
assertUnits("int[] form", 0, a, a, b);
|
||||
} finally {
|
||||
RenderSystem.destroySampler(a);
|
||||
RenderSystem.destroySampler(b);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void bindSamplersNullUnbindsRangeAndShortBufferThrows() {
|
||||
final int prior = RenderSystem.genSampler();
|
||||
final int next = RenderSystem.genSampler();
|
||||
try {
|
||||
for (int unit = 0; unit < UNITS; unit++) GLStateManager.glBindSampler(unit, prior);
|
||||
|
||||
GLStateManager.glBindSamplers(1, 2, (IntBuffer) null);
|
||||
|
||||
assertUnits("null count form", prior, 0, 0, prior);
|
||||
|
||||
for (int unit = 0; unit < UNITS; unit++) GLStateManager.glBindSampler(unit, prior);
|
||||
final IntBuffer buf = ints(next, next);
|
||||
|
||||
assertThrows(IllegalArgumentException.class, () -> GLStateManager.glBindSamplers(0, UNITS, buf));
|
||||
|
||||
assertEquals(0, buf.position());
|
||||
assertUnits("short buffer", prior, prior, prior, prior);
|
||||
} finally {
|
||||
RenderSystem.destroySampler(prior);
|
||||
RenderSystem.destroySampler(next);
|
||||
}
|
||||
}
|
||||
}
|
||||
+22
@@ -42,6 +42,28 @@ public class GLSM_TextureBindingTargetCache_UnitTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void proxyTargetBindIsNeitherForwardedNorCached() {
|
||||
drainErrors();
|
||||
final int t2d = GL11.glGenTextures();
|
||||
try {
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE5);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, t2d);
|
||||
|
||||
GLStateManager.glBindTexture(GL11.GL_PROXY_TEXTURE_2D, 0);
|
||||
GLStateManager.glBindTexture(GL12.GL_PROXY_TEXTURE_3D, t2d);
|
||||
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError(), "a proxy target must never reach the driver");
|
||||
assertEquals(t2d, GLStateManager.getBoundTextureForServerState(5), "a proxy bind must not disturb the cache");
|
||||
assertEquals(t2d, GL11.glGetInteger(GL11.GL_TEXTURE_BINDING_2D));
|
||||
} finally {
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, 0);
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.glDeleteTextures(t2d);
|
||||
drainErrors();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void popAttribRestoresRecordedTarget() {
|
||||
drainErrors();
|
||||
|
||||
+15
@@ -22,6 +22,21 @@ public class GLSM_TextureUploadGeneration_GLTest {
|
||||
return texId;
|
||||
}
|
||||
|
||||
@Test
|
||||
void aLevelBeyondTheShiftWidthClampsToOne() {
|
||||
final int texId = allocate(64);
|
||||
try {
|
||||
assertEquals(64, GLStateManager.glGetTexLevelParameteri(GL11.GL_TEXTURE_2D, 0, GL11.GL_TEXTURE_WIDTH));
|
||||
assertEquals(1, GLStateManager.glGetTexLevelParameteri(GL11.GL_TEXTURE_2D, 6, GL11.GL_TEXTURE_WIDTH));
|
||||
assertEquals(1, GLStateManager.glGetTexLevelParameteri(GL11.GL_TEXTURE_2D, 31, GL11.GL_TEXTURE_WIDTH));
|
||||
assertEquals(1, GLStateManager.glGetTexLevelParameteri(GL11.GL_TEXTURE_2D, 32, GL11.GL_TEXTURE_WIDTH));
|
||||
assertEquals(1, GLStateManager.glGetTexLevelParameteri(GL11.GL_TEXTURE_2D, 64, GL11.GL_TEXTURE_HEIGHT));
|
||||
} finally {
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, 0);
|
||||
GLStateManager.glDeleteTextures(texId);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void anUploadLeavesANonZeroGeneration() {
|
||||
final int texId = allocate(4);
|
||||
|
||||
@@ -44,22 +44,48 @@ class BackendPolicyTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void refreshPrefersTheExactRational() {
|
||||
assertEquals(144, RenderBackend.refreshHzFrom(144, 1, 0.0f));
|
||||
assertEquals(60, RenderBackend.refreshHzFrom(60000, 1001, 0.0f));
|
||||
void periodPrefersTheExactRational() {
|
||||
assertEquals(16_683_333L, RenderBackend.periodFromRational(60000, 1001, 0.0f));
|
||||
assertEquals(6_944_444L, RenderBackend.periodFromRational(144, 1, 0.0f));
|
||||
}
|
||||
|
||||
@Test
|
||||
void refreshRoundsUp() {
|
||||
assertEquals(144, RenderBackend.refreshHzFrom(0, 0, 143.98f));
|
||||
assertEquals(60, RenderBackend.refreshHzFrom(0, 0, 59.94f));
|
||||
assertEquals(60, RenderBackend.refreshHzFrom(0, 0, 60.0f));
|
||||
void periodFallsBackToTheFloatRate() {
|
||||
assertEquals(16_683_350L, RenderBackend.periodFromRational(0, 0, 59.94f));
|
||||
assertEquals(16_666_667L, RenderBackend.periodFromRational(0, 0, 60.0f));
|
||||
}
|
||||
|
||||
@Test
|
||||
void refreshIsZeroWhenUnknown() {
|
||||
assertEquals(0, RenderBackend.refreshHzFrom(0, 0, 0.0f));
|
||||
assertEquals(0, RenderBackend.refreshHzFrom(-1, -1, -1.0f));
|
||||
assertEquals(0, RenderBackend.refreshHzFrom(144, 0, 0.0f));
|
||||
void periodIsZeroWhenUnknown() {
|
||||
assertEquals(0L, RenderBackend.periodFromRational(0, 0, 0.0f));
|
||||
assertEquals(0L, RenderBackend.periodFromRational(-1, -1, -1.0f));
|
||||
assertEquals(0L, RenderBackend.periodFromRational(144, 0, 0.0f));
|
||||
}
|
||||
|
||||
@Test
|
||||
void integerNtscRatesAreAdjusted() {
|
||||
assertEquals(16_683_333L, RenderBackend.periodFromIntegerHz(59));
|
||||
assertEquals(6_951_389L, RenderBackend.periodFromIntegerHz(143));
|
||||
assertEquals(16_666_667L, RenderBackend.periodFromIntegerHz(60));
|
||||
assertEquals(6_944_444L, RenderBackend.periodFromIntegerHz(144));
|
||||
assertEquals(0L, RenderBackend.periodFromIntegerHz(0));
|
||||
assertEquals(0L, RenderBackend.periodFromIntegerHz(-60));
|
||||
assertEquals(60, RenderBackend.hzFromPeriod(RenderBackend.periodFromIntegerHz(59)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void hzFromPeriodRoundsToNearest() {
|
||||
assertEquals(60, RenderBackend.hzFromPeriod(16_666_667L));
|
||||
assertEquals(144, RenderBackend.hzFromPeriod(6_944_444L));
|
||||
assertEquals(0, RenderBackend.hzFromPeriod(0L));
|
||||
assertEquals(0, RenderBackend.hzFromPeriod(-1L));
|
||||
}
|
||||
|
||||
@Test
|
||||
void plausiblePeriodFilterKeeps20To1000Hz() {
|
||||
assertEquals(0L, RenderBackend.plausiblePeriodNanos(999_000L));
|
||||
assertEquals(1_000_000L, RenderBackend.plausiblePeriodNanos(1_000_000L));
|
||||
assertEquals(50_000_000L, RenderBackend.plausiblePeriodNanos(50_000_000L));
|
||||
assertEquals(0L, RenderBackend.plausiblePeriodNanos(51_000_000L));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package com.gtnewhorizons.angelica.glsm.backend;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
class GateRecorderTest {
|
||||
|
||||
@Test
|
||||
void theLongestBlockWithinAFrameWinsRegardlessOfOrder() {
|
||||
final RenderBackend.GateRecorder longestFirst = new RenderBackend.GateRecorder();
|
||||
longestFirst.recordGate(5_000_000L, 100L);
|
||||
longestFirst.recordGate(1_000_000L, 200L);
|
||||
|
||||
final RenderBackend.GateSample out = new RenderBackend.GateSample();
|
||||
longestFirst.readGateSample(out);
|
||||
assertEquals(5_000_000L, out.durationNanos);
|
||||
assertEquals(100L, out.endNanos);
|
||||
|
||||
final RenderBackend.GateRecorder shortestFirst = new RenderBackend.GateRecorder();
|
||||
shortestFirst.recordGate(1_000_000L, 10L);
|
||||
shortestFirst.recordGate(4_000_000L, 20L);
|
||||
|
||||
shortestFirst.readGateSample(out);
|
||||
assertEquals(4_000_000L, out.durationNanos);
|
||||
assertEquals(20L, out.endNanos);
|
||||
}
|
||||
|
||||
@Test
|
||||
void aReadConsumesTheSample() {
|
||||
final RenderBackend.GateRecorder recorder = new RenderBackend.GateRecorder();
|
||||
recorder.recordGate(2_000_000L, 50L);
|
||||
|
||||
final RenderBackend.GateSample out = new RenderBackend.GateSample();
|
||||
recorder.readGateSample(out);
|
||||
|
||||
recorder.readGateSample(out);
|
||||
assertEquals(0L, out.endNanos);
|
||||
assertEquals(0L, out.durationNanos);
|
||||
}
|
||||
|
||||
@Test
|
||||
void gpuWaitAccumulatesAndResetsOnRead() {
|
||||
final RenderBackend.GateRecorder recorder = new RenderBackend.GateRecorder();
|
||||
recorder.recordGate(1_000_000L, 10L);
|
||||
recorder.recordGpuWait(300L);
|
||||
recorder.recordGpuWait(700L);
|
||||
|
||||
final RenderBackend.GateSample out = new RenderBackend.GateSample();
|
||||
recorder.readGateSample(out);
|
||||
assertEquals(1_000L, out.gpuWaitNanos);
|
||||
|
||||
recorder.recordGate(1_000_000L, 11L);
|
||||
recorder.readGateSample(out);
|
||||
assertEquals(0L, out.gpuWaitNanos);
|
||||
}
|
||||
|
||||
@Test
|
||||
void gpuWaitIsDrainedOnASampleFreeRead() {
|
||||
final RenderBackend.GateRecorder recorder = new RenderBackend.GateRecorder();
|
||||
recorder.recordGpuWait(450L);
|
||||
|
||||
final RenderBackend.GateSample out = new RenderBackend.GateSample();
|
||||
recorder.readGateSample(out);
|
||||
|
||||
assertEquals(0L, out.endNanos);
|
||||
assertEquals(450L, out.gpuWaitNanos);
|
||||
|
||||
recorder.readGateSample(out);
|
||||
assertEquals(0L, out.gpuWaitNanos);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
package com.gtnewhorizons.angelica.glsm.backend;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.Timeout;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Deque;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentLinkedDeque;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
@Timeout(10)
|
||||
class MainThreadPumpTest {
|
||||
|
||||
private static final class DeferredExecutor implements Executor {
|
||||
final Deque<Runnable> tasks = new ConcurrentLinkedDeque<>();
|
||||
|
||||
@Override
|
||||
public void execute(Runnable command) {
|
||||
tasks.add(command);
|
||||
}
|
||||
|
||||
void runNext() {
|
||||
tasks.removeFirst().run();
|
||||
}
|
||||
|
||||
void awaitTask() {
|
||||
while (tasks.isEmpty()) {
|
||||
Thread.onSpinWait();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final class InlineExecutor implements Executor {
|
||||
final List<Runnable> submitted = Collections.synchronizedList(new ArrayList<>());
|
||||
|
||||
@Override
|
||||
public void execute(Runnable command) {
|
||||
submitted.add(command);
|
||||
command.run();
|
||||
}
|
||||
}
|
||||
|
||||
private static void awaitWaiting(Thread t) {
|
||||
while (t.getState() != Thread.State.WAITING && t.getState() != Thread.State.TIMED_WAITING) {
|
||||
assertTrue(t.isAlive(), "thread died instead of blocking");
|
||||
Thread.onSpinWait();
|
||||
}
|
||||
}
|
||||
|
||||
private static MainThreadPump pump(Executor executor, Runnable pumpBody, Runnable pollBody) {
|
||||
return new MainThreadPump(executor, () -> false, pumpBody, pollBody);
|
||||
}
|
||||
|
||||
@Test
|
||||
void repeatedPumpsSubmitTheSameRunnable() {
|
||||
final InlineExecutor executor = new InlineExecutor();
|
||||
final AtomicInteger pumps = new AtomicInteger();
|
||||
final AtomicInteger polls = new AtomicInteger();
|
||||
final MainThreadPump p = pump(executor, pumps::incrementAndGet, polls::incrementAndGet);
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
p.pumpMessages();
|
||||
}
|
||||
|
||||
assertEquals(16, executor.submitted.size());
|
||||
assertEquals(16, pumps.get());
|
||||
assertEquals(16, polls.get());
|
||||
final Runnable first = executor.submitted.get(0);
|
||||
for (Runnable r : executor.submitted) {
|
||||
assertSame(first, r, "pump must not allocate a new task per call");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void pumpBlocksUntilTheMainThreadRunsTheTask() throws Exception {
|
||||
final DeferredExecutor executor = new DeferredExecutor();
|
||||
final AtomicInteger polls = new AtomicInteger();
|
||||
final MainThreadPump p = pump(executor, () -> {}, polls::incrementAndGet);
|
||||
|
||||
final Thread caller = new Thread(p::pumpMessages, "pump-caller");
|
||||
caller.start();
|
||||
awaitWaiting(caller);
|
||||
executor.awaitTask();
|
||||
assertEquals(1, executor.tasks.size());
|
||||
assertEquals(0, polls.get());
|
||||
|
||||
executor.runNext();
|
||||
caller.join();
|
||||
assertEquals(1, polls.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
void aThrowingBodyPropagatesToTheCallerAndDoesNotLeaveItParked() {
|
||||
final InlineExecutor executor = new InlineExecutor();
|
||||
final AtomicInteger polls = new AtomicInteger();
|
||||
final AtomicInteger pumps = new AtomicInteger();
|
||||
final MainThreadPump p = pump(executor, () -> {
|
||||
if (pumps.incrementAndGet() == 1) throw new IllegalStateException("boom");
|
||||
}, polls::incrementAndGet);
|
||||
|
||||
final IllegalStateException thrown = assertThrows(IllegalStateException.class, p::pumpMessages);
|
||||
assertEquals("boom", thrown.getMessage());
|
||||
assertEquals(0, polls.get(), "poll must not run when the pump body failed");
|
||||
|
||||
p.pumpMessages();
|
||||
assertEquals(1, polls.get(), "a failed pump must not poison the next one");
|
||||
}
|
||||
|
||||
@Test
|
||||
void mainThreadCallersRunInlineWithoutTheExecutor() {
|
||||
final InlineExecutor executor = new InlineExecutor();
|
||||
final AtomicInteger pumps = new AtomicInteger();
|
||||
final AtomicInteger polls = new AtomicInteger();
|
||||
final MainThreadPump p = new MainThreadPump(executor, () -> true, pumps::incrementAndGet,
|
||||
polls::incrementAndGet);
|
||||
|
||||
p.pumpMessages();
|
||||
|
||||
assertTrue(executor.submitted.isEmpty(), "the main thread must not hop through the executor");
|
||||
assertEquals(1, pumps.get());
|
||||
assertEquals(1, polls.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
void anInterruptedCallerCompletesAndKeepsItsInterruptFlag() throws Exception {
|
||||
final DeferredExecutor executor = new DeferredExecutor();
|
||||
final MainThreadPump p = pump(executor, () -> {}, () -> {});
|
||||
final AtomicInteger stillInterrupted = new AtomicInteger(-1);
|
||||
|
||||
final Thread caller = new Thread(() -> {
|
||||
p.pumpMessages();
|
||||
stillInterrupted.set(Thread.currentThread().isInterrupted() ? 1 : 0);
|
||||
}, "pump-interrupted-caller");
|
||||
caller.start();
|
||||
awaitWaiting(caller);
|
||||
executor.awaitTask();
|
||||
caller.interrupt();
|
||||
assertFalse(executor.tasks.isEmpty());
|
||||
|
||||
executor.runNext();
|
||||
caller.join();
|
||||
assertEquals(1, stillInterrupted.get());
|
||||
}
|
||||
}
|
||||
+46
@@ -1,8 +1,12 @@
|
||||
package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.shader.SpirvCompiler;
|
||||
import com.gtnewhorizons.angelica.glsm.testutil.Reflect;
|
||||
import org.junit.jupiter.api.Tag;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.lwjgl.PointerBuffer;
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
import org.lwjgl.util.shaderc.Shaderc;
|
||||
@@ -12,6 +16,7 @@ import org.lwjgl.util.spvc.SpvcReflectedResource;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
@@ -33,6 +38,47 @@ class FFPUniformBlockSpirvLayoutTest {
|
||||
assertBlockLayout(FragmentShaderGenerator.generate(fk), Shaderc.shaderc_fragment_shader);
|
||||
}
|
||||
|
||||
static Stream<Arguments> instancedKeys() {
|
||||
return Stream.of(
|
||||
Arguments.of("cube-lightmap-texmat", cubeKey(true, true, false)),
|
||||
Arguments.of("cube-texgen", cubeKey(false, false, true)),
|
||||
Arguments.of("particle-fog-texmat", particleFogTexMatKey())
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource("instancedKeys")
|
||||
void instancedVariantCompilesToSpirv(String label, VertexKey key) {
|
||||
final String glsl = VertexShaderGenerator.generate(key);
|
||||
final SpirvCompiler.Result result = SpirvCompiler.compile(glsl, Shaderc.shaderc_vertex_shader, label, SpirvCompiler.Options.vulkanForced460Core());
|
||||
assertNotNull(result.spirv(), () -> "SPIR-V compile failed: " + result.error() + "\n" + glsl);
|
||||
MemoryUtil.memFree(result.spirv());
|
||||
}
|
||||
|
||||
private static VertexKey cubeKey(boolean lightmapUnit, boolean unit0TextureMatrix, boolean texGen) {
|
||||
long bits = bit("BIT_HAS_VERTEX_COLOR") | bit("BIT_HAS_VERTEX_NORMAL") | bit("BIT_HAS_VERTEX_TEX") | bit("BIT_UNIT_TEX_BASE");
|
||||
if (lightmapUnit) bits |= 1L << (position("BIT_UNIT_TEX_BASE") + 1);
|
||||
if (unit0TextureMatrix || texGen) bits |= bit("BIT_UNIT_TEXMAT_BASE");
|
||||
if (texGen) {
|
||||
bits |= (long) VertexKey.TG_OBJ_LINEAR << position("BIT_TEXGEN_S");
|
||||
bits |= (long) VertexKey.TG_OBJ_LINEAR << position("BIT_TEXGEN_T");
|
||||
}
|
||||
return VertexKey.fromPacked(VertexKey.withInstancing(bits, Instancing.CUBE));
|
||||
}
|
||||
|
||||
private static VertexKey particleFogTexMatKey() {
|
||||
final long bits = bit("BIT_UNIT_TEX_BASE") | 1L << (position("BIT_UNIT_TEX_BASE") + 1) | bit("BIT_UNIT_TEXMAT_BASE") | bit("BIT_FOG");
|
||||
return VertexKey.fromPacked(VertexKey.withInstancing(bits, Instancing.PARTICLE));
|
||||
}
|
||||
|
||||
private static long bit(String name) {
|
||||
return 1L << position(name);
|
||||
}
|
||||
|
||||
private static int position(String name) {
|
||||
return Reflect.getStatic(VertexKey.class, name);
|
||||
}
|
||||
|
||||
private static void assertBlockLayout(String glsl, int shaderKind) {
|
||||
final SpirvCompiler.Result result = SpirvCompiler.compile(glsl, shaderKind, "ffp-layout-test", SpirvCompiler.Options.vulkanForced460Core());
|
||||
assertNotNull(result.spirv(), () -> "SPIR-V compile failed: " + result.error() + "\n" + glsl);
|
||||
|
||||
+251
-85
@@ -2,14 +2,23 @@ package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLCoreTest;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.DeferredBlendHandler;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMHooks;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.PendingProgramSelection;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAddress0;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memPutFloat;
|
||||
import static com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFlags.COLOR_BIT;
|
||||
import static com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFlags.NORMAL_BIT;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
@@ -22,41 +31,203 @@ class InstancedFFPDrawGLTest {
|
||||
private static final int TEMPLATE_STRIDE = 32;
|
||||
private static final int INSTANCE_STRIDE = InstancedAttribs.STRIDE;
|
||||
|
||||
private static final int PARITY_SIZE = 256;
|
||||
private static final int INTEGER_WIDTH = 300;
|
||||
private static final int INTEGER_HEIGHT = 100;
|
||||
|
||||
private static final List<String> log = new ArrayList<>();
|
||||
|
||||
private static final PendingProgramSelection LOGGING_SELECTION = () -> log.add("resolve");
|
||||
|
||||
private static final class LoggingBlendHandler implements DeferredBlendHandler {
|
||||
@Override
|
||||
public boolean isBlendLocked() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOverrideHeld() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deferBlendModeToggle(boolean enabled) {}
|
||||
|
||||
@Override
|
||||
public void deferBlendFunc(int srcRgb, int dstRgb, int srcAlpha, int dstAlpha) {}
|
||||
|
||||
@Override
|
||||
public void flushDeferredBlend() {
|
||||
log.add("flush");
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean capturing;
|
||||
|
||||
private int vao, templateVbo, instanceVbo;
|
||||
|
||||
@BeforeAll
|
||||
static void registerForeignDrawEndListener() {
|
||||
capturing = true;
|
||||
GLSMHooks.FOREIGN_DRAW_END.addListener(event -> {
|
||||
if (capturing) log.add("listener");
|
||||
});
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
static void stopCapturing() {
|
||||
capturing = false;
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void cleanup() {
|
||||
GLStateManager.instancedFfpDrawActive = false;
|
||||
final ShaderManager sm = ShaderManager.getInstance();
|
||||
if (sm.isActive()) sm.deactivate();
|
||||
sm.disable();
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
GLSMHooks.pendingProgramSelection = null;
|
||||
GLSMHooks.blendHandler = null;
|
||||
log.clear();
|
||||
FfpFixture.IntegerInstances.delete();
|
||||
FfpFixture.resetFfpState();
|
||||
CubeParityFixture.disableDirectionalLight();
|
||||
CubeParityFixture.deleteResources();
|
||||
ParticleParityFixture.deleteResources();
|
||||
if (templateVbo != 0) { GLStateManager.glDeleteBuffers(templateVbo); templateVbo = 0; }
|
||||
if (instanceVbo != 0) { GLStateManager.glDeleteBuffers(instanceVbo); instanceVbo = 0; }
|
||||
if (vao != 0) { GLStateManager.glDeleteVertexArrays(vao); vao = 0; }
|
||||
}
|
||||
|
||||
@Test
|
||||
void vertexKeyCarriesInstancedBit() {
|
||||
GLStateManager.instancedFfpDrawActive = false;
|
||||
final long plain = VertexKey.packFromState(true, true, false, false, 0);
|
||||
GLStateManager.instancedFfpDrawActive = true;
|
||||
final long instanced = VertexKey.packFromState(true, true, false, false, 0);
|
||||
GLStateManager.instancedFfpDrawActive = false;
|
||||
void drawArraysResolvesBeforeFlushDeferredBlend() {
|
||||
GLSMHooks.pendingProgramSelection = LOGGING_SELECTION;
|
||||
GLSMHooks.blendHandler = new LoggingBlendHandler();
|
||||
|
||||
assertFalse(VertexKey.fromPacked(plain).instancedDraw());
|
||||
assertTrue(VertexKey.fromPacked(instanced).instancedDraw());
|
||||
assertEquals(1, Long.bitCount(plain ^ instanced), "only the instanced bit may differ");
|
||||
GLStateManager.glDrawArrays(GL11.GL_TRIANGLES, 0, 0);
|
||||
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
|
||||
|
||||
assertEquals(List.of("resolve", "flush"), log);
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveIsSuppressedWhileDisplayListIsRecording() {
|
||||
GLSMHooks.pendingProgramSelection = LOGGING_SELECTION;
|
||||
final int list = GLStateManager.glGenLists(1);
|
||||
|
||||
GLStateManager.glNewList(list, GL11.GL_COMPILE);
|
||||
GLSMHooks.resolvePendingProgram();
|
||||
assertTrue(log.isEmpty());
|
||||
GLStateManager.glEndList();
|
||||
|
||||
GLSMHooks.resolvePendingProgram();
|
||||
assertEquals(List.of("resolve"), log);
|
||||
}
|
||||
|
||||
@Test
|
||||
void nestedForeignDrawsResolveOnceAtOutermostEndBeforeListeners() {
|
||||
GLSMHooks.pendingProgramSelection = LOGGING_SELECTION;
|
||||
|
||||
GLStateManager.beginForeignDraw();
|
||||
GLStateManager.beginForeignDraw();
|
||||
GLStateManager.endForeignDraw();
|
||||
assertTrue(log.isEmpty());
|
||||
|
||||
GLStateManager.endForeignDraw();
|
||||
assertEquals(List.of("resolve", "listener"), log);
|
||||
}
|
||||
|
||||
@Test
|
||||
void unitCubeMatchesBakedQuadsWithLightmap() {
|
||||
assertCubeParity(true, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
void unitCubeMatchesBakedQuadsWhenLit() {
|
||||
assertCubeParity(false, true);
|
||||
}
|
||||
|
||||
private void assertCubeParity(boolean lightmap, boolean lit) {
|
||||
GLStateManager.glViewport(0, 0, PARITY_SIZE, PARITY_SIZE);
|
||||
CubeParityFixture.setupScene(lightmap, lit);
|
||||
|
||||
final ShaderManager sm = ShaderManager.getInstance();
|
||||
sm.enable();
|
||||
sm.activate();
|
||||
|
||||
final CubeParityFixture.Spec[] specs = CubeParityFixture.specs();
|
||||
|
||||
FfpFixture.clear();
|
||||
CubeParityFixture.drawReference(specs, lightmap);
|
||||
final int[] reference = FfpFixture.readRegion(PARITY_SIZE);
|
||||
|
||||
FfpFixture.clear();
|
||||
CubeParityFixture.drawInstanced(specs);
|
||||
final int[] instanced = FfpFixture.readRegion(PARITY_SIZE);
|
||||
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError(), "parity draws must not raise a GL error");
|
||||
|
||||
CubeParityFixture.assertPixelParity(reference, instanced, PARITY_SIZE, 0xFF000000, pixel -> "0x" + Integer.toHexString(pixel));
|
||||
}
|
||||
|
||||
@Test
|
||||
void particleUnit0TextureMatrixMatchesReference() {
|
||||
final ParticleParityFixture.Rotation rot = ParticleParityFixture.Rotation.of(35f, -12f, false);
|
||||
GLStateManager.glViewport(0, 0, PARITY_SIZE, PARITY_SIZE);
|
||||
ParticleParityFixture.setupScene(true);
|
||||
|
||||
final ShaderManager sm = ShaderManager.getInstance();
|
||||
sm.enable();
|
||||
sm.activate();
|
||||
|
||||
final ParticleParityFixture.Particle[] particles = ParticleParityFixture.particles();
|
||||
|
||||
FfpFixture.clear();
|
||||
ParticleParityFixture.drawReference(particles, rot.x(), rot.xz(), rot.z(), rot.yz(), rot.xy());
|
||||
final int[] reference = FfpFixture.readRegion(PARITY_SIZE);
|
||||
|
||||
FfpFixture.clear();
|
||||
ParticleParityFixture.drawInstanced(particles, rot.x(), rot.xz(), rot.z(), rot.yz(), rot.xy());
|
||||
final int[] instanced = FfpFixture.readRegion(PARITY_SIZE);
|
||||
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError(), "parity draws must not raise a GL error");
|
||||
ParticleParityFixture.assertPixelParity(reference, instanced, PARITY_SIZE, 0xFF000000, pixel -> "0x" + Integer.toHexString(pixel));
|
||||
}
|
||||
|
||||
@Test
|
||||
void entityAttribSelectsColorPerInstance() {
|
||||
GLStateManager.glViewport(0, 0, INTEGER_WIDTH, INTEGER_HEIGHT);
|
||||
GLStateManager.disableDepthTest();
|
||||
GLStateManager.disableCull();
|
||||
|
||||
FfpFixture.IntegerInstances.build(0.66f);
|
||||
|
||||
GLStateManager.glClearColor(0f, 0f, 0f, 1f);
|
||||
GLStateManager.glClear(GL11.GL_COLOR_BUFFER_BIT);
|
||||
|
||||
FfpFixture.IntegerInstances.draw();
|
||||
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError(), "instanced ivec4 draw must not raise a GL error");
|
||||
|
||||
assertPixel(51, 50, 255, 0, 0, "entity -1 must render red");
|
||||
assertPixel(150, 50, 0, 255, 0, "entity 7 must render green");
|
||||
assertPixel(249, 50, 0, 0, 255, "entity 42 must render blue");
|
||||
}
|
||||
|
||||
private static void assertPixel(int x, int y, int r, int g, int b, String label) {
|
||||
final ByteBuffer pixel = BufferUtils.createByteBuffer(4);
|
||||
GL11.glReadPixels(x, y, 1, 1, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, pixel);
|
||||
final int gotR = pixel.get(0) & 0xFF;
|
||||
final int gotG = pixel.get(1) & 0xFF;
|
||||
final int gotB = pixel.get(2) & 0xFF;
|
||||
assertEquals(r, gotR, () -> label + ": red channel, got (" + gotR + "," + gotG + "," + gotB + ")");
|
||||
assertEquals(g, gotG, () -> label + ": green channel, got (" + gotR + "," + gotG + "," + gotB + ")");
|
||||
assertEquals(b, gotB, () -> label + ": blue channel, got (" + gotR + "," + gotG + "," + gotB + ")");
|
||||
}
|
||||
|
||||
@Test
|
||||
void instancedSourceUsesInstanceAttribs() {
|
||||
GLStateManager.instancedFfpDrawActive = true;
|
||||
GLStateManager.ffpInstancing = Instancing.TEMPLATE;
|
||||
final VertexKey key = VertexKey.fromState(true, true, false, false, 0b10);
|
||||
GLStateManager.instancedFfpDrawActive = false;
|
||||
GLStateManager.ffpInstancing = Instancing.NONE;
|
||||
|
||||
final String source = VertexShaderGenerator.generate(key);
|
||||
assertTrue(source.contains("a_InstCol0"), "instance matrix attribs declared");
|
||||
assertTrue(source.contains("a_InstRow0"), "instance matrix attribs declared");
|
||||
assertTrue(source.contains("mat4 instMV"), "instance matrix used as modelview");
|
||||
assertTrue(source.contains("a_InstColor"), "instance color multiplier used");
|
||||
assertTrue(source.contains("a_InstLightmap"), "instance lightmap coord used");
|
||||
@@ -74,7 +245,7 @@ class InstancedFFPDrawGLTest {
|
||||
GLStateManager.disableCull();
|
||||
|
||||
buildTemplate();
|
||||
buildInstances();
|
||||
uploadInstances(0xFF0000FF, 0, 0xFF00FF00, 0);
|
||||
|
||||
final ShaderManager sm = ShaderManager.getInstance();
|
||||
sm.enable();
|
||||
@@ -83,14 +254,12 @@ class InstancedFFPDrawGLTest {
|
||||
GLStateManager.glClearColor(0f, 0f, 0f, 1f);
|
||||
GLStateManager.glClear(GL11.GL_COLOR_BUFFER_BIT);
|
||||
|
||||
GLStateManager.instancedFfpDrawActive = true;
|
||||
GLStateManager.glDrawArraysInstanced(GL11.GL_TRIANGLES, 0, 3, 2);
|
||||
GLStateManager.instancedFfpDrawActive = false;
|
||||
drawInstances();
|
||||
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError(), "instanced draw must not raise a GL error");
|
||||
|
||||
final int[] left = readPixel(200, 300);
|
||||
final int[] right = readPixel(600, 300);
|
||||
final int[] left = FfpFixture.readPixel(200, 300);
|
||||
final int[] right = FfpFixture.readPixel(600, 300);
|
||||
assertTrue(left[0] > 200 && left[1] < 50, "left instance red, got " + left[0] + "," + left[1] + "," + left[2]);
|
||||
assertTrue(right[1] > 200 && right[0] < 50, "right instance green, got " + right[0] + "," + right[1] + "," + right[2]);
|
||||
}
|
||||
@@ -106,7 +275,7 @@ class InstancedFFPDrawGLTest {
|
||||
GLStateManager.disableCull();
|
||||
|
||||
buildTemplate();
|
||||
buildInstancesWithAlpha();
|
||||
uploadInstances(0xFF0000FF, 0, 0x0D0000FF, 0);
|
||||
|
||||
final ShaderManager sm = ShaderManager.getInstance();
|
||||
sm.enable();
|
||||
@@ -117,14 +286,12 @@ class InstancedFFPDrawGLTest {
|
||||
GLStateManager.glClearColor(0f, 0f, 0f, 1f);
|
||||
GLStateManager.glClear(GL11.GL_COLOR_BUFFER_BIT);
|
||||
|
||||
GLStateManager.instancedFfpDrawActive = true;
|
||||
GLStateManager.glDrawArraysInstanced(GL11.GL_TRIANGLES, 0, 3, 2);
|
||||
GLStateManager.instancedFfpDrawActive = false;
|
||||
drawInstances();
|
||||
GLStateManager.disableAlphaTest();
|
||||
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
|
||||
final int[] left = readPixel(200, 300);
|
||||
final int[] right = readPixel(600, 300);
|
||||
final int[] left = FfpFixture.readPixel(200, 300);
|
||||
final int[] right = FfpFixture.readPixel(600, 300);
|
||||
assertTrue(left[0] > 200, "opaque instance drawn, got " + left[0]);
|
||||
assertTrue(right[0] < 20 && right[1] < 20, "alpha 0.05 instance discarded, got " + right[0] + "," + right[1]);
|
||||
}
|
||||
@@ -141,7 +308,7 @@ class InstancedFFPDrawGLTest {
|
||||
GLStateManager.disableCull();
|
||||
|
||||
buildTemplate();
|
||||
buildInstances();
|
||||
uploadInstances(0xFF0000FF, 0, 0xFF00FF00, 0);
|
||||
|
||||
final ShaderManager sm = ShaderManager.getInstance();
|
||||
sm.enable();
|
||||
@@ -150,48 +317,71 @@ class InstancedFFPDrawGLTest {
|
||||
GLStateManager.glClearColor(0f, 0f, 0f, 1f);
|
||||
GLStateManager.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
GLStateManager.instancedFfpDrawActive = true;
|
||||
GLStateManager.glDrawArraysInstanced(GL11.GL_TRIANGLES, 0, 3, 2);
|
||||
drawInstances();
|
||||
|
||||
GLStateManager.glDepthFunc(GL11.GL_EQUAL);
|
||||
GLStateManager.enableBlend();
|
||||
GLStateManager.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
GLStateManager.glColor4f(1f, 1f, 1f, 1f);
|
||||
rewriteInstanceColors(0x800000FF, 0x8000FF00);
|
||||
GLStateManager.glDrawArraysInstanced(GL11.GL_TRIANGLES, 0, 3, 2);
|
||||
GLStateManager.instancedFfpDrawActive = false;
|
||||
uploadInstances(0x800000FF, 0, 0x8000FF00, 0);
|
||||
drawInstances();
|
||||
|
||||
GLStateManager.disableBlend();
|
||||
GLStateManager.glDepthFunc(GL11.GL_LEQUAL);
|
||||
GLStateManager.disableDepthTest();
|
||||
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
|
||||
final int[] left = readPixel(200, 300);
|
||||
final int[] background = readPixel(400, 550);
|
||||
final int[] left = FfpFixture.readPixel(200, 300);
|
||||
final int[] background = FfpFixture.readPixel(400, 550);
|
||||
assertTrue(left[0] > 200, "overlay blended over base, got " + left[0]);
|
||||
assertTrue(background[0] < 20 && background[1] < 20, "overlay must not draw off-base, got " + background[0]);
|
||||
}
|
||||
|
||||
private void rewriteInstanceColors(int leftABGR, int rightABGR) {
|
||||
final ByteBuffer patch = BufferUtils.createByteBuffer(2 * INSTANCE_STRIDE);
|
||||
putInstance(patch, -0.5f, leftABGR);
|
||||
putInstance(patch, 0.5f, rightABGR);
|
||||
patch.flip();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, instanceVbo);
|
||||
GLStateManager.glBufferData(GL15.GL_ARRAY_BUFFER, patch, GL15.GL_STATIC_DRAW);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
@Test
|
||||
void perInstanceOverlayMixesOnlyTintedInstances() {
|
||||
GLStateManager.glMatrixMode(GL11.GL_PROJECTION);
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glViewport(0, 0, 800, 600);
|
||||
GLStateManager.disableDepthTest();
|
||||
GLStateManager.disableCull();
|
||||
|
||||
buildTemplate();
|
||||
uploadInstances(0xFFFFFFFF, 0, 0xFFFFFFFF, 0xFF0000FF);
|
||||
|
||||
final ShaderManager sm = ShaderManager.getInstance();
|
||||
sm.enable();
|
||||
sm.activate();
|
||||
|
||||
GLStateManager.glClearColor(0f, 0f, 0f, 1f);
|
||||
GLStateManager.glClear(GL11.GL_COLOR_BUFFER_BIT);
|
||||
|
||||
GLStateManager.setOverlayColor(0f, 1f, 0f, 1f);
|
||||
drawInstances();
|
||||
GLStateManager.setOverlayColor(0f, 0f, 0f, 0f);
|
||||
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError(), "instanced overlay draw must not raise a GL error");
|
||||
|
||||
final int[] left = FfpFixture.readPixel(200, 300);
|
||||
final int[] right = FfpFixture.readPixel(600, 300);
|
||||
assertTrue(left[0] > 200 && left[1] > 200 && left[2] > 200, "left instance overlay alpha 0 keeps its base color, got " + left[0] + "," + left[1] + "," + left[2]);
|
||||
assertTrue(right[0] > 200 && right[1] < 50 && right[2] < 50, "right instance is mixed to its own opaque red overlay, got " + right[0] + "," + right[1] + "," + right[2]);
|
||||
}
|
||||
|
||||
private void buildInstancesWithAlpha() {
|
||||
private void uploadInstances(int leftColor, int leftOverlay, int rightColor, int rightOverlay) {
|
||||
final ByteBuffer instances = BufferUtils.createByteBuffer(2 * INSTANCE_STRIDE);
|
||||
putInstance(instances, -0.5f, 0xFF0000FF);
|
||||
putInstance(instances, 0.5f, 0x0D0000FF);
|
||||
putInstance(instances, -0.5f, leftColor, leftOverlay);
|
||||
putInstance(instances, 0.5f, rightColor, rightOverlay);
|
||||
instances.flip();
|
||||
|
||||
instanceVbo = GLStateManager.glGenBuffers();
|
||||
final boolean first = instanceVbo == 0;
|
||||
if (first) instanceVbo = GLStateManager.glGenBuffers();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, instanceVbo);
|
||||
GLStateManager.glBufferData(GL15.GL_ARRAY_BUFFER, instances, GL15.GL_STATIC_DRAW);
|
||||
pointAndEnableInstanceAttribs();
|
||||
if (first) {
|
||||
pointAndEnableInstanceAttribs();
|
||||
}
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
}
|
||||
|
||||
@@ -225,47 +415,23 @@ class InstancedFFPDrawGLTest {
|
||||
buf.position(base + TEMPLATE_STRIDE);
|
||||
}
|
||||
|
||||
private void buildInstances() {
|
||||
final ByteBuffer instances = BufferUtils.createByteBuffer(2 * INSTANCE_STRIDE);
|
||||
putInstance(instances, -0.5f, 0xFF0000FF);
|
||||
putInstance(instances, 0.5f, 0xFF00FF00);
|
||||
instances.flip();
|
||||
|
||||
instanceVbo = GLStateManager.glGenBuffers();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, instanceVbo);
|
||||
GLStateManager.glBufferData(GL15.GL_ARRAY_BUFFER, instances, GL15.GL_STATIC_DRAW);
|
||||
pointAndEnableInstanceAttribs();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
}
|
||||
|
||||
private static void pointAndEnableInstanceAttribs() {
|
||||
for (int c = 0; c < 4; c++) {
|
||||
final int loc = InstancedAttribs.LOC_MATRIX_COL0 + c;
|
||||
GLStateManager.glEnableVertexAttribArray(loc);
|
||||
GLStateManager.glVertexAttribPointer(loc, 4, GL11.GL_FLOAT, false, INSTANCE_STRIDE, c * 16L);
|
||||
GLStateManager.glVertexAttribDivisor(loc, 1);
|
||||
}
|
||||
GLStateManager.glEnableVertexAttribArray(InstancedAttribs.LOC_COLOR);
|
||||
GLStateManager.glVertexAttribPointer(InstancedAttribs.LOC_COLOR, 4, GL11.GL_UNSIGNED_BYTE, true, INSTANCE_STRIDE, InstancedAttribs.OFFSET_COLOR);
|
||||
GLStateManager.glVertexAttribDivisor(InstancedAttribs.LOC_COLOR, 1);
|
||||
GLStateManager.glEnableVertexAttribArray(InstancedAttribs.LOC_LIGHTMAP);
|
||||
GLStateManager.glVertexAttribPointer(InstancedAttribs.LOC_LIGHTMAP, 2, GL11.GL_FLOAT, false, INSTANCE_STRIDE, InstancedAttribs.OFFSET_LIGHTMAP);
|
||||
GLStateManager.glVertexAttribDivisor(InstancedAttribs.LOC_LIGHTMAP, 1);
|
||||
InstancedAttribs.enableHeadArrays();
|
||||
InstancedAttribs.pointTemplate(0L);
|
||||
}
|
||||
|
||||
private void putInstance(ByteBuffer buf, float translateX, int colorABGR) {
|
||||
private static void putInstance(ByteBuffer buf, float translateX, int colorABGR, int overlayABGR) {
|
||||
final int base = buf.position();
|
||||
buf.putFloat(base, 1f).putFloat(base + 20, 1f).putFloat(base + 40, 1f).putFloat(base + 60, 1f);
|
||||
buf.putFloat(base + 48, translateX);
|
||||
buf.putInt(base + InstancedAttribs.OFFSET_COLOR, colorABGR);
|
||||
buf.putFloat(base + InstancedAttribs.OFFSET_LIGHTMAP, 240f);
|
||||
buf.putFloat(base + InstancedAttribs.OFFSET_LIGHTMAP + 4, 240f);
|
||||
final long ptr = memAddress0(buf) + base;
|
||||
InstancedAttribs.writeHead(ptr, FfpFixture.translationMatrix(translateX), 0, colorABGR, overlayABGR, 0L);
|
||||
memPutFloat(ptr + InstancedAttribs.OFFSET_LIGHTMAP, 240f);
|
||||
memPutFloat(ptr + InstancedAttribs.OFFSET_LIGHTMAP + 4, 240f);
|
||||
buf.position(base + INSTANCE_STRIDE);
|
||||
}
|
||||
|
||||
private static int[] readPixel(int x, int y) {
|
||||
final ByteBuffer pixel = BufferUtils.createByteBuffer(4);
|
||||
GL11.glReadPixels(x, y, 1, 1, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, pixel);
|
||||
return new int[] { pixel.get(0) & 0xFF, pixel.get(1) & 0xFF, pixel.get(2) & 0xFF };
|
||||
private static void drawInstances() {
|
||||
GLStateManager.ffpInstancing = Instancing.TEMPLATE;
|
||||
GLStateManager.glDrawArraysInstanced(GL11.GL_TRIANGLES, 0, 3, 2);
|
||||
GLStateManager.ffpInstancing = Instancing.NONE;
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package com.gtnewhorizons.angelica.glsm.hooks;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class GLSMInitConfigDefaultsTest {
|
||||
|
||||
@Test
|
||||
void emptyBuilderLeavesDsaToTheProbe() {
|
||||
assertTrue(GLSMInitConfig.builder().build().isDSAEnabled());
|
||||
assertFalse(GLSMInitConfig.builder().enableDSA(false).build().isDSAEnabled());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
package com.gtnewhorizons.angelica.glsm.loading;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.redirect.GLSMRedirector;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
import org.objectweb.asm.tree.InsnNode;
|
||||
import org.objectweb.asm.tree.MethodInsnNode;
|
||||
import org.objectweb.asm.tree.MethodNode;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotSame;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class ModRedirectorTest {
|
||||
|
||||
private static final String EXTRA_EXCLUSION = "com.example.excluded";
|
||||
|
||||
private static ClassNode emptyClass() {
|
||||
final ClassNode cn = new ClassNode();
|
||||
cn.version = Opcodes.V1_8;
|
||||
cn.access = Opcodes.ACC_PUBLIC;
|
||||
cn.name = "com/example/TestClass";
|
||||
cn.superName = "java/lang/Object";
|
||||
cn.methods = new ArrayList<>();
|
||||
|
||||
final MethodNode mn = new MethodNode(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "test", "()V", null, null);
|
||||
mn.instructions.add(new InsnNode(Opcodes.RETURN));
|
||||
cn.methods.add(mn);
|
||||
return cn;
|
||||
}
|
||||
|
||||
private static byte[] bytesWithoutGlCall() {
|
||||
return toBytes(emptyClass());
|
||||
}
|
||||
|
||||
private static byte[] bytesWithGlCall() {
|
||||
final ClassNode cn = emptyClass();
|
||||
final MethodNode mn = cn.methods.get(0);
|
||||
mn.instructions.clear();
|
||||
mn.instructions.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "org/lwjgl/opengl/GL11", "glPopMatrix", "()V", false));
|
||||
mn.instructions.add(new InsnNode(Opcodes.RETURN));
|
||||
return toBytes(cn);
|
||||
}
|
||||
|
||||
private static byte[] toBytes(ClassNode cn) {
|
||||
final ClassWriter cw = new ClassWriter(0);
|
||||
cn.accept(cw);
|
||||
return cw.toByteArray();
|
||||
}
|
||||
|
||||
private static ModRedirector redirector() {
|
||||
return new ModRedirector(ClassDump.DISABLED, EXTRA_EXCLUSION);
|
||||
}
|
||||
|
||||
private static void assertStandardExclusions(String[] actual) {
|
||||
final List<String> exclusions = Arrays.asList(actual);
|
||||
final String[] coreExclusions = new GLSMRedirector().getCoreExclusions();
|
||||
for (String coreExclusion : coreExclusions) {
|
||||
assertTrue(exclusions.contains(coreExclusion), coreExclusion);
|
||||
}
|
||||
assertTrue(exclusions.contains("com.gtnewhorizons.angelica.lwjgl3."));
|
||||
assertTrue(exclusions.contains(EXTRA_EXCLUSION));
|
||||
assertEquals(coreExclusions.length + 2, exclusions.size(), exclusions.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void exclusionsCombineCoreLwjgl3AndExtra() {
|
||||
assertStandardExclusions(redirector().getTransformerExclusions());
|
||||
}
|
||||
|
||||
@Test
|
||||
void earlyRedirectorExclusionsCombineCoreLwjgl3AndExtra() {
|
||||
assertStandardExclusions(new EarlyRedirectorCore(EXTRA_EXCLUSION).getTransformerExclusions());
|
||||
}
|
||||
|
||||
@Test
|
||||
void nullInputReturnsNull() {
|
||||
assertNull(redirector().transform("com.example.TestClass", null, this));
|
||||
}
|
||||
|
||||
@Test
|
||||
void excludedPrefixReturnsOriginalArray() {
|
||||
final byte[] original = bytesWithGlCall();
|
||||
assertSame(original, redirector().transform(EXTRA_EXCLUSION + ".TestClass", original, this));
|
||||
}
|
||||
|
||||
@Test
|
||||
void coreExcludedPrefixReturnsOriginalArray() {
|
||||
final byte[] original = bytesWithGlCall();
|
||||
assertSame(original, redirector().transform("org.lwjgl.opengl.TestClass", original, this));
|
||||
}
|
||||
|
||||
@Test
|
||||
void unchangedClassReturnsOriginalArray() {
|
||||
final byte[] original = bytesWithoutGlCall();
|
||||
assertSame(original, redirector().transform("com.example.TestClass", original, this));
|
||||
}
|
||||
|
||||
@Test
|
||||
void redirectedClassReturnsNewArray() {
|
||||
final byte[] original = bytesWithGlCall();
|
||||
assertNotSame(original, redirector().transform("com.example.TestClass", original, this));
|
||||
}
|
||||
|
||||
@Test
|
||||
void earlyRedirectorSkipsClassesOutsideItsTargets() {
|
||||
final byte[] original = bytesWithGlCall();
|
||||
final EarlyRedirectorCore early = new EarlyRedirectorCore(EXTRA_EXCLUSION);
|
||||
assertSame(original, early.transform("com.example.TestClass", original, this));
|
||||
}
|
||||
|
||||
@Test
|
||||
void earlyRedirectorTransformsItsTargets() {
|
||||
final byte[] original = bytesWithGlCall();
|
||||
final EarlyRedirectorCore early = new EarlyRedirectorCore(EXTRA_EXCLUSION);
|
||||
final String target = EcosystemNarrowRules.EARLY_REDIRECTOR_TARGETS[0] + "TestClass";
|
||||
assertNotSame(original, early.transform(target, original, this));
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,8 @@ import org.objectweb.asm.tree.MethodInsnNode;
|
||||
import org.objectweb.asm.tree.MethodNode;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -300,6 +302,74 @@ class GLSMRedirectorTest {
|
||||
assertTrue(unmappedSeen().isEmpty(), owner + "." + name + " should not be recorded");
|
||||
}
|
||||
|
||||
private static boolean glsmDeclares(String name, String desc) throws ClassNotFoundException {
|
||||
final Class<?> glsm = Class.forName(GLSM.replace('/', '.'), false, GLSMRedirectorTest.class.getClassLoader());
|
||||
for (Method m : glsm.getDeclaredMethods()) {
|
||||
final int mods = m.getModifiers();
|
||||
if (!Modifier.isPublic(mods) || !Modifier.isStatic(mods) || !m.getName().equals(name)) continue;
|
||||
if (Type.getMethodDescriptor(m).equals(desc)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void assertRedirectedToDeclaredTarget(String[][] cases) throws ClassNotFoundException {
|
||||
for (boolean aware : new boolean[]{false, true}) {
|
||||
for (String[] c : cases) {
|
||||
final String call = c[0] + "." + c[1] + c[2] + (aware ? " aware" : "");
|
||||
final ClassNode cn = makeClassWithMethodCall(c[0], c[1], c[2]);
|
||||
assertTrue(new GLSMRedirector().transformClassNode("com.example.TestClass", cn, aware), call);
|
||||
final MethodInsnNode target = firstCall(cn);
|
||||
assertEquals(GLSM, target.owner, call);
|
||||
assertEquals(c[1], target.name, call);
|
||||
assertEquals(c[2], target.desc, call);
|
||||
assertTrue(glsmDeclares(target.name, target.desc), () -> "GLStateManager lacks the target of " + call);
|
||||
}
|
||||
}
|
||||
assertTrue(unmappedSeen().isEmpty(), () -> "unexpected reports: " + unmappedSeen());
|
||||
}
|
||||
|
||||
@Test
|
||||
void samplerGenDeleteOverloadsHaveDeclaredTargets() throws Exception {
|
||||
assertRedirectedToDeclaredTarget(new String[][]{
|
||||
{"org/lwjgl/opengl/GL33", "glGenSamplers", "()I"},
|
||||
{"org/lwjgl/opengl/GL33", "glGenSamplers", "(Ljava/nio/IntBuffer;)V"},
|
||||
{"org/lwjgl/opengl/GL33C", "glGenSamplers", "([I)V"},
|
||||
{"org/lwjgl/opengl/GL33", "glGenSamplers", "([I)V"},
|
||||
{"org/lwjgl/opengl/GL33", "glDeleteSamplers", "(I)V"},
|
||||
{"org/lwjgl/opengl/GL33", "glDeleteSamplers", "(Ljava/nio/IntBuffer;)V"},
|
||||
{"org/lwjgl/opengl/GL33C", "glDeleteSamplers", "([I)V"},
|
||||
{"org/lwjgl/opengl/GL33", "glDeleteSamplers", "([I)V"},
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void bindSamplersHasDeclaredTargets() throws Exception {
|
||||
assertRedirectedToDeclaredTarget(new String[][]{
|
||||
{"org/lwjgl/opengl/GL44", "glBindSamplers", "(IILjava/nio/IntBuffer;)V"},
|
||||
{"org/lwjgl/opengl/GL44C", "glBindSamplers", "(ILjava/nio/IntBuffer;)V"},
|
||||
{"org/lwjgl/opengl/GL44", "glBindSamplers", "(I[I)V"},
|
||||
{"org/lwjgl/opengl/ARBMultiBind", "glBindSamplers", "(IILjava/nio/IntBuffer;)V"},
|
||||
{"org/lwjgl/opengl/ARBMultiBind", "glBindSamplers", "(ILjava/nio/IntBuffer;)V"},
|
||||
{"org/lwjgl/opengl/ARBMultiBind", "glBindSamplers", "(I[I)V"},
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void arbSamplerObjectsHasDeclaredTargets() throws Exception {
|
||||
assertRedirectedToDeclaredTarget(new String[][]{
|
||||
{"org/lwjgl/opengl/ARBSamplerObjects", "glGenSamplers", "()I"},
|
||||
{"org/lwjgl/opengl/ARBSamplerObjects", "glGenSamplers", "(Ljava/nio/IntBuffer;)V"},
|
||||
{"org/lwjgl/opengl/ARBSamplerObjects", "glGenSamplers", "([I)V"},
|
||||
{"org/lwjgl/opengl/ARBSamplerObjects", "glDeleteSamplers", "(I)V"},
|
||||
{"org/lwjgl/opengl/ARBSamplerObjects", "glDeleteSamplers", "(Ljava/nio/IntBuffer;)V"},
|
||||
{"org/lwjgl/opengl/ARBSamplerObjects", "glDeleteSamplers", "([I)V"},
|
||||
{"org/lwjgl/opengl/ARBSamplerObjects", "glIsSampler", "(I)Z"},
|
||||
{"org/lwjgl/opengl/ARBSamplerObjects", "glBindSampler", "(II)V"},
|
||||
{"org/lwjgl/opengl/ARBSamplerObjects", "glSamplerParameteri", "(III)V"},
|
||||
{"org/lwjgl/opengl/ARBSamplerObjects", "glSamplerParameterf", "(IIF)V"},
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void descKeyedSyncRedirectsOnlyLongForm() {
|
||||
assertRedirected("org/lwjgl/opengl/GL32C", "glFenceSync", "(II)J", "glFenceSync");
|
||||
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
package com.gtnewhorizons.angelica.glsm.streaming;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLCoreTest;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.RenderSystem;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.FfpExtendedAttribs;
|
||||
import com.gtnewhorizons.angelica.glsm.streaming.StreamingUploader.UploadStrategy;
|
||||
import com.gtnewhorizons.angelica.glsm.testutil.Reflect;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntSet;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.EnumSource;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
import org.lwjgl.opengl.GL31;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAlloc;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memFree;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assumptions.assumeTrue;
|
||||
|
||||
@GLCoreTest
|
||||
class StreamingBindingCacheGLTest {
|
||||
|
||||
private static ByteBuffer filled(int bytes) {
|
||||
final ByteBuffer data = memAlloc(bytes);
|
||||
for (int i = 0; i < bytes; i++) {
|
||||
data.put(i, (byte) i);
|
||||
}
|
||||
data.position(0).limit(bytes);
|
||||
return data;
|
||||
}
|
||||
|
||||
private static int glsmUniformBufferBinding() {
|
||||
final Object ctx = Reflect.invokeStatic(GLStateManager.class, "ctx", new Class<?>[0]);
|
||||
final Int2IntMap bound = Reflect.get(ctx, "boundOtherBuffers");
|
||||
return bound.get(GL31.GL_UNIFORM_BUFFER);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@EnumSource(UploadStrategy.class)
|
||||
void orphanUploadLeavesTheArrayBufferCacheMatchingTheDriver(UploadStrategy strategy) {
|
||||
final int other = GLStateManager.glGenBuffers();
|
||||
final OrphanStreamingBuffer buf = new OrphanStreamingBuffer();
|
||||
final ByteBuffer data = filled(64);
|
||||
try {
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, other);
|
||||
buf.upload(strategy, data);
|
||||
assertEquals(GL11.glGetInteger(GL15.GL_ARRAY_BUFFER_BINDING), GLStateManager.getBoundVBO(), "first upload respecifies the buffer");
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, other);
|
||||
buf.upload(strategy, data);
|
||||
assertEquals(GL11.glGetInteger(GL15.GL_ARRAY_BUFFER_BINDING), GLStateManager.getBoundVBO(), "second upload takes the in-capacity path");
|
||||
} finally {
|
||||
memFree(data);
|
||||
buf.destroy();
|
||||
GLStateManager.glDeleteBuffers(other);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void uniformRingOrphanPathLeavesTheUniformBufferCacheMatchingTheDriver() {
|
||||
final int other = GLStateManager.glGenBuffers();
|
||||
final ByteBuffer src = filled(64);
|
||||
UniformRingBuffer ring = null;
|
||||
try {
|
||||
GLStateManager.glBindBuffer(GL31.GL_UNIFORM_BUFFER, other);
|
||||
ring = new UniformRingBuffer(1024, 64, true);
|
||||
assertFalse(ring.isPersistent());
|
||||
assertEquals(GL11.glGetInteger(GL31.GL_UNIFORM_BUFFER_BINDING), glsmUniformBufferBinding(), "constructor allocation");
|
||||
GLStateManager.glBindBuffer(GL31.GL_UNIFORM_BUFFER, other);
|
||||
ring.writeBlock(src);
|
||||
assertEquals(GL11.glGetInteger(GL31.GL_UNIFORM_BUFFER_BINDING), glsmUniformBufferBinding(), "writeBlock");
|
||||
} finally {
|
||||
if (ring != null) ring.destroy();
|
||||
GLStateManager.glDeleteBuffers(other);
|
||||
memFree(src);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void persistentRingMarksItsOwnBufferAndLeavesTheCacheMatchingTheDriver() {
|
||||
assumeTrue(RenderSystem.supportsBufferStorage(), "GL4.4+ required");
|
||||
final int other = GLStateManager.glGenBuffers();
|
||||
PersistentStreamingBuffer ring = null;
|
||||
try {
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, other);
|
||||
ring = PersistentStreamingBuffer.createOrNull(4096, false);
|
||||
assertNotNull(ring);
|
||||
final IntSet persistent = Reflect.getStatic(FfpExtendedAttribs.class, "persistentSources");
|
||||
assertTrue(persistent.contains(ring.getBufferId()), "the ring buffer is the persistent source");
|
||||
assertFalse(persistent.contains(other), "the previously bound buffer is not persistent");
|
||||
assertEquals(GL11.glGetInteger(GL15.GL_ARRAY_BUFFER_BINDING), GLStateManager.getBoundVBO());
|
||||
} finally {
|
||||
if (ring != null) ring.destroy();
|
||||
GLStateManager.glDeleteBuffers(other);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -39,7 +39,12 @@ public class GLSMCoreExtension implements BeforeAllCallback, BeforeEachCallback,
|
||||
new ContextAttribs(3, 3).withProfileCore(true).withForwardCompatible(true));
|
||||
|
||||
GLSMExtension.setMainThread(Thread.currentThread());
|
||||
GLStateManager.initialize(GLSMInitConfig.builder().displaySize(800, 600).build());
|
||||
GLStateManager.initialize(GLSMInitConfig.builder()
|
||||
.displaySize(800, 600)
|
||||
.directDrawer(t -> {})
|
||||
.streamingDrawerDestroy(() -> {})
|
||||
.enableDSA(false)
|
||||
.build());
|
||||
GLStateManager.setRunningSplash(false);
|
||||
GLStateManager.markSplashComplete("test");
|
||||
|
||||
|
||||
@@ -69,6 +69,9 @@ public class GLSMExtension implements BeforeAllCallback, BeforeEachCallback, Aft
|
||||
displayMode = new DisplayMode(800, 600);
|
||||
final GLSMInitConfig config = GLSMInitConfig.builder()
|
||||
.displaySize(displayMode.getWidth(), displayMode.getHeight())
|
||||
.directDrawer(t -> {})
|
||||
.streamingDrawerDestroy(() -> {})
|
||||
.enableDSA(false)
|
||||
.build();
|
||||
setMainThread(Thread.currentThread());
|
||||
|
||||
|
||||
+308
@@ -0,0 +1,308 @@
|
||||
package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFlags;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.util.function.IntFunction;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAddress0;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memPutFloat;
|
||||
|
||||
public final class CubeParityFixture {
|
||||
|
||||
private static final int TEX_WIDTH = 64;
|
||||
private static final int TEX_HEIGHT = 32;
|
||||
private static final float SCALE = 0.0625f;
|
||||
private static final short BRIGHTNESS = 240;
|
||||
|
||||
public record Spec(int texU, int texV, float x, float y, float z, int sizeX, int sizeY, int sizeZ, float inflate, boolean mirror) {}
|
||||
|
||||
private static final float[] MODELVIEW = {
|
||||
1.25f, 0.0f, 0.0f, 0.0f,
|
||||
0.0f, 1.25f, 0.0f, 0.0f,
|
||||
0.375f, 0.25f, -0.25f, 0.0f,
|
||||
0.0f, 0.0f, 0.0f, 1.0f,
|
||||
};
|
||||
|
||||
private static final float[] MODELVIEW_CONFORMAL = {
|
||||
1.0f, 0.0f, 0.75f, 0.0f,
|
||||
0.0f, 1.25f, 0.0f, 0.0f,
|
||||
-0.75f, 0.0f, 1.0f, 0.0f,
|
||||
0.0f, 0.0f, 0.0f, 1.0f,
|
||||
};
|
||||
|
||||
private static float[] modelView = MODELVIEW;
|
||||
|
||||
private static int netTexture;
|
||||
private static int lightmapTexture;
|
||||
|
||||
private CubeParityFixture() {}
|
||||
|
||||
public static Spec[] specs() {
|
||||
return new Spec[] {
|
||||
new Spec(0, 0, -4, -6, -2, 8, 12, 4, 0.0f, false),
|
||||
new Spec(16, 8, -3, 1, -3, 6, 4, 6, 0.25f, true),
|
||||
};
|
||||
}
|
||||
|
||||
private static void setupCamera(boolean conformal) {
|
||||
modelView = conformal ? MODELVIEW_CONFORMAL : MODELVIEW;
|
||||
GLStateManager.glMatrixMode(GL11.GL_PROJECTION);
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
final FloatBuffer mv = BufferUtils.createFloatBuffer(16);
|
||||
mv.put(modelView).flip();
|
||||
GLStateManager.glLoadMatrix(mv);
|
||||
}
|
||||
|
||||
public static void setupScene(boolean lightmap, boolean lit) {
|
||||
GLStateManager.glDisable(GL11.GL_FOG);
|
||||
GLStateManager.glDisable(GL11.GL_BLEND);
|
||||
GLStateManager.glDisable(GL11.GL_ALPHA_TEST);
|
||||
GLStateManager.glEnable(GL11.GL_DEPTH_TEST);
|
||||
GLStateManager.glDepthFunc(GL11.GL_LEQUAL);
|
||||
GLStateManager.glDepthMask(true);
|
||||
GLStateManager.glEnable(GL11.GL_CULL_FACE);
|
||||
GLStateManager.glCullFace(GL11.GL_BACK);
|
||||
setupCamera(lit);
|
||||
if (lit) {
|
||||
enableDirectionalLight();
|
||||
} else {
|
||||
GLStateManager.glDisable(GL11.GL_LIGHTING);
|
||||
}
|
||||
|
||||
netTexture = createNetTexture();
|
||||
if (lightmap) {
|
||||
lightmapTexture = createNetTexture();
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE1);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, lightmapTexture);
|
||||
GLStateManager.glEnable(GL11.GL_TEXTURE_2D);
|
||||
GLStateManager.glMatrixMode(GL11.GL_TEXTURE);
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glScalef(0.00390625f, 0.00390625f, 0.00390625f);
|
||||
GLStateManager.glTranslatef(8.0f, 8.0f, 8.0f);
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, netTexture);
|
||||
}
|
||||
}
|
||||
|
||||
private static void enableDirectionalLight() {
|
||||
final FloatBuffer buf = BufferUtils.createFloatBuffer(4);
|
||||
GLStateManager.glEnable(GL11.GL_LIGHTING);
|
||||
GLStateManager.glEnable(GL11.GL_NORMALIZE);
|
||||
GLStateManager.glEnable(GL11.GL_LIGHT0);
|
||||
GLStateManager.glDisable(GL11.GL_LIGHT1);
|
||||
GLStateManager.glLightModel(GL11.GL_LIGHT_MODEL_AMBIENT, fill(buf, 0.2f, 0.2f, 0.2f, 1.0f));
|
||||
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_POSITION, fill(buf, 0.375f, 0.75f, 0.5f, 0.0f));
|
||||
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_DIFFUSE, fill(buf, 1.0f, 1.0f, 1.0f, 1.0f));
|
||||
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_AMBIENT, fill(buf, 0.0f, 0.0f, 0.0f, 1.0f));
|
||||
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_SPECULAR, fill(buf, 0.0f, 0.0f, 0.0f, 1.0f));
|
||||
GLStateManager.glEnable(GL11.GL_COLOR_MATERIAL);
|
||||
GLStateManager.glColorMaterial(GL11.GL_FRONT_AND_BACK, GL11.GL_AMBIENT_AND_DIFFUSE);
|
||||
}
|
||||
|
||||
public static void disableDirectionalLight() {
|
||||
GLStateManager.glDisable(GL11.GL_COLOR_MATERIAL);
|
||||
GLStateManager.glDisable(GL11.GL_LIGHT0);
|
||||
GLStateManager.glDisable(GL11.GL_NORMALIZE);
|
||||
GLStateManager.glDisable(GL11.GL_LIGHTING);
|
||||
}
|
||||
|
||||
private static FloatBuffer fill(FloatBuffer buf, float x, float y, float z, float w) {
|
||||
buf.clear();
|
||||
buf.put(x).put(y).put(z).put(w).flip();
|
||||
return buf;
|
||||
}
|
||||
|
||||
private static int createNetTexture() {
|
||||
final int id = GLStateManager.glGenTextures();
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, id);
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
|
||||
final ByteBuffer texels = BufferUtils.createByteBuffer(TEX_WIDTH * TEX_HEIGHT * 4);
|
||||
for (int y = 0; y < TEX_HEIGHT; y++) {
|
||||
for (int x = 0; x < TEX_WIDTH; x++) {
|
||||
texels.put((byte) (16 + x * 3));
|
||||
texels.put((byte) (16 + y * 7));
|
||||
texels.put((byte) (255 - x * 3));
|
||||
texels.put((byte) 255);
|
||||
}
|
||||
}
|
||||
texels.flip();
|
||||
GLStateManager.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA8, TEX_WIDTH, TEX_HEIGHT, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, texels);
|
||||
GLStateManager.glEnable(GL11.GL_TEXTURE_2D);
|
||||
return id;
|
||||
}
|
||||
|
||||
private static final int REF_STRIDE = 44;
|
||||
|
||||
private static int referenceVao;
|
||||
private static int referenceVbo;
|
||||
|
||||
private static ByteBuffer buildQuadData(Spec[] specs) {
|
||||
final ByteBuffer data = BufferUtils.createByteBuffer(specs.length * 24 * REF_STRIDE);
|
||||
for (Spec spec : specs) {
|
||||
final float[] corners = corners(spec);
|
||||
for (int face = 0; face < UnitCubeMesh.FACE_COUNT; face++) {
|
||||
final float[] uv = faceRect(spec, face);
|
||||
final float[] normal = UnitCubeMesh.FACES[face].normal();
|
||||
for (int corner = 0; corner < 4; corner++) {
|
||||
final int source = spec.mirror() ? 3 - corner : corner;
|
||||
final int index = UnitCubeMesh.FACES[face].corners()[source];
|
||||
final int base = data.position();
|
||||
data.putFloat(base, corners[index * 3] * SCALE);
|
||||
data.putFloat(base + 4, corners[index * 3 + 1] * SCALE);
|
||||
data.putFloat(base + 8, corners[index * 3 + 2] * SCALE);
|
||||
data.putInt(base + 12, 0xFFFFFFFF);
|
||||
data.putFloat(base + 16, uv[(source == 0 || source == 3) ? 2 : 0]);
|
||||
data.putFloat(base + 20, uv[source >= 2 ? 3 : 1]);
|
||||
data.putFloat(base + 24, BRIGHTNESS);
|
||||
data.putFloat(base + 28, BRIGHTNESS);
|
||||
data.putFloat(base + 32, spec.mirror() ? -normal[0] : normal[0]);
|
||||
data.putFloat(base + 36, normal[1]);
|
||||
data.putFloat(base + 40, normal[2]);
|
||||
data.position(base + REF_STRIDE);
|
||||
}
|
||||
}
|
||||
}
|
||||
data.flip();
|
||||
return data;
|
||||
}
|
||||
|
||||
private static int uploadQuads(int vbo, Spec[] specs, boolean lightmap) {
|
||||
final ByteBuffer data = buildQuadData(specs);
|
||||
if (vbo == 0) vbo = GLStateManager.glGenBuffers();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
|
||||
GLStateManager.glBufferData(GL15.GL_ARRAY_BUFFER, data, GL15.GL_STREAM_DRAW);
|
||||
FfpFixture.attrib(0, 3, GL11.GL_FLOAT, false, REF_STRIDE, 0);
|
||||
FfpFixture.attrib(1, 4, GL11.GL_UNSIGNED_BYTE, true, REF_STRIDE, 12);
|
||||
FfpFixture.attrib(2, 2, GL11.GL_FLOAT, false, REF_STRIDE, 16);
|
||||
if (lightmap) {
|
||||
FfpFixture.attrib(3, 2, GL11.GL_FLOAT, false, REF_STRIDE, 24);
|
||||
} else {
|
||||
GLStateManager.glDisableVertexAttribArray(3);
|
||||
}
|
||||
FfpFixture.attrib(4, 3, GL11.GL_FLOAT, false, REF_STRIDE, 32);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
return vbo;
|
||||
}
|
||||
|
||||
public static void drawReference(Spec[] specs, boolean lightmap) {
|
||||
if (referenceVao == 0) referenceVao = GLStateManager.glGenVertexArrays();
|
||||
GLStateManager.glBindVertexArray(referenceVao);
|
||||
referenceVbo = uploadQuads(referenceVbo, specs, lightmap);
|
||||
VAOManager.setCurrentVertexFlags(UnitCubeMesh.VERTEX_FLAGS | (lightmap ? VertexFlags.BRIGHTNESS_BIT : 0));
|
||||
|
||||
GLStateManager.glDrawArrays(GL11.GL_QUADS, 0, specs.length * 24);
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
}
|
||||
|
||||
private static float[] corners(Spec spec) {
|
||||
final float minX = spec.x() - spec.inflate();
|
||||
final float minY = spec.y() - spec.inflate();
|
||||
final float minZ = spec.z() - spec.inflate();
|
||||
final float maxX = (spec.x() + spec.sizeX()) + spec.inflate();
|
||||
final float maxY = (spec.y() + spec.sizeY()) + spec.inflate();
|
||||
final float maxZ = (spec.z() + spec.sizeZ()) + spec.inflate();
|
||||
final float lowX = spec.mirror() ? maxX : minX;
|
||||
final float highX = spec.mirror() ? minX : maxX;
|
||||
final float[] out = new float[UnitCubeMesh.CORNERS.length * 3];
|
||||
for (int index = 0; index < UnitCubeMesh.CORNERS.length; index++) {
|
||||
final float[] corner = UnitCubeMesh.CORNERS[index];
|
||||
out[index * 3] = corner[0] > 0f ? highX : lowX;
|
||||
out[index * 3 + 1] = corner[1] > 0f ? maxY : minY;
|
||||
out[index * 3 + 2] = corner[2] > 0f ? maxZ : minZ;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
private static float[] faceRect(Spec spec, int face) {
|
||||
final int i = spec.texU();
|
||||
final int j = spec.texV();
|
||||
final int k = spec.sizeX();
|
||||
final int l = spec.sizeY();
|
||||
final int m = spec.sizeZ();
|
||||
final int[] rect = switch (face) {
|
||||
case 0 -> new int[] { i + m + k, j + m, i + m + k + m, j + m + l };
|
||||
case 1 -> new int[] { i, j + m, i + m, j + m + l };
|
||||
case 2 -> new int[] { i + m, j, i + m + k, j + m };
|
||||
case 3 -> new int[] { i + m + k, j + m, i + m + k + k, j };
|
||||
case 4 -> new int[] { i + m, j + m, i + m + k, j + m + l };
|
||||
default -> new int[] { i + m + k + m, j + m, i + m + k + m + k, j + m + l };
|
||||
};
|
||||
return new float[] {
|
||||
rect[0] / (float) TEX_WIDTH, rect[1] / (float) TEX_HEIGHT,
|
||||
rect[2] / (float) TEX_WIDTH, rect[3] / (float) TEX_HEIGHT,
|
||||
};
|
||||
}
|
||||
|
||||
private static int instanceVbo;
|
||||
|
||||
public static void drawInstanced(Spec[] specs) {
|
||||
final ByteBuffer records = BufferUtils.createByteBuffer(specs.length * CubeInstancedAttribs.STRIDE);
|
||||
final long records0 = memAddress0(records);
|
||||
for (int index = 0; index < specs.length; index++) {
|
||||
final Spec spec = specs[index];
|
||||
final CubeParams cube = CubeParams.of(TEX_WIDTH, TEX_HEIGHT, spec.mirror(), spec.texU(), spec.texV(), spec.x(), spec.y(), spec.z(), spec.sizeX(), spec.sizeY(), spec.sizeZ(), spec.inflate());
|
||||
if (cube == null) {
|
||||
throw new AssertionError("spec " + index + " is not cube instanceable");
|
||||
}
|
||||
final long ptr = records0 + (long) index * CubeInstancedAttribs.STRIDE;
|
||||
cube.writeRows(ptr, modelView, 0, SCALE);
|
||||
InstancedAttribs.writeTail(ptr, 0xFFFFFFFF, 0, 0L);
|
||||
memPutFloat(ptr + CubeInstancedAttribs.OFFSET_LIGHTMAP_SCALE, BRIGHTNESS);
|
||||
memPutFloat(ptr + CubeInstancedAttribs.OFFSET_LIGHTMAP_SCALE + 4, BRIGHTNESS);
|
||||
cube.writeTexture(ptr);
|
||||
}
|
||||
records.position(0).limit(specs.length * CubeInstancedAttribs.STRIDE);
|
||||
|
||||
GLStateManager.glBindVertexArray(UnitCubeMesh.vao());
|
||||
VAOManager.setCurrentVertexFlags(UnitCubeMesh.VERTEX_FLAGS);
|
||||
if (instanceVbo == 0) instanceVbo = GLStateManager.glGenBuffers();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, instanceVbo);
|
||||
GLStateManager.glBufferData(GL15.GL_ARRAY_BUFFER, records, GL15.GL_STREAM_DRAW);
|
||||
CubeInstancedAttribs.pointInstanceAttribs(0L);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
|
||||
GLStateManager.ffpInstancing = Instancing.CUBE;
|
||||
GLStateManager.glDrawArraysInstanced(GL11.GL_QUADS, 0, UnitCubeMesh.VERTEX_COUNT, specs.length);
|
||||
GLStateManager.ffpInstancing = Instancing.NONE;
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
}
|
||||
|
||||
public static void assertPixelParity(int[] reference, int[] instanced, int size, int background, IntFunction<String> describe) {
|
||||
FfpFixture.assertPixelParity("instanced cubes", reference, instanced, size, background, 8, 1, describe);
|
||||
}
|
||||
|
||||
public static void deleteResources() {
|
||||
if (instanceVbo != 0) {
|
||||
GLStateManager.glDeleteBuffers(instanceVbo);
|
||||
instanceVbo = 0;
|
||||
}
|
||||
if (referenceVbo != 0) {
|
||||
GLStateManager.glDeleteBuffers(referenceVbo);
|
||||
referenceVbo = 0;
|
||||
}
|
||||
if (referenceVao != 0) {
|
||||
GLStateManager.glDeleteVertexArrays(referenceVao);
|
||||
referenceVao = 0;
|
||||
}
|
||||
if (netTexture != 0) {
|
||||
GLStateManager.glDeleteTextures(netTexture);
|
||||
netTexture = 0;
|
||||
}
|
||||
if (lightmapTexture != 0) {
|
||||
GLStateManager.glDeleteTextures(lightmapTexture);
|
||||
lightmapTexture = 0;
|
||||
}
|
||||
UnitCubeMesh.delete();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,272 @@
|
||||
package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.function.IntFunction;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAddress0;
|
||||
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.Assertions.fail;
|
||||
|
||||
public final class FfpFixture {
|
||||
|
||||
private FfpFixture() {}
|
||||
|
||||
public static final class IntegerInstances {
|
||||
|
||||
private static final int INSTANCES = 3;
|
||||
|
||||
private static final String VS_HEAD = """
|
||||
#version 330 core
|
||||
layout(location = 0) in vec3 a_Position;
|
||||
layout(location = 5) in vec4 a_InstRow0;
|
||||
layout(location = 6) in vec4 a_InstRow1;
|
||||
layout(location = 7) in vec4 a_InstRow2;
|
||||
layout(location = 11) in ivec4 iris_Entity;
|
||||
flat out vec3 v_Color;
|
||||
void main() {
|
||||
""";
|
||||
|
||||
private static final String VS_TAIL = """
|
||||
gl_Position = instMV * vec4(a_Position, 1.0);
|
||||
if (iris_Entity.x == -1) { v_Color = vec3(1.0, 0.0, 0.0); }
|
||||
else if (iris_Entity.x == 7) { v_Color = vec3(0.0, 1.0, 0.0); }
|
||||
else { v_Color = vec3(0.0, 0.0, 1.0); }
|
||||
}
|
||||
""";
|
||||
|
||||
private static final String VS = VS_HEAD + " mat4 instMV = " + InstancedGlslHelpers.mat4FromRows("a_InstRow0", "a_InstRow1", "a_InstRow2") + ";\n" + VS_TAIL;
|
||||
|
||||
private static final String FS = """
|
||||
#version 330 core
|
||||
flat in vec3 v_Color;
|
||||
out vec4 fragColor;
|
||||
void main() { fragColor = vec4(v_Color, 1.0); }
|
||||
""";
|
||||
|
||||
private static int program;
|
||||
private static int vao;
|
||||
private static int templateVbo;
|
||||
private static int instanceVbo;
|
||||
|
||||
private IntegerInstances() {}
|
||||
|
||||
public static void build(float spacing) {
|
||||
program = buildProgram();
|
||||
GLStateManager.glUseProgram(program);
|
||||
|
||||
buildTemplate();
|
||||
buildInstances(new float[] { -spacing, 0.0f, spacing }, new int[] { -1, 7, 42 });
|
||||
}
|
||||
|
||||
public static void draw() {
|
||||
GLStateManager.glDrawArraysInstanced(GL11.GL_QUADS, 0, 4, INSTANCES);
|
||||
}
|
||||
|
||||
public static void delete() {
|
||||
GLStateManager.glUseProgram(0);
|
||||
if (program != 0) { GLStateManager.glDeleteProgram(program); program = 0; }
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
if (templateVbo != 0) { GLStateManager.glDeleteBuffers(templateVbo); templateVbo = 0; }
|
||||
if (instanceVbo != 0) { GLStateManager.glDeleteBuffers(instanceVbo); instanceVbo = 0; }
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
if (vao != 0) { GLStateManager.glDeleteVertexArrays(vao); vao = 0; }
|
||||
}
|
||||
|
||||
private static void buildTemplate() {
|
||||
vao = GLStateManager.glGenVertexArrays();
|
||||
GLStateManager.glBindVertexArray(vao);
|
||||
|
||||
final ByteBuffer quad = BufferUtils.createByteBuffer(4 * 12);
|
||||
putVertex(quad, -0.15f, -0.8f);
|
||||
putVertex(quad, 0.15f, -0.8f);
|
||||
putVertex(quad, 0.15f, 0.8f);
|
||||
putVertex(quad, -0.15f, 0.8f);
|
||||
quad.flip();
|
||||
|
||||
templateVbo = GLStateManager.glGenBuffers();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, templateVbo);
|
||||
GLStateManager.glBufferData(GL15.GL_ARRAY_BUFFER, quad, GL15.GL_STATIC_DRAW);
|
||||
GLStateManager.glEnableVertexAttribArray(0);
|
||||
GLStateManager.glVertexAttribPointer(0, 3, GL11.GL_FLOAT, false, 12, 0L);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
}
|
||||
|
||||
private static void putVertex(ByteBuffer buf, float x, float y) {
|
||||
final int base = buf.position();
|
||||
buf.putFloat(base, x).putFloat(base + 4, y).putFloat(base + 8, 0f);
|
||||
buf.position(base + 12);
|
||||
}
|
||||
|
||||
private static void buildInstances(float[] translateX, int[] entities) {
|
||||
final ByteBuffer instances = BufferUtils.createByteBuffer(entities.length * InstancedAttribs.STRIDE);
|
||||
final long base0 = memAddress0(instances);
|
||||
for (int i = 0; i < entities.length; i++) {
|
||||
final long ptr = base0 + (long) i * InstancedAttribs.STRIDE;
|
||||
InstancedAttribs.writeHead(ptr, translationMatrix(translateX[i]), 0, 0xFFFFFFFF, 0, InstancedAttribs.packEntityInfo(entities[i], 0, 0));
|
||||
}
|
||||
instances.position(0).limit(entities.length * InstancedAttribs.STRIDE);
|
||||
|
||||
instanceVbo = GLStateManager.glGenBuffers();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, instanceVbo);
|
||||
GLStateManager.glBufferData(GL15.GL_ARRAY_BUFFER, instances, GL15.GL_STATIC_DRAW);
|
||||
InstancedAttribs.enableHeadArrays();
|
||||
InstancedAttribs.pointTemplate(0L);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
}
|
||||
|
||||
private static int buildProgram() {
|
||||
final int vs = compile(GL20.GL_VERTEX_SHADER, VS);
|
||||
final int fs = compile(GL20.GL_FRAGMENT_SHADER, FS);
|
||||
final int p = GLStateManager.glCreateProgram();
|
||||
GLStateManager.glAttachShader(p, vs);
|
||||
GLStateManager.glAttachShader(p, fs);
|
||||
GLStateManager.glLinkProgram(p);
|
||||
assertNotEquals(0, GLStateManager.glGetProgrami(p, GL20.GL_LINK_STATUS), () -> "test program must link: " + GLStateManager.glGetProgramInfoLog(p, 4096));
|
||||
GLStateManager.glDeleteShader(vs);
|
||||
GLStateManager.glDeleteShader(fs);
|
||||
return p;
|
||||
}
|
||||
|
||||
private static int compile(int type, String source) {
|
||||
final int shader = GLStateManager.glCreateShader(type);
|
||||
GLStateManager.glShaderSource(shader, source);
|
||||
GLStateManager.glCompileShader(shader);
|
||||
if (GLStateManager.glGetShaderi(shader, GL20.GL_COMPILE_STATUS) == 0) {
|
||||
fail("test shader failed to compile:\n" + GLStateManager.glGetShaderInfoLog(shader, 4096) + "\n\n" + source);
|
||||
}
|
||||
return shader;
|
||||
}
|
||||
}
|
||||
|
||||
public static void attrib(int loc, int size, int type, boolean normalized, int stride, int offset) {
|
||||
GLStateManager.glEnableVertexAttribArray(loc);
|
||||
GLStateManager.glVertexAttribPointer(loc, size, type, normalized, stride, offset);
|
||||
}
|
||||
|
||||
public static void clear() {
|
||||
GLStateManager.glClearColor(0f, 0f, 0f, 0f);
|
||||
GLStateManager.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
|
||||
}
|
||||
|
||||
public static int[] readRegion(int size) {
|
||||
final ByteBuffer pixels = BufferUtils.createByteBuffer(size * size * 4);
|
||||
GLStateManager.glReadPixels(0, 0, size, size, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, pixels);
|
||||
final int[] out = new int[size * size];
|
||||
for (int i = 0; i < out.length; i++) {
|
||||
out[i] = pixels.getInt(i * 4);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
public static int[] readPixel(int x, int y) {
|
||||
final ByteBuffer pixel = BufferUtils.createByteBuffer(4);
|
||||
GLStateManager.glReadPixels(x, y, 1, 1, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, pixel);
|
||||
return new int[] { pixel.get(0) & 0xFF, pixel.get(1) & 0xFF, pixel.get(2) & 0xFF, pixel.get(3) & 0xFF };
|
||||
}
|
||||
|
||||
public static int solidTexture(int r, int g, int b, int a) {
|
||||
final int id = GLStateManager.glGenTextures();
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, id);
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST);
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
|
||||
final ByteBuffer texels = BufferUtils.createByteBuffer(2 * 2 * 4);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
texels.put((byte) r).put((byte) g).put((byte) b).put((byte) a);
|
||||
}
|
||||
texels.flip();
|
||||
GLStateManager.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA8, 2, 2, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, texels);
|
||||
return id;
|
||||
}
|
||||
|
||||
public static float[] translationMatrix(float x) {
|
||||
return new float[] {
|
||||
1f, 0f, 0f, 0f,
|
||||
0f, 1f, 0f, 0f,
|
||||
0f, 0f, 1f, 0f,
|
||||
x, 0f, 0f, 1f,
|
||||
};
|
||||
}
|
||||
|
||||
public static void assertPixelParity(String subject, int[] reference, int[] candidate, int size, int background, int minCoverageDivisor, int channelTolerance, IntFunction<String> describe) {
|
||||
int covered = 0;
|
||||
for (int pixel : reference) {
|
||||
if (pixel != background) covered++;
|
||||
}
|
||||
final int coveredPixels = covered;
|
||||
assertTrue(coveredPixels > reference.length / minCoverageDivisor, () -> "reference draw covered only " + coveredPixels + " of " + reference.length + " pixels");
|
||||
|
||||
int mismatches = 0;
|
||||
int firstMismatch = -1;
|
||||
for (int i = 0; i < reference.length; i++) {
|
||||
if (!withinTolerance(reference[i], candidate[i], channelTolerance)) {
|
||||
mismatches++;
|
||||
if (firstMismatch < 0) firstMismatch = i;
|
||||
}
|
||||
}
|
||||
final int index = firstMismatch;
|
||||
final int differing = mismatches;
|
||||
assertEquals(0, mismatches, () -> subject + " differ from baked quads in " + differing + " of " + reference.length + " pixels, first at (" + (index % size) + "," + (index / size) + "): reference " + describe.apply(reference[index]) + " instanced " + describe.apply(candidate[index]));
|
||||
}
|
||||
|
||||
private static boolean withinTolerance(int a, int b, int tolerance) {
|
||||
if (tolerance == 0) return a == b;
|
||||
for (int shift = 0; shift <= 24; shift += 8) {
|
||||
final int ca = (a >> shift) & 0xFF;
|
||||
final int cb = (b >> shift) & 0xFF;
|
||||
if (Math.abs(ca - cb) > tolerance) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void assertOverlayFootprint(int[] base, int[] overlay, int background, String overlayName) {
|
||||
int covered = 0;
|
||||
for (int i = 0; i < base.length; i++) {
|
||||
final int index = i;
|
||||
if (base[i] != background) {
|
||||
covered++;
|
||||
assertTrue(base[i] != overlay[i], () -> "pixel " + index + " covered by the base draw did not change under the " + overlayName + ": 0x" + Integer.toHexString(overlay[index]));
|
||||
} else {
|
||||
assertEquals(base[i], overlay[i], () -> "pixel " + index + " outside the base footprint must not be touched by the " + overlayName);
|
||||
}
|
||||
}
|
||||
final int coveredPixels = covered;
|
||||
assertTrue(coveredPixels > base.length / 8, "base draw covered only " + coveredPixels + " of " + base.length + " pixels");
|
||||
}
|
||||
|
||||
public static void resetFfpState() {
|
||||
GLStateManager.ffpInstancing = Instancing.NONE;
|
||||
final ShaderManager sm = ShaderManager.getInstance();
|
||||
if (sm.isActive()) sm.deactivate();
|
||||
sm.disable();
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE1);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, 0);
|
||||
GLStateManager.glDisable(GL11.GL_TEXTURE_2D);
|
||||
GLStateManager.glMatrixMode(GL11.GL_TEXTURE);
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, 0);
|
||||
GLStateManager.glDisable(GL11.GL_TEXTURE_2D);
|
||||
GLStateManager.glMatrixMode(GL11.GL_TEXTURE);
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
|
||||
GLStateManager.glDisable(GL11.GL_BLEND);
|
||||
GLStateManager.glDepthMask(true);
|
||||
GLStateManager.glDepthFunc(GL11.GL_LEQUAL);
|
||||
GLStateManager.glDisable(GL11.GL_DEPTH_TEST);
|
||||
GLStateManager.glDisable(GL11.GL_CULL_FACE);
|
||||
GLStateManager.glDisable(GL11.GL_ALPHA_TEST);
|
||||
}
|
||||
}
|
||||
+216
@@ -0,0 +1,216 @@
|
||||
package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFlags;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.function.IntFunction;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAddress0;
|
||||
|
||||
public final class ParticleParityFixture {
|
||||
|
||||
public record Particle(float cx, float cy, float cz, float half, float u0, float v0, float u1, float v1, int colorABGR) {}
|
||||
|
||||
public record Rotation(float x, float xz, float z, float yz, float xy) {
|
||||
public static Rotation of(float yawDeg, float pitchDeg, boolean thirdPersonFront) {
|
||||
final float sign = thirdPersonFront ? -1f : 1f;
|
||||
final float yaw = (float) Math.toRadians(yawDeg);
|
||||
final float pitch = (float) Math.toRadians(pitchDeg);
|
||||
final float rotX = (float) Math.cos(yaw) * sign;
|
||||
final float rotZ = (float) Math.sin(yaw) * sign;
|
||||
final float rotYZ = -rotZ * (float) Math.sin(pitch) * sign;
|
||||
final float rotXY = rotX * (float) Math.sin(pitch) * sign;
|
||||
final float rotXZ = (float) Math.cos(pitch);
|
||||
return new Rotation(rotX, rotXZ, rotZ, rotYZ, rotXY);
|
||||
}
|
||||
|
||||
public float offsetX(float a, float b) {
|
||||
return a * x + b * yz;
|
||||
}
|
||||
|
||||
public float offsetY(float b) {
|
||||
return b * xz;
|
||||
}
|
||||
|
||||
public float offsetZ(float a, float b) {
|
||||
return a * z + b * xy;
|
||||
}
|
||||
}
|
||||
|
||||
private static final float[] CORNER_A = { -1.0f, -1.0f, 1.0f, 1.0f };
|
||||
private static final float[] CORNER_B = { -1.0f, 1.0f, 1.0f, -1.0f };
|
||||
|
||||
private static final int REF_STRIDE = 24;
|
||||
|
||||
private static int referenceVao;
|
||||
private static int referenceVbo;
|
||||
private static int instanceVbo;
|
||||
private static int checkerTexture;
|
||||
|
||||
private ParticleParityFixture() {}
|
||||
|
||||
public static float cornerA(int corner) {
|
||||
return CORNER_A[corner];
|
||||
}
|
||||
|
||||
public static float cornerB(int corner) {
|
||||
return CORNER_B[corner];
|
||||
}
|
||||
|
||||
public static Particle[] particles() {
|
||||
return new Particle[] {
|
||||
new Particle(-0.75f, 0.6f, 0f, 0.10f, 0f, 0f, 1f, 1f, 0xFF0000FF),
|
||||
new Particle(-0.45f, -0.55f, 0f, 0.16f, 0f, 0f, 1f, 1f, 0xFF00FF00),
|
||||
new Particle(-0.15f, 0.2f, 0f, 0.07f, 0f, 0f, 1f, 1f, 0xFFFF0000),
|
||||
new Particle(0.1f, 0.75f, 0f, -0.12f, 0f, 0f, 1f, 1f, 0xFF00FFFF),
|
||||
new Particle(0.35f, -0.3f, 0f, 0.20f, 0f, 0f, 1f, 1f, 0xFFFF00FF),
|
||||
new Particle(0.6f, 0.45f, 0f, -0.08f, 0f, 0f, 1f, 1f, 0xFFFFFF00),
|
||||
new Particle(0.8f, -0.7f, 0f, 0.13f, 0f, 0f, 1f, 1f, 0xFFFFFFFF),
|
||||
new Particle(-0.8f, -0.1f, 0f, 0.09f, 0f, 0f, 1f, 1f, 0xFF0080FF),
|
||||
};
|
||||
}
|
||||
|
||||
private static int createCheckerTexture() {
|
||||
final int id = GLStateManager.glGenTextures();
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, id);
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST);
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
|
||||
final ByteBuffer texels = BufferUtils.createByteBuffer(2 * 2 * 4);
|
||||
putTexel(texels, 255, 32, 32);
|
||||
putTexel(texels, 32, 255, 32);
|
||||
putTexel(texels, 32, 32, 255);
|
||||
putTexel(texels, 255, 255, 32);
|
||||
texels.flip();
|
||||
GLStateManager.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA8, 2, 2, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, texels);
|
||||
GLStateManager.glEnable(GL11.GL_TEXTURE_2D);
|
||||
return id;
|
||||
}
|
||||
|
||||
private static void putTexel(ByteBuffer buf, int r, int g, int b) {
|
||||
buf.put((byte) r).put((byte) g).put((byte) b).put((byte) 255);
|
||||
}
|
||||
|
||||
public static void setupScene(boolean textureMatrix) {
|
||||
GLStateManager.glDisable(GL11.GL_FOG);
|
||||
GLStateManager.glDisable(GL11.GL_BLEND);
|
||||
GLStateManager.glDisable(GL11.GL_ALPHA_TEST);
|
||||
GLStateManager.glDisable(GL11.GL_DEPTH_TEST);
|
||||
GLStateManager.glDisable(GL11.GL_CULL_FACE);
|
||||
GLStateManager.glDisable(GL11.GL_LIGHTING);
|
||||
GLStateManager.glMatrixMode(GL11.GL_PROJECTION);
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
GLStateManager.glLoadIdentity();
|
||||
|
||||
checkerTexture = createCheckerTexture();
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, checkerTexture);
|
||||
GLStateManager.glEnable(GL11.GL_TEXTURE_2D);
|
||||
GLStateManager.glMatrixMode(GL11.GL_TEXTURE);
|
||||
GLStateManager.glLoadIdentity();
|
||||
if (textureMatrix) {
|
||||
GLStateManager.glTranslatef(0.1f, 0.1f, 0.0f);
|
||||
GLStateManager.glScalef(0.8f, 0.8f, 1.0f);
|
||||
}
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
}
|
||||
|
||||
private static ByteBuffer buildReferenceData(Particle[] particles, float rotX, float rotXZ, float rotZ, float rotYZ, float rotXY) {
|
||||
final ByteBuffer data = BufferUtils.createByteBuffer(particles.length * 4 * REF_STRIDE);
|
||||
for (Particle p : particles) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
final float a = cornerA(i);
|
||||
final float b = cornerB(i);
|
||||
final float ox = a * rotX + b * rotYZ;
|
||||
final float oy = b * rotXZ;
|
||||
final float oz = a * rotZ + b * rotXY;
|
||||
final float u = a < 0f ? p.u0() : p.u1();
|
||||
final float v = b < 0f ? p.v0() : p.v1();
|
||||
final int base = data.position();
|
||||
data.putFloat(base, p.cx() + ox * p.half());
|
||||
data.putFloat(base + 4, p.cy() + oy * p.half());
|
||||
data.putFloat(base + 8, p.cz() + oz * p.half());
|
||||
data.putInt(base + 12, p.colorABGR());
|
||||
data.putFloat(base + 16, u);
|
||||
data.putFloat(base + 20, v);
|
||||
data.position(base + REF_STRIDE);
|
||||
}
|
||||
}
|
||||
data.flip();
|
||||
return data;
|
||||
}
|
||||
|
||||
public static void drawReference(Particle[] particles, float rotX, float rotXZ, float rotZ, float rotYZ, float rotXY) {
|
||||
final ByteBuffer data = buildReferenceData(particles, rotX, rotXZ, rotZ, rotYZ, rotXY);
|
||||
|
||||
if (referenceVao == 0) referenceVao = GLStateManager.glGenVertexArrays();
|
||||
GLStateManager.glBindVertexArray(referenceVao);
|
||||
if (referenceVbo == 0) referenceVbo = GLStateManager.glGenBuffers();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, referenceVbo);
|
||||
GLStateManager.glBufferData(GL15.GL_ARRAY_BUFFER, data, GL15.GL_STREAM_DRAW);
|
||||
FfpFixture.attrib(0, 3, GL11.GL_FLOAT, false, REF_STRIDE, 0);
|
||||
FfpFixture.attrib(1, 4, GL11.GL_UNSIGNED_BYTE, true, REF_STRIDE, 12);
|
||||
FfpFixture.attrib(2, 2, GL11.GL_FLOAT, false, REF_STRIDE, 16);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
VAOManager.setCurrentVertexFlags(VertexFlags.COLOR_BIT | VertexFlags.TEXTURE_BIT);
|
||||
|
||||
GLStateManager.glDrawArrays(GL11.GL_QUADS, 0, particles.length * 4);
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
}
|
||||
|
||||
public static void drawInstanced(Particle[] particles, float rotX, float rotXZ, float rotZ, float rotYZ, float rotXY) {
|
||||
ParticleQuadMesh.update(rotX, rotXZ, rotZ, rotYZ, rotXY);
|
||||
|
||||
final ByteBuffer records = BufferUtils.createByteBuffer(particles.length * ParticleInstancedAttribs.STRIDE);
|
||||
final long records0 = memAddress0(records);
|
||||
for (int i = 0; i < particles.length; i++) {
|
||||
final Particle p = particles[i];
|
||||
final long ptr = records0 + (long) i * ParticleInstancedAttribs.STRIDE;
|
||||
ParticleInstancedAttribs.writeInstance(ptr, p.cx(), p.cy(), p.cz(), p.half(), p.u0(), p.v0(), p.u1(), p.v1(), p.colorABGR(), 0);
|
||||
}
|
||||
records.position(0).limit(particles.length * ParticleInstancedAttribs.STRIDE);
|
||||
|
||||
GLStateManager.glBindVertexArray(ParticleQuadMesh.vao());
|
||||
VAOManager.setCurrentVertexFlags(ParticleQuadMesh.VERTEX_FLAGS);
|
||||
if (instanceVbo == 0) instanceVbo = GLStateManager.glGenBuffers();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, instanceVbo);
|
||||
GLStateManager.glBufferData(GL15.GL_ARRAY_BUFFER, records, GL15.GL_STREAM_DRAW);
|
||||
ParticleInstancedAttribs.pointInstanceAttribs(0L);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
|
||||
GLStateManager.ffpInstancing = Instancing.PARTICLE;
|
||||
GLStateManager.glDrawArraysInstanced(GL11.GL_QUADS, 0, ParticleQuadMesh.VERTEX_COUNT, particles.length);
|
||||
GLStateManager.ffpInstancing = Instancing.NONE;
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
}
|
||||
|
||||
public static void assertPixelParity(int[] reference, int[] instanced, int size, int background, IntFunction<String> describe) {
|
||||
FfpFixture.assertPixelParity("instanced particles", reference, instanced, size, background, 16, 1, describe);
|
||||
}
|
||||
|
||||
public static void deleteResources() {
|
||||
if (instanceVbo != 0) {
|
||||
GLStateManager.glDeleteBuffers(instanceVbo);
|
||||
instanceVbo = 0;
|
||||
}
|
||||
if (referenceVbo != 0) {
|
||||
GLStateManager.glDeleteBuffers(referenceVbo);
|
||||
referenceVbo = 0;
|
||||
}
|
||||
if (referenceVao != 0) {
|
||||
GLStateManager.glDeleteVertexArrays(referenceVao);
|
||||
referenceVao = 0;
|
||||
}
|
||||
if (checkerTexture != 0) {
|
||||
GLStateManager.glDeleteTextures(checkerTexture);
|
||||
checkerTexture = 0;
|
||||
}
|
||||
ParticleQuadMesh.delete();
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package com.gtnewhorizons.angelica.glsm.testutil;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.reflect.Fields;
|
||||
import com.gtnewhorizon.gtnhlib.reflect.Fields.LookupType;
|
||||
import sun.misc.Unsafe;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
@@ -99,6 +100,17 @@ public final class Reflect {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T allocate(Class<T> type) {
|
||||
try {
|
||||
final Field f = Unsafe.class.getDeclaredField("theUnsafe");
|
||||
f.setAccessible(true);
|
||||
return (T) ((Unsafe) f.get(null)).allocateInstance(type);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static Method resolve(Class<?> owner, String name, Class<?>[] signature) throws NoSuchMethodException {
|
||||
for (Class<?> c = owner; c != null; c = c.getSuperclass()) {
|
||||
try {
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
import com.gtnewhorizons.retrofuturagradle.MinecraftExtension
|
||||
import org.gradle.accessors.dm.LibrariesForLibs
|
||||
import xyz.wagyourtail.jvmdg.gradle.task.DowngradeJar
|
||||
import xyz.wagyourtail.jvmdg.gradle.task.files.DowngradeFiles
|
||||
|
||||
val libs = the<LibrariesForLibs>()
|
||||
|
||||
tasks.withType<DowngradeJar>().configureEach { logLevel.set("FATAL") }
|
||||
tasks.withType<DowngradeFiles>().configureEach { logLevel.set("FATAL") }
|
||||
|
||||
configurations.all {
|
||||
resolutionStrategy.dependencySubstitution {
|
||||
substitute(module("com.github.GTNewHorizons:lwjgl3ify"))
|
||||
.using(module("com.github.GTNewHorizons:lwjgl3ify:${libs.versions.lwjgl3ify.get()}"))
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven {
|
||||
name = "Maven Central Snapshots"
|
||||
url = uri("https://central.sonatype.com/repository/maven-snapshots/")
|
||||
content { includeGroup("org.lwjgl") }
|
||||
}
|
||||
}
|
||||
|
||||
val forwardedRunProperties = System.getProperties().stringPropertyNames().filter { it.startsWith("angelica.") || it.startsWith("quickPlay") }
|
||||
|
||||
configure<MinecraftExtension> {
|
||||
extraRunJvmArguments.set(extraRunJvmArguments.get().filterNot { arg -> forwardedRunProperties.any { arg.startsWith("-D$it=") } })
|
||||
}
|
||||
|
||||
tasks.withType<JavaExec>().matching { it.name.startsWith("runClient") }.configureEach {
|
||||
for (name in forwardedRunProperties) {
|
||||
jvmArgs("-D$name=${System.getProperty(name)}")
|
||||
}
|
||||
}
|
||||
|
||||
val isMacOs = org.gradle.internal.os.OperatingSystem.current().isMacOsX
|
||||
val lwjglDebug = project.extra.has("lwjglDebug") && project.extra["lwjglDebug"] as Boolean
|
||||
|
||||
tasks.withType<JavaExec>().configureEach {
|
||||
if (name.startsWith("runClient") && name != "runClient" && isMacOs) {
|
||||
jvmArgs("-XstartOnFirstThread")
|
||||
if ("angelica.sdlgpu.encoderAssertions" !in forwardedRunProperties) jvmArgs("-Dangelica.sdlgpu.encoderAssertions=" + if (lwjglDebug) "fatal" else "warn")
|
||||
if (lwjglDebug) {
|
||||
environment("METAL_DEVICE_WRAPPER_TYPE", "1")
|
||||
environment("METAL_DEBUG_ERROR_MODE", "0")
|
||||
environment("MTL_SHADER_VALIDATION", "1")
|
||||
environment("MTL_SHADER_VALIDATION_REPORT_TO_STDERR", "1")
|
||||
environment("MTL_DEBUG_LAYER", "1")
|
||||
environment("MallocScribble", "1")
|
||||
environment("MallocPreScribble", "1")
|
||||
environment("MallocGuardEdges", "1")
|
||||
environment("MallocStackLogging", "1")
|
||||
environment("MallocStackLoggingNoCompact", "1")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
configure<MinecraftExtension> {
|
||||
lwjgl3Bindings.addAll("shaderc", "spvc")
|
||||
lwjgl3Version = libs.versions.lwjgl3.get()
|
||||
}
|
||||
|
||||
tasks.named<Copy>("processResources") {
|
||||
val projectVersion = project.version.toString()
|
||||
inputs.property("version", projectVersion)
|
||||
filesMatching("META-INF/rfb-plugin/*") {
|
||||
expand("version" to projectVersion)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
|
||||
|
||||
val embedOnly: Configuration by configurations
|
||||
|
||||
tasks.named<ShadowJar>("shadowJar") {
|
||||
dependsOn(embedOnly)
|
||||
from(embedOnly.map(::zipTree))
|
||||
|
||||
duplicatesStrategy = DuplicatesStrategy.INCLUDE
|
||||
mergeServiceFiles()
|
||||
|
||||
minimize {
|
||||
exclude(project(rootProject.path))
|
||||
exclude(project(":glsm"))
|
||||
exclude(project(":lwjgl3-backend"))
|
||||
exclude(project(":sdl-gpu"))
|
||||
exclude(dependency("org.taumc:.*:.*"))
|
||||
exclude(dependency("org.embeddedt.celeritas:.*:.*"))
|
||||
exclude(dependency("org.antlr:.*:.*"))
|
||||
}
|
||||
|
||||
relocate("org.taumc.celeritas", "org.taumc.celeritas")
|
||||
relocate("org.embeddedt", "org.embeddedt")
|
||||
relocate("com.gtnewhorizons.angelica.config", "com.gtnewhorizons.angelica.config")
|
||||
relocate("com.gtnewhorizons.angelica.glsm", "com.gtnewhorizons.angelica.glsm")
|
||||
relocate("com.gtnewhorizons.angelica.lwjgl3", "com.gtnewhorizons.angelica.lwjgl3")
|
||||
relocate("com.gtnewhorizons.angelica.sdlgpu", "com.gtnewhorizons.angelica.sdlgpu")
|
||||
}
|
||||
@@ -6,10 +6,10 @@ log4j = "2.0-beta9"
|
||||
asmCompile = "5.0.3"
|
||||
# End Frozen
|
||||
|
||||
lwjgl3 = "3.4.2-20260602.093430-9"
|
||||
lwjgl3 = "3.4.2-SNAPSHOT"
|
||||
lwjgl3ify = "3.0.31"
|
||||
|
||||
celeritas = "2.5.10-GTNH"
|
||||
celeritas = "2.5.11-GTNH"
|
||||
gtnhlib = "0.11.36"
|
||||
eventbus = "7.0-beta.15"
|
||||
glslTransformLib = "0.2.0-33.g3ec381e"
|
||||
@@ -30,10 +30,10 @@ asmBuild = "9.7"
|
||||
mockito = "3.12.4"
|
||||
archunit = "1.4.1"
|
||||
ant = "1.8.2"
|
||||
distantHorizonsApi = "5.1.0"
|
||||
|
||||
[libraries]
|
||||
celeritas-common = { module = "org.embeddedt.celeritas:celeritas-common", version.ref = "celeritas" }
|
||||
celeritas-lwjgl2-service = { module = "org.embeddedt.celeritas:celeritas-lwjgl2-service", version.ref = "celeritas" }
|
||||
eventbus = { module = "net.minecraftforge:eventbus", version.ref = "eventbus" }
|
||||
glsl-transformation-lib = { module = "org.taumc:glsl-transformation-lib", version.ref = "glslTransformLib" }
|
||||
antlr4-runtime = { module = "org.antlr:antlr4-runtime", version.ref = "antlr4Runtime" }
|
||||
@@ -58,6 +58,7 @@ junit-platform-reporting = { module = "org.junit.platform:junit-platform-reporti
|
||||
mockito-core = { module = "org.mockito:mockito-core", version.ref = "mockito" }
|
||||
archunit-junit5 = { module = "com.tngtech.archunit:archunit-junit5", version.ref = "archunit" }
|
||||
ant = { module = "org.apache.ant:ant", version.ref = "ant" }
|
||||
distant-horizons-api = { module = "maven.modrinth:DistantHorizonsApi", version.ref = "distantHorizonsApi" }
|
||||
asm-tree-compile = { module = "org.ow2.asm:asm-tree", version.ref = "asmCompile" }
|
||||
asm-tree-test = { module = "org.ow2.asm:asm-tree", version.ref = "asmTest" }
|
||||
asm-build = { module = "org.ow2.asm:asm", version.ref = "asmBuild" }
|
||||
|
||||
@@ -61,6 +61,7 @@ dependencies {
|
||||
api(project(":glsm"))
|
||||
|
||||
compileOnly(libs.lwjgl3ify) { artifact { classifier = "dev" }; isTransitive = false }
|
||||
compileOnly(libs.retrofuturabootstrap) { isTransitive = false }
|
||||
compileOnly(libs.celeritas.common) { isTransitive = false }
|
||||
|
||||
// LWJGL3
|
||||
|
||||
+45
-7
@@ -4,9 +4,13 @@ import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.GLESCaps;
|
||||
import com.gtnewhorizons.angelica.glsm.RenderSystem;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.DebugMessageHandler;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.MainThreadPump;
|
||||
import com.gtnewhorizons.retrofuturabootstrap.MainStartOnFirstThread;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.RenderBackend;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.VSyncMode;
|
||||
import me.eigenraven.lwjgl3ify.api.Lwjgl3Aware;
|
||||
import me.eigenraven.lwjgl3ify.client.MainThreadExec;
|
||||
import org.lwjglx.Lwjgl3ifyEventLoop;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.lwjgl.opengl.ARBClearTexture;
|
||||
@@ -36,7 +40,9 @@ import org.lwjgl.opengl.GLDebugMessageCallback;
|
||||
import org.lwjgl.sdl.SDL_DisplayMode;
|
||||
import org.lwjgl.sdl.SDL_DropEvent;
|
||||
import org.lwjgl.sdl.SDL_EventFilter;
|
||||
import org.lwjgl.sdl.SDLError;
|
||||
import org.lwjgl.sdl.SDLEvents;
|
||||
import org.lwjgl.sdl.SDLTimer;
|
||||
import org.lwjgl.sdl.SDLVideo;
|
||||
import org.lwjgl.system.JNI;
|
||||
import org.lwjgl.system.MemoryStack;
|
||||
@@ -71,6 +77,7 @@ public final class Lwjgl3GLRenderBackend extends RenderBackend {
|
||||
private SDL_EventFilter dropEventFilter;
|
||||
private final ConcurrentLinkedQueue<String> droppedFiles = new ConcurrentLinkedQueue<>();
|
||||
private volatile boolean watchingDrops;
|
||||
private boolean swapIntervalHonored = true;
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
@@ -173,19 +180,50 @@ public final class Lwjgl3GLRenderBackend extends RenderBackend {
|
||||
|
||||
@Override
|
||||
protected VSyncMode applyVSyncMode(VSyncMode preferred) {
|
||||
final boolean enabled = preferred.tearFree();
|
||||
SDLVideo.SDL_GL_SetSwapInterval(enabled ? 1 : 0);
|
||||
return enabled ? VSyncMode.ON : VSyncMode.OFF;
|
||||
final int want = preferred.tearFree() ? 1 : 0;
|
||||
final boolean setOk = SDLVideo.SDL_GL_SetSwapInterval(want);
|
||||
final String setError = setOk ? null : SDLError.SDL_GetError();
|
||||
int got = want;
|
||||
try (MemoryStack stack = MemoryStack.stackPush()) {
|
||||
final IntBuffer readback = stack.callocInt(1);
|
||||
if (SDLVideo.SDL_GL_GetSwapInterval(readback)) got = readback.get(0);
|
||||
}
|
||||
if (!setOk) {
|
||||
LOGGER.warn("SDL_GL_SetSwapInterval({}) failed: {}", want, setError);
|
||||
} else if (got != want && !(want == 1 && got == -1)) {
|
||||
LOGGER.warn("Requested swap interval {} but the driver reports {}", want, got);
|
||||
}
|
||||
swapIntervalHonored = want == 0 || got != 0;
|
||||
return want != 0 && swapIntervalHonored ? VSyncMode.ON : VSyncMode.OFF;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int queryDisplayRefreshRateHz() {
|
||||
protected boolean applyHonored() {
|
||||
return swapIntervalHonored;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected long queryRefreshPeriodNanos() {
|
||||
final long window = Display.getWindow();
|
||||
final int displayId = window == 0 ? SDLVideo.SDL_GetPrimaryDisplay() : SDLVideo.SDL_GetDisplayForWindow(window);
|
||||
if (displayId == 0) return 0;
|
||||
if (displayId == 0) return 0L;
|
||||
final SDL_DisplayMode mode = SDLVideo.SDL_GetCurrentDisplayMode(displayId);
|
||||
if (mode == null) return 0;
|
||||
return refreshHzFrom(mode.refresh_rate_numerator(), mode.refresh_rate_denominator(), mode.refresh_rate());
|
||||
if (mode == null) return 0L;
|
||||
return periodFromRational(mode.refresh_rate_numerator(), mode.refresh_rate_denominator(), mode.refresh_rate());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parkNanos(long nanos) {
|
||||
SDLTimer.SDL_DelayNS(nanos);
|
||||
}
|
||||
|
||||
private static final class Pump {
|
||||
static final MainThreadPump INSTANCE = new MainThreadPump(MainStartOnFirstThread.instance(), MainThreadExec::isMainThread, Lwjgl3ifyEventLoop::pumpEvents, MainThreadPump::pollInput);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pumpDisplayMessages() {
|
||||
Pump.INSTANCE.pumpMessages();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Executable
+49
@@ -0,0 +1,49 @@
|
||||
#!/usr/bin/env bash
|
||||
# Usage: scripts/flyby-bench.sh <name> ffp|pack [flyby-capture.sh args...]
|
||||
# Env: FLYBY_SCENE, FLYBY_ORIGIN, TIME_OF_DAY, QUICKPLAY_WORLD, TRACY_CAPTURE, SDLGPU, RENDER_DISTANCE.
|
||||
# Forces particles:0 for the run and restores it after exit; the game rewrites options.txt on quit.
|
||||
# A worktree needs tracy-client/src/main/resources/natives copied in.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
NAME="${1:?usage: flyby-bench.sh <name> ffp|pack [extra gradle args...]}"
|
||||
MODE="${2:?usage: flyby-bench.sh <name> ffp|pack [extra gradle args...]}"
|
||||
shift 2
|
||||
|
||||
OUT_DIR="tmp/bench"
|
||||
OPTIONS="run/client/options.txt"
|
||||
SHADERS="run/client/config/shaders.properties"
|
||||
mkdir -p "$OUT_DIR"
|
||||
|
||||
PARTICLES_BEFORE="$(grep -E '^particles:' "$OPTIONS" | cut -d: -f2)"
|
||||
SHADERS_BEFORE="$(grep -E '^enableShaders=' "$SHADERS" | cut -d= -f2)"
|
||||
if [ -n "${RENDER_DISTANCE:-}" ]; then RENDER_DISTANCE_BEFORE="$(grep -E '^renderDistance:' "$OPTIONS" | cut -d: -f2)"; fi
|
||||
|
||||
restore() {
|
||||
sed -i '' "s/^particles:.*/particles:${PARTICLES_BEFORE}/" "$OPTIONS"
|
||||
sed -i '' "s/^enableShaders=.*/enableShaders=${SHADERS_BEFORE}/" "$SHADERS"
|
||||
if [ -n "${RENDER_DISTANCE:-}" ]; then
|
||||
sed -i '' "s/^renderDistance:.*/renderDistance:${RENDER_DISTANCE_BEFORE}/" "$OPTIONS"
|
||||
fi
|
||||
}
|
||||
trap restore EXIT
|
||||
|
||||
sed -i '' 's/^particles:.*/particles:0/' "$OPTIONS"
|
||||
case "$MODE" in
|
||||
ffp) sed -i '' 's/^enableShaders=.*/enableShaders=false/' "$SHADERS" ;;
|
||||
pack) sed -i '' 's/^enableShaders=.*/enableShaders=true/' "$SHADERS" ;;
|
||||
*) echo "error: mode must be ffp or pack" >&2; exit 1 ;;
|
||||
esac
|
||||
if [ -n "${RENDER_DISTANCE:-}" ]; then
|
||||
sed -i '' "s/^renderDistance:.*/renderDistance:${RENDER_DISTANCE}/" "$OPTIONS"
|
||||
fi
|
||||
|
||||
export FLYBY_SCENE="${FLYBY_SCENE:-scripts/flyby-scenes/entities-x8.txt}"
|
||||
export QUICKPLAY_WORLD="${QUICKPLAY_WORLD:-Flat World - No Mods}"
|
||||
if [ "$QUICKPLAY_WORLD" = "Flat World - No Mods" ]; then export FLYBY_ORIGIN="${FLYBY_ORIGIN-1509.5,-924.5,0}"; fi
|
||||
export TRACY_CAPTURE="${TRACY_CAPTURE:-$HOME/dev/mc/tracy/capture/build/tracy-capture}"
|
||||
export SDLGPU="${SDLGPU:-1}"
|
||||
|
||||
echo "=== $NAME ($MODE) start $(date '+%H:%M:%S')"
|
||||
scripts/flyby-capture.sh "$OUT_DIR/$NAME.tracy" circuit 128 -Dangelica.flyby.timeOfDay="${TIME_OF_DAY:-6000}" "$@" >| "$OUT_DIR/$NAME.console.log" 2>&1
|
||||
echo "=== $NAME exit=$? $(date '+%H:%M:%S'), $(grep -c 'did not execute' "$OUT_DIR/$NAME.console.log" || true) failed scene lines"
|
||||
@@ -5,25 +5,63 @@
|
||||
# * Game waits for the tracy connection, runs the route, returns to the original coordinates and exits.
|
||||
# * tracy-capture finalizes
|
||||
#
|
||||
# Usage: scripts/flyby-capture.sh <output.tracy> [route] [length]
|
||||
# Usage: scripts/flyby-capture.sh <output.tracy> [route] [length] [extra gradle args...]
|
||||
# route - straight | pan | circuit | static
|
||||
# length - Units: blocks (straight), blocks per leg (circuit), degrees (pan), ticks (static).
|
||||
# extra - passed through to gradle; -Dangelica.* reaches the client (e.g. -Dangelica.flyby.timeOfDay=1500).
|
||||
#
|
||||
# FLYBY_SPEED - blocks per tick. (0.5 ~= creative flight).
|
||||
# FLYBY_SPEED - blocks per tick. (0.5 ~= creative flight).
|
||||
# FLYBY_PACING - UNCAPPED | CONFIGURED. Passed through when set.
|
||||
# FLYBY_ORIGIN - x,z[,yaw] flight start. Unset or empty starts where the player is.
|
||||
# QUICKPLAY_WORLD - save to auto-load (default: latest).
|
||||
# FLYBY_SCENE - server command file run at flyby start.
|
||||
# SDLGPU=0|1 - force OpenGL (0) or SDL-GPU (1); unset uses the build default.
|
||||
#
|
||||
# Requires tracy-capture on PATH (TRACY_CAPTURE=/path/to/tracy-capture).
|
||||
# Launch the game to the main menu and load your benchmark save; the run starts once the world is up.
|
||||
# QuickPlay loads QUICKPLAY_WORLD on launch; the run starts once the world is up.
|
||||
#
|
||||
# Ensure the chunks in the route have been generated before capturing
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
OUT="${1:?usage: flyby-capture.sh <output.tracy> [route] [length]}"
|
||||
OUT="${1:?usage: flyby-capture.sh <output.tracy> [route] [length] [extra gradle args...]}"
|
||||
ROUTE="${2:-straight}"
|
||||
LENGTH="${3:-0}"
|
||||
if [ $# -ge 3 ]; then
|
||||
shift 3
|
||||
else
|
||||
shift $#
|
||||
fi
|
||||
WARMUP="${FLYBY_WARMUP:-400}"
|
||||
TRACY_CAPTURE="${TRACY_CAPTURE:-tracy-capture}"
|
||||
|
||||
BACKEND_ARGS=()
|
||||
if [ "${SDLGPU:-}" = "1" ]; then
|
||||
BACKEND_ARGS+=(-Dangelica.sdlgpu.enable=true)
|
||||
elif [ "${SDLGPU:-}" = "0" ]; then
|
||||
BACKEND_ARGS+=(-Dangelica.sdlgpu.enable=false)
|
||||
fi
|
||||
if [ -n "${FLYBY_PACING:-}" ]; then
|
||||
BACKEND_ARGS+=(-Dangelica.flyby.pacing="$FLYBY_PACING")
|
||||
fi
|
||||
if [ -n "${FLYBY_ORIGIN:-}" ]; then
|
||||
BACKEND_ARGS+=(-Dangelica.flyby.origin="$FLYBY_ORIGIN")
|
||||
fi
|
||||
|
||||
SCENE_ABS=""
|
||||
SCENE_ARGS=()
|
||||
if [ -n "${FLYBY_SCENE:-}" ]; then
|
||||
case "$FLYBY_SCENE" in
|
||||
/*) SCENE_ABS="$FLYBY_SCENE" ;;
|
||||
*) SCENE_ABS="$PWD/$FLYBY_SCENE" ;;
|
||||
esac
|
||||
if [ ! -f "$SCENE_ABS" ]; then
|
||||
echo "error: FLYBY_SCENE file not found: $SCENE_ABS" >&2
|
||||
exit 1
|
||||
fi
|
||||
SCENE_ARGS+=(-Dangelica.flyby.commands="$SCENE_ABS")
|
||||
fi
|
||||
|
||||
if ! command -v "$TRACY_CAPTURE" >/dev/null 2>&1 && [ ! -x "$TRACY_CAPTURE" ]; then
|
||||
cat >&2 <<'EOF'
|
||||
error: tracy-capture not found.
|
||||
@@ -55,8 +93,7 @@ cleanup() {
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
echo "==> launching game (route=$ROUTE warmup=$WARMUP length=${LENGTH:-default})"
|
||||
echo " load your benchmark save once the main menu appears"
|
||||
echo "==> launching game (route=$ROUTE warmup=$WARMUP length=${LENGTH:-default} scene=${SCENE_ABS:-none})"
|
||||
|
||||
./gradlew runClient25 --console=plain \
|
||||
-Dangelica.flyby.route="$ROUTE" \
|
||||
@@ -64,7 +101,11 @@ echo " load your benchmark save once the main menu appears"
|
||||
-Dangelica.flyby.speed="${FLYBY_SPEED:-0}" \
|
||||
-Dangelica.flyby.warmupTicks="$WARMUP" \
|
||||
-Dangelica.flyby.waitForTracy=true \
|
||||
-Dangelica.flyby.exitWhenDone=true
|
||||
-Dangelica.flyby.exitWhenDone=true \
|
||||
-DquickPlaySingleplayer="${QUICKPLAY_WORLD:-latest}" \
|
||||
${BACKEND_ARGS[@]+"${BACKEND_ARGS[@]}"} \
|
||||
${SCENE_ARGS[@]+"${SCENE_ARGS[@]}"} \
|
||||
"$@"
|
||||
|
||||
echo "==> game exited, finalising capture"
|
||||
cleanup
|
||||
|
||||
Executable
+158
@@ -0,0 +1,158 @@
|
||||
#!/usr/bin/env python3
|
||||
import csv
|
||||
import io
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
CSVEXPORT = os.environ.get("TRACY_CSVEXPORT", os.path.expanduser("~/dev/mc/tracy/csvexport/build/tracy-csvexport"))
|
||||
|
||||
PLOTS = [
|
||||
"gl.drawCalls", "gl.listPlaybacks", "gl.streamDraws", "entitiesRendered", "blockEntitiesRendered", "tesr.modelParts", "tesr.retainedDraws", "tesr.cacheMisses", "tesr.instancedDraws", "tesr.instancedInstances", "tesr.cubeInstances",
|
||||
"tesr.streamedInstances", "tesr.texMatrixRuns", "tesr.liveFallbacks", "tesr.bail.foreignProgram",
|
||||
"tesr.bail.material", "tesr.bail.texture", "entity.shadowQuads", "entity.shadowDraws", "items.instanced", "items.fallback", "items.glintInstanced",
|
||||
"items.bail.ineligible", "items.bail.material", "items.bail.isbrh", "items.bail.blockState", "items.bail.notAllowed", "items.bail.template", "items.bail.queue",
|
||||
"particles.direct", "particles.captured", "particles.spilled", "particles.undecodable", "particles.draws", "sdl.renderPasses", "sdl.submits", "sdl.gpuWaitUs",
|
||||
"sdl.acquireWaitUs",
|
||||
]
|
||||
ZONES = ["entityModelParts", "tesrBatch", "tesrInstanced", "entityLayerLoop", "tesrOpaque", "particlePass"]
|
||||
CLOCK = "gl.drawCalls"
|
||||
|
||||
|
||||
def run(args):
|
||||
out = subprocess.run([CSVEXPORT] + args, capture_output=True, text=True)
|
||||
if out.returncode != 0:
|
||||
sys.exit(out.stderr.strip() or f"csvexport failed: {' '.join(args)}")
|
||||
return list(csv.DictReader(io.StringIO(out.stdout)))
|
||||
|
||||
|
||||
def windows(path):
|
||||
marks = []
|
||||
for row in run(["-m", path]):
|
||||
name = row["MessageName"]
|
||||
if name.startswith("flyby leg ") or name.startswith("flyby turn ") or name.startswith("flyby end"):
|
||||
marks.append((int(row["total_ns"]), name))
|
||||
marks.sort()
|
||||
result = {}
|
||||
for i, (t, name) in enumerate(marks):
|
||||
if not name.startswith("flyby leg "):
|
||||
continue
|
||||
leg = int(name.split()[-1])
|
||||
if leg in result:
|
||||
continue
|
||||
end = marks[i + 1][0] if i + 1 < len(marks) else None
|
||||
result[leg] = (t, end)
|
||||
return result
|
||||
|
||||
|
||||
def inside(t, window):
|
||||
start, end = window
|
||||
return t >= start and (end is None or t < end)
|
||||
|
||||
|
||||
def stats(values):
|
||||
if not values:
|
||||
return None
|
||||
s = sorted(values)
|
||||
n = len(s)
|
||||
return (sum(s) / n, s[n // 2], s[min(n - 1, int(n * 0.99))], s[-1], n)
|
||||
|
||||
|
||||
def analyze(path):
|
||||
legs = windows(path)
|
||||
plots = {name: {leg: [] for leg in legs} for name in PLOTS + ["Frame"]}
|
||||
last_clock = None
|
||||
for row in run(["-u", "-p", "-f", "zzz-no-such-zone", path]):
|
||||
name = row["name"]
|
||||
if name not in plots:
|
||||
continue
|
||||
t = int(row["ns_since_start"])
|
||||
if name == CLOCK:
|
||||
if last_clock is not None:
|
||||
for leg, window in legs.items():
|
||||
if inside(t, window):
|
||||
plots["Frame"][leg].append((t - last_clock) / 1e6)
|
||||
break
|
||||
last_clock = t
|
||||
for leg, window in legs.items():
|
||||
if inside(t, window):
|
||||
plots[name][leg].append(float(row["value"]))
|
||||
break
|
||||
zones = {name: {leg: [] for leg in legs} for name in ZONES}
|
||||
for name in ZONES:
|
||||
for row in run(["-u", "-c", "-f", name, path]):
|
||||
if row["name"] != name:
|
||||
continue
|
||||
t = int(row["ns_since_start"])
|
||||
for leg, window in legs.items():
|
||||
if inside(t, window):
|
||||
zones[name][leg].append(int(row["exec_time_ns"]))
|
||||
break
|
||||
return legs, plots, zones
|
||||
|
||||
|
||||
def fmt(v, digits=1):
|
||||
if v is None:
|
||||
return "-"
|
||||
if isinstance(v, float):
|
||||
return f"{v:.{digits}f}"
|
||||
return str(v)
|
||||
|
||||
|
||||
def main(paths):
|
||||
labels = [os.path.splitext(os.path.basename(p))[0] for p in paths]
|
||||
data = [analyze(p) for p in paths]
|
||||
all_legs = sorted({leg for legs, _, _ in data for leg in legs})
|
||||
width = max(12, max(len(l) for l in labels) + 2)
|
||||
for leg in all_legs:
|
||||
print(f"\n== leg {leg}")
|
||||
print("metric".ljust(28) + "".join(l.rjust(width) for l in labels))
|
||||
|
||||
def line(metric, cells):
|
||||
print(metric.ljust(28) + "".join(fmt(c).rjust(width) for c in cells))
|
||||
|
||||
frame = [stats(plots["Frame"].get(leg, [])) for _, plots, _ in data]
|
||||
line("frames", [f[4] if f else None for f in frame])
|
||||
line("frame avg ms", [f[0] if f else None for f in frame])
|
||||
line("frame p50 ms", [f[1] if f else None for f in frame])
|
||||
line("frame p99 ms", [f[2] if f else None for f in frame])
|
||||
line("frame max ms", [f[3] if f else None for f in frame])
|
||||
busy = []
|
||||
for (_, plots, _), f in zip(data, frame):
|
||||
acquire = plots["sdl.acquireWaitUs"].get(leg, [])
|
||||
gpu = plots["sdl.gpuWaitUs"].get(leg, [])
|
||||
if f and acquire:
|
||||
busy.append(f[0] - sum(acquire) / len(acquire) / 1000.0 - (sum(gpu) / len(gpu) / 1000.0 if gpu else 0.0))
|
||||
else:
|
||||
busy.append(None)
|
||||
line("busy avg ms", busy)
|
||||
for name in PLOTS:
|
||||
per = []
|
||||
for _, plots, _ in data:
|
||||
v = plots[name].get(leg, [])
|
||||
per.append(sum(v) / len(v) if v else None)
|
||||
if all(c is None for c in per):
|
||||
continue
|
||||
line(name + " /frame", per)
|
||||
for name in ZONES:
|
||||
per = []
|
||||
for legs, _, zones in data:
|
||||
v = zones[name].get(leg, [])
|
||||
frames = len(plots_frames(data, legs, leg))
|
||||
per.append((sum(v) / 1000.0 / frames) if v and frames else None)
|
||||
if all(c is None for c in per):
|
||||
continue
|
||||
line(name + " us/frame", per)
|
||||
|
||||
|
||||
def plots_frames(data, legs, leg):
|
||||
for l, plots, _ in data:
|
||||
if l is legs:
|
||||
return plots["Frame"].get(leg, [])
|
||||
return []
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) < 2:
|
||||
sys.exit("usage: flyby-compare.py <capture.tracy> [more captures...]")
|
||||
main(sys.argv[1:])
|
||||
@@ -0,0 +1,881 @@
|
||||
# entities.txt at 8x density. Summons are relative to the flight start; start facing south (yaw 0).
|
||||
|
||||
# gamerules
|
||||
difficulty 1
|
||||
gamerule doMobSpawning false
|
||||
gamerule doFireTick false
|
||||
gamerule mobGriefing false
|
||||
|
||||
# hurt-flash + glint zombies, x=-5 leg
|
||||
summon Zombie ~-17 ~ ~10 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-14 ~ ~10 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-11 ~ ~10 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-8 ~ ~10 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-5 ~ ~10 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-2 ~ ~10 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~1 ~ ~10 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~4 ~ ~10 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-17 ~ ~14 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-14 ~ ~14 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-11 ~ ~14 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-8 ~ ~14 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-5 ~ ~14 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-2 ~ ~14 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~1 ~ ~14 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~4 ~ ~14 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-17 ~ ~18 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-14 ~ ~18 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-11 ~ ~18 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-8 ~ ~18 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-5 ~ ~18 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-2 ~ ~18 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~1 ~ ~18 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~4 ~ ~18 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-17 ~ ~22 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-14 ~ ~22 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-11 ~ ~22 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-8 ~ ~22 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-5 ~ ~22 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-2 ~ ~22 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~1 ~ ~22 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~4 ~ ~22 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-17 ~ ~26 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-14 ~ ~26 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-11 ~ ~26 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-8 ~ ~26 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-5 ~ ~26 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-2 ~ ~26 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~1 ~ ~26 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~4 ~ ~26 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-17 ~ ~30 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-14 ~ ~30 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-11 ~ ~30 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-8 ~ ~30 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-5 ~ ~30 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-2 ~ ~30 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~1 ~ ~30 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~4 ~ ~30 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-17 ~ ~34 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-14 ~ ~34 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-11 ~ ~34 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-8 ~ ~34 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-5 ~ ~34 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-2 ~ ~34 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~1 ~ ~34 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~4 ~ ~34 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-17 ~ ~38 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-14 ~ ~38 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-11 ~ ~38 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-8 ~ ~38 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-5 ~ ~38 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-2 ~ ~38 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~1 ~ ~38 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~4 ~ ~38 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
|
||||
# pigzombies, x=6 leg
|
||||
summon PigZombie ~-6 ~ ~10 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~-3 ~ ~10 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~ ~ ~10 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~3 ~ ~10 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~6 ~ ~10 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~9 ~ ~10 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~12 ~ ~10 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~15 ~ ~10 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~-6 ~ ~14 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~-3 ~ ~14 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~ ~ ~14 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~3 ~ ~14 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~6 ~ ~14 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~9 ~ ~14 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~12 ~ ~14 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~15 ~ ~14 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~-6 ~ ~18 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~-3 ~ ~18 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~ ~ ~18 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~3 ~ ~18 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~6 ~ ~18 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~9 ~ ~18 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~12 ~ ~18 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~15 ~ ~18 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~-6 ~ ~22 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~-3 ~ ~22 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~ ~ ~22 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~3 ~ ~22 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~6 ~ ~22 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~9 ~ ~22 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~12 ~ ~22 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~15 ~ ~22 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~-6 ~ ~26 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~-3 ~ ~26 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~ ~ ~26 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~3 ~ ~26 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~6 ~ ~26 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~9 ~ ~26 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~12 ~ ~26 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~15 ~ ~26 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~-6 ~ ~30 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~-3 ~ ~30 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~ ~ ~30 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~3 ~ ~30 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~6 ~ ~30 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~9 ~ ~30 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~12 ~ ~30 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~15 ~ ~30 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~-6 ~ ~34 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~-3 ~ ~34 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~ ~ ~34 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~3 ~ ~34 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~6 ~ ~34 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~9 ~ ~34 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~12 ~ ~34 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~15 ~ ~34 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~-6 ~ ~38 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~-3 ~ ~38 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~ ~ ~38 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~3 ~ ~38 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~6 ~ ~38 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~9 ~ ~38 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~12 ~ ~38 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~15 ~ ~38 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
|
||||
# passive mobs, grouped by species
|
||||
summon Cow ~-17 ~ ~44 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-14 ~ ~44 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-11 ~ ~44 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-8 ~ ~44 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-5 ~ ~44 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-2 ~ ~44 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~1 ~ ~44 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~4 ~ ~44 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-17 ~ ~48 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-14 ~ ~48 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-11 ~ ~48 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-8 ~ ~48 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-5 ~ ~48 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-2 ~ ~48 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~1 ~ ~48 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~4 ~ ~48 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-17 ~ ~52 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-14 ~ ~52 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-11 ~ ~52 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-8 ~ ~52 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-5 ~ ~52 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-2 ~ ~52 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~1 ~ ~52 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~4 ~ ~52 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-17 ~ ~56 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-14 ~ ~56 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-11 ~ ~56 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-8 ~ ~56 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-5 ~ ~56 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-2 ~ ~56 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~1 ~ ~56 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~4 ~ ~56 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-17 ~ ~60 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-14 ~ ~60 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-11 ~ ~60 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-8 ~ ~60 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-5 ~ ~60 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-2 ~ ~60 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~1 ~ ~60 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~4 ~ ~60 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-17 ~ ~64 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-14 ~ ~64 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-11 ~ ~64 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-8 ~ ~64 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-5 ~ ~64 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-2 ~ ~64 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~1 ~ ~64 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~4 ~ ~64 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Pig ~-6 ~ ~44 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Pig ~-3 ~ ~44 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Pig ~ ~ ~44 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Pig ~3 ~ ~44 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Pig ~6 ~ ~44 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Pig ~9 ~ ~44 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Pig ~12 ~ ~44 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Pig ~15 ~ ~44 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Pig ~-6 ~ ~48 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Pig ~-3 ~ ~48 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Pig ~ ~ ~48 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Pig ~3 ~ ~48 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Pig ~6 ~ ~48 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Pig ~9 ~ ~48 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Pig ~12 ~ ~48 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Pig ~15 ~ ~48 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Pig ~-6 ~ ~52 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Pig ~-3 ~ ~52 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Pig ~ ~ ~52 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Pig ~3 ~ ~52 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Pig ~6 ~ ~52 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Pig ~9 ~ ~52 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Pig ~12 ~ ~52 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Pig ~15 ~ ~52 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Pig ~-6 ~ ~56 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Pig ~-3 ~ ~56 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Pig ~ ~ ~56 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Pig ~3 ~ ~56 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Pig ~6 ~ ~56 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Pig ~9 ~ ~56 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Pig ~12 ~ ~56 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Pig ~15 ~ ~56 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Pig ~-6 ~ ~60 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Pig ~-3 ~ ~60 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Pig ~ ~ ~60 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Pig ~3 ~ ~60 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Pig ~6 ~ ~60 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Pig ~9 ~ ~60 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Pig ~12 ~ ~60 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Pig ~15 ~ ~60 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Pig ~-6 ~ ~64 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Pig ~-3 ~ ~64 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Pig ~ ~ ~64 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Pig ~3 ~ ~64 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Pig ~6 ~ ~64 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Pig ~9 ~ ~64 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Pig ~12 ~ ~64 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Pig ~15 ~ ~64 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Sheep ~-21 ~ ~44 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Sheep ~-18 ~ ~44 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Sheep ~-15 ~ ~44 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Sheep ~-12 ~ ~44 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Sheep ~-9 ~ ~44 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Sheep ~-6 ~ ~44 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Sheep ~-3 ~ ~44 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Sheep ~ ~ ~44 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Sheep ~-21 ~ ~48 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Sheep ~-18 ~ ~48 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Sheep ~-15 ~ ~48 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Sheep ~-12 ~ ~48 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Sheep ~-9 ~ ~48 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Sheep ~-6 ~ ~48 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Sheep ~-3 ~ ~48 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Sheep ~ ~ ~48 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Sheep ~-21 ~ ~52 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Sheep ~-18 ~ ~52 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Sheep ~-15 ~ ~52 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Sheep ~-12 ~ ~52 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Sheep ~-9 ~ ~52 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Sheep ~-6 ~ ~52 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Sheep ~-3 ~ ~52 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Sheep ~ ~ ~52 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Sheep ~-21 ~ ~56 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Sheep ~-18 ~ ~56 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Sheep ~-15 ~ ~56 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Sheep ~-12 ~ ~56 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Sheep ~-9 ~ ~56 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Sheep ~-6 ~ ~56 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Sheep ~-3 ~ ~56 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Sheep ~ ~ ~56 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Sheep ~-21 ~ ~60 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Sheep ~-18 ~ ~60 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Sheep ~-15 ~ ~60 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Sheep ~-12 ~ ~60 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Sheep ~-9 ~ ~60 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Sheep ~-6 ~ ~60 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Sheep ~-3 ~ ~60 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Sheep ~ ~ ~60 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Sheep ~-21 ~ ~64 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Sheep ~-18 ~ ~64 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Sheep ~-15 ~ ~64 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Sheep ~-12 ~ ~64 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Sheep ~-9 ~ ~64 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Sheep ~-6 ~ ~64 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Sheep ~-3 ~ ~64 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Sheep ~ ~ ~64 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Chicken ~-2 ~ ~44 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Chicken ~1 ~ ~44 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Chicken ~4 ~ ~44 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Chicken ~7 ~ ~44 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Chicken ~10 ~ ~44 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Chicken ~13 ~ ~44 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Chicken ~16 ~ ~44 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Chicken ~19 ~ ~44 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Chicken ~-2 ~ ~48 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Chicken ~1 ~ ~48 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Chicken ~4 ~ ~48 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Chicken ~7 ~ ~48 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Chicken ~10 ~ ~48 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Chicken ~13 ~ ~48 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Chicken ~16 ~ ~48 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Chicken ~19 ~ ~48 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Chicken ~-2 ~ ~52 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Chicken ~1 ~ ~52 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Chicken ~4 ~ ~52 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Chicken ~7 ~ ~52 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Chicken ~10 ~ ~52 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Chicken ~13 ~ ~52 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Chicken ~16 ~ ~52 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Chicken ~19 ~ ~52 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Chicken ~-2 ~ ~56 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Chicken ~1 ~ ~56 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Chicken ~4 ~ ~56 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Chicken ~7 ~ ~56 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Chicken ~10 ~ ~56 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Chicken ~13 ~ ~56 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Chicken ~16 ~ ~56 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Chicken ~19 ~ ~56 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Chicken ~-2 ~ ~60 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Chicken ~1 ~ ~60 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Chicken ~4 ~ ~60 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Chicken ~7 ~ ~60 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Chicken ~10 ~ ~60 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Chicken ~13 ~ ~60 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Chicken ~16 ~ ~60 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Chicken ~19 ~ ~60 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Chicken ~-2 ~ ~64 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Chicken ~1 ~ ~64 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Chicken ~4 ~ ~64 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Chicken ~7 ~ ~64 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Chicken ~10 ~ ~64 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Chicken ~13 ~ ~64 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Chicken ~16 ~ ~64 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Chicken ~19 ~ ~64 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
|
||||
# villagers
|
||||
summon Villager ~-17 ~ ~70 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Villager ~-14 ~ ~70 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Villager ~-11 ~ ~70 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Villager ~-8 ~ ~70 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Villager ~-5 ~ ~70 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Villager ~-2 ~ ~70 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Villager ~1 ~ ~70 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Villager ~4 ~ ~70 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Villager ~-17 ~ ~74 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Villager ~-14 ~ ~74 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Villager ~-11 ~ ~74 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Villager ~-8 ~ ~74 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Villager ~-5 ~ ~74 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Villager ~-2 ~ ~74 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Villager ~1 ~ ~74 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Villager ~4 ~ ~74 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Villager ~-17 ~ ~78 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Villager ~-14 ~ ~78 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Villager ~-11 ~ ~78 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Villager ~-8 ~ ~78 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Villager ~-5 ~ ~78 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Villager ~-2 ~ ~78 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Villager ~1 ~ ~78 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Villager ~4 ~ ~78 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Villager ~-17 ~ ~82 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Villager ~-14 ~ ~82 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Villager ~-11 ~ ~82 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Villager ~-8 ~ ~82 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Villager ~-5 ~ ~82 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Villager ~-2 ~ ~82 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Villager ~1 ~ ~82 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Villager ~4 ~ ~82 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Villager ~-17 ~ ~86 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Villager ~-14 ~ ~86 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Villager ~-11 ~ ~86 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Villager ~-8 ~ ~86 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Villager ~-5 ~ ~86 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Villager ~-2 ~ ~86 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Villager ~1 ~ ~86 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Villager ~4 ~ ~86 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Villager ~-17 ~ ~90 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Villager ~-14 ~ ~90 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Villager ~-11 ~ ~90 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Villager ~-8 ~ ~90 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Villager ~-5 ~ ~90 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Villager ~-2 ~ ~90 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Villager ~1 ~ ~90 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Villager ~4 ~ ~90 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
|
||||
# dropped items, x=2 leg, 12 item types cycled twice
|
||||
summon Item ~-10 ~ ~60 {Item:{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-7 ~ ~60 {Item:{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-4 ~ ~60 {Item:{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-1 ~ ~60 {Item:{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~2 ~ ~60 {Item:{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~5 ~ ~60 {Item:{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~8 ~ ~60 {Item:{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~11 ~ ~60 {Item:{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-10 ~ ~62 {Item:{id:264s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-7 ~ ~62 {Item:{id:264s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-4 ~ ~62 {Item:{id:264s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-1 ~ ~62 {Item:{id:264s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~2 ~ ~62 {Item:{id:264s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~5 ~ ~62 {Item:{id:264s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~8 ~ ~62 {Item:{id:264s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~11 ~ ~62 {Item:{id:264s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-10 ~ ~64 {Item:{id:265s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-7 ~ ~64 {Item:{id:265s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-4 ~ ~64 {Item:{id:265s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-1 ~ ~64 {Item:{id:265s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~2 ~ ~64 {Item:{id:265s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~5 ~ ~64 {Item:{id:265s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~8 ~ ~64 {Item:{id:265s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~11 ~ ~64 {Item:{id:265s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-10 ~ ~66 {Item:{id:261s,Count:1b,tag:{ench:[{id:48s,lvl:1s}]}},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-7 ~ ~66 {Item:{id:261s,Count:1b,tag:{ench:[{id:48s,lvl:1s}]}},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-4 ~ ~66 {Item:{id:261s,Count:1b,tag:{ench:[{id:48s,lvl:1s}]}},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-1 ~ ~66 {Item:{id:261s,Count:1b,tag:{ench:[{id:48s,lvl:1s}]}},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~2 ~ ~66 {Item:{id:261s,Count:1b,tag:{ench:[{id:48s,lvl:1s}]}},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~5 ~ ~66 {Item:{id:261s,Count:1b,tag:{ench:[{id:48s,lvl:1s}]}},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~8 ~ ~66 {Item:{id:261s,Count:1b,tag:{ench:[{id:48s,lvl:1s}]}},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~11 ~ ~66 {Item:{id:261s,Count:1b,tag:{ench:[{id:48s,lvl:1s}]}},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-10 ~ ~68 {Item:{id:322s,Count:1b,Damage:1s},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-7 ~ ~68 {Item:{id:322s,Count:1b,Damage:1s},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-4 ~ ~68 {Item:{id:322s,Count:1b,Damage:1s},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-1 ~ ~68 {Item:{id:322s,Count:1b,Damage:1s},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~2 ~ ~68 {Item:{id:322s,Count:1b,Damage:1s},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~5 ~ ~68 {Item:{id:322s,Count:1b,Damage:1s},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~8 ~ ~68 {Item:{id:322s,Count:1b,Damage:1s},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~11 ~ ~68 {Item:{id:322s,Count:1b,Damage:1s},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-10 ~ ~70 {Item:{id:403s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-7 ~ ~70 {Item:{id:403s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-4 ~ ~70 {Item:{id:403s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-1 ~ ~70 {Item:{id:403s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~2 ~ ~70 {Item:{id:403s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~5 ~ ~70 {Item:{id:403s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~8 ~ ~70 {Item:{id:403s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~11 ~ ~70 {Item:{id:403s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-10 ~ ~72 {Item:{id:373s,Count:1b,Damage:8193s},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-7 ~ ~72 {Item:{id:373s,Count:1b,Damage:8193s},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-4 ~ ~72 {Item:{id:373s,Count:1b,Damage:8193s},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-1 ~ ~72 {Item:{id:373s,Count:1b,Damage:8193s},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~2 ~ ~72 {Item:{id:373s,Count:1b,Damage:8193s},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~5 ~ ~72 {Item:{id:373s,Count:1b,Damage:8193s},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~8 ~ ~72 {Item:{id:373s,Count:1b,Damage:8193s},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~11 ~ ~72 {Item:{id:373s,Count:1b,Damage:8193s},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-10 ~ ~74 {Item:{id:299s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-7 ~ ~74 {Item:{id:299s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-4 ~ ~74 {Item:{id:299s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-1 ~ ~74 {Item:{id:299s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~2 ~ ~74 {Item:{id:299s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~5 ~ ~74 {Item:{id:299s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~8 ~ ~74 {Item:{id:299s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~11 ~ ~74 {Item:{id:299s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-10 ~ ~76 {Item:{id:1s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-7 ~ ~76 {Item:{id:1s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-4 ~ ~76 {Item:{id:1s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-1 ~ ~76 {Item:{id:1s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~2 ~ ~76 {Item:{id:1s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~5 ~ ~76 {Item:{id:1s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~8 ~ ~76 {Item:{id:1s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~11 ~ ~76 {Item:{id:1s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-10 ~ ~78 {Item:{id:20s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-7 ~ ~78 {Item:{id:20s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-4 ~ ~78 {Item:{id:20s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-1 ~ ~78 {Item:{id:20s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~2 ~ ~78 {Item:{id:20s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~5 ~ ~78 {Item:{id:20s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~8 ~ ~78 {Item:{id:20s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~11 ~ ~78 {Item:{id:20s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-10 ~ ~80 {Item:{id:50s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-7 ~ ~80 {Item:{id:50s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-4 ~ ~80 {Item:{id:50s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-1 ~ ~80 {Item:{id:50s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~2 ~ ~80 {Item:{id:50s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~5 ~ ~80 {Item:{id:50s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~8 ~ ~80 {Item:{id:50s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~11 ~ ~80 {Item:{id:50s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-10 ~ ~82 {Item:{id:4s,Count:64b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-7 ~ ~82 {Item:{id:4s,Count:64b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-4 ~ ~82 {Item:{id:4s,Count:64b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-1 ~ ~82 {Item:{id:4s,Count:64b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~2 ~ ~82 {Item:{id:4s,Count:64b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~5 ~ ~82 {Item:{id:4s,Count:64b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~8 ~ ~82 {Item:{id:4s,Count:64b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~11 ~ ~82 {Item:{id:4s,Count:64b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-10 ~ ~84 {Item:{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-7 ~ ~84 {Item:{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-4 ~ ~84 {Item:{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-1 ~ ~84 {Item:{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~2 ~ ~84 {Item:{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~5 ~ ~84 {Item:{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~8 ~ ~84 {Item:{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~11 ~ ~84 {Item:{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-10 ~ ~86 {Item:{id:264s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-7 ~ ~86 {Item:{id:264s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-4 ~ ~86 {Item:{id:264s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-1 ~ ~86 {Item:{id:264s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~2 ~ ~86 {Item:{id:264s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~5 ~ ~86 {Item:{id:264s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~8 ~ ~86 {Item:{id:264s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~11 ~ ~86 {Item:{id:264s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-10 ~ ~88 {Item:{id:265s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-7 ~ ~88 {Item:{id:265s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-4 ~ ~88 {Item:{id:265s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-1 ~ ~88 {Item:{id:265s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~2 ~ ~88 {Item:{id:265s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~5 ~ ~88 {Item:{id:265s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~8 ~ ~88 {Item:{id:265s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~11 ~ ~88 {Item:{id:265s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-10 ~ ~90 {Item:{id:261s,Count:1b,tag:{ench:[{id:48s,lvl:1s}]}},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-7 ~ ~90 {Item:{id:261s,Count:1b,tag:{ench:[{id:48s,lvl:1s}]}},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-4 ~ ~90 {Item:{id:261s,Count:1b,tag:{ench:[{id:48s,lvl:1s}]}},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-1 ~ ~90 {Item:{id:261s,Count:1b,tag:{ench:[{id:48s,lvl:1s}]}},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~2 ~ ~90 {Item:{id:261s,Count:1b,tag:{ench:[{id:48s,lvl:1s}]}},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~5 ~ ~90 {Item:{id:261s,Count:1b,tag:{ench:[{id:48s,lvl:1s}]}},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~8 ~ ~90 {Item:{id:261s,Count:1b,tag:{ench:[{id:48s,lvl:1s}]}},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~11 ~ ~90 {Item:{id:261s,Count:1b,tag:{ench:[{id:48s,lvl:1s}]}},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-10 ~ ~92 {Item:{id:322s,Count:1b,Damage:1s},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-7 ~ ~92 {Item:{id:322s,Count:1b,Damage:1s},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-4 ~ ~92 {Item:{id:322s,Count:1b,Damage:1s},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-1 ~ ~92 {Item:{id:322s,Count:1b,Damage:1s},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~2 ~ ~92 {Item:{id:322s,Count:1b,Damage:1s},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~5 ~ ~92 {Item:{id:322s,Count:1b,Damage:1s},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~8 ~ ~92 {Item:{id:322s,Count:1b,Damage:1s},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~11 ~ ~92 {Item:{id:322s,Count:1b,Damage:1s},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-10 ~ ~94 {Item:{id:403s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-7 ~ ~94 {Item:{id:403s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-4 ~ ~94 {Item:{id:403s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-1 ~ ~94 {Item:{id:403s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~2 ~ ~94 {Item:{id:403s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~5 ~ ~94 {Item:{id:403s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~8 ~ ~94 {Item:{id:403s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~11 ~ ~94 {Item:{id:403s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-10 ~ ~96 {Item:{id:373s,Count:1b,Damage:8193s},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-7 ~ ~96 {Item:{id:373s,Count:1b,Damage:8193s},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-4 ~ ~96 {Item:{id:373s,Count:1b,Damage:8193s},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-1 ~ ~96 {Item:{id:373s,Count:1b,Damage:8193s},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~2 ~ ~96 {Item:{id:373s,Count:1b,Damage:8193s},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~5 ~ ~96 {Item:{id:373s,Count:1b,Damage:8193s},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~8 ~ ~96 {Item:{id:373s,Count:1b,Damage:8193s},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~11 ~ ~96 {Item:{id:373s,Count:1b,Damage:8193s},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-10 ~ ~98 {Item:{id:299s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-7 ~ ~98 {Item:{id:299s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-4 ~ ~98 {Item:{id:299s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-1 ~ ~98 {Item:{id:299s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~2 ~ ~98 {Item:{id:299s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~5 ~ ~98 {Item:{id:299s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~8 ~ ~98 {Item:{id:299s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~11 ~ ~98 {Item:{id:299s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-10 ~ ~100 {Item:{id:1s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-7 ~ ~100 {Item:{id:1s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-4 ~ ~100 {Item:{id:1s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-1 ~ ~100 {Item:{id:1s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~2 ~ ~100 {Item:{id:1s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~5 ~ ~100 {Item:{id:1s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~8 ~ ~100 {Item:{id:1s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~11 ~ ~100 {Item:{id:1s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-10 ~ ~102 {Item:{id:20s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-7 ~ ~102 {Item:{id:20s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-4 ~ ~102 {Item:{id:20s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-1 ~ ~102 {Item:{id:20s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~2 ~ ~102 {Item:{id:20s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~5 ~ ~102 {Item:{id:20s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~8 ~ ~102 {Item:{id:20s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~11 ~ ~102 {Item:{id:20s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-10 ~ ~104 {Item:{id:50s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-7 ~ ~104 {Item:{id:50s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-4 ~ ~104 {Item:{id:50s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-1 ~ ~104 {Item:{id:50s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~2 ~ ~104 {Item:{id:50s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~5 ~ ~104 {Item:{id:50s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~8 ~ ~104 {Item:{id:50s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~11 ~ ~104 {Item:{id:50s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-10 ~ ~106 {Item:{id:4s,Count:64b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-7 ~ ~106 {Item:{id:4s,Count:64b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-4 ~ ~106 {Item:{id:4s,Count:64b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-1 ~ ~106 {Item:{id:4s,Count:64b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~2 ~ ~106 {Item:{id:4s,Count:64b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~5 ~ ~106 {Item:{id:4s,Count:64b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~8 ~ ~106 {Item:{id:4s,Count:64b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~11 ~ ~106 {Item:{id:4s,Count:64b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
|
||||
# fire strip
|
||||
setblock ~4 ~ ~110 minecraft:dispenser 0 replace {CustomName:"flyby"}
|
||||
setblock ~4 ~1 ~110 minecraft:fire
|
||||
setblock ~-4 ~ ~114 minecraft:dispenser 0 replace {CustomName:"flyby"}
|
||||
setblock ~-4 ~1 ~114 minecraft:fire
|
||||
setblock ~4 ~ ~118 minecraft:dispenser 0 replace {CustomName:"flyby"}
|
||||
setblock ~4 ~1 ~118 minecraft:fire
|
||||
setblock ~-4 ~ ~122 minecraft:dispenser 0 replace {CustomName:"flyby"}
|
||||
setblock ~-4 ~1 ~122 minecraft:fire
|
||||
|
||||
# slowed cows, z=133 leg
|
||||
summon Cow ~-22 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-19 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-16 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-13 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-10 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-7 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-4 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-1 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-26 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-23 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-20 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-17 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-14 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-11 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-8 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-5 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-30 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-27 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-24 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-21 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-18 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-15 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-12 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-9 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-34 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-31 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-28 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-25 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-22 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-19 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-16 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-13 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-38 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-35 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-32 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-29 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-26 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-23 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-20 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-17 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-42 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-39 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-36 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-33 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-30 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-27 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-24 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-21 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-46 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-43 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-40 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-37 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-34 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-31 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-28 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-25 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-50 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-47 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-44 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-41 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-38 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-35 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-32 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-29 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-54 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-51 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-48 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-45 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-42 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-39 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-36 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-33 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-58 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-55 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-52 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-49 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-46 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-43 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-40 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-37 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-62 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-59 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-56 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-53 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-50 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-47 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-44 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-41 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-66 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-63 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-60 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-57 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-54 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-51 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-48 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-45 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-70 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-67 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-64 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-61 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-58 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-55 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-52 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-49 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-74 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-71 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-68 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-65 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-62 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-59 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-56 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-53 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-78 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-75 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-72 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-69 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-66 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-63 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-60 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-57 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-82 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-79 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-76 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-73 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-70 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-67 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-64 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-61 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
|
||||
# plain zombies, z=123 leg
|
||||
summon Zombie ~-92 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-89 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-86 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-83 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-80 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-77 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-74 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-71 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-96 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-93 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-90 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-87 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-84 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-81 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-78 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-75 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-100 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-97 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-94 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-91 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-88 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-85 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-82 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-79 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-104 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-101 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-98 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-95 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-92 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-89 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-86 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-83 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-108 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-105 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-102 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-99 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-96 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-93 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-90 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-87 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-112 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-109 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-106 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-103 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-100 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-97 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-94 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-91 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-116 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-113 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-110 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-107 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-104 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-101 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-98 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-95 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-120 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-117 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-114 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-111 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-108 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-105 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-102 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-99 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
|
||||
# dropped items, z=126 leg, 12 item types once
|
||||
summon Item ~-28 ~ ~126 {Item:{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-25 ~ ~126 {Item:{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-22 ~ ~126 {Item:{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-19 ~ ~126 {Item:{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-16 ~ ~126 {Item:{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-13 ~ ~126 {Item:{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-10 ~ ~126 {Item:{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-7 ~ ~126 {Item:{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-32 ~ ~126 {Item:{id:264s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-29 ~ ~126 {Item:{id:264s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-26 ~ ~126 {Item:{id:264s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-23 ~ ~126 {Item:{id:264s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-20 ~ ~126 {Item:{id:264s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-17 ~ ~126 {Item:{id:264s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-14 ~ ~126 {Item:{id:264s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-11 ~ ~126 {Item:{id:264s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-36 ~ ~126 {Item:{id:265s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-33 ~ ~126 {Item:{id:265s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-30 ~ ~126 {Item:{id:265s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-27 ~ ~126 {Item:{id:265s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-24 ~ ~126 {Item:{id:265s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-21 ~ ~126 {Item:{id:265s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-18 ~ ~126 {Item:{id:265s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-15 ~ ~126 {Item:{id:265s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-40 ~ ~126 {Item:{id:261s,Count:1b,tag:{ench:[{id:48s,lvl:1s}]}},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-37 ~ ~126 {Item:{id:261s,Count:1b,tag:{ench:[{id:48s,lvl:1s}]}},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-34 ~ ~126 {Item:{id:261s,Count:1b,tag:{ench:[{id:48s,lvl:1s}]}},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-31 ~ ~126 {Item:{id:261s,Count:1b,tag:{ench:[{id:48s,lvl:1s}]}},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-28 ~ ~126 {Item:{id:261s,Count:1b,tag:{ench:[{id:48s,lvl:1s}]}},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-25 ~ ~126 {Item:{id:261s,Count:1b,tag:{ench:[{id:48s,lvl:1s}]}},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-22 ~ ~126 {Item:{id:261s,Count:1b,tag:{ench:[{id:48s,lvl:1s}]}},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-19 ~ ~126 {Item:{id:261s,Count:1b,tag:{ench:[{id:48s,lvl:1s}]}},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-44 ~ ~126 {Item:{id:322s,Count:1b,Damage:1s},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-41 ~ ~126 {Item:{id:322s,Count:1b,Damage:1s},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-38 ~ ~126 {Item:{id:322s,Count:1b,Damage:1s},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-35 ~ ~126 {Item:{id:322s,Count:1b,Damage:1s},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-32 ~ ~126 {Item:{id:322s,Count:1b,Damage:1s},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-29 ~ ~126 {Item:{id:322s,Count:1b,Damage:1s},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-26 ~ ~126 {Item:{id:322s,Count:1b,Damage:1s},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-23 ~ ~126 {Item:{id:322s,Count:1b,Damage:1s},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-48 ~ ~126 {Item:{id:403s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-45 ~ ~126 {Item:{id:403s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-42 ~ ~126 {Item:{id:403s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-39 ~ ~126 {Item:{id:403s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-36 ~ ~126 {Item:{id:403s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-33 ~ ~126 {Item:{id:403s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-30 ~ ~126 {Item:{id:403s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-27 ~ ~126 {Item:{id:403s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-52 ~ ~126 {Item:{id:373s,Count:1b,Damage:8193s},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-49 ~ ~126 {Item:{id:373s,Count:1b,Damage:8193s},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-46 ~ ~126 {Item:{id:373s,Count:1b,Damage:8193s},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-43 ~ ~126 {Item:{id:373s,Count:1b,Damage:8193s},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-40 ~ ~126 {Item:{id:373s,Count:1b,Damage:8193s},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-37 ~ ~126 {Item:{id:373s,Count:1b,Damage:8193s},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-34 ~ ~126 {Item:{id:373s,Count:1b,Damage:8193s},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-31 ~ ~126 {Item:{id:373s,Count:1b,Damage:8193s},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-56 ~ ~126 {Item:{id:299s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-53 ~ ~126 {Item:{id:299s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-50 ~ ~126 {Item:{id:299s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-47 ~ ~126 {Item:{id:299s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-44 ~ ~126 {Item:{id:299s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-41 ~ ~126 {Item:{id:299s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-38 ~ ~126 {Item:{id:299s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-35 ~ ~126 {Item:{id:299s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-60 ~ ~126 {Item:{id:1s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-57 ~ ~126 {Item:{id:1s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-54 ~ ~126 {Item:{id:1s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-51 ~ ~126 {Item:{id:1s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-48 ~ ~126 {Item:{id:1s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-45 ~ ~126 {Item:{id:1s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-42 ~ ~126 {Item:{id:1s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-39 ~ ~126 {Item:{id:1s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-64 ~ ~126 {Item:{id:20s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-61 ~ ~126 {Item:{id:20s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-58 ~ ~126 {Item:{id:20s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-55 ~ ~126 {Item:{id:20s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-52 ~ ~126 {Item:{id:20s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-49 ~ ~126 {Item:{id:20s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-46 ~ ~126 {Item:{id:20s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-43 ~ ~126 {Item:{id:20s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-68 ~ ~126 {Item:{id:50s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-65 ~ ~126 {Item:{id:50s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-62 ~ ~126 {Item:{id:50s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-59 ~ ~126 {Item:{id:50s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-56 ~ ~126 {Item:{id:50s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-53 ~ ~126 {Item:{id:50s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-50 ~ ~126 {Item:{id:50s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-47 ~ ~126 {Item:{id:50s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-72 ~ ~126 {Item:{id:4s,Count:64b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-69 ~ ~126 {Item:{id:4s,Count:64b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-66 ~ ~126 {Item:{id:4s,Count:64b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-63 ~ ~126 {Item:{id:4s,Count:64b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-60 ~ ~126 {Item:{id:4s,Count:64b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-57 ~ ~126 {Item:{id:4s,Count:64b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-54 ~ ~126 {Item:{id:4s,Count:64b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-51 ~ ~126 {Item:{id:4s,Count:64b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
@@ -0,0 +1,139 @@
|
||||
# Summons are relative to the flight start; start facing south (yaw 0).
|
||||
|
||||
# gamerules
|
||||
difficulty 1
|
||||
gamerule doMobSpawning false
|
||||
gamerule doFireTick false
|
||||
gamerule mobGriefing false
|
||||
|
||||
# hurt-flash + glint zombies, x=-5 leg
|
||||
summon Zombie ~-5 ~ ~10 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-5 ~ ~14 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-5 ~ ~18 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-5 ~ ~22 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-5 ~ ~26 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-5 ~ ~30 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-5 ~ ~34 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-5 ~ ~38 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:19b,Amplifier:0b,Duration:1000000},{Id:10b,Amplifier:1b,Duration:1000000}],Equipment:[{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},{id:313s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:312s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:311s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:310s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
|
||||
# pigzombies, x=6 leg
|
||||
summon PigZombie ~6 ~ ~10 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~6 ~ ~14 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~6 ~ ~18 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~6 ~ ~22 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~6 ~ ~26 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~6 ~ ~30 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~6 ~ ~34 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
summon PigZombie ~6 ~ ~38 {PersistenceRequired:1b,HealF:200.0f,Attributes:[{Name:"generic.maxHealth",Base:200.0d},{Name:"generic.movementSpeed",Base:0.0d}],Equipment:[{id:283s,Count:1b},{id:317s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:316s,Count:1b},{id:315s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}},{id:314s,Count:1b,tag:{ench:[{id:0s,lvl:1s}]}}],ForgeData:{flyby:1b}}
|
||||
|
||||
# passive mobs, grouped by species
|
||||
summon Cow ~-5 ~ ~44 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-5 ~ ~48 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-5 ~ ~52 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-5 ~ ~56 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-5 ~ ~60 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-5 ~ ~64 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Pig ~6 ~ ~44 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Pig ~6 ~ ~48 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Pig ~6 ~ ~52 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Pig ~6 ~ ~56 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Pig ~6 ~ ~60 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Pig ~6 ~ ~64 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Sheep ~-9 ~ ~44 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Sheep ~-9 ~ ~48 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Sheep ~-9 ~ ~52 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Sheep ~-9 ~ ~56 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Sheep ~-9 ~ ~60 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Sheep ~-9 ~ ~64 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Chicken ~10 ~ ~44 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Chicken ~10 ~ ~48 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Chicken ~10 ~ ~52 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Chicken ~10 ~ ~56 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Chicken ~10 ~ ~60 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Chicken ~10 ~ ~64 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
|
||||
# villagers
|
||||
summon Villager ~-5 ~ ~70 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Villager ~-5 ~ ~74 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Villager ~-5 ~ ~78 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Villager ~-5 ~ ~82 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Villager ~-5 ~ ~86 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Villager ~-5 ~ ~90 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
|
||||
# dropped items, x=2 leg, 12 item types cycled twice
|
||||
summon Item ~2 ~ ~60 {Item:{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~2 ~ ~62 {Item:{id:264s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~2 ~ ~64 {Item:{id:265s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~2 ~ ~66 {Item:{id:261s,Count:1b,tag:{ench:[{id:48s,lvl:1s}]}},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~2 ~ ~68 {Item:{id:322s,Count:1b,Damage:1s},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~2 ~ ~70 {Item:{id:403s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~2 ~ ~72 {Item:{id:373s,Count:1b,Damage:8193s},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~2 ~ ~74 {Item:{id:299s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~2 ~ ~76 {Item:{id:1s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~2 ~ ~78 {Item:{id:20s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~2 ~ ~80 {Item:{id:50s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~2 ~ ~82 {Item:{id:4s,Count:64b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~2 ~ ~84 {Item:{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~2 ~ ~86 {Item:{id:264s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~2 ~ ~88 {Item:{id:265s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~2 ~ ~90 {Item:{id:261s,Count:1b,tag:{ench:[{id:48s,lvl:1s}]}},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~2 ~ ~92 {Item:{id:322s,Count:1b,Damage:1s},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~2 ~ ~94 {Item:{id:403s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~2 ~ ~96 {Item:{id:373s,Count:1b,Damage:8193s},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~2 ~ ~98 {Item:{id:299s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~2 ~ ~100 {Item:{id:1s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~2 ~ ~102 {Item:{id:20s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~2 ~ ~104 {Item:{id:50s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~2 ~ ~106 {Item:{id:4s,Count:64b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
|
||||
# fire strip
|
||||
setblock ~4 ~ ~110 minecraft:dispenser 0 replace {CustomName:"flyby"}
|
||||
setblock ~4 ~1 ~110 minecraft:fire
|
||||
setblock ~-4 ~ ~114 minecraft:dispenser 0 replace {CustomName:"flyby"}
|
||||
setblock ~-4 ~1 ~114 minecraft:fire
|
||||
setblock ~4 ~ ~118 minecraft:dispenser 0 replace {CustomName:"flyby"}
|
||||
setblock ~4 ~1 ~118 minecraft:fire
|
||||
setblock ~-4 ~ ~122 minecraft:dispenser 0 replace {CustomName:"flyby"}
|
||||
setblock ~-4 ~1 ~122 minecraft:fire
|
||||
|
||||
# slowed cows, z=133 leg
|
||||
summon Cow ~-10 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-14 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-18 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-22 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-26 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-30 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-34 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-38 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-42 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-46 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-50 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-54 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-58 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-62 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-66 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
summon Cow ~-70 ~ ~133 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ActiveEffects:[{Id:1b,Amplifier:0b,Duration:1000000}],ForgeData:{flyby:1b}}
|
||||
|
||||
# plain zombies, z=123 leg
|
||||
summon Zombie ~-80 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-84 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-88 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-92 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-96 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-100 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-104 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
summon Zombie ~-108 ~ ~123 {PersistenceRequired:1b,Attributes:[{Name:"generic.movementSpeed",Base:0.0d}],ForgeData:{flyby:1b}}
|
||||
|
||||
# dropped items, z=126 leg, 12 item types once
|
||||
summon Item ~-16 ~ ~126 {Item:{id:276s,Count:1b,tag:{ench:[{id:16s,lvl:1s}]}},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-20 ~ ~126 {Item:{id:264s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-24 ~ ~126 {Item:{id:265s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-28 ~ ~126 {Item:{id:261s,Count:1b,tag:{ench:[{id:48s,lvl:1s}]}},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-32 ~ ~126 {Item:{id:322s,Count:1b,Damage:1s},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-36 ~ ~126 {Item:{id:403s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-40 ~ ~126 {Item:{id:373s,Count:1b,Damage:8193s},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-44 ~ ~126 {Item:{id:299s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-48 ~ ~126 {Item:{id:1s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-52 ~ ~126 {Item:{id:20s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-56 ~ ~126 {Item:{id:50s,Count:1b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
summon Item ~-60 ~ ~126 {Item:{id:4s,Count:64b},Age:-32768s,Lifespan:2147483647,Owner:"flyby",ForgeData:{flyby:1b}}
|
||||
@@ -77,6 +77,9 @@ dependencies {
|
||||
testImplementation(platform(libs.junit.bom))
|
||||
testImplementation(libs.junit.jupiter)
|
||||
testImplementation(testFixtures(project(":glsm")))
|
||||
testImplementation(project(":glsm")) {
|
||||
capabilities { requireCapability("${project.group}:glsm-stubs") }
|
||||
}
|
||||
testImplementation(libs.celeritas.common) { isTransitive = false }
|
||||
testImplementation(libs.lwjgl3.opengl)
|
||||
// GL.<clinit> loads liblwjgl_opengl.so. Test scope only: a root-level runtimeOnly collides with lwjgl3Bindings.
|
||||
@@ -84,15 +87,77 @@ dependencies {
|
||||
testRuntimeOnly(libs.junit.platform.launcher)
|
||||
testRuntimeOnly(libs.log4j.core)
|
||||
testRuntimeOnly(libs.fastutil)
|
||||
testRuntimeOnly(libs.joml)
|
||||
testRuntimeOnly(libs.commons.lang3)
|
||||
testRuntimeOnly(libs.guava)
|
||||
testImplementation(libs.asm.tree.test)
|
||||
}
|
||||
|
||||
val glsmSdlTag = "glsm-sdl"
|
||||
|
||||
val glsmSdlStubs = sourceSets.create("glsmSdlStubs") {
|
||||
java.srcDir("src/glsmSdlStubs/java")
|
||||
}
|
||||
|
||||
val glsmSdlAgent = sourceSets.create("glsmSdlAgent") {
|
||||
java.srcDir("src/glsmSdlAgent/java")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
"glsmSdlAgentCompileOnly"(project(":glsm"))
|
||||
"glsmSdlAgentCompileOnly"(libs.asm.tree.test)
|
||||
testImplementation(glsmSdlAgent.output)
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
useJUnitPlatform()
|
||||
useJUnitPlatform {
|
||||
excludeTags(glsmSdlTag)
|
||||
}
|
||||
if (System.getProperty("os.name").startsWith("Mac")) {
|
||||
jvmArgs("-XstartOnFirstThread")
|
||||
}
|
||||
}
|
||||
|
||||
val glsmSdlRedirectAgentJar by tasks.registering(Jar::class) {
|
||||
archiveClassifier = "glsm-sdl-redirect-agent"
|
||||
from(glsmSdlAgent.output)
|
||||
manifest {
|
||||
attributes("Premain-Class" to "com.gtnewhorizons.angelica.sdlgpu.glsm.GlsmSdlRedirectAgent")
|
||||
}
|
||||
}
|
||||
|
||||
val glsmSdlTest by tasks.registering(Test::class) {
|
||||
description = "Runs the headless GLSM-on-SDL FFP rig in its own JVM, with the SDL GPU gate engaged."
|
||||
group = "verification"
|
||||
testClassesDirs = sourceSets["test"].output.classesDirs
|
||||
classpath = glsmSdlStubs.output.classesDirs.plus(
|
||||
sourceSets["test"].runtimeClasspath.filter { file ->
|
||||
!file.name.startsWith("lwjgl-2.") && !file.name.startsWith("lwjgl_util-") && !file.name.startsWith("lwjgl-platform-")
|
||||
}
|
||||
)
|
||||
useJUnitPlatform {
|
||||
includeTags(glsmSdlTag)
|
||||
}
|
||||
dependsOn(tasks.named("glsmSdlStubsClasses"), glsmSdlRedirectAgentJar)
|
||||
val agentJarPath = glsmSdlRedirectAgentJar.flatMap { it.archiveFile }.map { it.asFile.path }
|
||||
jvmArgumentProviders.add(CommandLineArgumentProvider { listOf("-javaagent:${agentJarPath.get()}") })
|
||||
jvmArgs(
|
||||
"-Dangelica.sdlgpu.enable=true",
|
||||
"-Dangelica.sdlgpu.disablePresenterThread=true",
|
||||
"-Dceleritas.lwjglService=com.gtnewhorizons.angelica.sdlgpu.SDLGPULWJGLService",
|
||||
)
|
||||
if (System.getProperty("os.name").startsWith("Mac")) {
|
||||
jvmArgs("-XstartOnFirstThread")
|
||||
}
|
||||
testLogging {
|
||||
events("passed", "skipped", "failed")
|
||||
exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
|
||||
}
|
||||
mustRunAfter(tasks.test)
|
||||
}
|
||||
|
||||
tasks.check { dependsOn(glsmSdlTest) }
|
||||
|
||||
tasks.named<JavaCompile>("compileJava") {
|
||||
val classesDir = destinationDirectory
|
||||
doLast { injectLwjgl3Aware(classesDir.get().asFile) }
|
||||
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
package com.gtnewhorizons.angelica.sdlgpu.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.redirect.GLSMRedirector;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
|
||||
import java.lang.instrument.ClassFileTransformer;
|
||||
import java.lang.instrument.Instrumentation;
|
||||
import java.security.ProtectionDomain;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public final class GlsmSdlRedirectAgent {
|
||||
|
||||
private static final String GTNHLIB = "com/gtnewhorizon/gtnhlib/";
|
||||
private static final List<String> FAILURES = Collections.synchronizedList(new ArrayList<>());
|
||||
private static final AtomicInteger TRANSFORMED_COUNT = new AtomicInteger();
|
||||
|
||||
private GlsmSdlRedirectAgent() {}
|
||||
|
||||
public static void premain(String args, Instrumentation instrumentation) {
|
||||
instrumentation.addTransformer(new Redirector());
|
||||
}
|
||||
|
||||
public static List<String> failures() {
|
||||
return List.copyOf(FAILURES);
|
||||
}
|
||||
|
||||
public static int transformedCount() {
|
||||
return TRANSFORMED_COUNT.get();
|
||||
}
|
||||
|
||||
private static final class Redirector implements ClassFileTransformer {
|
||||
|
||||
private final GLSMRedirector redirector = new GLSMRedirector();
|
||||
|
||||
@Override
|
||||
public byte[] transform(ClassLoader loader, String className, Class<?> redefined, ProtectionDomain domain, byte[] classfile) {
|
||||
if (className == null || !className.startsWith(GTNHLIB)) return null;
|
||||
try {
|
||||
if (!redirector.shouldTransform(classfile)) return null;
|
||||
final ClassNode node = new ClassNode();
|
||||
new ClassReader(classfile).accept(node, 0);
|
||||
if (!redirector.transformClassNode(className.replace('/', '.'), node)) return null;
|
||||
final ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS);
|
||||
node.accept(writer);
|
||||
TRANSFORMED_COUNT.incrementAndGet();
|
||||
return writer.toByteArray();
|
||||
} catch (Throwable t) {
|
||||
FAILURES.add(className + ": " + t);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package org.lwjgl;
|
||||
|
||||
public final class Sys {
|
||||
|
||||
private Sys() {}
|
||||
|
||||
public static boolean is64Bit() {
|
||||
return !"32".equals(System.getProperty("sun.arch.data.model"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package org.lwjgl.opengl;
|
||||
|
||||
public class ContextCapabilities {
|
||||
|
||||
public boolean OpenGL30 = true;
|
||||
public boolean OpenGL31 = true;
|
||||
public boolean OpenGL32 = true;
|
||||
public boolean OpenGL33 = true;
|
||||
public boolean OpenGL40 = true;
|
||||
public boolean OpenGL42 = true;
|
||||
public boolean OpenGL43 = true;
|
||||
public boolean OpenGL44 = true;
|
||||
public boolean OpenGL45 = true;
|
||||
|
||||
public boolean GL_ARB_buffer_storage = true;
|
||||
public boolean GL_ARB_clear_texture = true;
|
||||
public boolean GL_ARB_compute_shader = true;
|
||||
public boolean GL_ARB_direct_state_access = false;
|
||||
public boolean GL_ARB_draw_buffers_blend = false;
|
||||
public boolean GL_ARB_multi_draw_indirect = false;
|
||||
public boolean GL_ARB_sampler_objects = true;
|
||||
public boolean GL_ARB_shader_image_load_store = true;
|
||||
public boolean GL_ARB_shader_storage_buffer_object = true;
|
||||
public boolean GL_ARB_tessellation_shader = false;
|
||||
|
||||
public boolean GL_EXT_direct_state_access = false;
|
||||
public boolean GL_EXT_gpu_shader4 = true;
|
||||
public boolean GL_EXT_shader_image_load_store = true;
|
||||
|
||||
public boolean GL_NVX_gpu_memory_info = false;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package org.lwjgl.opengl;
|
||||
|
||||
public interface Drawable {
|
||||
|
||||
void makeCurrent();
|
||||
|
||||
void releaseContext();
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package org.lwjgl.opengl;
|
||||
|
||||
public final class GLContext {
|
||||
|
||||
private static final ContextCapabilities CAPABILITIES = new ContextCapabilities();
|
||||
|
||||
private GLContext() {}
|
||||
|
||||
public static ContextCapabilities getCapabilities() {
|
||||
return CAPABILITIES;
|
||||
}
|
||||
}
|
||||
Loaded 100 of 458 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user