mirror of
https://github.com/GTNewHorizons/Angelica.git
synced 2026-11-15 01:34:48 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c578970a66 | ||
|
|
ef68af5850 | ||
|
|
b6a4497fb3 | ||
|
|
939e31461a | ||
|
|
02f0b0fca1 | ||
|
|
b7d439e6b1 | ||
|
|
c36647af34 | ||
|
|
f787801a0e | ||
|
|
f192793395 | ||
|
|
3876f7e693 | ||
|
|
94ee37bfed | ||
|
|
26c161f0e8 | ||
|
|
75639d3b73 | ||
|
|
a07cecb879 | ||
|
|
81b82748ce | ||
|
|
8d0c90c423 | ||
|
|
8ce6a2f8c6 | ||
|
|
2aa4ac7509 | ||
|
|
8f98151049 | ||
|
|
2b70318327 | ||
|
|
3b4ebcf611 | ||
|
|
2845c9e078 | ||
|
|
6f054a09a9 |
No files matched your search
@@ -22,6 +22,8 @@ indent_size = 2
|
||||
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
|
||||
|
||||
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/
|
||||
@@ -101,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
|
||||
+4
-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"
|
||||
@@ -206,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")
|
||||
}
|
||||
|
||||
@@ -233,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)
|
||||
}
|
||||
}
|
||||
+29
-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)
|
||||
}
|
||||
@@ -288,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")
|
||||
|
||||
@@ -52,6 +52,10 @@ public final class SystemProperties {
|
||||
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");
|
||||
@@ -60,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;
|
||||
}
|
||||
@@ -102,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;
|
||||
|
||||
@@ -27,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;
|
||||
@@ -101,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));
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import org.lwjgl.opengl.OpenGLException;
|
||||
|
||||
final class GLErrorReporter {
|
||||
|
||||
private GLErrorReporter() {}
|
||||
|
||||
static void reportError(int error) {
|
||||
throw new OpenGLException(error);
|
||||
}
|
||||
}
|
||||
@@ -10,8 +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;
|
||||
@@ -52,6 +54,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.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;
|
||||
@@ -91,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;
|
||||
@@ -424,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;
|
||||
|
||||
@@ -493,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());
|
||||
}
|
||||
|
||||
@@ -1052,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;
|
||||
@@ -1304,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) {
|
||||
@@ -1399,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1449,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;
|
||||
@@ -1465,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;
|
||||
@@ -1529,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;
|
||||
@@ -2003,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) {
|
||||
@@ -3534,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;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3748,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);
|
||||
@@ -3771,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) {
|
||||
@@ -3795,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);
|
||||
@@ -3902,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) {
|
||||
@@ -3994,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()) {
|
||||
@@ -4554,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);
|
||||
@@ -4568,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()));
|
||||
@@ -4691,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); }
|
||||
|
||||
@@ -5246,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() {
|
||||
@@ -5267,7 +5389,7 @@ public class GLStateManager {
|
||||
}
|
||||
|
||||
public static void pumpDisplayMessages() {
|
||||
Display.processMessages();
|
||||
RENDER_BACKEND.pumpDisplayMessages();
|
||||
}
|
||||
|
||||
public static int glGetError() {
|
||||
@@ -5276,6 +5398,11 @@ public class GLStateManager {
|
||||
return RENDER_BACKEND.getError();
|
||||
}
|
||||
|
||||
public static void checkGLError() {
|
||||
final int error = glGetError();
|
||||
if (error != GL11.GL_NO_ERROR) GLErrorReporter.reportError(error);
|
||||
}
|
||||
|
||||
public static String glGetString(int pname) {
|
||||
if (!RENDER_BACKEND.hasContext()) return "no valid GL/render context";
|
||||
return RENDER_BACKEND.getString(pname);
|
||||
@@ -6155,6 +6282,10 @@ public class GLStateManager {
|
||||
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) { onBufferUnmap(target); return RENDER_BACKEND.unmapBuffer(target); }
|
||||
|
||||
|
||||
@@ -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) {
|
||||
@@ -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,25 @@ 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 samplerParameterf(int sampler, int pname, float param) {
|
||||
if (!supportsSamplerObjects || sampler == 0) return;
|
||||
GLStateManager.glSamplerParameterf(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);
|
||||
}
|
||||
}
|
||||
@@ -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 = 5;
|
||||
public static final int LOC_MATRIX_COL1 = 6;
|
||||
public static final int LOC_MATRIX_COL2 = 7;
|
||||
public static final int LOC_MATRIX_COL3 = 8;
|
||||
public static final int LOC_COLOR = 9;
|
||||
public static final int LOC_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;
|
||||
}
|
||||
}
|
||||
@@ -97,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 };
|
||||
@@ -127,6 +129,7 @@ public final class ShaderManager {
|
||||
}
|
||||
|
||||
public void preDraw() {
|
||||
GLSMHooks.resolvePendingProgram();
|
||||
final DeferredBlendHandler bh = GLSMHooks.blendHandler;
|
||||
if (bh != null) bh.flushDeferredBlend();
|
||||
|
||||
|
||||
@@ -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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -14,6 +15,12 @@ public final class GLSMHooks {
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
@@ -555,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")
|
||||
@@ -767,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()
|
||||
@@ -796,6 +810,8 @@ public class GLSMRedirector {
|
||||
.add("gluBuild2DMipmaps")
|
||||
.add("gluErrorString")
|
||||
);
|
||||
// Must honor disableErrorChecks like glGetError, or it throws on errors those checks left latched
|
||||
methodRedirects.put("org/lwjgl/opengl/Util", RedirectMap.newMap().add("checkGLError"));
|
||||
|
||||
// Quadric type replacements
|
||||
typeRedirects.put("org/lwjgl/util/glu/Sphere", "com/gtnewhorizons/angelica/glsm/compat/lwjgl/AngelicaSphere");
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
+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;
|
||||
|
||||
@@ -274,6 +276,11 @@ class GLSMRedirectorTest {
|
||||
assertTrue(unmappedSeen().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
void utilCheckGLErrorIsRedirected() throws Exception {
|
||||
assertRedirectedToDeclaredTarget(new String[][]{{"org/lwjgl/opengl/Util", "checkGLError", "()V"}});
|
||||
}
|
||||
|
||||
@Test
|
||||
void indexedGettersHaveRealTargets() throws Exception {
|
||||
final Class<?> glsm = Class.forName(GLSM.replace('/', '.'), false, getClass().getClassLoader());
|
||||
@@ -300,6 +307,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"
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -150,6 +150,7 @@ public final class SDLGPUGate {
|
||||
try {
|
||||
device().claimWindow(ctx.window());
|
||||
engaged = true;
|
||||
SDLGPULWJGLService.bind();
|
||||
BackendManager.RENDER_BACKEND.onPostWindowCreate(ctx.window());
|
||||
} catch (Throwable t) {
|
||||
initFailure = t;
|
||||
@@ -158,9 +159,10 @@ public final class SDLGPUGate {
|
||||
}
|
||||
|
||||
public static void fallBackToGL() {
|
||||
if (SDLGPULWJGLService.isOfferedAsAvailable()) {
|
||||
if (SDLGPULWJGLService.isConstructed()) {
|
||||
throw new IllegalStateException("Celeritas already selected the SDL GPU LWJGL service; falling back to OpenGL would leave it bound to a dead backend");
|
||||
}
|
||||
SDLGPULWJGLService.unbind();
|
||||
disarmed = true;
|
||||
engaged = false;
|
||||
deviceReady = false;
|
||||
|
||||
@@ -5,10 +5,10 @@ import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.BackendManager;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.device.MemoryStackWrapper;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.util.MemoryAccess;
|
||||
import com.mitchej123.lwjgl.DebugMessageHandler;
|
||||
import com.mitchej123.lwjgl.GLExtension;
|
||||
import com.mitchej123.lwjgl.LWJGLService;
|
||||
import com.mitchej123.lwjgl.MemoryStack;
|
||||
import org.taumc.celeritas.lwjgl.DebugMessageHandler;
|
||||
import org.taumc.celeritas.lwjgl.GLExtension;
|
||||
import org.taumc.celeritas.lwjgl.LWJGLService;
|
||||
import org.taumc.celeritas.lwjgl.MemoryStack;
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
import java.nio.Buffer;
|
||||
@@ -17,19 +17,32 @@ import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.util.Arrays;
|
||||
|
||||
/** SDL GPU implementation of {@link LWJGLService} for Celeritas */
|
||||
public final class SDLGPULWJGLService extends LWJGLService {
|
||||
public final class SDLGPULWJGLService implements LWJGLService {
|
||||
|
||||
private static volatile boolean offeredAsAvailable;
|
||||
private static final ThreadLocal<MemoryStackWrapper> STACK =
|
||||
ThreadLocal.withInitial(() -> new MemoryStackWrapper(org.lwjgl.system.MemoryStack.stackGet()));
|
||||
|
||||
public static boolean isOfferedAsAvailable() { return offeredAsAvailable; }
|
||||
private static volatile boolean constructed;
|
||||
|
||||
@Override public int getPriority() {
|
||||
if (!SystemProperties.USE_SDL_GPU || !SDLGPUGate.isSDLGPUAvailable() || !SDLGPUGate.isEngaged()) {
|
||||
return PRIORITY_UNAVAILABLE;
|
||||
public static SDLGPULWJGLService create() {
|
||||
if (!SDLGPUGate.isEngaged()) {
|
||||
throw new IllegalStateException("SDL GPU is not engaged; " + SystemProperties.KEY_CELERITAS_LWJGL_SERVICE + " must not name " + SDLGPULWJGLService.class.getName());
|
||||
}
|
||||
constructed = true;
|
||||
return new SDLGPULWJGLService();
|
||||
}
|
||||
|
||||
public static boolean isConstructed() { return constructed; }
|
||||
|
||||
static void bind() {
|
||||
System.setProperty(SystemProperties.KEY_CELERITAS_LWJGL_SERVICE, SDLGPULWJGLService.class.getName());
|
||||
}
|
||||
|
||||
static void unbind() {
|
||||
final String key = SystemProperties.KEY_CELERITAS_LWJGL_SERVICE;
|
||||
if (SDLGPULWJGLService.class.getName().equals(System.getProperty(key))) {
|
||||
System.clearProperty(key);
|
||||
}
|
||||
offeredAsAvailable = true;
|
||||
return 200;
|
||||
}
|
||||
|
||||
@Override public boolean isOpenGLVersionSupported(int major, int minor) {
|
||||
@@ -47,7 +60,6 @@ public final class SDLGPULWJGLService extends LWJGLService {
|
||||
ARB_uniform_buffer_object,
|
||||
ARB_vertex_array_object,
|
||||
ARB_shader_storage_buffer_object,
|
||||
ARB_instanced_arrays,
|
||||
KHR_debug
|
||||
-> true;
|
||||
case ARB_timer_query,
|
||||
@@ -63,30 +75,58 @@ public final class SDLGPULWJGLService extends LWJGLService {
|
||||
|
||||
@Override public int getPointerSize() { return 8; } // 64-bit
|
||||
|
||||
@Override public int glGenBuffers() { return BackendManager.RENDER_BACKEND.genBuffers(); }
|
||||
@Override public void glDeleteBuffers(int buffer) { BackendManager.RENDER_BACKEND.deleteBuffers(buffer); }
|
||||
@Override public int glGenBuffers() { return GLStateManager.glGenBuffers(); }
|
||||
@Override public void glDeleteBuffers(int buffer) { GLStateManager.glDeleteBuffers(buffer); }
|
||||
@Override public void glBindBuffer(int target, int buffer) { GLStateManager.glBindBuffer(target, buffer); }
|
||||
@Override public void glBufferData(int target, long size, int usage) { BackendManager.RENDER_BACKEND.bufferData(target, size, usage); }
|
||||
@Override public void glBufferData(int target, ByteBuffer data, int usage) { BackendManager.RENDER_BACKEND.bufferData(target, data, usage); }
|
||||
@Override public void glBufferData(int target, long size, int usage) {
|
||||
GLStateManager.glBufferData(target, size, usage);
|
||||
}
|
||||
@Override public void glBufferData(int target, ByteBuffer data, int usage) {
|
||||
GLStateManager.glBufferData(target, data, usage);
|
||||
}
|
||||
@Override public void glBufferData(int target, long size, long data, int usage) {
|
||||
if (data != 0) {
|
||||
BackendManager.RENDER_BACKEND.bufferData(target, MemoryUtil.memByteBuffer(data, (int) size), usage);
|
||||
GLStateManager.nglBufferData(target, size, data, usage);
|
||||
} else {
|
||||
BackendManager.RENDER_BACKEND.bufferData(target, size, usage);
|
||||
GLStateManager.glBufferData(target, size, usage);
|
||||
}
|
||||
}
|
||||
@Override public void glBufferSubData(int target, long offset, ByteBuffer data) { BackendManager.RENDER_BACKEND.bufferSubData(target, offset, data); }
|
||||
@Override public void glBufferSubData(int target, long offset, long size, long data) {
|
||||
BackendManager.RENDER_BACKEND.bufferSubData(target, offset, MemoryUtil.memByteBuffer(data, (int) size));
|
||||
@Override public void glBufferStorage(int target, long size, int flags) {
|
||||
GLStateManager.glBufferStorage(target, size, flags);
|
||||
}
|
||||
@Override public void glBlitFramebuffer(int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, int dstX1, int dstY1, int mask, int filter) {
|
||||
GLStateManager.glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
|
||||
}
|
||||
@Override public void glDrawArrays(int mode, int first, int count) {
|
||||
GLStateManager.glDrawArrays(mode, first, count);
|
||||
}
|
||||
@Override public void glDrawBuffers(int[] buffers) {
|
||||
try (var stack = MemoryStack.stackPush()) {
|
||||
GLStateManager.glDrawBuffers(stack.ints(buffers));
|
||||
}
|
||||
}
|
||||
@Override public void glTexImage2D(int target, int level, int internalFormat, int width, int height, int border, int format, int type, ByteBuffer pixels) {
|
||||
GLStateManager.glTexImage2D(target, level, internalFormat, width, height, border, format, type, pixels);
|
||||
}
|
||||
@Override public void glTexParameteri(int target, int pname, int param) { GLStateManager.glTexParameteri(target, pname, param); }
|
||||
@Override public ByteBuffer glMapBufferRange(int target, long offset, long length, int flags) {
|
||||
return GLStateManager.glMapBufferRange(target, offset, length, flags);
|
||||
}
|
||||
@Override public void glBufferStorage(int target, long size, int flags) { BackendManager.RENDER_BACKEND.bufferStorage(target, size, flags); }
|
||||
@Override public ByteBuffer glMapBufferRange(int target, long offset, long length, int flags) { return BackendManager.RENDER_BACKEND.mapBufferRange(target, offset, length, flags); }
|
||||
@Override public long nglMapBuffer(int target, int access) { return 0; }
|
||||
@Override public ByteBuffer glMapBuffer(int target, int access) { return BackendManager.RENDER_BACKEND.mapBuffer(target, access); }
|
||||
@Override public void glUnmapBuffer(int target) { BackendManager.RENDER_BACKEND.unmapBuffer(target); }
|
||||
@Override public void glFlushMappedBufferRange(int target, long offset, long length) { BackendManager.RENDER_BACKEND.flushMappedBufferRange(target, offset, length); }
|
||||
@Override public void glCopyBufferSubData(int readTarget, int writeTarget, long readOffset, long writeOffset, long size) { BackendManager.RENDER_BACKEND.copyBufferSubData(readTarget, writeTarget, readOffset, writeOffset, size); }
|
||||
@Override public void glBindBufferBase(int target, int index, int buffer) { BackendManager.RENDER_BACKEND.bindBufferBase(target, index, buffer); }
|
||||
@Override public ByteBuffer glMapBuffer(int target, int access) {
|
||||
return GLStateManager.glMapBuffer(target, access);
|
||||
}
|
||||
@Override public void glUnmapBuffer(int target) { GLStateManager.glUnmapBuffer(target); }
|
||||
@Override public void glFlushMappedBufferRange(int target, long offset, long length) {
|
||||
GLStateManager.glFlushMappedBufferRange(target, offset, length);
|
||||
}
|
||||
@Override public void glCopyBufferSubData(int readTarget, int writeTarget, long readOffset, long writeOffset,
|
||||
long size) {
|
||||
GLStateManager.glCopyBufferSubData(readTarget, writeTarget, readOffset, writeOffset, size);
|
||||
}
|
||||
@Override public void glBindBufferBase(int target, int index, int buffer) {
|
||||
GLStateManager.glBindBufferBase(target, index, buffer);
|
||||
}
|
||||
|
||||
@Override public int glGenVertexArrays() { return GLStateManager.glGenVertexArrays(); }
|
||||
@Override public void glDeleteVertexArrays(int array) { GLStateManager.glDeleteVertexArrays(array); }
|
||||
@@ -96,63 +136,80 @@ public final class SDLGPULWJGLService extends LWJGLService {
|
||||
@Override public void glEnableVertexAttribArray(int index) { GLStateManager.glEnableVertexAttribArray(index); }
|
||||
@Override public void glVertexAttribDivisor(int index, int divisor) { GLStateManager.glVertexAttribDivisor(index, divisor); }
|
||||
|
||||
@Override public int glCreateShader(int type) { return BackendManager.RENDER_BACKEND.createShader(type); }
|
||||
@Override public void glShaderSource(int shader, CharSequence source) { BackendManager.RENDER_BACKEND.shaderSource(shader, source); }
|
||||
@Override public void glShaderSourceSafe(int shader, CharSequence source) { BackendManager.RENDER_BACKEND.shaderSource(shader, source); }
|
||||
@Override public void glCompileShader(int shader) { BackendManager.RENDER_BACKEND.compileShader(shader); }
|
||||
@Override public String glGetShaderInfoLog(int shader, int maxLength) { return BackendManager.RENDER_BACKEND.getShaderInfoLog(shader, maxLength); }
|
||||
@Override public int glGetShaderi(int shader, int pname) { return BackendManager.RENDER_BACKEND.getShaderi(shader, pname); }
|
||||
@Override public void glDeleteShader(int shader) { BackendManager.RENDER_BACKEND.deleteShader(shader); }
|
||||
@Override public int glCreateShader(int type) { return GLStateManager.glCreateShader(type); }
|
||||
@Override public void glShaderSource(int shader, CharSequence source) {
|
||||
GLStateManager.glShaderSource(shader, source);
|
||||
}
|
||||
@Override public void glShaderSourceSafe(int shader, CharSequence source) {
|
||||
GLStateManager.glShaderSource(shader, source);
|
||||
}
|
||||
@Override public void glCompileShader(int shader) { GLStateManager.glCompileShader(shader); }
|
||||
@Override public String glGetShaderInfoLog(int shader, int maxLength) {
|
||||
return GLStateManager.glGetShaderInfoLog(shader, maxLength);
|
||||
}
|
||||
@Override public int glGetShaderi(int shader, int pname) { return GLStateManager.glGetShaderi(shader, pname); }
|
||||
@Override public void glDeleteShader(int shader) { GLStateManager.glDeleteShader(shader); }
|
||||
|
||||
@Override public int glCreateProgram() { return BackendManager.RENDER_BACKEND.createProgram(); }
|
||||
@Override public void glAttachShader(int program, int shader) { BackendManager.RENDER_BACKEND.attachShader(program, shader); }
|
||||
@Override public void glLinkProgram(int program) { BackendManager.RENDER_BACKEND.linkProgram(program); }
|
||||
@Override public String glGetProgramInfoLog(int program, int maxLength) { return BackendManager.RENDER_BACKEND.getProgramInfoLog(program, maxLength); }
|
||||
@Override public int glGetProgrami(int program, int pname) { return BackendManager.RENDER_BACKEND.getProgrami(program, pname); }
|
||||
@Override public int glCreateProgram() { return GLStateManager.glCreateProgram(); }
|
||||
@Override public void glAttachShader(int program, int shader) { GLStateManager.glAttachShader(program, shader); }
|
||||
@Override public void glLinkProgram(int program) { GLStateManager.glLinkProgram(program); }
|
||||
@Override public String glGetProgramInfoLog(int program, int maxLength) {
|
||||
return GLStateManager.glGetProgramInfoLog(program, maxLength);
|
||||
}
|
||||
@Override public int glGetProgrami(int program, int pname) { return GLStateManager.glGetProgrami(program, pname); }
|
||||
@Override public void glUseProgram(int program) { GLStateManager.glUseProgram(program); }
|
||||
@Override public void glDeleteProgram(int program) { BackendManager.RENDER_BACKEND.deleteProgram(program); }
|
||||
@Override public void glBindAttribLocation(int program, int index, CharSequence name) { BackendManager.RENDER_BACKEND.bindAttribLocation(program, index, name); }
|
||||
@Override public void glDeleteProgram(int program) { GLStateManager.glDeleteProgram(program); }
|
||||
@Override public void glBindAttribLocation(int program, int index, CharSequence name) {
|
||||
GLStateManager.glBindAttribLocation(program, index, name);
|
||||
}
|
||||
@Override public void glBindFragDataLocation(int program, int colorNumber, CharSequence name) {}
|
||||
|
||||
@Override public int glGetUniformLocation(int program, CharSequence name) { return BackendManager.RENDER_BACKEND.getUniformLocation(program, name); }
|
||||
@Override public int glGetUniformLocation(int program, CharSequence name) {
|
||||
return GLStateManager.glGetUniformLocation(program, name);
|
||||
}
|
||||
@Override public int glGetUniformBlockIndex(int program, CharSequence name) { return -1; /* TODO */ }
|
||||
@Override public void glUniformBlockBinding(int program, int blockIndex, int blockBinding) { /* TODO */ }
|
||||
@Override public void glUniform1f(int location, float v0) { BackendManager.RENDER_BACKEND.uniform1f(location, v0); }
|
||||
@Override public void glUniform1i(int location, int v0) { BackendManager.RENDER_BACKEND.uniform1i(location, v0); }
|
||||
@Override public void glUniform1fv(int location, FloatBuffer value) { BackendManager.RENDER_BACKEND.uniform1fv(location, value); }
|
||||
@Override public void glUniform2i(int location, int v0, int v1) { BackendManager.RENDER_BACKEND.uniform2i(location, v0, v1); }
|
||||
@Override public void glUniform3f(int location, float v0, float v1, float v2) { BackendManager.RENDER_BACKEND.uniform3f(location, v0, v1, v2); }
|
||||
@Override public void glUniform3fv(int location, FloatBuffer value) { BackendManager.RENDER_BACKEND.uniform3(location, value); }
|
||||
@Override public void glUniform3fv(int location, float[] value) {
|
||||
try (var stack = MemoryStack.stackPush()) {
|
||||
final FloatBuffer buf = stack.floats(value);
|
||||
BackendManager.RENDER_BACKEND.uniform3(location, buf);
|
||||
}
|
||||
@Override public void glUniform1f(int location, float v0) { GLStateManager.glUniform1f(location, v0); }
|
||||
@Override public void glUniform1i(int location, int v0) { GLStateManager.glUniform1i(location, v0); }
|
||||
@Override public void glUniform1fv(int location, FloatBuffer value) { GLStateManager.glUniform1(location, value); }
|
||||
@Override public void glUniform2i(int location, int v0, int v1) { GLStateManager.glUniform2i(location, v0, v1); }
|
||||
@Override public void glUniform3f(int location, float v0, float v1, float v2) {
|
||||
GLStateManager.glUniform3f(location, v0, v1, v2);
|
||||
}
|
||||
@Override public void glUniform4fv(int location, FloatBuffer value) { BackendManager.RENDER_BACKEND.uniform4(location, value); }
|
||||
@Override public void glUniform4fv(int location, float[] value) {
|
||||
try (var stack = MemoryStack.stackPush()) {
|
||||
final FloatBuffer buf = stack.floats(value);
|
||||
BackendManager.RENDER_BACKEND.uniform4(location, buf);
|
||||
}
|
||||
@Override public void glUniform3fv(int location, FloatBuffer value) { GLStateManager.glUniform3(location, value); }
|
||||
@Override public void glUniform3fv(int location, float[] value) { GLStateManager.glUniform3(location, value); }
|
||||
@Override public void glUniform4fv(int location, FloatBuffer value) { GLStateManager.glUniform4(location, value); }
|
||||
@Override public void glUniform4fv(int location, float[] value) { GLStateManager.glUniform4(location, value); }
|
||||
@Override public void glUniformMatrix3fv(int location, boolean transpose, FloatBuffer value) {
|
||||
GLStateManager.glUniformMatrix3(location, transpose, value);
|
||||
}
|
||||
@Override public void glUniformMatrix4fv(int location, boolean transpose, FloatBuffer value) {
|
||||
GLStateManager.glUniformMatrix4(location, transpose, value);
|
||||
}
|
||||
@Override public void glUniformMatrix3fv(int location, boolean transpose, FloatBuffer value) { BackendManager.RENDER_BACKEND.uniformMatrix3(location, transpose, value); }
|
||||
@Override public void glUniformMatrix4fv(int location, boolean transpose, FloatBuffer value) { BackendManager.RENDER_BACKEND.uniformMatrix4(location, transpose, value); }
|
||||
|
||||
@Override public void glDrawElementsBaseVertex(int mode, int count, int type, long indices, int basevertex) { BackendManager.RENDER_BACKEND.drawElementsBaseVertex(mode, count, type, indices, basevertex); }
|
||||
@Override public void glMultiDrawElementsBaseVertex(int mode, long pCount, int type, long pIndices, int drawcount, long pBaseVertex) { BackendManager.RENDER_BACKEND.multiDrawElementsBaseVertex(mode, pCount, type, pIndices, drawcount, pBaseVertex); }
|
||||
@Override public void glMultiDrawElementsIndirect(int mode, int type, long indirect, int drawcount, int stride) { BackendManager.RENDER_BACKEND.multiDrawElementsIndirect(mode, type, indirect, drawcount, stride); }
|
||||
@Override public void glDrawElementsBaseVertex(int mode, int count, int type, long indices, int basevertex) {
|
||||
GLStateManager.glDrawElementsBaseVertex(mode, count, type, indices, basevertex);
|
||||
}
|
||||
@Override public void glMultiDrawElementsBaseVertex(int mode, long pCount, int type, long pIndices, int drawcount,
|
||||
long pBaseVertex) {
|
||||
GLStateManager.glMultiDrawElementsBaseVertex(mode, pCount, type, pIndices, drawcount, pBaseVertex);
|
||||
}
|
||||
@Override public void glMultiDrawElementsIndirect(int mode, int type, long indirect, int drawcount, int stride) {
|
||||
GLStateManager.glMultiDrawElementsIndirect(mode, type, indirect, drawcount, stride);
|
||||
}
|
||||
|
||||
@Override public long glFenceSync(int condition, int flags) { return BackendManager.RENDER_BACKEND.fenceSync(condition, flags); }
|
||||
@Override public int glClientWaitSync(long sync, int flags, long timeout) { return BackendManager.RENDER_BACKEND.clientWaitSync(sync, flags, timeout); }
|
||||
@Override public long glFenceSync(int condition, int flags) { return GLStateManager.glFenceSync(condition, flags); }
|
||||
@Override public int glClientWaitSync(long sync, int flags, long timeout) {
|
||||
return GLStateManager.glClientWaitSync(sync, flags, timeout);
|
||||
}
|
||||
@Override public int glGetSynci(long sync, int pname, IntBuffer length) {
|
||||
if (length != null && length.remaining() > 0) length.put(0, 1);
|
||||
return ((SDLGPURenderBackend) BackendManager.RENDER_BACKEND).getSyncStatus(sync);
|
||||
}
|
||||
@Override public void glWaitSync(long sync, int flags, long timeout) {
|
||||
((SDLGPURenderBackend) BackendManager.RENDER_BACKEND).waitSync(sync, flags, timeout);
|
||||
GLStateManager.glWaitSync(sync, flags, timeout);
|
||||
}
|
||||
@Override public void glDeleteSync(long sync) { BackendManager.RENDER_BACKEND.deleteSync(sync); }
|
||||
@Override public void glDeleteSync(long sync) { GLStateManager.glDeleteSync(sync); }
|
||||
|
||||
private long[] queryElapsed = new long[64];
|
||||
private int activeQueryId = -1;
|
||||
@@ -164,7 +221,7 @@ public final class SDLGPULWJGLService extends LWJGLService {
|
||||
}
|
||||
}
|
||||
|
||||
@Override public int glGenQueries() { return BackendManager.RENDER_BACKEND.genQueries(); }
|
||||
@Override public int glGenQueries() { return GLStateManager.glGenQueries(); }
|
||||
@Override public void glDeleteQueries(int query) { }
|
||||
@Override public void glBeginQuery(int target, int id) {
|
||||
ensureQueryCapacity(id);
|
||||
@@ -183,29 +240,42 @@ public final class SDLGPULWJGLService extends LWJGLService {
|
||||
|
||||
@Override public int setupDebugCallback(DebugMessageHandler handler) { return 0; }
|
||||
@Override public void disableDebugCallback() { }
|
||||
@Override public void glObjectLabel(int identifier, int name, CharSequence label) { BackendManager.RENDER_BACKEND.objectLabel(identifier, name, label); }
|
||||
@Override public void glPushDebugGroup(int source, int id, CharSequence message) { BackendManager.RENDER_BACKEND.pushDebugGroup(source, id, message); }
|
||||
@Override public void glPopDebugGroup() { BackendManager.RENDER_BACKEND.popDebugGroup(); }
|
||||
|
||||
@Override public int glGenTextures() { return BackendManager.RENDER_BACKEND.genTextures(); }
|
||||
@Override public void glGenTextures(int[] textures) {
|
||||
for (int i = 0; i < textures.length; i++) textures[i] = BackendManager.RENDER_BACKEND.genTextures();
|
||||
@Override public void glObjectLabel(int identifier, int name, CharSequence label) {
|
||||
GLStateManager.glObjectLabel(identifier, name, label);
|
||||
}
|
||||
@Override public void glDeleteTextures(int texture) { BackendManager.RENDER_BACKEND.deleteTextures(texture); }
|
||||
@Override public void glPushDebugGroup(int source, int id, CharSequence message) {
|
||||
GLStateManager.glPushDebugGroup(source, id, message);
|
||||
}
|
||||
@Override public void glPopDebugGroup() { GLStateManager.glPopDebugGroup(); }
|
||||
|
||||
@Override public int glGenTextures() { return GLStateManager.glGenTextures(); }
|
||||
@Override public void glGenTextures(int[] textures) {
|
||||
for (int i = 0; i < textures.length; i++) textures[i] = GLStateManager.glGenTextures();
|
||||
}
|
||||
@Override public void glDeleteTextures(int texture) { GLStateManager.glDeleteTextures(texture); }
|
||||
@Override public void glDeleteTextures(int[] textures) {
|
||||
for (int t : textures) BackendManager.RENDER_BACKEND.deleteTextures(t);
|
||||
for (int t : textures) GLStateManager.glDeleteTextures(t);
|
||||
}
|
||||
@Override public void glBindTexture(int target, int texture) { GLStateManager.glBindTexture(target, texture); }
|
||||
@Override public void glActiveTexture(int texture) { GLStateManager.glActiveTexture(texture); }
|
||||
@Override public int glGetTexLevelParameteri(int target, int level, int pname) { return BackendManager.RENDER_BACKEND.getTexLevelParameteri(target, level, pname); }
|
||||
@Override public void glCopyTexSubImage2D(int target, int level, int xoffset, int yoffset, int x, int y, int width, int height) { BackendManager.RENDER_BACKEND.copyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height); }
|
||||
@Override public void glPixelStorei(int pname, int param) { BackendManager.RENDER_BACKEND.pixelStorei(pname, param); }
|
||||
@Override public int glGetTexLevelParameteri(int target, int level, int pname) {
|
||||
return GLStateManager.glGetTexLevelParameteri(target, level, pname);
|
||||
}
|
||||
@Override public void glCopyTexSubImage2D(int target, int level, int xoffset, int yoffset, int x, int y, int width,
|
||||
int height) {
|
||||
GLStateManager.glCopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height);
|
||||
}
|
||||
@Override public void glPixelStorei(int pname, int param) { GLStateManager.glPixelStorei(pname, param); }
|
||||
|
||||
@Override public int glGenFramebuffers() { return BackendManager.RENDER_BACKEND.genFramebuffers(); }
|
||||
@Override public void glDeleteFramebuffers(int framebuffer) { BackendManager.RENDER_BACKEND.deleteFramebuffers(framebuffer); }
|
||||
@Override public int glGenFramebuffers() { return GLStateManager.glGenFramebuffers(); }
|
||||
@Override public void glDeleteFramebuffers(int framebuffer) { GLStateManager.glDeleteFramebuffers(framebuffer); }
|
||||
@Override public void glBindFramebuffer(int target, int framebuffer) { GLStateManager.glBindFramebuffer(target, framebuffer); }
|
||||
@Override public int glCheckFramebufferStatus(int target) { return BackendManager.RENDER_BACKEND.checkFramebufferStatus(target); }
|
||||
@Override public void glFramebufferTexture2D(int target, int attachment, int textarget, int texture, int level) { BackendManager.RENDER_BACKEND.framebufferTexture2D(target, attachment, textarget, texture, level); }
|
||||
@Override public int glCheckFramebufferStatus(int target) {
|
||||
return GLStateManager.glCheckFramebufferStatus(target);
|
||||
}
|
||||
@Override public void glFramebufferTexture2D(int target, int attachment, int textarget, int texture, int level) {
|
||||
GLStateManager.glFramebufferTexture2D(target, attachment, textarget, texture, level);
|
||||
}
|
||||
|
||||
@Override public void glEnable(int cap) { GLStateManager.glEnable(cap); }
|
||||
@Override public void glDisable(int cap) { GLStateManager.glDisable(cap); }
|
||||
@@ -215,23 +285,26 @@ public final class SDLGPULWJGLService extends LWJGLService {
|
||||
@Override public void glDepthMask(boolean flag) { GLStateManager.glDepthMask(flag); }
|
||||
@Override public void glColorMask(boolean red, boolean green, boolean blue, boolean alpha) { GLStateManager.glColorMask(red, green, blue, alpha); }
|
||||
@Override public void glViewport(int x, int y, int width, int height) { GLStateManager.glViewport(x, y, width, height); }
|
||||
@Override public void glClear(int mask) { BackendManager.RENDER_BACKEND.clear(mask); }
|
||||
@Override public void glClearColor(float red, float green, float blue, float alpha) { BackendManager.RENDER_BACKEND.clearColor(red, green, blue, alpha); }
|
||||
@Override public int glGetError() { return BackendManager.RENDER_BACKEND.getError(); }
|
||||
|
||||
@Override public void glMatrixMode(int mode) { /* Handled by GLSM FFP emulation */ }
|
||||
@Override public void glLoadMatrixf(FloatBuffer m) { /* Handled by GLSM FFP emulation */ }
|
||||
|
||||
@Override public int glGetInteger(int pname) { return BackendManager.RENDER_BACKEND.getInteger(pname); }
|
||||
@Override public void glGetIntegerv(int pname, int[] params) {
|
||||
if (params.length > 0) params[0] = BackendManager.RENDER_BACKEND.getInteger(pname);
|
||||
@Override public void glClear(int mask) { GLStateManager.glClear(mask); }
|
||||
@Override public void glClearColor(float red, float green, float blue, float alpha) {
|
||||
GLStateManager.glClearColor(red, green, blue, alpha);
|
||||
}
|
||||
@Override public int glGetError() { return GLStateManager.glGetError(); }
|
||||
|
||||
@Override public void glMatrixMode(int mode) { GLStateManager.glMatrixMode(mode); }
|
||||
@Override public void glLoadMatrixf(FloatBuffer m) { GLStateManager.glLoadMatrix(m); }
|
||||
|
||||
@Override public int glGetInteger(int pname) { return GLStateManager.glGetInteger(pname); }
|
||||
@Override public void glGetIntegerv(int pname, int[] params) { GLStateManager.glGetInteger(pname, params); }
|
||||
@Override public boolean glGetBoolean(int pname) { return GLStateManager.glGetBoolean(pname); }
|
||||
@Override public String glGetString(int pname) { return GLStateManager.glGetString(pname); }
|
||||
@Override public int glGetAttribLocation(int program, CharSequence name) {
|
||||
return GLStateManager.glGetAttribLocation(program, name);
|
||||
}
|
||||
@Override public boolean glGetBoolean(int pname) { return BackendManager.RENDER_BACKEND.getBoolean(pname); }
|
||||
@Override public String glGetString(int pname) { return BackendManager.RENDER_BACKEND.getString(pname); }
|
||||
@Override public int glGetAttribLocation(int program, CharSequence name) { return BackendManager.RENDER_BACKEND.getAttribLocation(program, name); }
|
||||
|
||||
@Override public MemoryStack stackPush() {
|
||||
return new MemoryStackWrapper(org.lwjgl.system.MemoryStack.stackPush());
|
||||
org.lwjgl.system.MemoryStack.stackPush();
|
||||
return STACK.get();
|
||||
}
|
||||
|
||||
@Override public long nmemAlloc(long size) { return MemoryUtil.nmemAlloc(size); }
|
||||
|
||||
+482
-182
File diff suppressed because it is too large.
Load diff
@@ -335,7 +335,7 @@ public final class Device {
|
||||
}
|
||||
vsyncMode = candidate;
|
||||
LOG.info("SDL present mode {} (preferred={}), refresh {}Hz, {}, video={}, framesInFlight={}, supported: vsync={} immediate={} mailbox={}",
|
||||
candidate, preferred, getDisplayRefreshRateHz(),
|
||||
candidate, preferred, RenderBackend.hzFromPeriod(getDisplayRefreshPeriodNanos()),
|
||||
(SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN) != 0 ? "fullscreen" : "windowed",
|
||||
SDL_GetCurrentVideoDriver(), FRAMES_IN_FLIGHT,
|
||||
supportsVSyncMode(VSyncMode.ON), supportsVSyncMode(VSyncMode.OFF), supportsVSyncMode(VSyncMode.MAILBOX));
|
||||
@@ -376,13 +376,13 @@ public final class Device {
|
||||
};
|
||||
}
|
||||
|
||||
public int getDisplayRefreshRateHz() {
|
||||
public long getDisplayRefreshPeriodNanos() {
|
||||
final long window = Display.getWindow();
|
||||
final int displayId = window == 0 ? SDL_GetPrimaryDisplay() : SDL_GetDisplayForWindow(window);
|
||||
if (displayId == 0) return 0;
|
||||
if (displayId == 0) return 0L;
|
||||
final SDL_DisplayMode mode = SDL_GetCurrentDisplayMode(displayId);
|
||||
if (mode == null) return 0;
|
||||
return RenderBackend.refreshHzFrom(mode.refresh_rate_numerator(), mode.refresh_rate_denominator(), mode.refresh_rate());
|
||||
if (mode == null) return 0L;
|
||||
return RenderBackend.periodFromRational(mode.refresh_rate_numerator(), mode.refresh_rate_denominator(), mode.refresh_rate());
|
||||
}
|
||||
|
||||
public int getSwapchainTextureFormat() {
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
package com.gtnewhorizons.angelica.sdlgpu.device;
|
||||
|
||||
import com.mitchej123.lwjgl.MemoryStack;
|
||||
import org.taumc.celeritas.lwjgl.MemoryStack;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
|
||||
@@ -54,6 +54,28 @@ public final class ContextState {
|
||||
|
||||
public final PixelStoreState pixelStore = new PixelStoreState();
|
||||
|
||||
public static final class ProxyTextureState {
|
||||
public int target;
|
||||
public int width;
|
||||
public int height;
|
||||
public int depth;
|
||||
public int internalFormat;
|
||||
|
||||
public void accept(int target, int width, int height, int depth, int internalFormat) {
|
||||
this.target = target;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.depth = depth;
|
||||
this.internalFormat = internalFormat;
|
||||
}
|
||||
|
||||
public void reject(int target) {
|
||||
accept(target, 0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public final ProxyTextureState proxyTexture = new ProxyTextureState();
|
||||
|
||||
public static final class VAOState {
|
||||
public final boolean[] attribEnabled = new boolean[MAX_VERTEX_ATTRIBS];
|
||||
public int attribEnabledMask;
|
||||
@@ -211,6 +233,7 @@ public final class ContextState {
|
||||
|
||||
public int boundArrayBuffer;
|
||||
public int boundIndirectBuffer;
|
||||
public int boundDispatchIndirectBuffer;
|
||||
public int boundUniformBuffer;
|
||||
public int boundCopyReadBuffer;
|
||||
public int boundCopyWriteBuffer;
|
||||
@@ -226,6 +249,7 @@ public final class ContextState {
|
||||
case GL30.GL_PIXEL_UNPACK_BUFFER -> boundPixelUnpackBuffer;
|
||||
case GL30.GL_PIXEL_PACK_BUFFER -> boundPixelPackBuffer;
|
||||
case GL40.GL_DRAW_INDIRECT_BUFFER -> boundIndirectBuffer;
|
||||
case GL43.GL_DISPATCH_INDIRECT_BUFFER -> boundDispatchIndirectBuffer;
|
||||
case GL31.GL_COPY_READ_BUFFER -> boundCopyReadBuffer;
|
||||
case GL31.GL_COPY_WRITE_BUFFER -> boundCopyWriteBuffer;
|
||||
case GL43.GL_SHADER_STORAGE_BUFFER -> boundSSBO;
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package com.gtnewhorizons.angelica.sdlgpu.frame;
|
||||
|
||||
import com.gtnewhorizons.angelica.config.SystemProperties;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.BackendManager;
|
||||
import com.gtnewhorizons.angelica.glsm.profiling.Tracy;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.device.Device;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.resource.FboState;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.resource.ResourceManager;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.util.MemoryAccess;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.util.ThreadRegistry;
|
||||
@@ -43,6 +45,7 @@ public final class FrameManager {
|
||||
private static final Tracy.ZoneId Z_SDL_SUBMIT = Tracy.zoneId("sdlSubmit", Tracy.COLOR_SWAP);
|
||||
private static final Tracy.ZoneId Z_SDL_ACQUIRE_WAIT = Tracy.zoneId("sdlAcquireWait", Tracy.COLOR_SWAP);
|
||||
|
||||
private static final Tracy.ZoneId Z_SDL_GPU_WAIT = Tracy.zoneId("sdlGpuWait", Tracy.COLOR_SWAP);
|
||||
private final Device device;
|
||||
private ResourceManager resourceManager;
|
||||
|
||||
@@ -83,8 +86,7 @@ public final class FrameManager {
|
||||
public int batchBreakSamplerThisFrame;
|
||||
public int stateAppliesThisFrame;
|
||||
public long acquireWaitNanosThisFrame;
|
||||
public volatile long lastFrameGateNanos;
|
||||
public volatile long lastFrameGateEndNanos;
|
||||
public long gpuWaitNanosThisFrame;
|
||||
public int midFrameSubmitsThisFrame;
|
||||
public int mipGensThisFrame;
|
||||
public int mipGenSubmitsThisFrame;
|
||||
@@ -597,6 +599,7 @@ public final class FrameManager {
|
||||
f.stateAppliesThisFrame = 0;
|
||||
f.acquireWaitNanosThisFrame = 0;
|
||||
f.mipGensThisFrame = 0;
|
||||
f.gpuWaitNanosThisFrame = 0;
|
||||
f.mipGenSubmitsThisFrame = 0;
|
||||
f.uploadFlushSubmitsThisFrame = 0;
|
||||
f.blitsThisFrame = 0;
|
||||
@@ -773,7 +776,10 @@ public final class FrameManager {
|
||||
}
|
||||
|
||||
public int getSwapchainFormat() {
|
||||
return device.getSwapchainTextureFormat();
|
||||
if (device.getClaimedWindow() != 0L) return device.getSwapchainTextureFormat();
|
||||
if (resourceManager == null || !finalTarget.isReady()) return 0;
|
||||
final FboState fbo = resourceManager.getFbo(finalTarget.fboId());
|
||||
return fbo != null ? fbo.getColorFormat() : 0;
|
||||
}
|
||||
|
||||
public boolean isFrameActive() {
|
||||
@@ -873,14 +879,6 @@ public final class FrameManager {
|
||||
registeredFrames.clear();
|
||||
}
|
||||
|
||||
public long lastFrameGateNanos() {
|
||||
return frame().lastFrameGateNanos;
|
||||
}
|
||||
|
||||
public long lastFrameGateEndNanos() {
|
||||
return frame().lastFrameGateEndNanos;
|
||||
}
|
||||
|
||||
private volatile Presenter presenter;
|
||||
|
||||
public void setPresenter(Presenter presenter) {
|
||||
@@ -894,6 +892,7 @@ public final class FrameManager {
|
||||
}
|
||||
|
||||
public void presentFinalTarget() {
|
||||
if (BackendManager.RENDER_BACKEND.isPresentSuppressed()) return;
|
||||
if (!finalTarget.hasContent()) return;
|
||||
present(finalTarget.colorTexture(), finalTarget.width(), finalTarget.height(), SDL_FLIP_NONE, false);
|
||||
}
|
||||
@@ -916,16 +915,13 @@ public final class FrameManager {
|
||||
}
|
||||
|
||||
private final AtomicLong windowAcquireWaitNanos = new AtomicLong();
|
||||
private final AtomicLong windowGpuWaitNanos = new AtomicLong();
|
||||
private final AtomicInteger windowPresentSkips = new AtomicInteger();
|
||||
|
||||
private void drainWindowPresentCounters(FrameState f) {
|
||||
if (f == windowFrame) return;
|
||||
final long acquire = windowAcquireWaitNanos.getAndSet(0L);
|
||||
if (acquire != 0L) {
|
||||
f.acquireWaitNanosThisFrame += acquire;
|
||||
f.lastFrameGateNanos = windowFrame.lastFrameGateNanos;
|
||||
f.lastFrameGateEndNanos = windowFrame.lastFrameGateEndNanos;
|
||||
}
|
||||
f.acquireWaitNanosThisFrame += windowAcquireWaitNanos.getAndSet(0L);
|
||||
f.gpuWaitNanosThisFrame += windowGpuWaitNanos.getAndSet(0L);
|
||||
f.presentSkipsThisFrame += windowPresentSkips.getAndSet(0);
|
||||
}
|
||||
|
||||
@@ -934,6 +930,11 @@ public final class FrameManager {
|
||||
else f.acquireWaitNanosThisFrame += waitNanos;
|
||||
}
|
||||
|
||||
private void noteGpuWait(FrameState f, long waitNanos) {
|
||||
if (f == windowFrame) windowGpuWaitNanos.addAndGet(waitNanos);
|
||||
else f.gpuWaitNanosThisFrame += waitNanos;
|
||||
}
|
||||
|
||||
private void notePresentSkip(FrameState f) {
|
||||
if (f == windowFrame) windowPresentSkips.incrementAndGet();
|
||||
else f.presentSkipsThisFrame++;
|
||||
@@ -962,13 +963,31 @@ public final class FrameManager {
|
||||
final int w;
|
||||
final int h;
|
||||
final boolean callOk;
|
||||
final boolean swapchainReady;
|
||||
Tracy.beginZone(Z_SDL_GPU_WAIT);
|
||||
try {
|
||||
swapchainReady = SDL_WaitForGPUSwapchain(device.getDevice(), window);
|
||||
} finally {
|
||||
Tracy.endZone();
|
||||
}
|
||||
final long t1 = System.nanoTime();
|
||||
BackendManager.RENDER_BACKEND.recordGpuWait(t1 - t0);
|
||||
noteGpuWait(f, t1 - t0);
|
||||
|
||||
if (!swapchainReady) {
|
||||
SDL_CancelGPUCommandBuffer(cb);
|
||||
f.swapchainUnavailable = true;
|
||||
notePresentSkip(f);
|
||||
return false;
|
||||
}
|
||||
|
||||
try (var stack = stackPush()) {
|
||||
final PointerBuffer pTexture = stack.pointers(0);
|
||||
final IntBuffer pWidth = stack.ints(0);
|
||||
final IntBuffer pHeight = stack.ints(0);
|
||||
Tracy.beginZone(Z_SDL_ACQUIRE_WAIT);
|
||||
try {
|
||||
callOk = SDL_WaitAndAcquireGPUSwapchainTexture(cb, window, pTexture, pWidth, pHeight);
|
||||
callOk = SDL_AcquireGPUSwapchainTexture(cb, window, pTexture, pWidth, pHeight);
|
||||
} finally {
|
||||
Tracy.endZone();
|
||||
}
|
||||
@@ -981,9 +1000,6 @@ public final class FrameManager {
|
||||
throw re;
|
||||
}
|
||||
final long tEnd = System.nanoTime();
|
||||
f.lastFrameGateNanos = tEnd - t0;
|
||||
f.lastFrameGateEndNanos = tEnd;
|
||||
noteAcquireWait(f, tEnd - t0);
|
||||
|
||||
if (!callOk || tex == 0) {
|
||||
SDL_CancelGPUCommandBuffer(cb);
|
||||
@@ -992,6 +1008,9 @@ public final class FrameManager {
|
||||
return false;
|
||||
}
|
||||
|
||||
BackendManager.RENDER_BACKEND.recordGate(tEnd - t0, tEnd);
|
||||
noteAcquireWait(f, tEnd - t1);
|
||||
|
||||
try (MemoryStack stack = stackPush()) {
|
||||
final long info = stack.ncalloc(SDL_GPUBlitInfo.ALIGNOF, 1, SDL_GPUBlitInfo.SIZEOF);
|
||||
writeBlitRegion(info + SDL_GPUBlitInfo.SOURCE, srcTexture, srcW, srcH);
|
||||
|
||||
@@ -8,23 +8,34 @@ public final class Presenter {
|
||||
private final FrameManager frameManager;
|
||||
private final Executor windowThreadExecutor;
|
||||
private final Semaphore pending = new Semaphore(1);
|
||||
private final Runnable task;
|
||||
|
||||
private long srcTexture;
|
||||
private int srcW;
|
||||
private int srcH;
|
||||
private int flipMode;
|
||||
|
||||
public Presenter(FrameManager frameManager, Executor windowThreadExecutor) {
|
||||
this.frameManager = frameManager;
|
||||
this.windowThreadExecutor = windowThreadExecutor;
|
||||
this.task = () -> {
|
||||
try {
|
||||
frameManager.presentOnWindowThread(srcTexture, srcW, srcH, flipMode);
|
||||
} finally {
|
||||
pending.release();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public void requestPresent(long srcTexture, int srcW, int srcH, int flipMode) {
|
||||
pending.acquireUninterruptibly();
|
||||
this.srcTexture = srcTexture;
|
||||
this.srcW = srcW;
|
||||
this.srcH = srcH;
|
||||
this.flipMode = flipMode;
|
||||
boolean submitted = false;
|
||||
try {
|
||||
windowThreadExecutor.execute(() -> {
|
||||
try {
|
||||
frameManager.presentOnWindowThread(srcTexture, srcW, srcH, flipMode);
|
||||
} finally {
|
||||
pending.release();
|
||||
}
|
||||
});
|
||||
windowThreadExecutor.execute(task);
|
||||
submitted = true;
|
||||
} finally {
|
||||
if (!submitted) pending.release();
|
||||
|
||||
@@ -37,6 +37,7 @@ final class SdlFramePlots {
|
||||
private static final long PLOT_PER_PASS_BLOCK_FLUSHES = Tracy.plotHandle("sdl.perPassBlockFlushes");
|
||||
private static final long PLOT_PER_PASS_BLOCK_BYTES = Tracy.plotHandle("sdl.perPassBlockBytes", TracyBackend.PLOT_FORMAT_MEMORY);
|
||||
private static final long PLOT_ACQUIRE_WAIT_US = Tracy.plotHandle("sdl.acquireWaitUs");
|
||||
private static final long PLOT_GPU_WAIT_US = Tracy.plotHandle("sdl.gpuWaitUs");
|
||||
private static final long PLOT_PRESENT_TO_PRESENT_US = Tracy.plotHandle("sdl.presentToPresentUs");
|
||||
private static final long PLOT_FRAMES_IN_FLIGHT = Tracy.plotHandle("sdl.framesInFlight");
|
||||
private static final long PLOT_MIP_GENS = Tracy.plotHandle("sdl.mipGens");
|
||||
@@ -97,6 +98,7 @@ final class SdlFramePlots {
|
||||
Tracy.plotInt(PLOT_PER_PASS_BLOCK_FLUSHES, f.uniformBlockFlushesThisFrame[ShaderManager.BLOCK_PER_PASS]);
|
||||
Tracy.plotInt(PLOT_PER_PASS_BLOCK_BYTES, f.uniformBlockBytesThisFrame[ShaderManager.BLOCK_PER_PASS]);
|
||||
Tracy.plotInt(PLOT_ACQUIRE_WAIT_US, f.acquireWaitNanosThisFrame / 1000);
|
||||
Tracy.plotInt(PLOT_GPU_WAIT_US, f.gpuWaitNanosThisFrame / 1000);
|
||||
final long nowNanos = System.nanoTime();
|
||||
if (f.lastEndFrameNanos != 0) Tracy.plotInt(PLOT_PRESENT_TO_PRESENT_US, (nowNanos - f.lastEndFrameNanos) / 1000);
|
||||
f.lastEndFrameNanos = nowNanos;
|
||||
|
||||
@@ -109,6 +109,7 @@ public final class DrawDispatch {
|
||||
}
|
||||
|
||||
public void issueIndexedDraw(ContextState st, long rp, int ebo, int indexType, int count, int instances, int firstIndex, int baseVertex) {
|
||||
if (count <= 0 || instances <= 0) return;
|
||||
final EBOSplitScanner.EboSplit[] full;
|
||||
if (st.primitiveRestartEnabled) {
|
||||
full = resourceManager.getOrScanSplits(ebo, indexType, st.primitiveRestartSentinel);
|
||||
@@ -131,7 +132,7 @@ public final class DrawDispatch {
|
||||
}
|
||||
}
|
||||
|
||||
public void drawTriangleFanAsTriangleList(ContextState st, int first, int count) {
|
||||
public boolean drawTriangleFanAsTriangleList(ContextState st, int first, int count) {
|
||||
final int numTriangles = count - 2;
|
||||
final boolean use32bit = (first + count - 1) > 65535;
|
||||
final int bytesPerIndex = use32bit ? 4 : 2;
|
||||
@@ -180,13 +181,14 @@ public final class DrawDispatch {
|
||||
|
||||
setPrimitiveTypeForDraw(st, SDL_GPU_PRIMITIVETYPE_TRIANGLELIST);
|
||||
pipelineApplier.ensureRenderPass(st);
|
||||
if (!frameManager.isRenderPassActive()) return;
|
||||
if (!pipelineApplier.applyPipelineAndState(st)) return;
|
||||
if (!frameManager.isRenderPassActive()) return false;
|
||||
if (!pipelineApplier.applyPipelineAndState(st)) return false;
|
||||
|
||||
final int elementSize = use32bit ? SDL_GPU_INDEXELEMENTSIZE_32BIT : SDL_GPU_INDEXELEMENTSIZE_16BIT;
|
||||
final long rp = frameManager.getRenderPass();
|
||||
bindIndexBufferIfChanged(st, rp, st.fanIndexBuffer, elementSize, uploadOffset);
|
||||
SDL_DrawGPUIndexedPrimitives(rp, numTriangles * 3, 1, 0, 0, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
private long createFanIndexBuffer(int capacity) {
|
||||
|
||||
@@ -0,0 +1,211 @@
|
||||
package com.gtnewhorizons.angelica.sdlgpu.pipeline;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.FFPUniformBlock;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.frame.ContextState;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.frame.FrameManager;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.resource.PersistentMapping;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.resource.ResourceManager;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.shader.ShaderManager;
|
||||
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public final class FFPTrace {
|
||||
private static final Logger LOG = LogManager.getLogger("Angelica-SDLGPU");
|
||||
private static final String PREFIX = "[FFPTrace] ";
|
||||
private static final int MAX_KEYS = 512;
|
||||
|
||||
private final ResourceManager resourceManager;
|
||||
private final LongOpenHashSet seenKeys = new LongOpenHashSet();
|
||||
private final StringBuilder sb = new StringBuilder(1024);
|
||||
private int drawsThisFrame;
|
||||
private boolean capReported;
|
||||
|
||||
public FFPTrace(ResourceManager resourceManager) {
|
||||
this.resourceManager = resourceManager;
|
||||
}
|
||||
|
||||
public synchronized void trace(ContextState st, String kind, int mode, int count, int indexType, int first, int baseVertex) {
|
||||
drawsThisFrame++;
|
||||
final long key = key(st);
|
||||
if (seenKeys.contains(key)) return;
|
||||
if (seenKeys.size() >= MAX_KEYS) {
|
||||
if (!capReported) {
|
||||
capReported = true;
|
||||
LOG.info("{}key cap {} reached; no further distinct draw states will be logged", PREFIX, MAX_KEYS);
|
||||
}
|
||||
return;
|
||||
}
|
||||
seenKeys.add(key);
|
||||
LOG.info(format(st, kind, mode, count, indexType, first, baseVertex));
|
||||
}
|
||||
|
||||
public synchronized void frameEnd(FrameManager.FrameState f) {
|
||||
LOG.info("{}frame {} end: draws={} dropped={}", PREFIX, f.frameNumber, drawsThisFrame, f.droppedDrawsThisFrame);
|
||||
drawsThisFrame = 0;
|
||||
}
|
||||
|
||||
private long key(ContextState st) {
|
||||
final PipelineCache p = st.pipeline;
|
||||
long h = p.lastKey();
|
||||
h = Hashing.fmix64(h, st.boundFboId);
|
||||
h = Hashing.fmix64(h, Hashing.packHiLo(st.boundTextures[0], st.boundTextures[1]));
|
||||
h = Hashing.fmix64(h, Hashing.packHiLo(st.scissorEnabled ? 1 : 0, 0));
|
||||
h = Hashing.fmix64(h, Hashing.packHiLo(Float.floatToRawIntBits(st.viewportX), Float.floatToRawIntBits(st.viewportY)));
|
||||
h = Hashing.fmix64(h, Hashing.packHiLo(Float.floatToRawIntBits(st.viewportW), Float.floatToRawIntBits(st.viewportH)));
|
||||
h = Hashing.fmix64(h, Hashing.packHiLo(st.scissorX, st.scissorY));
|
||||
h = Hashing.fmix64(h, Hashing.packHiLo(st.scissorW, st.scissorH));
|
||||
return h;
|
||||
}
|
||||
|
||||
private String format(ContextState st, String kind, int mode, int count, int indexType, int first, int baseVertex) {
|
||||
final PipelineCache p = st.pipeline;
|
||||
final ContextState.VAOState vao = st.currentVao;
|
||||
final ShaderManager.ProgramObject prog = st.boundProgramObj;
|
||||
sb.setLength(0);
|
||||
sb.append(PREFIX);
|
||||
sb.append("kind=").append(kind);
|
||||
sb.append(" mode=0x").append(Integer.toHexString(mode));
|
||||
sb.append(" count=").append(count);
|
||||
if (indexType != 0) sb.append(" indexType=0x").append(Integer.toHexString(indexType));
|
||||
sb.append(" first=").append(first).append(" baseVertex=").append(baseVertex);
|
||||
|
||||
sb.append(" | prog=").append(st.boundProgram);
|
||||
if (prog == null) {
|
||||
sb.append(" progObj=null");
|
||||
} else {
|
||||
sb.append(" ffpUboBinding=").append(prog.externalUboBinding);
|
||||
sb.append(" fragSamplers=").append(prog.fragmentSamplerNames);
|
||||
sb.append(" vsInputs=[");
|
||||
boolean firstName = true;
|
||||
for (int i = 0; i < prog.vertexInputName.length; i++) {
|
||||
final String n = prog.vertexInputName[i];
|
||||
if (n == null) continue;
|
||||
if (!firstName) sb.append(',');
|
||||
firstName = false;
|
||||
sb.append(i).append(':').append(n);
|
||||
}
|
||||
sb.append(']');
|
||||
}
|
||||
|
||||
sb.append(" | fbo=").append(st.boundFboId);
|
||||
sb.append(" vp=(").append(st.viewportX).append(',').append(st.viewportY).append(',')
|
||||
.append(st.viewportW).append(',').append(st.viewportH).append(')');
|
||||
sb.append(" scissor=").append(st.scissorEnabled);
|
||||
if (st.scissorEnabled) {
|
||||
sb.append('(').append(st.scissorX).append(',').append(st.scissorY).append(',')
|
||||
.append(st.scissorW).append(',').append(st.scissorH).append(')');
|
||||
}
|
||||
sb.append(" cull=").append(p.cullEnabled).append("/mode=").append(p.cullFaceMode)
|
||||
.append("/front=").append(p.frontFace).append("/all=").append(p.cullAll);
|
||||
sb.append(" depth=").append(p.depthTestEnabled).append("/func=").append(p.depthCompareOp)
|
||||
.append("/write=").append(p.depthWriteEnabled);
|
||||
sb.append(" blend=").append(p.blendEnabledPerDrawBuffer[0])
|
||||
.append("/src=").append(p.srcColorFactors[0]).append("/dst=").append(p.dstColorFactors[0])
|
||||
.append("/srcA=").append(p.srcAlphaFactors[0]).append("/dstA=").append(p.dstAlphaFactors[0])
|
||||
.append("/op=").append(p.colorBlendOp).append('/').append(p.alphaBlendOp);
|
||||
sb.append(" colorMask=0x").append(Integer.toHexString(p.colorWriteMask));
|
||||
sb.append(" primitiveType=").append(p.primitiveType);
|
||||
|
||||
appendTextureUnit(st, 0);
|
||||
appendTextureUnit(st, 1);
|
||||
|
||||
sb.append(" | attribs=[");
|
||||
int enabled = vao.attribEnabledMask;
|
||||
boolean firstAttrib = true;
|
||||
while (enabled != 0) {
|
||||
final int i = Integer.numberOfTrailingZeros(enabled);
|
||||
enabled &= enabled - 1;
|
||||
final int b = vao.attribBinding[i];
|
||||
if (!firstAttrib) sb.append(' ');
|
||||
firstAttrib = false;
|
||||
sb.append(i).append(":size=").append(vao.attribSize[i])
|
||||
.append(",type=0x").append(Integer.toHexString(vao.attribType[i]))
|
||||
.append(",norm=").append(vao.attribNormalized[i])
|
||||
.append(",int=").append(vao.attribIsInteger[i])
|
||||
.append(",stride=").append(vao.attribStride[i])
|
||||
.append(",off=").append(vao.attribOffset[i])
|
||||
.append(",buf=").append(vao.bindingBuffer[b])
|
||||
.append(",bufOff=").append(vao.bindingOffset[b]);
|
||||
}
|
||||
sb.append("] enabledMask=0x").append(Integer.toHexString(vao.attribEnabledMask))
|
||||
.append(" shaderInputMask=0x").append(Integer.toHexString(p.shaderInputMask))
|
||||
.append(" ebo=").append(vao.elementBuffer);
|
||||
|
||||
appendFfpUniforms(st, prog);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private void appendTextureUnit(ContextState st, int unit) {
|
||||
final int id = st.boundTextures[unit];
|
||||
sb.append(" | tex").append(unit).append("=").append(id);
|
||||
if (id != 0) {
|
||||
sb.append(" handle=").append(resourceManager.getTextureHandle(id) != 0);
|
||||
final ResourceManager.TextureMeta meta = resourceManager.getTextureMeta(id);
|
||||
if (meta == null) {
|
||||
sb.append(" meta=null");
|
||||
} else {
|
||||
sb.append(" size=").append(meta.width()).append('x').append(meta.height())
|
||||
.append(" levels=").append(meta.levels())
|
||||
.append(" sdlFormat=").append(meta.sdlFormat())
|
||||
.append(" glFormat=0x").append(Integer.toHexString(meta.glFormat()));
|
||||
}
|
||||
}
|
||||
sb.append(" ffpEnabled=").append(GLStateManager.getTextures().getTextureUnitStates(unit).isEnabled());
|
||||
}
|
||||
|
||||
private void appendFfpUniforms(ContextState st, ShaderManager.ProgramObject prog) {
|
||||
if (prog == null) return;
|
||||
final int binding = prog.externalUboBinding;
|
||||
if (binding < 0 || binding >= ContextState.MAX_INDEXED_BUFFERS) return;
|
||||
final int glId = st.boundUboByIndex[binding];
|
||||
if (glId == 0) {
|
||||
sb.append(" | ffpUbo=unbound");
|
||||
return;
|
||||
}
|
||||
final PersistentMapping pm = resourceManager.getPersistentMapping(glId);
|
||||
final ByteBuffer buf = pm != null ? pm.staging : resourceManager.getUboShadow(glId);
|
||||
if (buf == null) {
|
||||
sb.append(" | ffpUbo=buffer").append(glId).append(" noStaging");
|
||||
return;
|
||||
}
|
||||
final int base = st.uboRangeOffset[binding];
|
||||
sb.append(" | ffpUbo=buffer").append(glId).append(" base=").append(base);
|
||||
appendUboFloat(buf, base, FFPUniformBlock.ALPHA_REF, " u_AlphaRef=");
|
||||
appendUboMatrix(buf, base, FFPUniformBlock.MVP_MATRIX, " u_MVPMatrix=");
|
||||
appendUboMatrix(buf, base, FFPUniformBlock.LIGHTMAP_TEXTURE_MATRIX, " u_LightmapTextureMatrix=");
|
||||
}
|
||||
|
||||
private static float floatIgnoringLimit(ByteBuffer buf, int at) {
|
||||
return MemoryUtil.memGetFloat(MemoryUtil.memAddress0(buf) + at);
|
||||
}
|
||||
|
||||
private void appendUboFloat(ByteBuffer buf, int base, int memberOffset, String label) {
|
||||
final int at = base + memberOffset;
|
||||
sb.append(label);
|
||||
if (at < 0 || at + 4 > buf.capacity()) {
|
||||
sb.append("oob");
|
||||
return;
|
||||
}
|
||||
sb.append(floatIgnoringLimit(buf, at));
|
||||
}
|
||||
|
||||
private void appendUboMatrix(ByteBuffer buf, int base, int memberOffset, String label) {
|
||||
final int at = base + memberOffset;
|
||||
sb.append(label);
|
||||
if (at < 0 || at + 64 > buf.capacity()) {
|
||||
sb.append("oob");
|
||||
return;
|
||||
}
|
||||
sb.append('[');
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (i > 0) sb.append(',');
|
||||
sb.append(floatIgnoringLimit(buf, at + i * 4));
|
||||
}
|
||||
sb.append(']');
|
||||
}
|
||||
}
|
||||
+44
-24
@@ -69,8 +69,11 @@ public final class PipelineApplier {
|
||||
private final IntOpenHashSet dummyVboZeroWarned = new IntOpenHashSet();
|
||||
private final IntOpenHashSet staleFboWarned = new IntOpenHashSet();
|
||||
private final LongOpenHashSet bogusScissorWarned = new LongOpenHashSet();
|
||||
private final LongOpenHashSet clampedScissorWarned = new LongOpenHashSet();
|
||||
private final IntOpenHashSet pushUniformsNullCbWarned = new IntOpenHashSet();
|
||||
|
||||
private final int[] scissorRect = new int[4];
|
||||
|
||||
public PipelineApplier(FrameManager frameManager, ResourceManager resourceManager, ShaderManager shaderManager, PipelineStore pipelineStore, FBOClearTracker fboClearTracker, PersistentBufferSync persistentSync, SamplerBinder samplerBinder, StorageTextureBinder storageTextureBinder, StorageBufferBinder storageBufferBinder) {
|
||||
this.frameManager = frameManager;
|
||||
this.resourceManager = resourceManager;
|
||||
@@ -337,6 +340,11 @@ public final class PipelineApplier {
|
||||
st.ringChunkUsedBlocks = 0;
|
||||
}
|
||||
|
||||
private int fbo0Height(ContextState st) {
|
||||
final int h = frameManager.getFbo0Height();
|
||||
return h > 0 ? h : (int) (st.viewportY + st.viewportH);
|
||||
}
|
||||
|
||||
public boolean applyPipelineAndState(ContextState st) {
|
||||
return applyPipelineAndState(st, frameManager.frame());
|
||||
}
|
||||
@@ -435,11 +443,14 @@ public final class PipelineApplier {
|
||||
}
|
||||
|
||||
if (st.viewportDirty) {
|
||||
float vpY = st.viewportY;
|
||||
float vpH = st.viewportH;
|
||||
final float vpY;
|
||||
final float vpH;
|
||||
if (renderingToFbo) {
|
||||
vpY = st.viewportY + st.viewportH;
|
||||
vpH = -st.viewportH;
|
||||
} else {
|
||||
vpY = fbo0Height(st) - st.viewportY - st.viewportH;
|
||||
vpH = st.viewportH;
|
||||
}
|
||||
final long addr = st.cachedViewport.address();
|
||||
MemoryAccess.putFloat(addr + SDL_GPUViewport.X, st.viewportX);
|
||||
@@ -453,32 +464,40 @@ public final class PipelineApplier {
|
||||
}
|
||||
|
||||
if (st.scissorDirty) {
|
||||
final long addr = st.cachedScissor.address();
|
||||
final int sx, sy, sw, sh;
|
||||
final int fbHeight;
|
||||
if (st.scissorEnabled) {
|
||||
if (renderingToFbo) {
|
||||
fbHeight = (fboState != null && fboState.height > 0) ? fboState.height : (int)(st.viewportY + st.viewportH);
|
||||
} else {
|
||||
fbHeight = (int)(st.viewportY + st.viewportH);
|
||||
}
|
||||
sx = st.scissorX;
|
||||
sy = fbHeight - st.scissorY - st.scissorH;
|
||||
sy = renderingToFbo ? st.scissorY : (fbo0Height(st) - st.scissorY - st.scissorH);
|
||||
sw = st.scissorW;
|
||||
sh = st.scissorH;
|
||||
MemoryAccess.putInt(addr + SDL_Rect.X, sx);
|
||||
MemoryAccess.putInt(addr + SDL_Rect.Y, sy);
|
||||
MemoryAccess.putInt(addr + SDL_Rect.W, sw);
|
||||
MemoryAccess.putInt(addr + SDL_Rect.H, sh);
|
||||
if ((sw <= 0 || sh <= 0) && bogusScissorWarned.add(Hashing.packHiLo(sw, sh))) {
|
||||
LOG.warn("applyPipelineAndState: scissor degenerate (x={} y={} w={} h={}) - all fragments clipped; boundProgram={} boundFbo={} fbHeight={} scissorEn={} src=[{},{},{},{}]", sx, sy, sw, sh, st.boundProgram, st.boundFboId, fbHeight, st.scissorEnabled, st.scissorX, st.scissorY, st.scissorW, st.scissorH);
|
||||
LOG.warn("applyPipelineAndState: scissor degenerate (x={} y={} w={} h={}) - all fragments clipped; boundProgram={} boundFbo={} scissorEn={} src=[{},{},{},{}]", sx, sy, sw, sh, st.boundProgram, st.boundFboId, st.scissorEnabled, st.scissorX, st.scissorY, st.scissorW, st.scissorH);
|
||||
}
|
||||
} else {
|
||||
MemoryAccess.putInt(addr + SDL_Rect.X, (int) st.viewportX);
|
||||
MemoryAccess.putInt(addr + SDL_Rect.Y, (int) st.viewportY);
|
||||
MemoryAccess.putInt(addr + SDL_Rect.W, (int) st.viewportW);
|
||||
MemoryAccess.putInt(addr + SDL_Rect.H, (int) st.viewportH);
|
||||
sx = (int) st.viewportX;
|
||||
sy = renderingToFbo ? (int) st.viewportY : (int) (fbo0Height(st) - st.viewportY - st.viewportH);
|
||||
sw = (int) st.viewportW;
|
||||
sh = (int) st.viewportH;
|
||||
}
|
||||
|
||||
final boolean haveFboSize = renderingToFbo && fboState != null;
|
||||
final int targetW = haveFboSize ? fboState.width : frameManager.getFbo0Width();
|
||||
final int targetH = haveFboSize ? fboState.height : frameManager.getFbo0Height();
|
||||
final boolean nonEmpty = ScissorClamp.clamp(sx, sy, sw, sh, targetW, targetH, scissorRect);
|
||||
final int cx = scissorRect[ScissorClamp.X];
|
||||
final int cy = scissorRect[ScissorClamp.Y];
|
||||
final int cw = scissorRect[ScissorClamp.W];
|
||||
final int ch = scissorRect[ScissorClamp.H];
|
||||
if ((cx != sx || cy != sy || cw != sw || ch != sh) && clampedScissorWarned.add(Hashing.packHiLo(st.boundProgram, st.boundFboId))) {
|
||||
LOG.warn("applyPipelineAndState: scissor (x={} y={} w={} h={}) is outside the {}x{} target - clamped to (x={} y={} w={} h={}); boundProgram={} boundFbo={} scissorEn={}", sx, sy, sw, sh, targetW, targetH, cx, cy, cw, ch, st.boundProgram, st.boundFboId, st.scissorEnabled);
|
||||
}
|
||||
if (!nonEmpty) return false;
|
||||
|
||||
final long addr = st.cachedScissor.address();
|
||||
MemoryAccess.putInt(addr + SDL_Rect.X, cx);
|
||||
MemoryAccess.putInt(addr + SDL_Rect.Y, cy);
|
||||
MemoryAccess.putInt(addr + SDL_Rect.W, cw);
|
||||
MemoryAccess.putInt(addr + SDL_Rect.H, ch);
|
||||
SDL_SetGPUScissor(rp, st.cachedScissor);
|
||||
st.scissorDirty = false;
|
||||
}
|
||||
@@ -532,7 +551,7 @@ public final class PipelineApplier {
|
||||
final long elemAddr = st.vboBindingsAddr + (long) i * bindingSize;
|
||||
if (vboHandle != 0) {
|
||||
MemoryAccess.putAddress(elemAddr + SDL_GPUBufferBinding.BUFFER, vboHandle);
|
||||
MemoryAccess.putInt(elemAddr + SDL_GPUBufferBinding.OFFSET, (int) vao.bindingOffset[b]);
|
||||
MemoryAccess.putInt(elemAddr + SDL_GPUBufferBinding.OFFSET, PipelineCache.vertexOffsetBase(vao.bindingOffset[b]));
|
||||
} else {
|
||||
MemoryAccess.putAddress(elemAddr + SDL_GPUBufferBinding.BUFFER, dummyVBO);
|
||||
MemoryAccess.putInt(elemAddr + SDL_GPUBufferBinding.OFFSET, i * 16);
|
||||
@@ -674,10 +693,9 @@ public final class PipelineApplier {
|
||||
|
||||
public float[] reuseOrAlloc(ContextState st, int location, int length) {
|
||||
final ShaderManager.ProgramObject prog = st.boundProgramObj;
|
||||
if (prog != null && location >= 0 && location < prog.uniformSlotCount) {
|
||||
final float[] existing = st.uniformStaging(prog).uniformDataBySlot[location];
|
||||
if (existing != null && existing.length == length) return existing;
|
||||
}
|
||||
if (prog == null || location < 0 || location >= prog.uniformSlotCount) return null;
|
||||
final float[] existing = st.uniformStaging(prog).uniformDataBySlot[location];
|
||||
if (existing != null && existing.length == length) return existing;
|
||||
return new float[length];
|
||||
}
|
||||
|
||||
@@ -825,11 +843,13 @@ public final class PipelineApplier {
|
||||
final int count = n / floatsPerMatrix;
|
||||
if (count == 0) {
|
||||
final float[] v = reuseOrAlloc(st, location, n);
|
||||
if (v == null) return;
|
||||
value.get(value.position(), v);
|
||||
putUniform(st, location, v);
|
||||
return;
|
||||
}
|
||||
final float[] out = reuseOrAlloc(st, location, count * floatsPerMatrix);
|
||||
if (out == null) return;
|
||||
MatrixMarshal.marshalMatrixToColumnMajor(value, value.position(), count, size, transpose, out, 0);
|
||||
putUniform(st, location, out);
|
||||
}
|
||||
|
||||
+75
-22
@@ -59,11 +59,11 @@ public final class PipelineCache {
|
||||
|
||||
public int primitiveType = SDL_GPU_PRIMITIVETYPE_TRIANGLELIST;
|
||||
|
||||
public final boolean[] blendEnabledPerAttachment = new boolean[ContextState.MAX_COLOR_ATTACHMENTS];
|
||||
public int srcColorFactor = SDL_GPU_BLENDFACTOR_ONE;
|
||||
public int dstColorFactor = SDL_GPU_BLENDFACTOR_ZERO;
|
||||
public int srcAlphaFactor = SDL_GPU_BLENDFACTOR_ONE;
|
||||
public int dstAlphaFactor = SDL_GPU_BLENDFACTOR_ZERO;
|
||||
public final boolean[] blendEnabledPerDrawBuffer = new boolean[ContextState.MAX_COLOR_ATTACHMENTS];
|
||||
public final int[] srcColorFactors = new int[ContextState.MAX_COLOR_ATTACHMENTS];
|
||||
public final int[] dstColorFactors = new int[ContextState.MAX_COLOR_ATTACHMENTS];
|
||||
public final int[] srcAlphaFactors = new int[ContextState.MAX_COLOR_ATTACHMENTS];
|
||||
public final int[] dstAlphaFactors = new int[ContextState.MAX_COLOR_ATTACHMENTS];
|
||||
public int colorBlendOp = SDL_GPU_BLENDOP_ADD;
|
||||
public int alphaBlendOp = SDL_GPU_BLENDOP_ADD;
|
||||
|
||||
@@ -160,15 +160,35 @@ public final class PipelineCache {
|
||||
return true;
|
||||
}
|
||||
|
||||
private int attachmentForSlot(int slot) {
|
||||
final int[] db = drawBuffers;
|
||||
if (db == null || slot < 0 || slot >= db.length) return 0;
|
||||
final int idx = db[slot];
|
||||
return (idx >= 0 && idx < ContextState.MAX_COLOR_ATTACHMENTS) ? idx : 0;
|
||||
private boolean blendEnabledForSlot(int slot) {
|
||||
return slot >= 0 && slot < ContextState.MAX_COLOR_ATTACHMENTS && blendEnabledPerDrawBuffer[slot];
|
||||
}
|
||||
|
||||
private boolean blendEnabledForSlot(int slot) {
|
||||
return blendEnabledPerAttachment[attachmentForSlot(slot)];
|
||||
public boolean setBlendFactors(int sc, int dc, int sa, int da) {
|
||||
boolean changed = false;
|
||||
for (int i = 0; i < ContextState.MAX_COLOR_ATTACHMENTS; i++) {
|
||||
if (srcColorFactors[i] != sc || dstColorFactors[i] != dc || srcAlphaFactors[i] != sa || dstAlphaFactors[i] != da) {
|
||||
srcColorFactors[i] = sc;
|
||||
dstColorFactors[i] = dc;
|
||||
srcAlphaFactors[i] = sa;
|
||||
dstAlphaFactors[i] = da;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
if (changed) markOutputDirty();
|
||||
return changed;
|
||||
}
|
||||
|
||||
public boolean setBlendFactors(int drawBuffer, int sc, int dc, int sa, int da) {
|
||||
if (srcColorFactors[drawBuffer] == sc && dstColorFactors[drawBuffer] == dc && srcAlphaFactors[drawBuffer] == sa && dstAlphaFactors[drawBuffer] == da) {
|
||||
return false;
|
||||
}
|
||||
srcColorFactors[drawBuffer] = sc;
|
||||
dstColorFactors[drawBuffer] = dc;
|
||||
srcAlphaFactors[drawBuffer] = sa;
|
||||
dstAlphaFactors[drawBuffer] = da;
|
||||
markOutputDirty();
|
||||
return true;
|
||||
}
|
||||
|
||||
public int maxAttribs;
|
||||
@@ -183,6 +203,14 @@ public final class PipelineCache {
|
||||
|
||||
public PipelineCache() {
|
||||
this.maxAttribs = ContextState.MAX_VERTEX_ATTRIBS;
|
||||
Arrays.fill(srcColorFactors, SDL_GPU_BLENDFACTOR_ONE);
|
||||
Arrays.fill(dstColorFactors, SDL_GPU_BLENDFACTOR_ZERO);
|
||||
Arrays.fill(srcAlphaFactors, SDL_GPU_BLENDFACTOR_ONE);
|
||||
Arrays.fill(dstAlphaFactors, SDL_GPU_BLENDFACTOR_ZERO);
|
||||
}
|
||||
|
||||
long lastKey() {
|
||||
return lastKey;
|
||||
}
|
||||
|
||||
public void markInputDirty() { inputDirty = true; lastKey = 0L; }
|
||||
@@ -195,6 +223,16 @@ public final class PipelineCache {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void setVertexInputs(int mask, int[] vecSize, int[] baseType, String[] names) {
|
||||
if (shaderInputMask != mask || !Arrays.equals(shaderInputVecSize, vecSize) || !Arrays.equals(shaderInputBaseType, baseType)) {
|
||||
markInputDirty();
|
||||
}
|
||||
shaderInputMask = mask;
|
||||
shaderInputVecSize = vecSize;
|
||||
shaderInputBaseType = baseType;
|
||||
shaderInputName = names;
|
||||
}
|
||||
|
||||
public void markOutputDirty() { outputDirty = true; lastKey = 0L; }
|
||||
public void markShaderDirty() { shaderDirty = true; lastKey = 0L; }
|
||||
|
||||
@@ -292,9 +330,11 @@ public final class PipelineCache {
|
||||
| (((long) blendBits & 0xFFFFFFFFL) << 32);
|
||||
long h = OUTPUT_SEED;
|
||||
h = Hashing.fmix64(h, bits);
|
||||
h = Hashing.fmix64(h, Hashing.packHiLo(primitiveType, srcColorFactor));
|
||||
h = Hashing.fmix64(h, Hashing.packHiLo(dstColorFactor, srcAlphaFactor));
|
||||
h = Hashing.fmix64(h, Hashing.packHiLo(dstAlphaFactor, colorBlendOp));
|
||||
h = Hashing.fmix64(h, Hashing.packHiLo(primitiveType, colorBlendOp));
|
||||
for (int i = 0; i < ctfLen; i++) {
|
||||
h = Hashing.fmix64(h, Hashing.packHiLo(srcColorFactors[i], dstColorFactors[i]));
|
||||
h = Hashing.fmix64(h, Hashing.packHiLo(srcAlphaFactors[i], dstAlphaFactors[i]));
|
||||
}
|
||||
h = Hashing.fmix64(h, Hashing.packHiLo(alphaBlendOp, depthCompareOp));
|
||||
h = Hashing.fmix64(h, Hashing.packHiLo(stencilFrontCompareOp, stencilFrontFailOp));
|
||||
h = Hashing.fmix64(h, Hashing.packHiLo(stencilFrontDepthFailOp, stencilFrontPassOp));
|
||||
@@ -338,6 +378,7 @@ public final class PipelineCache {
|
||||
| (((long) vao.attribType[i] & 0xFFFFL) << 16)
|
||||
| ((vao.attribNormalized[i] ? 1L : 0L) << 32)
|
||||
| ((vao.attribIsInteger[i] ? 1L : 0L) << 33)
|
||||
| (((long) vertexOffsetMisalign(vao.bindingOffset[b]) & 3L) << 34)
|
||||
| (((long) b & 0xFF) << 40)
|
||||
| (((long) vao.attribStride[i] & 0xFFFFL) << 48);
|
||||
final long w1 =
|
||||
@@ -386,11 +427,11 @@ public final class PipelineCache {
|
||||
desc.format(colorTargetFormats()[i]);
|
||||
desc.blend_state()
|
||||
.enable_blend(blendEnabledForSlot(i))
|
||||
.src_color_blendfactor(srcColorFactor)
|
||||
.dst_color_blendfactor(dstColorFactor)
|
||||
.src_color_blendfactor(srcColorFactors[i])
|
||||
.dst_color_blendfactor(dstColorFactors[i])
|
||||
.color_blend_op(colorBlendOp)
|
||||
.src_alpha_blendfactor(srcAlphaFactor)
|
||||
.dst_alpha_blendfactor(dstAlphaFactor)
|
||||
.src_alpha_blendfactor(srcAlphaFactors[i])
|
||||
.dst_alpha_blendfactor(dstAlphaFactors[i])
|
||||
.alpha_blend_op(alphaBlendOp)
|
||||
.color_write_mask((byte) colorWriteMask)
|
||||
.enable_color_write_mask(true);
|
||||
@@ -500,8 +541,10 @@ public final class PipelineCache {
|
||||
.append(" backCmp=").append(stencilBackCompareOp).append('\n');
|
||||
}
|
||||
sb.append(" Blend: enabled=").append(blendEnabledForSlot(0))
|
||||
.append(" srcColor=").append(srcColorFactor).append(" dstColor=").append(dstColorFactor).append(" colorOp=").append(colorBlendOp)
|
||||
.append(" srcAlpha=").append(srcAlphaFactor).append(" dstAlpha=").append(dstAlphaFactor).append(" alphaOp=").append(alphaBlendOp)
|
||||
.append(" srcColor=").append(srcColorFactors[0]).append(" dstColor=").append(dstColorFactors[0])
|
||||
.append(" colorOp=").append(colorBlendOp)
|
||||
.append(" srcAlpha=").append(srcAlphaFactors[0]).append(" dstAlpha=").append(dstAlphaFactors[0])
|
||||
.append(" alphaOp=").append(alphaBlendOp)
|
||||
.append(" mask=0x").append(Integer.toHexString(colorWriteMask)).append('\n');
|
||||
sb.append(" Rasterizer: cull=").append(getEffectiveCullMode())
|
||||
.append(" front=").append(effectiveFrontFace())
|
||||
@@ -620,6 +663,16 @@ public final class PipelineCache {
|
||||
|
||||
private static final int GL_TYPE_BASE = GL11.GL_BYTE;
|
||||
static final int INVALID_FMT = -1;
|
||||
|
||||
public static final int VERTEX_BUFFER_OFFSET_ALIGN = 4;
|
||||
|
||||
public static int vertexOffsetBase(long bindingOffset) {
|
||||
return (int) (bindingOffset & ~(VERTEX_BUFFER_OFFSET_ALIGN - 1));
|
||||
}
|
||||
|
||||
public static int vertexOffsetMisalign(long bindingOffset) {
|
||||
return (int) (bindingOffset & (VERTEX_BUFFER_OFFSET_ALIGN - 1));
|
||||
}
|
||||
private static final int[][] VTX_FMT = buildVertexFormatTable();
|
||||
|
||||
private static int[] fmtSame(int s1, int s2, int s3, int s4) {
|
||||
@@ -797,7 +850,7 @@ public final class PipelineCache {
|
||||
final int boundStride = vao.bindingStride[b];
|
||||
final int stride = boundStride != 0 ? boundStride : (vao.attribStride[i] != 0 ? vao.attribStride[i] : vao.attribSize[i] * GLTypes.sizeBytes(vao.attribType[i]));
|
||||
final int divisor = vao.bindingDivisor[b];
|
||||
final int relOff = vao.attribRelativeOffset[i];
|
||||
final int relOff = vao.attribRelativeOffset[i] + vertexOffsetMisalign(vao.bindingOffset[b]);
|
||||
final int attribAlign = GLTypes.sizeBytes(vao.attribType[i]);
|
||||
if (attribAlign > 0 && (stride % attribAlign) != 0) {
|
||||
LOG.warn("[PipelineCache] unaligned vertex stride: location={} stride={} type=0x{} size={} -- attribAddress will not satisfy format alignment; fix producer", i, stride, Integer.toHexString(vao.attribType[i]), vao.attribSize[i]);
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.gtnewhorizons.angelica.sdlgpu.pipeline;
|
||||
|
||||
public final class ScissorClamp {
|
||||
|
||||
public static final int X = 0;
|
||||
public static final int Y = 1;
|
||||
public static final int W = 2;
|
||||
public static final int H = 3;
|
||||
|
||||
private ScissorClamp() {}
|
||||
|
||||
public static boolean clamp(int x, int y, int w, int h, int targetW, int targetH, int[] out) {
|
||||
if (targetW <= 0 || targetH <= 0) {
|
||||
out[X] = x;
|
||||
out[Y] = y;
|
||||
out[W] = w;
|
||||
out[H] = h;
|
||||
return w > 0 && h > 0;
|
||||
}
|
||||
final int x0 = Math.max(x, 0);
|
||||
final int y0 = Math.max(y, 0);
|
||||
final int x1 = (int) Math.min((long) x + w, targetW);
|
||||
final int y1 = (int) Math.min((long) y + h, targetH);
|
||||
out[X] = x0;
|
||||
out[Y] = y0;
|
||||
out[W] = Math.max(x1 - x0, 0);
|
||||
out[H] = Math.max(y1 - y0, 0);
|
||||
return out[W] > 0 && out[H] > 0;
|
||||
}
|
||||
}
|
||||
Loaded 100 of 428 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user