diff --git a/common/src/main/java/foundry/veil/api/client/necromancer/animation/keyframe/Interpolation.java b/common/src/main/java/foundry/veil/api/client/necromancer/animation/keyframe/Interpolation.java
index d19e405a9..c15a767b2 100644
--- a/common/src/main/java/foundry/veil/api/client/necromancer/animation/keyframe/Interpolation.java
+++ b/common/src/main/java/foundry/veil/api/client/necromancer/animation/keyframe/Interpolation.java
@@ -1,20 +1,56 @@
package foundry.veil.api.client.necromancer.animation.keyframe;
import net.minecraft.util.Mth;
+import org.jetbrains.annotations.ApiStatus;
import org.joml.Quaternionf;
import org.joml.Quaternionfc;
// todo: support cubic interpolation w/ derivatives
public enum Interpolation {
- NEAREST_NEIGHBOR(
- (a, b, t) -> t < 0.5 ? a : b,
- (a, b, t, result) -> result.set(t < 0.5 ? a : b)
+ /**
+ * @since 4.5.0
+ */
+ STEP(
+ (a, b, t) -> t < 1F ? a : b,
+ (a, b, t, result) -> result.set(t < 1 ? a : b)
),
LINEAR(
(a, b, t) -> Mth.lerp(t, a, b),
Quaternionfc::slerp
+ ),
+ /**
+ * @since 4.5.0
+ */
+ EASE_IN(
+ (a, b, t) -> Mth.lerp(t * t, a, b),
+ (a, b, t, result) -> result.set(a).slerp(b, t * t)
+ ),
+ /**
+ * @since 4.5.0
+ */
+ EASE_OUT(
+ (a, b, t) -> Mth.lerp(1F - (1F - t) * (1F - t), a, b),
+ (a, b, t, result) -> result.set(a).slerp(b, 1F - (1F - t) * (1F - t))
+ ),
+ /**
+ * @since 4.5.0
+ */
+ EASE_IN_OUT(
+ (a, b, t) -> Mth.lerp(easeInOut(t), a, b),
+ (a, b, t, result) -> result.set(a).slerp(b, easeInOut(t))
);
+ /**
+ * @deprecated Use {@link #STEP} instead
+ */
+ @ApiStatus.ScheduledForRemoval(inVersion = "5.0.0")
+ @Deprecated
+ public static final Interpolation NEAREST_NEIGHBOR = STEP;
+
+ private static float easeInOut(float t) {
+ return t < 0.5F ? 2F * t * t : 1F - (float) Math.pow(-2F * t + 2F, 2) / 2F;
+ }
+
private final FloatInterpolator fInterpolator;
private final QuaternionInterpolator qInterpolator;
diff --git a/common/src/main/java/foundry/veil/api/client/necromancer/animation/keyframe/KeyframeTimeline.java b/common/src/main/java/foundry/veil/api/client/necromancer/animation/keyframe/KeyframeTimeline.java
index 626a872e2..dbd9f5cec 100644
--- a/common/src/main/java/foundry/veil/api/client/necromancer/animation/keyframe/KeyframeTimeline.java
+++ b/common/src/main/java/foundry/veil/api/client/necromancer/animation/keyframe/KeyframeTimeline.java
@@ -13,11 +13,11 @@ protected float getAdjacentKeyframes(float time, boolean looped, Keyframe[] list
listToPopulate[0] = keyframes[previousIndex];
int nextIndex = currentIndex + 1;
- nextIndex = looped ? nextIndex % keyframes.length : Math.max(nextIndex, keyframes.length - 1);
+ nextIndex = looped ? nextIndex % keyframes.length : Math.min(nextIndex, keyframes.length - 1);
listToPopulate[2] = keyframes[nextIndex];
int nextNextIndex = currentIndex + 2;
- nextNextIndex = looped ? nextNextIndex % keyframes.length : Math.max(nextNextIndex, keyframes.length - 1);
+ nextNextIndex = looped ? nextNextIndex % keyframes.length : Math.min(nextNextIndex, keyframes.length - 1);
listToPopulate[3] = keyframes[nextNextIndex];
// interpolation factor between the two keyframes
@@ -43,12 +43,11 @@ private int findKeyframeIndex(float time, boolean looped) {
float t1 = keyframes[mid].time();
float t2 = keyframes[mid + 1].time();
- // current time is between these two keyframes!
- if (time > t1 && time < t2) {
+ if (time >= t1 && time < t2) {
return mid;
} else if (time > t1) {
low = mid + 1;
- } else if (time < t1) {
+ } else {
high = mid - 1;
}
}
diff --git a/common/src/main/java/foundry/veil/api/client/necromancer/animation/keyframe/KeyframedAnimation.java b/common/src/main/java/foundry/veil/api/client/necromancer/animation/keyframe/KeyframedAnimation.java
index 485697432..2862997c1 100644
--- a/common/src/main/java/foundry/veil/api/client/necromancer/animation/keyframe/KeyframedAnimation.java
+++ b/common/src/main/java/foundry/veil/api/client/necromancer/animation/keyframe/KeyframedAnimation.java
@@ -61,7 +61,7 @@ public void apply(P parent, S skeleton, float mixFactor, float time) {
Mth.lerp(mixFactor, 1, interpolation.interpolate(a.transform().sz(), b.transform().sz(), t))
);
} else {
- bone.size.set(
+ bone.position.set(
Mth.lerp(mixFactor, bone.position.x, interpolation.interpolate(a.transform().px(), b.transform().px(), t)),
Mth.lerp(mixFactor, bone.position.y, interpolation.interpolate(a.transform().py(), b.transform().py(), t)),
Mth.lerp(mixFactor, bone.position.z, interpolation.interpolate(a.transform().pz(), b.transform().pz(), t))
@@ -79,16 +79,16 @@ public void apply(P parent, S skeleton, float mixFactor, float time) {
}
}
- public static class Builder {
+ public static class Builder
, S extends Skeleton> {
boolean looped = false, additive = false;
Map> timelines = new HashMap<>();
- Builder looped(boolean isLooped) {
+ public Builder looped(boolean isLooped) {
this.looped = isLooped;
return this;
}
- Builder additive(boolean isAdditive) {
+ public Builder
additive(boolean isAdditive) {
this.additive = isAdditive;
return this;
}
@@ -104,7 +104,7 @@ public void addKeyframe(String boneId, float time, Interpolation interpolation,
timelines.get(boneId).add(new Keyframe(time, interpolation, new Keyframe.KeyframeTransform(position, size, orientation)));
}
- public KeyframedAnimation, ?> build() {
+ public KeyframedAnimation
build() {
Map builtTimelines = new HashMap<>();
for (Map.Entry> timeline : timelines.entrySet()) {
List keyframeList = timeline.getValue();
diff --git a/common/src/main/java/foundry/veil/api/client/necromancer/render/Skin.java b/common/src/main/java/foundry/veil/api/client/necromancer/render/Skin.java
index 89d25643e..39365be8e 100644
--- a/common/src/main/java/foundry/veil/api/client/necromancer/render/Skin.java
+++ b/common/src/main/java/foundry/veil/api/client/necromancer/render/Skin.java
@@ -12,6 +12,7 @@
import foundry.veil.api.client.render.shader.block.DynamicShaderBlock;
import foundry.veil.api.client.render.vertex.VertexArray;
import foundry.veil.api.client.render.vertex.VertexArrayBuilder;
+import foundry.veil.api.compat.ImmersivePortalsCompat;
import it.unimi.dsi.fastutil.floats.FloatList;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
@@ -32,8 +33,7 @@
import java.util.List;
import static org.lwjgl.opengl.ARBDirectStateAccess.glNamedBufferSubData;
-import static org.lwjgl.opengl.GL15C.glBindBuffer;
-import static org.lwjgl.opengl.GL15C.glBufferSubData;
+import static org.lwjgl.opengl.GL15C.*;
import static org.lwjgl.opengl.GL30C.glUniform1ui;
import static org.lwjgl.opengl.GL31C.GL_UNIFORM_BUFFER;
@@ -120,7 +120,7 @@ public void render(RenderType renderType, List transforms, List listeners = ((PipelineReloadableResourceManagerAccessor) resourceManager).getListeners();
@@ -269,6 +272,14 @@ public LightRenderer getLightRenderer() {
return this.lightRenderer;
}
+ /**
+ * @return The manager for screen shaking
+ * @since 4.5.0
+ */
+ public ScreenShakeManager getScreenShakeManager() {
+ return this.screenShakeManager;
+ }
+
/**
* @return The gui info instance
*/
diff --git a/common/src/main/java/foundry/veil/api/client/render/ext/VeilDebug.java b/common/src/main/java/foundry/veil/api/client/render/ext/VeilDebug.java
index dd8766df4..4de73825d 100644
--- a/common/src/main/java/foundry/veil/api/client/render/ext/VeilDebug.java
+++ b/common/src/main/java/foundry/veil/api/client/render/ext/VeilDebug.java
@@ -1,12 +1,10 @@
package foundry.veil.api.client.render.ext;
import foundry.veil.Veil;
+import foundry.veil.api.compat.ImmersivePortalsCompat;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
-import org.lwjgl.opengl.GL;
-import org.lwjgl.opengl.GL11;
-import org.lwjgl.opengl.GLCapabilities;
-import org.lwjgl.opengl.KHRDebug;
+import org.lwjgl.opengl.*;
import java.util.concurrent.atomic.AtomicInteger;
@@ -44,7 +42,9 @@ public void debugMessageInsert(int type, int id, int severity, CharSequence mess
@Override
public void objectLabel(int identifier, int name, @Nullable CharSequence label) {
if (label != null) {
- glObjectLabel(identifier, name, label);
+ if (!ImmersivePortalsCompat.isLoaded()) {
+ glObjectLabel(identifier, name, label);
+ }
} else {
nglObjectLabel(identifier, name, 0, 0L);
}
diff --git a/common/src/main/java/foundry/veil/api/client/render/framebuffer/AdvancedFbo.java b/common/src/main/java/foundry/veil/api/client/render/framebuffer/AdvancedFbo.java
index ecd7f5ae2..914e2c412 100644
--- a/common/src/main/java/foundry/veil/api/client/render/framebuffer/AdvancedFbo.java
+++ b/common/src/main/java/foundry/veil/api/client/render/framebuffer/AdvancedFbo.java
@@ -580,6 +580,33 @@ static Builder copy(RenderTarget parent) {
return AdvancedFboImpl.copy(parent);
}
+ /**
+ * Gets the attachment type of the main depth buffer.
+ * @return Either {@code GL_DEPTH_ATTACHMENT} or {@code GL_DEPTH_STENCIL_ATTACHMENT}
+ * @apiNote If no match is found for whatever reason, {@code GL_DEPTH_ATTACHMENT} is returned.
+ */
+ static int getDepthAttachmentType(int textureId) {
+ int boundTexture = glGetInteger(GL_TEXTURE_2D);
+
+ glBindTexture(GL_TEXTURE_2D, textureId);
+
+ int depthFormat = glGetTexLevelParameteri(GL_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT);
+
+ glBindTexture(GL_TEXTURE_2D, boundTexture);
+
+ for (FramebufferAttachmentDefinition.Format format : FramebufferAttachmentDefinition.Format.VALUES) {
+ if (format.getInternalFormat() == depthFormat) {
+ if (format.getFormat() == GL_DEPTH_COMPONENT) {
+ return GL_DEPTH_ATTACHMENT;
+ } else if (format.getFormat() == GL_DEPTH_STENCIL) {
+ return GL_DEPTH_STENCIL_ATTACHMENT;
+ }
+ }
+ }
+
+ return GL_DEPTH_ATTACHMENT;
+ }
+
/**
* A builder used to attach buffers to an {@link AdvancedFbo}.
*
@@ -992,7 +1019,7 @@ public Builder setDepthTextureWrapper(int textureId) {
*/
public Builder setDepthTextureWrapper(int textureId, int layer) {
return this.setDepthBuffer(new AdvancedFboMutableTextureAttachment(
- GL_DEPTH_ATTACHMENT,
+ getDepthAttachmentType(textureId),
textureId,
layer,
this.name));
diff --git a/common/src/main/java/foundry/veil/api/client/render/light/renderer/LightRenderer.java b/common/src/main/java/foundry/veil/api/client/render/light/renderer/LightRenderer.java
index fc03f029a..a9cef30e9 100644
--- a/common/src/main/java/foundry/veil/api/client/render/light/renderer/LightRenderer.java
+++ b/common/src/main/java/foundry/veil/api/client/render/light/renderer/LightRenderer.java
@@ -10,6 +10,7 @@
import foundry.veil.api.client.render.framebuffer.AdvancedFbo;
import foundry.veil.api.client.render.light.data.LightData;
import foundry.veil.api.client.render.vertex.VertexArray;
+import foundry.veil.api.compat.ImmersivePortalsCompat;
import foundry.veil.impl.client.render.light.VoxelShadowGrid;
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap;
import net.minecraft.resources.ResourceLocation;
@@ -101,7 +102,7 @@ public boolean render(CullFrustum frustum, AdvancedFbo lightFbo, AdvancedFbo lig
lightInscatteringFbo.clear(GL_COLOR_BUFFER_BIT);
}
- if (!hasRendered) {
+ if (!hasRendered && !ImmersivePortalsCompat.isLoaded()) {
renderer.disableBuffers(BUFFER_ID, DynamicBufferType.ALBEDO, DynamicBufferType.NORMAL);
return false;
}
diff --git a/common/src/main/java/foundry/veil/api/client/render/vertex/VertexArray.java b/common/src/main/java/foundry/veil/api/client/render/vertex/VertexArray.java
index b2417e097..424001e93 100644
--- a/common/src/main/java/foundry/veil/api/client/render/vertex/VertexArray.java
+++ b/common/src/main/java/foundry/veil/api/client/render/vertex/VertexArray.java
@@ -8,6 +8,7 @@
import foundry.veil.api.client.render.VeilRenderSystem;
import foundry.veil.api.client.render.rendertype.VeilRenderType;
import foundry.veil.api.client.render.shader.program.ShaderProgram;
+import foundry.veil.api.compat.ImmersivePortalsCompat;
import foundry.veil.impl.client.render.vertex.ARBVertexArray;
import foundry.veil.impl.client.render.vertex.DSAVertexArray;
import foundry.veil.impl.client.render.vertex.LegacyVertexArray;
@@ -79,7 +80,7 @@ protected VertexArray(int id, Function builder)
private static void loadType() {
if (vertexArrayType == null) {
- if (VeilRenderSystem.directStateAccessSupported()) {
+ if (VeilRenderSystem.directStateAccessSupported() && !ImmersivePortalsCompat.isLoaded()) {
vertexArrayType = VertexArrayType.DSA;
} else {
GLCapabilities caps = GL.getCapabilities();
@@ -219,7 +220,7 @@ public VertexFormat.Mode getDrawMode() {
* @param usage The draw usage
*/
public static void upload(int buffer, ByteBuffer data, DrawUsage usage) {
- if (VeilRenderSystem.directStateAccessSupported()) {
+ if (VeilRenderSystem.directStateAccessSupported() && !ImmersivePortalsCompat.isLoaded()) {
glNamedBufferData(buffer, data, usage.getGlType());
} else {
glBindBuffer(GL_ARRAY_BUFFER, buffer);
diff --git a/common/src/main/java/foundry/veil/api/compat/ImmersivePortalsCompat.java b/common/src/main/java/foundry/veil/api/compat/ImmersivePortalsCompat.java
new file mode 100644
index 000000000..79d5b49d3
--- /dev/null
+++ b/common/src/main/java/foundry/veil/api/compat/ImmersivePortalsCompat.java
@@ -0,0 +1,31 @@
+package foundry.veil.api.compat;
+
+import com.mojang.blaze3d.shaders.Program;
+import foundry.veil.Veil;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ServiceLoader;
+
+public interface ImmersivePortalsCompat {
+
+ /**
+ * Retrieves the compat instance. This will be null if Immersive Portals is not installed.
+ */
+ @Nullable
+ ImmersivePortalsCompat INSTANCE = (Veil.platform().isModLoaded("immersive_portals") || Veil.platform().isModLoaded("imm_ptl")) ? ServiceLoader.load(ImmersivePortalsCompat.class).findFirst().orElse(null) : null;
+
+ /**
+ * @return Whether Immersive Portals is loaded
+ */
+ static boolean isLoaded() {
+ return INSTANCE != null;
+ }
+
+ void init();
+
+ String transform(Program.Type type, String shaderId, String inputCode);
+
+ boolean shouldAddUniform(String shaderName);
+
+ boolean renderingThroughPortal();
+}
diff --git a/common/src/main/java/foundry/veil/api/quasar/data/EmitterShapeSettings.java b/common/src/main/java/foundry/veil/api/quasar/data/EmitterShapeSettings.java
index a66b6f430..8d2c2393b 100644
--- a/common/src/main/java/foundry/veil/api/quasar/data/EmitterShapeSettings.java
+++ b/common/src/main/java/foundry/veil/api/quasar/data/EmitterShapeSettings.java
@@ -7,11 +7,10 @@
import net.minecraft.core.Holder;
import net.minecraft.resources.RegistryFileCodec;
import net.minecraft.resources.ResourceLocation;
+import net.minecraft.util.Mth;
import net.minecraft.util.RandomSource;
import org.jetbrains.annotations.Nullable;
-import org.joml.Vector3d;
-import org.joml.Vector3dc;
-import org.joml.Vector3fc;
+import org.joml.*;
public record EmitterShapeSettings(EmitterShape shape,
Vector3fc dimensions,
@@ -30,6 +29,13 @@ public Vector3d getPos(RandomSource randomSource, Vector3dc pos) {
return this.shape.getPoint(randomSource, this.dimensions, this.rotation, pos, this.fromSurface);
}
+ /**
+ * @since 4.5.0
+ */
+ public Vector3d getPos(RandomSource randomSource, Vector3dc pos, Quaternionfc rot) {
+ return this.shape.getPoint(randomSource, this.dimensions, this.rotation.add(rot.getEulerAnglesXYZ(new Vector3f()).mul(Mth.RAD_TO_DEG), new Vector3f()), pos, this.fromSurface);
+ }
+
public @Nullable ResourceLocation getRegistryId() {
return QuasarParticles.registryAccess().registry(QuasarParticles.EMITTER_SHAPE_SETTINGS).map(registry -> registry.getKey(this)).orElse(null);
}
diff --git a/common/src/main/java/foundry/veil/api/quasar/data/ParticleModuleTypeRegistry.java b/common/src/main/java/foundry/veil/api/quasar/data/ParticleModuleTypeRegistry.java
index 97d77601b..93a130af3 100644
--- a/common/src/main/java/foundry/veil/api/quasar/data/ParticleModuleTypeRegistry.java
+++ b/common/src/main/java/foundry/veil/api/quasar/data/ParticleModuleTypeRegistry.java
@@ -11,6 +11,7 @@
import foundry.veil.api.quasar.data.module.init.*;
import foundry.veil.api.quasar.data.module.render.ColorParticleModuleData;
import foundry.veil.api.quasar.data.module.render.TrailParticleModuleData;
+import foundry.veil.api.quasar.data.module.update.TickRotationParticleModuleData;
import foundry.veil.api.quasar.data.module.update.TickSizeParticleModuleData;
import foundry.veil.api.quasar.data.module.update.TickSubEmitterModuleData;
import foundry.veil.api.quasar.emitters.module.init.InitRandomRotationModuleData;
@@ -137,6 +138,10 @@ public class ParticleModuleTypeRegistry {
// UPDATE
public static final ModuleType TICK_SIZE = registerModule("size", TickSizeParticleModuleData.CODEC, () -> new TickSizeParticleModuleData(MolangExpression.of(1)));
+ /**
+ * @since 4.5.0
+ */
+ public static final ModuleType TICK_ROTATION = registerModule("rotation", TickRotationParticleModuleData.CODEC, () -> new TickRotationParticleModuleData(MolangExpression.ZERO, MolangExpression.ZERO, MolangExpression.ZERO));
public static final ModuleType TICK_SUB_EMITTER = registerModule("tick_sub_emitter", TickSubEmitterModuleData.CODEC, () -> new TickSubEmitterModuleData(ResourceLocation.withDefaultNamespace(""), 5));
// UPDATE - COLLISION
public static final ModuleType DIE_ON_COLLISION = registerModule("die_on_collision", DieOnCollisionModuleData.CODEC, DieOnCollisionModuleData::new);
diff --git a/common/src/main/java/foundry/veil/api/quasar/data/ParticleSettings.java b/common/src/main/java/foundry/veil/api/quasar/data/ParticleSettings.java
index 30ec38e71..4836ccee8 100644
--- a/common/src/main/java/foundry/veil/api/quasar/data/ParticleSettings.java
+++ b/common/src/main/java/foundry/veil/api/quasar/data/ParticleSettings.java
@@ -7,41 +7,52 @@
import net.minecraft.resources.RegistryFileCodec;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.RandomSource;
+import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import org.joml.Vector3f;
import org.joml.Vector3fc;
public record ParticleSettings(float particleSpeed,
+ float particleSpeedVariation,
float particleSize,
float particleSizeVariation,
int particleLifetime,
float particleLifetimeVariation,
Vector3fc initialDirection,
boolean randomInitialDirection,
+ Vector3fc initialDirectionVariation,
Vector3fc initialRotation,
boolean randomInitialRotation,
+ Vector3fc initialRotationVariation,
boolean randomSpeed,
boolean randomSize,
boolean randomLifetime) {
public static final Codec DIRECT_CODEC = RecordCodecBuilder.create(instance -> instance.group(
Codec.FLOAT.fieldOf("particle_speed").forGetter(ParticleSettings::particleSpeed),
+ Codec.FLOAT.optionalFieldOf("particle_speed_variation", 0f).forGetter(ParticleSettings::particleSpeedVariation),
Codec.FLOAT.fieldOf("base_particle_size").forGetter(ParticleSettings::particleSize),
Codec.FLOAT.optionalFieldOf("particle_size_variation", 0f).forGetter(ParticleSettings::particleSizeVariation),
Codec.INT.fieldOf("particle_lifetime").forGetter(ParticleSettings::particleLifetime),
Codec.FLOAT.optionalFieldOf("particle_lifetime_variation", 0f).forGetter(ParticleSettings::particleLifetimeVariation),
CodecUtil.VECTOR3FC_CODEC.optionalFieldOf("initial_direction", new Vector3f(1)).forGetter(ParticleSettings::initialDirection),
Codec.BOOL.optionalFieldOf("random_initial_direction", false).forGetter(ParticleSettings::randomInitialDirection),
+ CodecUtil.VECTOR3FC_CODEC.optionalFieldOf("initial_direction_variation", new Vector3f(0)).forGetter(ParticleSettings::initialDirectionVariation),
CodecUtil.VECTOR3FC_CODEC.optionalFieldOf("initial_rotation", new Vector3f(0)).forGetter(ParticleSettings::initialRotation),
Codec.BOOL.optionalFieldOf("random_initial_rotation", false).forGetter(ParticleSettings::randomInitialRotation),
+ CodecUtil.VECTOR3FC_CODEC.optionalFieldOf("initial_rotation_variation", new Vector3f(0)).forGetter(ParticleSettings::initialRotationVariation),
Codec.BOOL.optionalFieldOf("random_speed", false).forGetter(ParticleSettings::randomSpeed),
Codec.BOOL.optionalFieldOf("random_size", false).forGetter(ParticleSettings::randomSize),
Codec.BOOL.optionalFieldOf("random_lifetime", false).forGetter(ParticleSettings::randomLifetime)
).apply(instance, ParticleSettings::new));
public static final Codec> CODEC = RegistryFileCodec.create(QuasarParticles.PARTICLE_SETTINGS, DIRECT_CODEC);
+ @ApiStatus.Internal
+ public ParticleSettings {
+ }
+
public float particleSpeed(RandomSource random) {
- return this.randomSpeed ? this.particleSpeed + (random.nextFloat() * 0.5f - 0.5f) * this.particleSpeed : this.particleSpeed;
+ return this.randomSpeed ? this.particleSpeed + random.nextFloat() * this.particleSpeedVariation : this.particleSpeed;
}
public float particleSize(RandomSource random) {
@@ -53,14 +64,14 @@ public int particleLifetime(RandomSource random) {
}
public Vector3fc initialDirection(RandomSource random) {
- return this.randomInitialDirection ? this.initialDirection.mul(random.nextFloat() * 2 - 1, random.nextFloat() * 2 - 1, random.nextFloat() * 2 - 1, new Vector3f()) : this.initialDirection;
+ return this.randomInitialDirection ? this.initialDirection.add(this.initialDirectionVariation.mul(random.nextFloat() * 2 - 1, random.nextFloat() * 2 - 1, random.nextFloat() * 2 - 1, new Vector3f()), new Vector3f()) : this.initialDirection;
}
/**
* @since 4.3.0
*/
public Vector3fc initialRotation(RandomSource random) {
- return this.randomInitialRotation ? this.initialRotation.mul(random.nextFloat() * 2 - 1, random.nextFloat() * 2 - 1, random.nextFloat() * 2 - 1, new Vector3f()) : this.initialRotation;
+ return this.randomInitialRotation ? this.initialRotation.add(this.initialRotationVariation.mul(random.nextFloat() * 2 - 1, random.nextFloat() * 2 - 1, random.nextFloat() * 2 - 1, new Vector3f()), new Vector3f()) : this.initialRotation;
}
public Vector3f particleDirection(RandomSource random) {
diff --git a/common/src/main/java/foundry/veil/api/quasar/data/module/init/InitialVelocityModuleData.java b/common/src/main/java/foundry/veil/api/quasar/data/module/init/InitialVelocityModuleData.java
index 855e998da..42b3b5b75 100644
--- a/common/src/main/java/foundry/veil/api/quasar/data/module/init/InitialVelocityModuleData.java
+++ b/common/src/main/java/foundry/veil/api/quasar/data/module/init/InitialVelocityModuleData.java
@@ -11,6 +11,7 @@
import foundry.veil.api.quasar.particle.ParticleModuleSet;
import foundry.veil.api.util.CodecUtil;
import imgui.ImGui;
+import org.joml.Quaterniond;
import org.joml.Vector3d;
import org.joml.Vector3dc;
@@ -37,7 +38,7 @@ public InitialVelocityModuleData(Vector3dc velocityDirection,
@Override
public void addModules(ParticleModuleSet.Builder builder) {
// TODO takesParentRotation
- builder.addModule((InitParticleModule) particle -> particle.getVelocity().add(this.velocityDirection.normalize(this.strength, new Vector3d())));
+ builder.addModule((InitParticleModule) particle -> particle.getVelocity().add(this.velocityDirection.normalize(this.strength, new Vector3d()).rotate(particle.getEmitter().getRotation().get(new Quaterniond()))));
}
@Override
diff --git a/common/src/main/java/foundry/veil/api/quasar/data/module/update/TickRotationParticleModuleData.java b/common/src/main/java/foundry/veil/api/quasar/data/module/update/TickRotationParticleModuleData.java
new file mode 100644
index 000000000..3a48ebb1c
--- /dev/null
+++ b/common/src/main/java/foundry/veil/api/quasar/data/module/update/TickRotationParticleModuleData.java
@@ -0,0 +1,112 @@
+package foundry.veil.api.quasar.data.module.update;
+
+import com.mojang.serialization.MapCodec;
+import com.mojang.serialization.codecs.RecordCodecBuilder;
+import foundry.veil.api.client.editor.EditorAttributeProvider;
+import foundry.veil.api.molang.MolangExpressionCodec;
+import foundry.veil.api.molang.VeilMolang;
+import foundry.veil.api.quasar.data.ParticleModuleTypeRegistry;
+import foundry.veil.api.quasar.data.module.ModuleType;
+import foundry.veil.api.quasar.data.module.ParticleModuleData;
+import foundry.veil.api.quasar.emitters.module.UpdateParticleModule;
+import foundry.veil.api.quasar.particle.ParticleModuleSet;
+import gg.moonflower.molangcompiler.api.MolangExpression;
+import imgui.ImGui;
+import imgui.type.ImString;
+import net.minecraft.util.Mth;
+
+/**
+ * @since 4.5.0
+ */
+public final class TickRotationParticleModuleData implements ParticleModuleData, EditorAttributeProvider {
+ public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group(
+ MolangExpressionCodec.CODEC.fieldOf("x").forGetter(TickRotationParticleModuleData::rotationX),
+ MolangExpressionCodec.CODEC.fieldOf("y").forGetter(TickRotationParticleModuleData::rotationY),
+ MolangExpressionCodec.CODEC.fieldOf("z").forGetter(TickRotationParticleModuleData::rotationZ)
+ ).apply(instance, TickRotationParticleModuleData::new));
+ private MolangExpression rotationX, rotationY, rotationZ;
+
+ public TickRotationParticleModuleData(MolangExpression rotationX,
+ MolangExpression rotationY,
+ MolangExpression rotationZ) {
+ this.rotationX = rotationX;
+ this.rotationY = rotationY;
+ this.rotationZ = rotationZ;
+ }
+
+ @Override
+ public void renderImGuiAttributes() {
+ ImString rotationXInput = new ImString();
+ String rotationXString = this.rotationX.toString();
+ if (rotationXString.startsWith("return (")) {
+ rotationXInput.set(rotationXString.substring(8, rotationXString.length() - 1));
+ } else {
+ rotationXInput.set(rotationXString);
+ }
+
+ if (ImGui.inputText("x", rotationXInput)) {
+ try {
+ this.rotationX = VeilMolang.get().compile(rotationXInput.get());
+ } catch (Exception ignored) {
+ }
+ }
+
+ ImString rotationYInput = new ImString();
+ String rotationYString = this.rotationY.toString();
+ if (rotationYString.startsWith("return (")) {
+ rotationYInput.set(rotationYString.substring(8, rotationYString.length() - 1));
+ } else {
+ rotationYInput.set(rotationYString);
+ }
+
+ if (ImGui.inputText("y", rotationYInput)) {
+ try {
+ this.rotationY = VeilMolang.get().compile(rotationYInput.get());
+ } catch (Exception ignored) {
+ }
+ }
+
+ ImString rotationZInput = new ImString();
+ String rotationZString = this.rotationZ.toString();
+ if (rotationZString.startsWith("return (")) {
+ rotationZInput.set(rotationZString.substring(8, rotationZString.length() - 1));
+ } else {
+ rotationZInput.set(rotationZString);
+ }
+
+ if (ImGui.inputText("z", rotationZInput)) {
+ try {
+ this.rotationZ = VeilMolang.get().compile(rotationZInput.get());
+ } catch (Exception ignored) {
+ }
+ }
+ }
+
+ @Override
+ public void addModules(ParticleModuleSet.Builder builder) {
+ builder.addModule((UpdateParticleModule) particle -> {
+ try {
+ particle.setRotation(particle.getEnvironment().resolve(this.rotationX) * Mth.DEG_TO_RAD, particle.getEnvironment().resolve(this.rotationY) * Mth.DEG_TO_RAD, particle.getEnvironment().resolve(this.rotationZ) * Mth.DEG_TO_RAD);
+ } catch (Exception ignored) {
+
+ }
+ });
+ }
+
+ @Override
+ public ModuleType> getType() {
+ return ParticleModuleTypeRegistry.TICK_ROTATION;
+ }
+
+ public MolangExpression rotationX() {
+ return rotationX;
+ }
+
+ public MolangExpression rotationY() {
+ return rotationY;
+ }
+
+ public MolangExpression rotationZ() {
+ return rotationZ;
+ }
+}
diff --git a/common/src/main/java/foundry/veil/api/quasar/emitters/module/force/ConstantForceModule.java b/common/src/main/java/foundry/veil/api/quasar/emitters/module/force/ConstantForceModule.java
index 6c81aeadb..3fe8435ea 100644
--- a/common/src/main/java/foundry/veil/api/quasar/emitters/module/force/ConstantForceModule.java
+++ b/common/src/main/java/foundry/veil/api/quasar/emitters/module/force/ConstantForceModule.java
@@ -2,6 +2,7 @@
import foundry.veil.api.quasar.emitters.module.ForceParticleModule;
import foundry.veil.api.quasar.particle.QuasarParticle;
+import org.joml.Quaterniond;
import org.joml.Vector3d;
import org.joml.Vector3dc;
@@ -21,7 +22,8 @@ public ConstantForceModule(Vector3d acceleration) {
@Override
public void applyForce(QuasarParticle particle) {
- particle.getVelocity().add(this.acceleration.x * this.strength, this.acceleration.y * this.strength, this.acceleration.z * this.strength);
+ Vector3d rotatedAcceleration = this.acceleration.rotate(particle.getEmitter().getRotation().get(new Quaterniond()), new Vector3d());
+ particle.getVelocity().add(rotatedAcceleration.x * this.strength, rotatedAcceleration.y * this.strength, rotatedAcceleration.z * this.strength);
}
@Override
diff --git a/common/src/main/java/foundry/veil/api/quasar/emitters/module/force/SimplePositionedForce.java b/common/src/main/java/foundry/veil/api/quasar/emitters/module/force/SimplePositionedForce.java
index b7e46bae3..0374cdfe5 100644
--- a/common/src/main/java/foundry/veil/api/quasar/emitters/module/force/SimplePositionedForce.java
+++ b/common/src/main/java/foundry/veil/api/quasar/emitters/module/force/SimplePositionedForce.java
@@ -2,6 +2,7 @@
import foundry.veil.api.quasar.emitters.module.ForceParticleModule;
import foundry.veil.api.quasar.particle.QuasarParticle;
+import org.joml.Quaterniond;
import org.joml.Vector3d;
import org.joml.Vector3dc;
@@ -23,7 +24,8 @@ protected SimplePositionedForce(Vector3dc position, boolean localPosition) {
protected Vector3d getDeltaPosition(QuasarParticle particle) {
if (this.localPosition) {
- return this.position.add(particle.getEmitter().getPosition(), this.tempPos).sub(particle.getPosition());
+ Vector3d rotatedPosition = position.rotate(particle.getEmitter().getRotation().get(new Quaterniond()), new Vector3d());
+ return rotatedPosition.add(particle.getEmitter().getPosition(), this.tempPos).sub(particle.getPosition());
}
return this.position.sub(particle.getPosition(), this.tempPos);
}
diff --git a/common/src/main/java/foundry/veil/api/quasar/emitters/module/force/VortexForceModule.java b/common/src/main/java/foundry/veil/api/quasar/emitters/module/force/VortexForceModule.java
index ff63d8983..a60d9baa3 100644
--- a/common/src/main/java/foundry/veil/api/quasar/emitters/module/force/VortexForceModule.java
+++ b/common/src/main/java/foundry/veil/api/quasar/emitters/module/force/VortexForceModule.java
@@ -2,6 +2,7 @@
import foundry.veil.api.quasar.data.module.force.VortexForceData;
import foundry.veil.api.quasar.particle.QuasarParticle;
+import org.joml.Quaterniond;
import org.joml.Vector3d;
import org.joml.Vector3dc;
@@ -38,9 +39,11 @@ public void applyForce(QuasarParticle particle) {
}
// apply force to particle to move around the vortex center on the vortex axis, but do not modify outwards/inwards velocity
- Vector3d particleToCenterOnAxis = diff.sub(this.vortexAxis.mul(diff.dot(this.vortexAxis), this.dot));
+ Vector3d rotatedAxis = this.vortexAxis.rotate(particle.getEmitter().getRotation().get(new Quaterniond()), new Vector3d()).normalize();
+
+ Vector3d particleToCenterOnAxis = diff.sub(rotatedAxis.mul(diff.dot(rotatedAxis), this.dot));
particleToCenterOnAxis.normalize();
- particleToCenterOnAxis.cross(this.vortexAxis).mul(this.strength);
+ particleToCenterOnAxis.cross(rotatedAxis).mul(this.strength);
particle.getVelocity().add(particleToCenterOnAxis);
}
diff --git a/common/src/main/java/foundry/veil/api/quasar/emitters/shape/Cube.java b/common/src/main/java/foundry/veil/api/quasar/emitters/shape/Cube.java
index 672577268..c6c639f7f 100644
--- a/common/src/main/java/foundry/veil/api/quasar/emitters/shape/Cube.java
+++ b/common/src/main/java/foundry/veil/api/quasar/emitters/shape/Cube.java
@@ -12,7 +12,7 @@
public class Cube implements EmitterShape {
@Override
- public Vector3d getPoint(RandomSource randomSource, Vector3fc dimensions, Vector3fc rotation, Vector3dc position, boolean fromSurface) {
+ public Vector3d getPoint(RandomSource randomSource, Vector3fc dimensions, Vector3fc shapeRotation, Vector3dc position, boolean fromSurface) {
double x = randomSource.nextDouble() * 2 - 1;
double y = randomSource.nextDouble() * 2 - 1;
double z = randomSource.nextDouble() * 2 - 1;
@@ -28,7 +28,11 @@ public Vector3d getPoint(RandomSource randomSource, Vector3fc dimensions, Vector
}
Vector3d normal = new Vector3d(x, y, z);
Vector3d pos = normal.mul(dimensions).mul(0.5);
- pos = pos.rotate(new Quaterniond().rotationXYZ((float) Math.toRadians(rotation.x()), (float) Math.toRadians(rotation.y()), (float) Math.toRadians(rotation.z())));
+ new Matrix4d().rotate(new Quaternionf().rotationXYZ((float) Math.toRadians(shapeRotation.x()), (float) Math.toRadians(shapeRotation.y()), (float) Math.toRadians(shapeRotation.z()))).transformPosition(pos);
+ //pos.rotateAxis(Math.toRadians(shapeRotation.x()), 1, 0, 0);
+ //pos.rotateAxis(Math.toRadians(shapeRotation.y()), 0, 1, 0);
+ //pos.rotateAxis(Math.toRadians(shapeRotation.z()), 0, 0, 1);
+ //pos.rotate(new Quaterniond().rotateLocalX((float) Math.toRadians(shapeRotation.x())).rotateLocalY( (float) Math.toRadians(shapeRotation.y())).rotateLocalZ( (float) Math.toRadians(shapeRotation.z())));
return pos.add(position);
}
diff --git a/common/src/main/java/foundry/veil/api/quasar/emitters/shape/Cylinder.java b/common/src/main/java/foundry/veil/api/quasar/emitters/shape/Cylinder.java
index df3336c7c..eff0c9d31 100644
--- a/common/src/main/java/foundry/veil/api/quasar/emitters/shape/Cylinder.java
+++ b/common/src/main/java/foundry/veil/api/quasar/emitters/shape/Cylinder.java
@@ -10,7 +10,7 @@
public class Cylinder implements EmitterShape {
@Override
- public Vector3d getPoint(RandomSource randomSource, Vector3fc dimensions, Vector3fc rotation, Vector3dc position, boolean fromSurface) {
+ public Vector3d getPoint(RandomSource randomSource, Vector3fc dimensions, Vector3fc shapeRotation, Vector3dc position, boolean fromSurface) {
double theta = randomSource.nextDouble() * 2 * Math.PI;
double x = Math.cos(theta);
double y = randomSource.nextDouble() * 2 - 1;
@@ -27,7 +27,7 @@ public Vector3d getPoint(RandomSource randomSource, Vector3fc dimensions, Vector
);
}
Vector3d pos = normal.mul(dim);
- pos = pos.rotate(new Quaterniond().rotationXYZ((float) Math.toRadians(rotation.x()), (float) Math.toRadians(rotation.y()), (float) Math.toRadians(rotation.z())));
+ pos = pos.rotate(new Quaterniond().rotationXYZ((float) Math.toRadians(shapeRotation.x()), (float) Math.toRadians(shapeRotation.y()), (float) Math.toRadians(shapeRotation.z())));
pos.mul(0.5);
return pos.add(position);
}
diff --git a/common/src/main/java/foundry/veil/api/quasar/emitters/shape/Disc.java b/common/src/main/java/foundry/veil/api/quasar/emitters/shape/Disc.java
index 9056c9f79..6d626e33a 100644
--- a/common/src/main/java/foundry/veil/api/quasar/emitters/shape/Disc.java
+++ b/common/src/main/java/foundry/veil/api/quasar/emitters/shape/Disc.java
@@ -10,7 +10,7 @@
public class Disc implements EmitterShape {
@Override
- public Vector3d getPoint(RandomSource randomSource, Vector3fc dimensions, Vector3fc rotation, Vector3dc position, boolean fromSurface) {
+ public Vector3d getPoint(RandomSource randomSource, Vector3fc dimensions, Vector3fc shapeRotation, Vector3dc position, boolean fromSurface) {
double x = randomSource.nextDouble() * 2 - 1;
double y = 0;
double z = randomSource.nextDouble() * 2 - 1;
@@ -26,7 +26,7 @@ public Vector3d getPoint(RandomSource randomSource, Vector3fc dimensions, Vector
);
}
Vector3d pos = normal.mul(dim).mul(0.5);
- pos = pos.rotate(new Quaterniond().rotationXYZ((float) Math.toRadians(rotation.x()), (float) Math.toRadians(rotation.y()), (float) Math.toRadians(rotation.z())));
+ pos = pos.rotate(new Quaterniond().rotationXYZ((float) Math.toRadians(shapeRotation.x()), (float) Math.toRadians(shapeRotation.y()), (float) Math.toRadians(shapeRotation.z())));
return pos.add(position);
}
diff --git a/common/src/main/java/foundry/veil/api/quasar/emitters/shape/EmitterShape.java b/common/src/main/java/foundry/veil/api/quasar/emitters/shape/EmitterShape.java
index a9bf3ab2a..db7025d8a 100644
--- a/common/src/main/java/foundry/veil/api/quasar/emitters/shape/EmitterShape.java
+++ b/common/src/main/java/foundry/veil/api/quasar/emitters/shape/EmitterShape.java
@@ -12,7 +12,7 @@
public interface EmitterShape {
- Vector3d getPoint(RandomSource randomSource, Vector3fc dimensions, Vector3fc rotation, Vector3dc position, boolean fromSurface);
+ Vector3d getPoint(RandomSource randomSource, Vector3fc dimensions, Vector3fc shapeRotation, Vector3dc position, boolean fromSurface);
void renderShape(PoseStack stack, VertexConsumer consumer, Vector3fc dimensions, Vector3fc rotation);
diff --git a/common/src/main/java/foundry/veil/api/quasar/emitters/shape/Hemisphere.java b/common/src/main/java/foundry/veil/api/quasar/emitters/shape/Hemisphere.java
index d7d9505b8..f57714773 100644
--- a/common/src/main/java/foundry/veil/api/quasar/emitters/shape/Hemisphere.java
+++ b/common/src/main/java/foundry/veil/api/quasar/emitters/shape/Hemisphere.java
@@ -12,7 +12,7 @@
public class Hemisphere implements EmitterShape {
@Override
- public Vector3d getPoint(RandomSource randomSource, Vector3fc dimensions, Vector3fc rotation, Vector3dc position, boolean fromSurface) {
+ public Vector3d getPoint(RandomSource randomSource, Vector3fc dimensions, Vector3fc shapeRotation, Vector3dc position, boolean fromSurface) {
double theta = randomSource.nextDouble() * 2 * Math.PI;
double phi = randomSource.nextDouble() * Math.PI / 2;
double x = Math.cos(theta) * Math.sin(phi);
@@ -30,7 +30,7 @@ public Vector3d getPoint(RandomSource randomSource, Vector3fc dimensions, Vector
);
}
Vector3d pos = normal.mul(dim).mul(0.5);
- pos = pos.rotate(new Quaterniond().rotationXYZ((float) Math.toRadians(rotation.x()), (float) Math.toRadians(rotation.y()), (float) Math.toRadians(rotation.z())));
+ pos = pos.rotate(new Quaterniond().rotationXYZ((float) Math.toRadians(shapeRotation.x()), (float) Math.toRadians(shapeRotation.y()), (float) Math.toRadians(shapeRotation.z())));
return pos.add(position);
}
diff --git a/common/src/main/java/foundry/veil/api/quasar/emitters/shape/Plane.java b/common/src/main/java/foundry/veil/api/quasar/emitters/shape/Plane.java
index 41e0f5d92..b55a69106 100644
--- a/common/src/main/java/foundry/veil/api/quasar/emitters/shape/Plane.java
+++ b/common/src/main/java/foundry/veil/api/quasar/emitters/shape/Plane.java
@@ -12,7 +12,7 @@
public class Plane implements EmitterShape {
@Override
- public Vector3d getPoint(RandomSource randomSource, Vector3fc dimensions, Vector3fc rotation, Vector3dc position, boolean fromSurface) {
+ public Vector3d getPoint(RandomSource randomSource, Vector3fc dimensions, Vector3fc shapeRotation, Vector3dc position, boolean fromSurface) {
double x = randomSource.nextDouble() - 0.5;
double y = 0;
double z = randomSource.nextDouble() - 0.5;
@@ -25,7 +25,7 @@ public Vector3d getPoint(RandomSource randomSource, Vector3fc dimensions, Vector
}
Vector3d normal = new Vector3d(x, y, z);
Vector3d pos = normal.mul(dimensions);
- pos = pos.rotate(new Quaterniond().rotationXYZ((float) Math.toRadians(rotation.x()), (float) Math.toRadians(rotation.y()), (float) Math.toRadians(rotation.z())));
+ pos = pos.rotate(new Quaterniond().rotationXYZ((float) Math.toRadians(shapeRotation.x()), (float) Math.toRadians(shapeRotation.y()), (float) Math.toRadians(shapeRotation.z())));
return pos.add(position);
}
diff --git a/common/src/main/java/foundry/veil/api/quasar/emitters/shape/Point.java b/common/src/main/java/foundry/veil/api/quasar/emitters/shape/Point.java
index 3d501e2a0..6c5472b9f 100644
--- a/common/src/main/java/foundry/veil/api/quasar/emitters/shape/Point.java
+++ b/common/src/main/java/foundry/veil/api/quasar/emitters/shape/Point.java
@@ -12,7 +12,7 @@
public class Point implements EmitterShape {
@Override
- public Vector3d getPoint(RandomSource randomSource, Vector3fc dimensions, Vector3fc rotation, Vector3dc position, boolean fromSurface) {
+ public Vector3d getPoint(RandomSource randomSource, Vector3fc dimensions, Vector3fc shapeRotation, Vector3dc position, boolean fromSurface) {
return new Vector3d(position);
}
diff --git a/common/src/main/java/foundry/veil/api/quasar/emitters/shape/Sphere.java b/common/src/main/java/foundry/veil/api/quasar/emitters/shape/Sphere.java
index 3eb49a674..6bd20bc2c 100644
--- a/common/src/main/java/foundry/veil/api/quasar/emitters/shape/Sphere.java
+++ b/common/src/main/java/foundry/veil/api/quasar/emitters/shape/Sphere.java
@@ -11,7 +11,7 @@
public class Sphere implements EmitterShape {
@Override
- public Vector3d getPoint(RandomSource randomSource, Vector3fc dimensions, Vector3fc rotation, Vector3dc position, boolean fromSurface) {
+ public Vector3d getPoint(RandomSource randomSource, Vector3fc dimensions, Vector3fc shapeRotation, Vector3dc position, boolean fromSurface) {
double x = randomSource.nextDouble() - 0.5;
double y = randomSource.nextDouble() - 0.5;
double z = randomSource.nextDouble() - 0.5;
@@ -27,7 +27,7 @@ public Vector3d getPoint(RandomSource randomSource, Vector3fc dimensions, Vector
);
}
Vector3d pos = normal.mul(dim).mul(0.5);
- pos = pos.rotate(new Quaterniond().rotationXYZ((float) Math.toRadians(rotation.x()), (float) Math.toRadians(rotation.y()), (float) Math.toRadians(rotation.z())));
+ pos = pos.rotate(new Quaterniond().rotationXYZ((float) Math.toRadians(shapeRotation.x()), (float) Math.toRadians(shapeRotation.y()), (float) Math.toRadians(shapeRotation.z())));
return pos.add(position);
}
diff --git a/common/src/main/java/foundry/veil/api/quasar/emitters/shape/Torus.java b/common/src/main/java/foundry/veil/api/quasar/emitters/shape/Torus.java
index 0aa61a816..9eb7a19b6 100644
--- a/common/src/main/java/foundry/veil/api/quasar/emitters/shape/Torus.java
+++ b/common/src/main/java/foundry/veil/api/quasar/emitters/shape/Torus.java
@@ -3,15 +3,14 @@
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import net.minecraft.util.RandomSource;
-import org.joml.Vector3d;
-import org.joml.Vector3dc;
-import org.joml.Vector3f;
-import org.joml.Vector3fc;
+import org.joml.*;
+
+import java.lang.Math;
public class Torus implements EmitterShape {
@Override
- public Vector3d getPoint(RandomSource randomSource, Vector3fc dimensions, Vector3fc rotation, Vector3dc position, boolean fromSurface) {
+ public Vector3d getPoint(RandomSource randomSource, Vector3fc dimensions, Vector3fc shapeRotation, Vector3dc position, boolean fromSurface) {
double theta = randomSource.nextDouble() * 2 * Math.PI;
double phi = randomSource.nextDouble() * 2 * Math.PI;
double x = Math.cos(theta) * (1 + 0.5 * Math.cos(phi));
@@ -29,7 +28,7 @@ public Vector3d getPoint(RandomSource randomSource, Vector3fc dimensions, Vector
);
}
Vector3d pos = normal.mul(dim);
- pos = pos.rotateX((float) Math.toRadians(rotation.x())).rotateY((float) Math.toRadians(rotation.y())).rotateZ((float) Math.toRadians(rotation.z()));
+ pos = pos.rotateX((float) Math.toRadians(shapeRotation.x())).rotateY((float) Math.toRadians(shapeRotation.y())).rotateZ((float) Math.toRadians(shapeRotation.z()));
return pos.add(position);
}
diff --git a/common/src/main/java/foundry/veil/api/quasar/particle/ParticleEmitter.java b/common/src/main/java/foundry/veil/api/quasar/particle/ParticleEmitter.java
index a443512de..5e3f242a0 100644
--- a/common/src/main/java/foundry/veil/api/quasar/particle/ParticleEmitter.java
+++ b/common/src/main/java/foundry/veil/api/quasar/particle/ParticleEmitter.java
@@ -20,11 +20,9 @@
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.UnmodifiableView;
-import org.joml.Vector3d;
-import org.joml.Vector3dc;
-import org.joml.Vector3f;
-import org.joml.Vector3fc;
+import org.joml.*;
+import java.lang.Math;
import java.util.*;
import java.util.concurrent.CompletableFuture;
@@ -53,6 +51,7 @@ public class ParticleEmitter {
private final List modulesView;
private final RandomSource randomSource;
private final Vector3d position;
+ private final Quaternionf rotation;
private final Vector3d offset;
private final List particles;
@@ -80,6 +79,7 @@ protected ParticleEmitter(ParticleSystemManager particleManager, ClientLevel lev
this.modulesView = Collections.unmodifiableList(this.modules);
this.randomSource = RandomSource.create();
this.position = new Vector3d();
+ this.rotation = new Quaternionf();
this.offset = new Vector3d();
this.particles = new LinkedList<>();
@@ -115,9 +115,9 @@ protected void spawn() {
this.particleManager.reserve(count);
for (int i = 0; i < count; i++) {
- Vector3dc particlePos = this.emitterShapeSettings.get(i % this.emitterShapeSettings.size()).getPos(this.randomSource, this.position);
- Vector3fc particleDirection = this.particleSettings.particleDirection(this.randomSource);
- Vector3fc particleRotation = this.particleSettings.initialRotation(this.randomSource).mul(Mth.DEG_TO_RAD, new Vector3f());
+ Vector3dc particlePos = this.emitterShapeSettings.get(i % this.emitterShapeSettings.size()).getPos(this.randomSource, this.getPosition(), this.getRotation());
+ Vector3fc particleDirection = this.particleSettings.particleDirection(this.randomSource).rotate(this.getRotation());
+ Vector3fc particleRotation = this.particleSettings.initialRotation(this.randomSource).mul(Mth.DEG_TO_RAD, new Vector3f()).add(this.getRotation().getEulerAnglesXYZ(new Vector3f()));
// TODO
// this.getParticleData().getInitModules().stream().filter(force -> force instanceof InitialVelocityForce).forEach(f -> {
@@ -346,6 +346,15 @@ public Vector3d getPosition() {
return this.position;
}
+ /**
+ * Rotation of the emitter
+ *
+ * @since 4.5.0
+ */
+ public Quaternionf getRotation() {
+ return this.rotation;
+ }
+
public ParticleEmitterData getData() {
return this.emitterData;
}
@@ -431,6 +440,28 @@ public void setPosition(double x, double y, double z) {
}
}
+ /**
+ * Sets the rotation of the particle emitter.
+ *
+ * @param x The rotation about the X axis, in radians.
+ * @param y The rotation about the Y axis, in radians.
+ * @param z The rotation about the Z axis, in radians.
+ * @since 4.5.0
+ */
+ public void setRotation(float x, float y, float z) {
+ this.rotation.identity().rotateLocalX(x).rotateLocalY(y).rotateLocalZ(z);
+ }
+
+ /**
+ * Sets the rotation of the particle emitter.
+ *
+ * @param rotation The rotation of the emitter.
+ * @since 4.5.0
+ */
+ public void setRotation(Quaternionfc rotation) {
+ this.rotation.set(rotation);
+ }
+
public void setMaxLifetime(int maxLifetime) {
this.maxLifetime = maxLifetime;
}
diff --git a/common/src/main/java/foundry/veil/api/quasar/particle/QuasarParticle.java b/common/src/main/java/foundry/veil/api/quasar/particle/QuasarParticle.java
index 0408be00e..9a33dc8be 100644
--- a/common/src/main/java/foundry/veil/api/quasar/particle/QuasarParticle.java
+++ b/common/src/main/java/foundry/veil/api/quasar/particle/QuasarParticle.java
@@ -268,6 +268,13 @@ public Vector3f getRotation() {
return this.rotation;
}
+ /**
+ * @since 4.5.0
+ */
+ public void setRotation(float x, float y, float z) {
+ this.rotation.set(x, y, z);
+ }
+
public float getRadius() {
return this.radius;
}
diff --git a/common/src/main/java/foundry/veil/api/quasar/particle/RenderStyle.java b/common/src/main/java/foundry/veil/api/quasar/particle/RenderStyle.java
index 529fa08ff..d53ec269e 100644
--- a/common/src/main/java/foundry/veil/api/quasar/particle/RenderStyle.java
+++ b/common/src/main/java/foundry/veil/api/quasar/particle/RenderStyle.java
@@ -271,7 +271,7 @@ protected void putBufferData(QuasarParticle particle, Camera camera, ByteBuffer
Matrix4f transformationMatrix = new Matrix4f()
.translate(renderOffset.x, renderOffset.y, renderOffset.z)
- .rotate(new Quaternionf().rotateLocalX(rotation.x()).rotateLocalY(rotation.y()).rotateLocalZ(rotation.z()));
+ .rotate(new Quaternionf().rotationXYZ(rotation.x(), rotation.y(), rotation.z()));
transformationMatrix.get(buffer.position(), buffer);
buffer.position(buffer.position() + Float.BYTES * 16);
@@ -360,7 +360,7 @@ protected void putBufferData(QuasarParticle particle, Camera camera, ByteBuffer
Matrix4f transformationMatrix = new Matrix4f()
.translate(renderOffset.x, renderOffset.y, renderOffset.z)
- .rotate(faceCameraRotation.rotateLocalX(rotation.x()).rotateLocalY(rotation.y()).rotateLocalZ(rotation.z()));
+ .rotate(faceCameraRotation.rotateXYZ(rotation.x(), rotation.y(), rotation.z()));
transformationMatrix.get(buffer.position(), buffer);
buffer.position(buffer.position() + Float.BYTES * 16);
diff --git a/common/src/main/java/foundry/veil/api/screenshake/ScreenShakeManager.java b/common/src/main/java/foundry/veil/api/screenshake/ScreenShakeManager.java
new file mode 100644
index 000000000..c68f20041
--- /dev/null
+++ b/common/src/main/java/foundry/veil/api/screenshake/ScreenShakeManager.java
@@ -0,0 +1,59 @@
+package foundry.veil.api.screenshake;
+
+import foundry.veil.api.screenshake.type.ScreenShakeType;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.Contract;
+import org.joml.Vector3f;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * @since 4.5.0
+ */
+public final class ScreenShakeManager {
+
+ private final List screenShakes = new LinkedList<>();
+ private final Vector3f accumulated = new Vector3f();
+ private final Vector3f renderPosition = new Vector3f();
+
+ /**
+ * Add a screen shake to the camera.
+ */
+ public void addScreenShake(ScreenShakeType instance) {
+ this.screenShakes.add(instance);
+ }
+
+ /**
+ * Remove a screen shake from the camera.
+ */
+ public void removeScreenShake(ScreenShakeType instance) {
+ this.screenShakes.remove(instance);
+ }
+
+ @ApiStatus.Internal
+ public void tick() {
+ this.renderPosition.set(this.accumulated);
+ this.accumulated.set(0);
+
+ if (this.screenShakes.isEmpty()) {
+ return;
+ }
+
+ Iterator iterator = this.screenShakes.iterator();
+ while (iterator.hasNext()) {
+ ScreenShakeType screenShake = iterator.next();
+ screenShake.tick();
+ this.accumulated.add(screenShake.getPositionOffset());
+ if (screenShake.isRemoved()) {
+ iterator.remove();
+ }
+ }
+ }
+
+ @Contract("_->new")
+ public Vector3f getPosition(float partialTick) {
+ return this.renderPosition.lerp(this.accumulated, partialTick, new Vector3f());
+ }
+}
diff --git a/common/src/main/java/foundry/veil/api/screenshake/type/GlobalScreenShake.java b/common/src/main/java/foundry/veil/api/screenshake/type/GlobalScreenShake.java
new file mode 100644
index 000000000..9376a4e4e
--- /dev/null
+++ b/common/src/main/java/foundry/veil/api/screenshake/type/GlobalScreenShake.java
@@ -0,0 +1,40 @@
+package foundry.veil.api.screenshake.type;
+
+import foundry.veil.api.molang.VeilMolang;
+import gg.moonflower.molangcompiler.api.MolangExpression;
+import gg.moonflower.molangcompiler.api.MolangRuntime;
+import gg.moonflower.molangcompiler.api.exception.MolangSyntaxException;
+import net.minecraft.util.RandomSource;
+
+import java.util.function.Supplier;
+
+/**
+ * A screen shake that affects the player regardless of distance.
+ *
+ * @author Neddslayer
+ * @since 4.5.0
+ */
+public class GlobalScreenShake extends ScreenShakeType {
+
+ private final MolangExpression expression;
+ private final Supplier environment;
+
+ public GlobalScreenShake(String strengthExpression, int length) throws MolangSyntaxException {
+ this(VeilMolang.get().compile(strengthExpression), length);
+ }
+
+ public GlobalScreenShake(MolangExpression strengthExpression, int length) {
+ super(RandomSource.create(), length);
+ this.expression = strengthExpression;
+ this.environment = () -> MolangRuntime.runtime()
+ .setQuery("age", this::age)
+ .setQuery("agePercent", () -> this.age() / this.length())
+ .setQuery("length", this::length)
+ .create();
+ }
+
+ @Override
+ protected float getStrength() {
+ return this.environment.get().safeResolve(this.expression);
+ }
+}
diff --git a/common/src/main/java/foundry/veil/api/screenshake/type/LocalScreenShake.java b/common/src/main/java/foundry/veil/api/screenshake/type/LocalScreenShake.java
new file mode 100644
index 000000000..ce6b81695
--- /dev/null
+++ b/common/src/main/java/foundry/veil/api/screenshake/type/LocalScreenShake.java
@@ -0,0 +1,44 @@
+package foundry.veil.api.screenshake.type;
+
+import foundry.veil.api.client.util.Easing;
+import gg.moonflower.molangcompiler.api.MolangExpression;
+import gg.moonflower.molangcompiler.api.exception.MolangSyntaxException;
+import net.minecraft.client.Camera;
+import net.minecraft.client.Minecraft;
+import net.minecraft.util.Mth;
+import net.minecraft.world.phys.Vec3;
+
+/**
+ * Screen shake that originates from a point in the world, strength decreasing with distance.
+ *
+ * @author Neddslayer
+ * @since 4.5.0
+ */
+public class LocalScreenShake extends GlobalScreenShake {
+
+ private final float radius;
+ private final Easing falloff;
+ private final Vec3 position;
+
+ public LocalScreenShake(String expression, Vec3 position, int length, float radius, Easing falloff) throws MolangSyntaxException {
+ super(expression, length);
+ this.radius = radius;
+ this.falloff = falloff;
+ this.position = position;
+ }
+
+ public LocalScreenShake(MolangExpression expression, Vec3 position, int length, float radius, Easing falloff) {
+ super(expression, length);
+ this.radius = radius;
+ this.falloff = falloff;
+ this.position = position;
+ }
+
+ @Override
+ protected float getStrength() {
+ float strength = super.getStrength();
+ Camera camera = Minecraft.getInstance().gameRenderer.getMainCamera();
+ float distanceMultiplier = Mth.clamp((float) (1.0f - (camera.getPosition().distanceTo(this.position) / this.radius)), 0, 1);
+ return this.falloff.ease(distanceMultiplier) * strength;
+ }
+}
diff --git a/common/src/main/java/foundry/veil/api/screenshake/type/ScreenShakeType.java b/common/src/main/java/foundry/veil/api/screenshake/type/ScreenShakeType.java
new file mode 100644
index 000000000..15b498613
--- /dev/null
+++ b/common/src/main/java/foundry/veil/api/screenshake/type/ScreenShakeType.java
@@ -0,0 +1,75 @@
+package foundry.veil.api.screenshake.type;
+
+import net.minecraft.util.RandomSource;
+import org.jetbrains.annotations.ApiStatus;
+import org.joml.Vector3f;
+
+/**
+ * Defines the behavior of a screenshake
+ *
+ * @author Neddslayer
+ * @since 4.5.0
+ */
+public abstract class ScreenShakeType {
+
+ private final Vector3f positionOffset = new Vector3f();
+ private final RandomSource randomSource;
+ private final int length;
+ protected int ticksRemaining;
+
+ public ScreenShakeType(RandomSource randomSource, int length) {
+ this.randomSource = randomSource;
+ this.length = length;
+ this.ticksRemaining = this.length;
+ }
+
+ @ApiStatus.Internal
+ public void tick() {
+ this.ticksRemaining--;
+ if (!this.isRemoved()) {
+ float strength = this.getStrength();
+ this.positionOffset.set(this.randomOffset(strength), this.randomOffset(strength), this.randomOffset(strength));
+ }
+ }
+
+ private float randomOffset(float strength) {
+ return (this.randomSource.nextFloat() - 0.5f) * strength;
+ }
+
+ public boolean isRemoved() {
+ return this.ticksRemaining <= 0;
+ }
+
+ /**
+ * Immediately remove the screenshake.
+ */
+ public void remove() {
+ this.ticksRemaining = Integer.MIN_VALUE;
+ }
+
+ /**
+ * @return How long the screenshake has been running for.
+ */
+ public float age() {
+ return this.length - this.ticksRemaining;
+ }
+
+ /**
+ * @return The lifetime of the screenshake.
+ */
+ public float length() {
+ return this.length;
+ }
+
+ /**
+ * @return For the current tick, where the screenshake has offset the camera.
+ */
+ public Vector3f getPositionOffset() {
+ return this.positionOffset;
+ }
+
+ /**
+ * The intensity of the screenshake, up to the implementation of the {@code ScreenShakeType}.
+ */
+ protected abstract float getStrength();
+}
diff --git a/common/src/main/java/foundry/veil/impl/client/editor/ParticleEditorInspector.java b/common/src/main/java/foundry/veil/impl/client/editor/ParticleEditorInspector.java
index 2559041a0..fe67299b5 100644
--- a/common/src/main/java/foundry/veil/impl/client/editor/ParticleEditorInspector.java
+++ b/common/src/main/java/foundry/veil/impl/client/editor/ParticleEditorInspector.java
@@ -41,16 +41,17 @@
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.Mth;
+import net.minecraft.world.entity.Entity;
+import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.ApiStatus;
-import org.joml.Matrix4f;
-import org.joml.Vector3f;
-import org.joml.Vector3fc;
+import org.joml.*;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
+import java.lang.Math;
import java.nio.file.Path;
import java.util.*;
import java.util.function.Supplier;
@@ -131,7 +132,7 @@ protected void renderComponents() {
Integer.MAX_VALUE,
new EmitterSettings(
List.of(Holder.direct(new EmitterShapeSettings(EmitterShapeRegistry.POINT.get(), new Vector3f(1), new Vector3f(0), true))),
- Holder.direct(new ParticleSettings(0.1f, 0.1f, 0, 60, 0, new Vector3f(1), true, new Vector3f(0), false, false, false, false)),
+ Holder.direct(new ParticleSettings(0.1f, 0, 0.1f, 0, 60, 0, new Vector3f(0), true, new Vector3f(1), new Vector3f(0), false, new Vector3f(0), false, false, false)),
false
),
Holder.direct(new QuasarParticleData(true, false, 0.0f, List.of(), null, false, RenderStyleRegistry.CUBE.get()))
@@ -228,10 +229,52 @@ private void renderParticleAttributes() {
ImGui.text("Particles: " + emitter.getParticleCount());
+ Vector3f emitterRotationEuler = emitter.getRotationVector().mul(Mth.RAD_TO_DEG, new Vector3f());
+
float[] editPos = new float[]{(float) emitter.getPosition().x(), (float) emitter.getPosition().y(), (float) emitter.getPosition().z()};
+ float[] editRot = new float[]{emitterRotationEuler.x(), emitterRotationEuler.y(), emitterRotationEuler.z()};
if (ImGui.dragFloat3("position", editPos, 0.02F)) {
- emitter.setPosition(editPos[0], editPos[1], editPos[2]);
+ Entity attached = emitter.getAttachedEntity();
+ Vector3f entityPos = attached == null ? new Vector3f() : new Vector3f((float) attached.getX(), (float) attached.getY(), (float) attached.getZ());
+ emitter.setAttachedEntity(null);
+ emitter.setPosition(editPos[0] - entityPos.x, editPos[1] - entityPos.y, editPos[2] - entityPos.z);
+ emitter.setAttachedEntity(attached);
+ }
+
+ if (ImGui.dragFloat3("rotation", editRot, 0.1F)) {
+ emitter.setRotation(editRot[0] * Mth.DEG_TO_RAD, editRot[1] * Mth.DEG_TO_RAD, editRot[2] * Mth.DEG_TO_RAD);
+ }
+
+ if (Minecraft.getInstance().crosshairPickEntity != null) {
+ if (ImGui.button("Attach to entity")) {
+ emitter.setAttachedEntity(Minecraft.getInstance().crosshairPickEntity);
+ emitter.setPosition(Vec3.ZERO);
+ }
+ } else if (Minecraft.getInstance().hitResult != null && Minecraft.getInstance().hitResult.getType() == HitResult.Type.BLOCK) {
+ if (ImGui.button("Place on block")) {
+ emitter.setAttachedEntity(null);
+ BlockHitResult blockHitResult = ((BlockHitResult) Minecraft.getInstance().hitResult);
+ emitter.setPosition(blockHitResult.getBlockPos().getCenter().add(new Vec3(blockHitResult.getDirection().step().mul(0.5f))));
+ emitter.setRotation(blockHitResult.getDirection().getRotation());
+ }
+ } else {
+ if (ImGui.button("Move to view")) {
+ emitter.setAttachedEntity(null);
+ Camera camera = Minecraft.getInstance().gameRenderer.getMainCamera();
+ emitter.setPosition(camera.getPosition().add(new Vec3(camera.getLookVector()).scale(4)));
+ }
+ }
+
+ if (emitter.getAttachedEntity() != null) {
+ ImGui.sameLine();
+ if (ImGui.button("Remove from entity")) {
+ Entity attached = emitter.getAttachedEntity();
+ Vector3d targetPosition = new Vector3d(attached.getX(), attached.getY(), attached.getZ());
+ Vector3d offset = emitter.getPosition().sub(targetPosition, new Vector3d());
+ emitter.setAttachedEntity(null);
+ emitter.setPosition(targetPosition.add(offset));
+ }
}
// General (emitters)
@@ -249,7 +292,7 @@ private void renderParticleAttributes() {
float width = ImGui.getContentRegionAvailX() * 0.333f;
ImGui.setNextItemWidth(width);
- if (ImGui.dragScalar("rate", editRate, 0.02F)) {
+ if (ImGui.dragScalar("rate", editRate, 0.02F, 1, Integer.MAX_VALUE)) {
emitter.setRate(editRate[0]);
}
ImGui.sameLine();
@@ -364,6 +407,13 @@ private void renderParticleSettings(MutableParticleEmitter emitter) {
emitter.setParticleDirection(editDirection[0], editDirection[1], editDirection[2]);
}
+ if (settings.randomInitialDirection()) {
+ float[] editDirectionVariation = new float[]{settings.initialDirectionVariation().x(), settings.initialDirectionVariation().y(), settings.initialDirectionVariation().z()};
+ if (ImGui.dragFloat3("initial_direction_variation", editDirectionVariation, 0.01F)) {
+ emitter.setParticleDirectionVariation(editDirectionVariation[0], editDirectionVariation[1], editDirectionVariation[2]);
+ }
+ }
+
if (ImGui.checkbox("random_initial_rotation", settings.randomInitialRotation())) {
emitter.toggleRandomRotation();
}
@@ -373,14 +423,29 @@ private void renderParticleSettings(MutableParticleEmitter emitter) {
emitter.setParticleRotation(editRotation[0], editRotation[1], editRotation[2]);
}
+ if (settings.randomInitialRotation()) {
+ float[] editRotationVariation = new float[]{settings.initialRotationVariation().x(), settings.initialRotationVariation().y(), settings.initialRotationVariation().z()};
+ if (ImGui.dragFloat3("initial_rotation_variation", editRotationVariation, 0.025F)) {
+ emitter.setParticleRotationVariation(editRotationVariation[0], editRotationVariation[1], editRotationVariation[2]);
+ }
+ }
+
if (ImGui.checkbox("random_speed", settings.randomSpeed())) {
emitter.toggleRandomSpeed();
}
- float[] editSpeed = new float[]{settings.particleSpeed()};
+ if (settings.randomSpeed()) {
+ float[] editSpeed = new float[]{settings.particleSpeed(), settings.particleSpeed() + settings.particleSpeedVariation()};
- if (ImGui.dragScalar("particle_speed", editSpeed, 0.01F)) {
- emitter.setParticleSpeed(editSpeed[0]);
+ if (ImGui.dragFloat2("particle_speed", editSpeed, 0.01F, 0, editSpeed[1] + 0.01F)) {
+ emitter.setParticleSpeed(editSpeed[0], editSpeed[1]);
+ }
+ } else {
+ float[] editSpeed = new float[]{settings.particleSpeed()};
+
+ if (ImGui.dragScalar("particle_speed", editSpeed, 0.01F)) {
+ emitter.setParticleSpeed(editSpeed[0], 0);
+ }
}
if (ImGui.checkbox("random_size", settings.randomSize())) {
@@ -390,7 +455,7 @@ private void renderParticleSettings(MutableParticleEmitter emitter) {
if (settings.randomSize()) {
float[] editParticleSize = new float[]{settings.particleSize(), settings.particleSize() + settings.particleSizeVariation()};
- if (ImGui.dragFloat2("particle_size", editParticleSize, 0.01F, Math.max(editParticleSize[0], 0.001f), editParticleSize[1])) {
+ if (ImGui.dragFloat2("particle_size", editParticleSize, 0.01F, Math.max(editParticleSize[0] - 0.01F, 0.001f), editParticleSize[1] + 0.01F)) {
emitter.setParticleSize(editParticleSize[0], editParticleSize[1]);
}
} else {
@@ -407,7 +472,7 @@ private void renderParticleSettings(MutableParticleEmitter emitter) {
if (settings.randomLifetime()) {
int[] editParticleLifetime = new int[]{settings.particleLifetime(), settings.particleLifetime() + (int) settings.particleLifetimeVariation()};
- if (ImGui.dragInt2("particle_lifetime", editParticleLifetime, 0.03F, Math.max(editParticleLifetime[0], 0))) {
+ if (ImGui.dragInt2("particle_lifetime", editParticleLifetime, 0.03F, editParticleLifetime[0] - 1, editParticleLifetime[1] + 1)) {
emitter.setParticleLifetime(editParticleLifetime[0], editParticleLifetime[1]);
}
} else {
@@ -505,6 +570,10 @@ private void renderModules(MutableParticleEmitter emitter) {
String name = String.valueOf(ParticleModuleTypeRegistry.REGISTRY.getKey(module.getType()));
if (ImGui.collapsingHeader(name)) {
ImGui.indent();
+ ModuleType.DeprecationStatus status = module.getType().deprecationStatus();
+ if (status != null) {
+ ImGui.textColored(0xFF00FFFF, "DEPRECATED MODULE: %s will be removed in %s (%s)".formatted(name, status.removeVersion(), status.reason()));
+ }
if (module instanceof EditorAttributeProvider attributeProvider) {
attributeProvider.renderImGuiAttributes();
}
@@ -516,10 +585,6 @@ private void renderModules(MutableParticleEmitter emitter) {
}
ImGui.unindent();
}
- ModuleType.DeprecationStatus status = module.getType().deprecationStatus();
- if (status != null && ImGui.isItemHovered()) {
- ImGui.setTooltip("%s will be removed in %s".formatted(name, status.removeVersion()));
- }
ImGui.popID();
id++;
}
@@ -632,6 +697,9 @@ private static class MutableParticleEmitter extends ParticleEmitter {
public boolean renderEmitterShape = false;
public boolean renderDirection;
+ // In order to facilitate an easier use of rotation, we store this as a Vector3f instead of a Quaternion
+ private final Vector3f rotation = new Vector3f();
+
private MutableParticleEmitter(ParticleSystemManager particleManager, ClientLevel level, ParticleEmitterData data) {
super(particleManager, level, data);
this.particleData = new QuasarParticleData(this.particleData.shouldCollide(), this.particleData.faceVelocity(), this.particleData.velocityStretchFactor(), this.particleData.modules(), this.particleData.spriteData(), this.particleData.additive(), this.particleData.renderStyle());
@@ -656,7 +724,7 @@ public void render(MatrixStack matrixStack, MultiBufferSource bufferSource, Came
for (EmitterShapeSettings shapeSettings : this.getEmitterShapeSettings()) {
matrixStack.matrixPush();
matrixStack.translate(this.getPosition());
- shapeSettings.shape().renderShape(matrixStack.toPoseStack(), debugBuilder, shapeSettings.dimensions(), shapeSettings.rotation());
+ shapeSettings.shape().renderShape(matrixStack.toPoseStack(), debugBuilder, shapeSettings.dimensions(), shapeSettings.rotation().add(this.rotation.mul(Mth.RAD_TO_DEG, new Vector3f()), new Vector3f()));
matrixStack.matrixPop();
}
matrixStack.matrixPop();
@@ -667,12 +735,24 @@ public void render(MatrixStack matrixStack, MultiBufferSource bufferSource, Came
matrixStack.translate(-camera.getPosition().x, -camera.getPosition().y, -camera.getPosition().z);
matrixStack.matrixPush();
matrixStack.translate(this.getPosition());
+ matrixStack.rotate(this.getRotation());
- Matrix4f matrix4f = matrixStack.position();
+ Matrix4f pose = matrixStack.position();
- debugBuilder.addVertex(matrix4f, 0, 0, 0).setColor(1, 1f, 1f, 1).setNormal(0, 1, 0);
Vector3f direction = this.getParticleSettings().initialDirection().normalize(new Vector3f()).mul(this.getParticleSettings().particleSpeed() * 10);
- debugBuilder.addVertex(matrix4f, direction.x, direction.y, direction.z).setColor(1f, 0.15f, 0.15f, 1f).setNormal(0, 1, 0);
+ debugBuilder.addVertex(pose, 0, 0, 0).setColor(1, 1f, 1f, 1).setNormal(matrixStack.pose(), direction.x, direction.y, direction.z);
+ debugBuilder.addVertex(pose, direction.x, direction.y, direction.z).setColor(1f, 0.15f, 0.15f, 1f).setNormal(matrixStack.pose(), direction.x, direction.y, direction.z);
+
+ if (this.getParticleSettings().randomInitialDirection()) {
+ Vector3f minDirection = this.getParticleSettings().initialDirection().add(this.getParticleSettings().initialDirectionVariation().mul(-1, new Vector3f()), new Vector3f()).mul(this.getParticleSettings().particleSpeed() * 10);
+ Vector3f maxDirection = this.getParticleSettings().initialDirection().add(this.getParticleSettings().initialDirectionVariation(), new Vector3f()).mul(this.getParticleSettings().particleSpeed() * 10);
+ debugBuilder.addVertex(pose, 0, 0, 0).setColor(1, 1f, 1f, 1).setNormal(matrixStack.pose(), minDirection.x, minDirection.y, minDirection.z);
+ debugBuilder.addVertex(pose, minDirection.x, minDirection.y, minDirection.z).setColor(0.15f, 0.15f, 1f, 1f).setNormal(matrixStack.pose(), minDirection.x, minDirection.y, minDirection.z);
+
+ debugBuilder.addVertex(pose, 0, 0, 0).setColor(1, 1f, 1f, 1).setNormal(matrixStack.pose(), maxDirection.x, maxDirection.y, maxDirection.z);
+ debugBuilder.addVertex(pose, maxDirection.x, maxDirection.y, maxDirection.z).setColor(0.15f, 0.15f, 1f, 1f).setNormal(matrixStack.pose(), maxDirection.x, maxDirection.y, maxDirection.z);
+ }
+
matrixStack.matrixPop();
matrixStack.matrixPop();
}
@@ -689,6 +769,25 @@ protected void tick() {
}
}
+ public Vector3f getRotationVector() {
+ return this.rotation;
+ }
+
+ @Override
+ public Quaternionf getRotation() {
+ return new Quaternionf().rotationXYZ(this.rotation.x, this.rotation.y, this.rotation.z);
+ }
+
+ @Override
+ public void setRotation(Quaternionfc newRot) {
+ newRot.getEulerAnglesXYZ(this.rotation);
+ }
+
+ @Override
+ public void setRotation(float x, float y, float z) {
+ this.rotation.set(x, y, z);
+ }
+
public void setRate(int rate) {
super.setRate(rate);
@@ -729,9 +828,11 @@ public void resetShapeTransform(int index) {
this.updateShapeSettings(index, oldShape.shape(), new Vector3f(1, 1, 1), new Vector3f(0, 0, 0), oldShape.fromSurface());
}
- public void setParticleSpeed(float speed) {
+ public void setParticleSpeed(float min, float max) {
+ max = Math.max(min, max);
this.setParticleSettings(new ParticleSettingsBuilder(this.getParticleSettings())
- .setParticleSpeed(speed)
+ .setParticleSpeed(min)
+ .setParticleSpeedVariation(max - min)
.build());
}
@@ -787,12 +888,24 @@ public void setParticleDirection(float x, float y, float z) {
.build());
}
+ public void setParticleDirectionVariation(float x, float y, float z) {
+ this.setParticleSettings(new ParticleSettingsBuilder(this.getParticleSettings())
+ .setInitialDirectionVariation(new Vector3f(x, y, z))
+ .build());
+ }
+
public void setParticleRotation(float x, float y, float z) {
this.setParticleSettings(new ParticleSettingsBuilder(this.getParticleSettings())
.setInitialRotation(new Vector3f(x, y, z))
.build());
}
+ public void setParticleRotationVariation(float x, float y, float z) {
+ this.setParticleSettings(new ParticleSettingsBuilder(this.getParticleSettings())
+ .setInitialRotationVariation(new Vector3f(x, y, z))
+ .build());
+ }
+
public void forceRemove() {
this.forceRemoved = true;
this.remove();
@@ -800,28 +913,34 @@ public void forceRemove() {
private static class ParticleSettingsBuilder {
private float particleSpeed;
+ private float particleSpeedVariation;
private float particleSize;
private float particleSizeVariation;
private int particleLifetime;
private float particleLifetimeVariation;
private Vector3fc initialDirection;
private boolean randomInitialDirection;
+ private Vector3fc initialDirectionVariation;
private Vector3fc initialRotation;
private boolean randomInitialRotation;
+ private Vector3fc initialRotationVariation;
private boolean randomSpeed;
private boolean randomSize;
private boolean randomLifetime;
public ParticleSettingsBuilder(ParticleSettings from) {
this.particleSpeed = from.particleSpeed();
+ this.particleSpeedVariation = from.particleSpeedVariation();
this.particleSize = from.particleSize();
this.particleSizeVariation = from.particleSizeVariation();
this.particleLifetime = from.particleLifetime();
this.particleLifetimeVariation = from.particleLifetimeVariation();
this.initialDirection = from.initialDirection();
this.randomInitialDirection = from.randomInitialDirection();
+ this.initialDirectionVariation = from.initialDirectionVariation();
this.initialRotation = from.initialRotation();
this.randomInitialRotation = from.randomInitialRotation();
+ this.initialRotationVariation = from.initialRotationVariation();
this.randomSpeed = from.randomSpeed();
this.randomSize = from.randomSize();
this.randomLifetime = from.randomLifetime();
@@ -887,8 +1006,23 @@ public ParticleSettingsBuilder setRandomLifetime(boolean randomLifetime) {
return this;
}
+ public ParticleSettingsBuilder setInitialDirectionVariation(Vector3fc initialDirectionVariation) {
+ this.initialDirectionVariation = initialDirectionVariation;
+ return this;
+ }
+
+ public ParticleSettingsBuilder setInitialRotationVariation(Vector3fc initialRotationVariation) {
+ this.initialRotationVariation = initialRotationVariation;
+ return this;
+ }
+
+ public ParticleSettingsBuilder setParticleSpeedVariation(float particleSpeedVariation) {
+ this.particleSpeedVariation = particleSpeedVariation;
+ return this;
+ }
+
public ParticleSettings build() {
- return new ParticleSettings(this.particleSpeed, this.particleSize, this.particleSizeVariation, this.particleLifetime, this.particleLifetimeVariation, this.initialDirection, this.randomInitialDirection, this.initialRotation, this.randomInitialRotation, this.randomSpeed, this.randomSize, this.randomLifetime);
+ return new ParticleSettings(this.particleSpeed, this.particleSpeedVariation, this.particleSize, this.particleSizeVariation, this.particleLifetime, this.particleLifetimeVariation, this.initialDirection, this.randomInitialDirection, this.initialDirectionVariation, this.initialRotation, this.randomInitialRotation, this.initialRotationVariation, this.randomSpeed, this.randomSize, this.randomLifetime);
}
}
}
diff --git a/common/src/main/java/foundry/veil/impl/client/necromancer/render/NecromancerRenderDispatcher.java b/common/src/main/java/foundry/veil/impl/client/necromancer/render/NecromancerRenderDispatcher.java
index 9212a4982..64bb73fb1 100644
--- a/common/src/main/java/foundry/veil/impl/client/necromancer/render/NecromancerRenderDispatcher.java
+++ b/common/src/main/java/foundry/veil/impl/client/necromancer/render/NecromancerRenderDispatcher.java
@@ -14,6 +14,7 @@
import foundry.veil.api.client.render.shader.block.DynamicShaderBlock;
import foundry.veil.api.client.render.shader.block.ShaderBlock;
import foundry.veil.api.client.render.vertex.VertexArray;
+import foundry.veil.api.compat.ImmersivePortalsCompat;
import it.unimi.dsi.fastutil.floats.FloatArrayList;
import it.unimi.dsi.fastutil.floats.FloatList;
import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap;
@@ -90,7 +91,7 @@ private static void updateBlockSize(int skeletonCount, int dataSize) {
boneBlock.setSize(Skeleton.UNIFORM_STRIDE * newSize);
VeilRenderSystem.renderer().getShaderDefinitions().set("NECROMANCER_BONE_BUFFER_SIZE", Long.toString(newSize));
- if (VeilRenderSystem.directStateAccessSupported()) {
+ if (VeilRenderSystem.directStateAccessSupported() && !ImmersivePortalsCompat.isLoaded()) {
glNamedBufferData(boneBuffer, boneBlock.getSize(), GL_DYNAMIC_DRAW);
} else {
glBindBuffer(GL_UNIFORM_BUFFER, boneBuffer);
diff --git a/common/src/main/java/foundry/veil/impl/client/render/dynamicbuffer/DynamicBufferManager.java b/common/src/main/java/foundry/veil/impl/client/render/dynamicbuffer/DynamicBufferManager.java
index 071f3899c..7104f4111 100644
--- a/common/src/main/java/foundry/veil/impl/client/render/dynamicbuffer/DynamicBufferManager.java
+++ b/common/src/main/java/foundry/veil/impl/client/render/dynamicbuffer/DynamicBufferManager.java
@@ -10,6 +10,7 @@
import foundry.veil.api.client.render.dynamicbuffer.DynamicBuffersChange;
import foundry.veil.api.client.render.framebuffer.AdvancedFbo;
import foundry.veil.api.client.render.framebuffer.FramebufferManager;
+import foundry.veil.api.compat.ImmersivePortalsCompat;
import foundry.veil.ext.RenderTargetExtension;
import foundry.veil.ext.ShaderInstanceExtension;
import foundry.veil.mixin.dynamicbuffer.accessor.DynamicBufferGameRendererAccessor;
@@ -30,7 +31,7 @@
import static org.lwjgl.opengl.GL11C.*;
import static org.lwjgl.opengl.GL12C.*;
import static org.lwjgl.opengl.GL14C.GL_TEXTURE_LOD_BIAS;
-import static org.lwjgl.opengl.GL30C.GL_COLOR_ATTACHMENT1;
+import static org.lwjgl.opengl.GL30C.*;
@ApiStatus.Internal
public class DynamicBufferManager implements NativeResource {
@@ -162,7 +163,7 @@ public boolean isEnabled() {
}
public void setEnabled(boolean enabled) {
- if (!Veil.IRIS) {
+ if (!Veil.IRIS && (!ImmersivePortalsCompat.isLoaded() || !ImmersivePortalsCompat.INSTANCE.renderingThroughPortal())) {
this.enabled = enabled;
}
}
@@ -331,6 +332,7 @@ public void endFrame() {
if (!shaderIterator.hasNext()) {
Veil.LOGGER.info("Finished uploading vanilla shaders");
+ this.swapShaders.clear();
}
}
diff --git a/common/src/main/java/foundry/veil/impl/client/render/dynamicbuffer/VanillaShaderCompiler.java b/common/src/main/java/foundry/veil/impl/client/render/dynamicbuffer/VanillaShaderCompiler.java
index 9cec7db4e..e2a286a98 100644
--- a/common/src/main/java/foundry/veil/impl/client/render/dynamicbuffer/VanillaShaderCompiler.java
+++ b/common/src/main/java/foundry/veil/impl/client/render/dynamicbuffer/VanillaShaderCompiler.java
@@ -5,6 +5,7 @@
import com.mojang.blaze3d.vertex.VertexFormat;
import foundry.veil.Veil;
import foundry.veil.api.client.render.VeilRenderSystem;
+import foundry.veil.api.compat.ImmersivePortalsCompat;
import foundry.veil.ext.ShaderInstanceExtension;
import foundry.veil.impl.ThreadTaskScheduler;
import foundry.veil.impl.client.render.shader.processor.VanillaShaderProcessor;
@@ -111,6 +112,10 @@ public CompletableFuture> reload(Collection shaders) {
int activeBuffers = VeilRenderSystem.renderer().getDynamicBufferManger().getActiveBuffers();
GLCapabilities capabilities = GL.getCapabilities();
+ if (ImmersivePortalsCompat.isLoaded()) {
+ ImmersivePortalsCompat.INSTANCE.init();
+ }
+
Stopwatch stopwatch = Stopwatch.createStarted();
ThreadTaskScheduler scheduler = new ThreadTaskScheduler("VeilVanillaShaderCompile", Math.max(1, Runtime.getRuntime().availableProcessors() / 6), () -> {
for (String lastFrameShader : LAST_FRAME_SHADERS) {
diff --git a/common/src/main/java/foundry/veil/impl/client/render/pipeline/VeilFirstPersonRenderer.java b/common/src/main/java/foundry/veil/impl/client/render/pipeline/VeilFirstPersonRenderer.java
index 862938b7a..ee3135e32 100644
--- a/common/src/main/java/foundry/veil/impl/client/render/pipeline/VeilFirstPersonRenderer.java
+++ b/common/src/main/java/foundry/veil/impl/client/render/pipeline/VeilFirstPersonRenderer.java
@@ -9,6 +9,7 @@
import foundry.veil.api.client.render.framebuffer.VeilFramebuffers;
import foundry.veil.api.client.render.post.PostPipeline;
import foundry.veil.api.client.render.post.PostProcessingManager;
+import foundry.veil.api.compat.ImmersivePortalsCompat;
import foundry.veil.ext.RenderTargetExtension;
import foundry.veil.impl.client.render.dynamicbuffer.DynamicBufferManager;
import net.minecraft.client.Minecraft;
@@ -39,7 +40,7 @@ public static void bind(int mask) {
free();
firstPerson = AdvancedFbo.withSize(w, h)
.addColorTextureWrapper(framebufferTexture)
- .setFormat(stencil ? FramebufferAttachmentDefinition.Format.DEPTH32F_STENCIL8 : FramebufferAttachmentDefinition.Format.DEPTH_COMPONENT)
+ .setFormat((stencil && !ImmersivePortalsCompat.isLoaded()) ? FramebufferAttachmentDefinition.Format.DEPTH32F_STENCIL8 : FramebufferAttachmentDefinition.Format.DEPTH_COMPONENT)
.setDepthTextureBuffer()
.setDebugLabel("Veil First Person")
.build(true);
diff --git a/common/src/main/java/foundry/veil/impl/client/render/shader/processor/VanillaShaderProcessor.java b/common/src/main/java/foundry/veil/impl/client/render/shader/processor/VanillaShaderProcessor.java
index 061e271d1..0d738de9c 100644
--- a/common/src/main/java/foundry/veil/impl/client/render/shader/processor/VanillaShaderProcessor.java
+++ b/common/src/main/java/foundry/veil/impl/client/render/shader/processor/VanillaShaderProcessor.java
@@ -7,7 +7,9 @@
import foundry.veil.api.client.render.shader.processor.ShaderImporter;
import foundry.veil.api.client.render.shader.processor.ShaderInjectProcessor;
import foundry.veil.api.client.render.shader.processor.ShaderPreProcessor;
+import foundry.veil.api.compat.ImmersivePortalsCompat;
import foundry.veil.impl.client.render.dynamicbuffer.DynamicBufferProcessor;
+import foundry.veil.impl.compat.ImmersivePortalsShaderPreProcessor;
import io.github.ocelot.glslprocessor.api.GlslParser;
import io.github.ocelot.glslprocessor.api.GlslSyntaxException;
import io.github.ocelot.glslprocessor.api.node.GlslTree;
@@ -34,6 +36,9 @@ public static void setup(ResourceProvider provider) {
ShaderProcessorList list = new ShaderProcessorList(provider);
list.addPreprocessor(new ShaderInjectProcessor(), false);
list.addPreprocessor(new DynamicBufferProcessor(), false);
+ if (ImmersivePortalsCompat.isLoaded()) {
+ list.addPreprocessor(new ImmersivePortalsShaderPreProcessor(), false);
+ }
VeilClient.clientPlatform().onRegisterShaderPreProcessors(provider, list);
PROCESSOR.set(list);
}
diff --git a/common/src/main/java/foundry/veil/impl/compat/ImmersivePortalsShaderPreProcessor.java b/common/src/main/java/foundry/veil/impl/compat/ImmersivePortalsShaderPreProcessor.java
new file mode 100644
index 000000000..3c9cec43e
--- /dev/null
+++ b/common/src/main/java/foundry/veil/impl/compat/ImmersivePortalsShaderPreProcessor.java
@@ -0,0 +1,51 @@
+package foundry.veil.impl.compat;
+
+import com.mojang.blaze3d.shaders.Program;
+import foundry.veil.api.client.render.shader.processor.ShaderPreProcessor;
+import foundry.veil.api.compat.ImmersivePortalsCompat;
+import io.github.ocelot.glslprocessor.api.GlslParser;
+import io.github.ocelot.glslprocessor.api.GlslSyntaxException;
+import io.github.ocelot.glslprocessor.api.node.GlslTree;
+import io.github.ocelot.glslprocessor.lib.anarres.cpp.LexerException;
+import org.jetbrains.annotations.ApiStatus;
+
+import java.io.IOException;
+
+import static org.lwjgl.opengl.GL20.GL_FRAGMENT_SHADER;
+import static org.lwjgl.opengl.GL20.GL_VERTEX_SHADER;
+
+@ApiStatus.Internal
+public class ImmersivePortalsShaderPreProcessor implements ShaderPreProcessor {
+
+ @Override
+ public void modify(Context ctx, GlslTree tree) throws IOException, GlslSyntaxException, LexerException {
+ ImmersivePortalsCompat compat = ImmersivePortalsCompat.INSTANCE;
+ if (ctx.name() == null || compat == null) return;
+
+ Program.Type type;
+ if (ctx.type() == GL_VERTEX_SHADER) {
+ type = Program.Type.VERTEX;
+ } else if (ctx.type() == GL_FRAGMENT_SHADER) {
+ type = Program.Type.FRAGMENT;
+ } else {
+ return;
+ }
+
+ String key = ctx.name().toString();
+ if (!compat.shouldAddUniform(key)) {
+ key = ctx.name().getPath();
+ if (!compat.shouldAddUniform(key)) {
+ key = key.substring(key.lastIndexOf('/') + 1, key.length() - 4);
+ if (!compat.shouldAddUniform(key)) {
+ return;
+ }
+ }
+ }
+
+ String transformed = compat.transform(type, key, tree.toSourceString());
+ GlslTree newShader = GlslParser.parse(transformed);
+
+ tree.getBody().clear();
+ tree.getBody().addAll(newShader.getBody());
+ }
+}
diff --git a/common/src/main/java/foundry/veil/mixin/screenshake/client/ScreenShakeGameRendererMixin.java b/common/src/main/java/foundry/veil/mixin/screenshake/client/ScreenShakeGameRendererMixin.java
new file mode 100644
index 000000000..4e6fa9f04
--- /dev/null
+++ b/common/src/main/java/foundry/veil/mixin/screenshake/client/ScreenShakeGameRendererMixin.java
@@ -0,0 +1,36 @@
+package foundry.veil.mixin.screenshake.client;
+
+import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
+import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
+import com.llamalad7.mixinextras.sugar.Local;
+import foundry.veil.api.client.render.VeilRenderSystem;
+import net.minecraft.client.Camera;
+import net.minecraft.client.renderer.GameRenderer;
+import net.minecraft.world.phys.Vec3;
+import org.joml.Quaternionf;
+import org.joml.Vector3f;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+
+@Mixin(GameRenderer.class)
+public class ScreenShakeGameRendererMixin {
+
+ @WrapOperation(method = "renderLevel", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Camera;getPosition()Lnet/minecraft/world/phys/Vec3;"))
+ private Vec3 veil$moveCameraPosition(Camera instance, Operation original, @Local(name = "f") float partialTick) {
+ Vector3f pos = VeilRenderSystem.renderer().getScreenShakeManager().getPosition(partialTick);
+ return original.call(instance).add(pos.x, pos.y, pos.z);
+ }
+
+ @WrapOperation(method = "renderLevel", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Camera;rotation()Lorg/joml/Quaternionf;"))
+ private Quaternionf veil$offsetCameraRotation(Camera instance, Operation original, @Local(name = "f") float partialTick) {
+ Vector3f offset = VeilRenderSystem.renderer().getScreenShakeManager().getPosition(partialTick).mul(0.1f);
+ return original.call(instance).rotateXYZ(offset.x, offset.y, offset.z, new Quaternionf());
+ }
+
+ @Inject(method = "tick", at = @At("HEAD"))
+ private void veil$tickScreenShake(CallbackInfo ci) {
+ VeilRenderSystem.renderer().getScreenShakeManager().tick();
+ }
+}
diff --git a/common/src/main/java/foundry/veil/mixin/screenshake/client/ScreenShakeLevelRendererMixin.java b/common/src/main/java/foundry/veil/mixin/screenshake/client/ScreenShakeLevelRendererMixin.java
new file mode 100644
index 000000000..1eccbca01
--- /dev/null
+++ b/common/src/main/java/foundry/veil/mixin/screenshake/client/ScreenShakeLevelRendererMixin.java
@@ -0,0 +1,22 @@
+package foundry.veil.mixin.screenshake.client;
+
+import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
+import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
+import com.llamalad7.mixinextras.sugar.Local;
+import foundry.veil.api.client.render.VeilRenderSystem;
+import net.minecraft.client.Camera;
+import net.minecraft.client.renderer.LevelRenderer;
+import net.minecraft.world.phys.Vec3;
+import org.joml.Vector3f;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.injection.At;
+
+@Mixin(LevelRenderer.class)
+public class ScreenShakeLevelRendererMixin {
+
+ @WrapOperation(method = "renderLevel", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Camera;getPosition()Lnet/minecraft/world/phys/Vec3;"))
+ private Vec3 veil$moveCameraPosition(Camera instance, Operation original, @Local(name = "f") float partialTick) {
+ Vector3f pos = VeilRenderSystem.renderer().getScreenShakeManager().getPosition(partialTick);
+ return original.call(instance).add(pos.x, pos.y, pos.z);
+ }
+}
diff --git a/common/src/main/resources/veil.screenshake.mixins.json b/common/src/main/resources/veil.screenshake.mixins.json
new file mode 100644
index 000000000..1a5de4376
--- /dev/null
+++ b/common/src/main/resources/veil.screenshake.mixins.json
@@ -0,0 +1,15 @@
+{
+ "required": true,
+ "minVersion": "0.8",
+ "package": "foundry.veil.mixin.screenshake",
+ "compatibilityLevel": "JAVA_21",
+ "plugin": "foundry.veil.VeilMixinPluginImpl",
+ "client": [
+ "client.ScreenShakeGameRendererMixin",
+ "client.ScreenShakeLevelRendererMixin"
+ ],
+ "injectors": {
+ "defaultRequire": 1
+ }
+}
+
\ No newline at end of file
diff --git a/fabric/build.gradle b/fabric/build.gradle
index 4ae3a71ef..4506eb287 100644
--- a/fabric/build.gradle
+++ b/fabric/build.gradle
@@ -64,4 +64,7 @@ dependencies {
include(api("io.github.ocelot:glsl-processor:${glsl_processor_version}"))
modCompileOnly "foundry.imguimc:imguimc-fabric-${minecraft_version}:$imguimc_version"
+
+ // Immersive portals compat
+ modCompileOnly "maven.modrinth:immersiveportals:v${immptl_version}-mc${minecraft_version}"
}
diff --git a/fabric/src/main/java/foundry/veil/fabric/VeilFabricClient.java b/fabric/src/main/java/foundry/veil/fabric/VeilFabricClient.java
index 6c8e95b5b..8cb0ee065 100644
--- a/fabric/src/main/java/foundry/veil/fabric/VeilFabricClient.java
+++ b/fabric/src/main/java/foundry/veil/fabric/VeilFabricClient.java
@@ -1,11 +1,16 @@
package foundry.veil.fabric;
import com.mojang.brigadier.Command;
+import com.mojang.brigadier.arguments.FloatArgumentType;
+import com.mojang.brigadier.arguments.IntegerArgumentType;
+import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import foundry.veil.Veil;
import foundry.veil.VeilClient;
import foundry.veil.api.client.render.VeilRenderSystem;
+import foundry.veil.api.client.render.VeilRenderer;
import foundry.veil.api.client.render.dynamicbuffer.DynamicBufferType;
+import foundry.veil.api.client.util.Easing;
import foundry.veil.api.quasar.data.QuasarParticles;
import foundry.veil.api.quasar.particle.ParticleEmitter;
import foundry.veil.api.quasar.particle.ParticleSystemManager;
@@ -15,6 +20,8 @@
import foundry.veil.impl.client.imgui.VeilImGuiCompat;
import foundry.veil.impl.client.render.shader.VeilVanillaShaders;
import foundry.veil.impl.network.VeilClientServerFlags;
+import foundry.veil.api.screenshake.type.GlobalScreenShake;
+import foundry.veil.api.screenshake.type.LocalScreenShake;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager;
import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback;
@@ -106,6 +113,38 @@ public void onInitializeClient() {
}))
));
dispatcher.register(debugBuilder);
+
+ LiteralArgumentBuilder shakeBuilder = LiteralArgumentBuilder.literal("shake");
+ shakeBuilder
+ .then(ClientCommandManager.literal("local")
+ .then(ClientCommandManager.argument("expression", StringArgumentType.string()).then(ClientCommandManager.argument("position", Vec3Argument.vec3()).then(ClientCommandManager.argument("length", IntegerArgumentType.integer(0)).then(ClientCommandManager.argument("radius", FloatArgumentType.floatArg(0))
+ .executes(ctx -> {
+ try {
+ VeilRenderer renderer = VeilRenderSystem.renderer();
+ renderer.getScreenShakeManager().addScreenShake(new LocalScreenShake(StringArgumentType.getString(ctx, "expression"), ctx.getArgument("position", WorldCoordinates.class).getPosition(ctx.getSource().getEntity().createCommandSourceStack()), IntegerArgumentType.getInteger(ctx, "length"), FloatArgumentType.getFloat(ctx, "radius"), Easing.LINEAR));
+
+ return Command.SINGLE_SUCCESS;
+ } catch (Exception e) {
+ return 0;
+ }
+ })
+ ))))
+ )
+ .then(ClientCommandManager.literal("global")
+ .then(ClientCommandManager.argument("expression", StringArgumentType.string()).then(ClientCommandManager.argument("length", IntegerArgumentType.integer(0))
+ .executes(ctx -> {
+ try {
+ VeilRenderer renderer = VeilRenderSystem.renderer();
+ renderer.getScreenShakeManager().addScreenShake(new GlobalScreenShake(StringArgumentType.getString(ctx, "expression"), ctx.getArgument("length", Integer.class)));
+
+ return Command.SINGLE_SUCCESS;
+ } catch (Exception e) {
+ return 0;
+ }
+ })
+ ))
+ );
+ dispatcher.register(shakeBuilder);
}
});
}
diff --git a/fabric/src/main/java/foundry/veil/fabric/compat/immptl/VeilFabricImmersivePortalsCompat.java b/fabric/src/main/java/foundry/veil/fabric/compat/immptl/VeilFabricImmersivePortalsCompat.java
new file mode 100644
index 000000000..64615f81a
--- /dev/null
+++ b/fabric/src/main/java/foundry/veil/fabric/compat/immptl/VeilFabricImmersivePortalsCompat.java
@@ -0,0 +1,35 @@
+package foundry.veil.fabric.compat.immptl;
+
+import com.mojang.blaze3d.shaders.Program;
+import foundry.veil.api.compat.ImmersivePortalsCompat;
+import net.minecraft.client.Minecraft;
+import qouteall.imm_ptl.core.render.ShaderCodeTransformation;
+
+public class VeilFabricImmersivePortalsCompat implements ImmersivePortalsCompat {
+ private static boolean hasInitialized = false;
+ public static boolean renderingPortal = false;
+
+ @Override
+ public void init() {
+ if (!Minecraft.getInstance().getResourceManager().getNamespaces().isEmpty() && !hasInitialized) {
+ // prevent initializing more than once
+ hasInitialized = true;
+ ShaderCodeTransformation.init();
+ }
+ }
+
+ @Override
+ public String transform(Program.Type type, String shaderId, String inputCode) {
+ return ShaderCodeTransformation.transform(type, shaderId, inputCode);
+ }
+
+ @Override
+ public boolean shouldAddUniform(String shaderName) {
+ return ShaderCodeTransformation.shouldAddUniform(shaderName);
+ }
+
+ @Override
+ public boolean renderingThroughPortal() {
+ return renderingPortal;
+ }
+}
diff --git a/fabric/src/main/java/foundry/veil/fabric/mixin/compat/immersive_portals/MyGameRendererMixin.java b/fabric/src/main/java/foundry/veil/fabric/mixin/compat/immersive_portals/MyGameRendererMixin.java
new file mode 100644
index 000000000..5cfb21201
--- /dev/null
+++ b/fabric/src/main/java/foundry/veil/fabric/mixin/compat/immersive_portals/MyGameRendererMixin.java
@@ -0,0 +1,42 @@
+package foundry.veil.fabric.mixin.compat.immersive_portals;
+
+import com.llamalad7.mixinextras.sugar.Local;
+import foundry.veil.api.client.render.VeilRenderSystem;
+import foundry.veil.fabric.compat.immptl.VeilFabricImmersivePortalsCompat;
+import net.minecraft.client.Camera;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.multiplayer.ClientLevel;
+import net.minecraft.world.phys.Vec3;
+import org.joml.Matrix4f;
+import org.joml.Quaternionf;
+import org.spongepowered.asm.mixin.Final;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+import qouteall.imm_ptl.core.render.MyGameRenderer;
+
+import java.util.function.Consumer;
+
+@Mixin(MyGameRenderer.class)
+public class MyGameRendererMixin {
+
+ @Shadow
+ @Final
+ public static Minecraft client;
+
+ @Inject(method = "switchAndRenderTheWorld", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/systems/RenderSystem;applyModelViewMatrix()V", ordinal = 0))
+ private static void veil$prePortalRender(ClientLevel newWorld, Vec3 thisTickCameraPos, Vec3 lastTickCameraPos, Consumer invokeWrapper, int renderDistance, boolean doRenderHand, CallbackInfo ci) {
+ VeilFabricImmersivePortalsCompat.renderingPortal = true;
+ }
+
+ @Inject(method = "switchAndRenderTheWorld", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/systems/RenderSystem;applyModelViewMatrix()V", ordinal = 1))
+ private static void veil$postPortalRender(ClientLevel newWorld, Vec3 thisTickCameraPos, Vec3 lastTickCameraPos, Consumer invokeWrapper, int renderDistance, boolean doRenderHand, CallbackInfo ci, @Local(name = "oldProjectionMatrix") Matrix4f oldProjectionMatrix, @Local(name = "oldCamera") Camera oldCamera) {
+ VeilFabricImmersivePortalsCompat.renderingPortal = false;
+ Quaternionf quaternionf = oldCamera.rotation().conjugate(new Quaternionf());
+ Matrix4f matrix4f2 = (new Matrix4f()).rotation(quaternionf);
+ VeilRenderSystem.renderer().getCameraMatrices().update(oldProjectionMatrix, matrix4f2, oldCamera.getPosition().x(), oldCamera.getPosition().y(), oldCamera.getPosition().z());
+ }
+
+}
diff --git a/fabric/src/main/resources/META-INF/services/foundry.veil.api.compat.ImmersivePortalsCompat b/fabric/src/main/resources/META-INF/services/foundry.veil.api.compat.ImmersivePortalsCompat
new file mode 100644
index 000000000..9500c3c59
--- /dev/null
+++ b/fabric/src/main/resources/META-INF/services/foundry.veil.api.compat.ImmersivePortalsCompat
@@ -0,0 +1 @@
+foundry.veil.fabric.compat.immptl.VeilFabricImmersivePortalsCompat
\ No newline at end of file
diff --git a/fabric/src/main/resources/fabric.mod.json b/fabric/src/main/resources/fabric.mod.json
index 17c9fb22d..199de3573 100644
--- a/fabric/src/main/resources/fabric.mod.json
+++ b/fabric/src/main/resources/fabric.mod.json
@@ -52,6 +52,7 @@
"${mod_id}.rendertype.mixins.json",
"${mod_id}.resource.mixins.json",
"${mod_id}.scheduler.mixins.json",
+ "${mod_id}.screenshake.mixins.json",
"${mod_id}.shader.mixins.json",
"${mod_id}.shader_recompile.mixins.json",
"${mod_id}.fabric.mixins.json"
diff --git a/fabric/src/main/resources/veil.fabric.mixins.json b/fabric/src/main/resources/veil.fabric.mixins.json
index 01693d86b..e9bff5ba4 100644
--- a/fabric/src/main/resources/veil.fabric.mixins.json
+++ b/fabric/src/main/resources/veil.fabric.mixins.json
@@ -45,7 +45,8 @@
"compat.sodium.ShaderChunkRendererMixin",
"compat.sodium.ShaderLoaderMixin",
"compat.sodium.ShaderParserMixin",
- "compat.sodium.SodiumWorldRendererAccessor"
+ "compat.sodium.SodiumWorldRendererAccessor",
+ "compat.immersive_portals.MyGameRendererMixin"
],
"injectors": {
"defaultRequire": 1
diff --git a/gradle.properties b/gradle.properties
index 24e95d455..f210ab302 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -25,6 +25,7 @@ imguimc_version=2.0.0
lwjgl_version=3.3.3
# There is no 0.8.12-alpha.3 for NeoForge, so just use 0.8.12-alpha.2
sodium_version=0.8.12-alpha.2+mc1.21.1
+immptl_version=6.0.6
# Fabric
## See https://fabricmc.net/develop for new versions
diff --git a/neoforge/build.gradle b/neoforge/build.gradle
index 4570cbe79..c80b3af42 100644
--- a/neoforge/build.gradle
+++ b/neoforge/build.gradle
@@ -67,4 +67,7 @@ dependencies {
// Allows sodium compat to compile properly
compileOnly "org.sinytra.forgified-fabric-api:forgified-fabric-api:0.104.0+2.0.19+1.21.1"
+
+ // Immersive portals compat
+ compileOnly "maven.modrinth:immersive-portals-neoforge:${immptl_version}"
}
diff --git a/neoforge/src/main/java/foundry/veil/forge/VeilForgeClientEvents.java b/neoforge/src/main/java/foundry/veil/forge/VeilForgeClientEvents.java
index 93779c8cf..5e449b585 100644
--- a/neoforge/src/main/java/foundry/veil/forge/VeilForgeClientEvents.java
+++ b/neoforge/src/main/java/foundry/veil/forge/VeilForgeClientEvents.java
@@ -2,11 +2,15 @@
import com.mojang.brigadier.Command;
import com.mojang.brigadier.CommandDispatcher;
+import com.mojang.brigadier.arguments.FloatArgumentType;
+import com.mojang.brigadier.arguments.IntegerArgumentType;
+import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import foundry.veil.Veil;
import foundry.veil.api.client.render.VeilRenderSystem;
import foundry.veil.api.client.render.VeilRenderer;
import foundry.veil.api.client.render.dynamicbuffer.DynamicBufferType;
+import foundry.veil.api.client.util.Easing;
import foundry.veil.api.quasar.data.QuasarParticles;
import foundry.veil.api.quasar.particle.ParticleEmitter;
import foundry.veil.api.quasar.particle.ParticleSystemManager;
@@ -15,6 +19,8 @@
import foundry.veil.impl.client.VeilClientSchedulerImpl;
import foundry.veil.impl.client.imgui.VeilImGuiCompat;
import foundry.veil.impl.network.VeilClientServerFlags;
+import foundry.veil.api.screenshake.type.GlobalScreenShake;
+import foundry.veil.api.screenshake.type.LocalScreenShake;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
import net.minecraft.commands.arguments.ResourceLocationArgument;
@@ -118,6 +124,37 @@ public static void registerClientCommands(RegisterClientCommandsEvent event) {
}))
));
dispatcher.register(debugBuilder);
+
+ LiteralArgumentBuilder shakeBuilder = Commands.literal("shake");
+ shakeBuilder
+ .then(Commands.literal("local")
+ .then(Commands.argument("expression", StringArgumentType.string()).then(Commands.argument("position", Vec3Argument.vec3()).then(Commands.argument("length", IntegerArgumentType.integer(0)).then(Commands.argument("radius", FloatArgumentType.floatArg(0))
+ .executes(ctx -> {
+ try {
+ VeilRenderer renderer = VeilRenderSystem.renderer();
+ renderer.getScreenShakeManager().addScreenShake(new LocalScreenShake(StringArgumentType.getString(ctx, "expression"), Vec3Argument.getVec3(ctx, "position"), IntegerArgumentType.getInteger(ctx, "length"), FloatArgumentType.getFloat(ctx, "radius"), Easing.LINEAR));
+
+ return Command.SINGLE_SUCCESS;
+ } catch (Exception e) {
+ return 0;
+ }
+ })
+ ))))
+ )
+ .then(Commands.literal("global")
+ .then(Commands.argument("expression", StringArgumentType.string()).then(Commands.argument("length", IntegerArgumentType.integer(0))
+ .executes(ctx -> {
+ try {
+ VeilRenderer renderer = VeilRenderSystem.renderer();
+ renderer.getScreenShakeManager().addScreenShake(new GlobalScreenShake(StringArgumentType.getString(ctx, "expression"), ctx.getArgument("length", Integer.class)));
+
+ return Command.SINGLE_SUCCESS;
+ } catch (Exception e) {
+ return 0;
+ }
+ })))
+ );
+ dispatcher.register(shakeBuilder);
}
}
diff --git a/neoforge/src/main/java/foundry/veil/forge/compat/immptl/VeilForgeImmersivePortalsCompat.java b/neoforge/src/main/java/foundry/veil/forge/compat/immptl/VeilForgeImmersivePortalsCompat.java
new file mode 100644
index 000000000..e02c7b127
--- /dev/null
+++ b/neoforge/src/main/java/foundry/veil/forge/compat/immptl/VeilForgeImmersivePortalsCompat.java
@@ -0,0 +1,35 @@
+package foundry.veil.forge.compat.immptl;
+
+import com.mojang.blaze3d.shaders.Program;
+import foundry.veil.api.compat.ImmersivePortalsCompat;
+import net.minecraft.client.Minecraft;
+import qouteall.imm_ptl.core.render.ShaderCodeTransformation;
+
+public class VeilForgeImmersivePortalsCompat implements ImmersivePortalsCompat {
+ private static boolean hasInitialized = false;
+ public static boolean renderingPortal = false;
+
+ @Override
+ public void init() {
+ if (!Minecraft.getInstance().getResourceManager().getNamespaces().isEmpty() && !hasInitialized) {
+ // prevent initializing more than once
+ hasInitialized = true;
+ ShaderCodeTransformation.init();
+ }
+ }
+
+ @Override
+ public String transform(Program.Type type, String shaderId, String inputCode) {
+ return ShaderCodeTransformation.transform(type, shaderId, inputCode);
+ }
+
+ @Override
+ public boolean shouldAddUniform(String shaderName) {
+ return ShaderCodeTransformation.shouldAddUniform(shaderName);
+ }
+
+ @Override
+ public boolean renderingThroughPortal() {
+ return renderingPortal;
+ }
+}
diff --git a/neoforge/src/main/java/foundry/veil/forge/mixin/compat/imm_ptl/MyGameRendererMixin.java b/neoforge/src/main/java/foundry/veil/forge/mixin/compat/imm_ptl/MyGameRendererMixin.java
new file mode 100644
index 000000000..7a5d99b6c
--- /dev/null
+++ b/neoforge/src/main/java/foundry/veil/forge/mixin/compat/imm_ptl/MyGameRendererMixin.java
@@ -0,0 +1,42 @@
+package foundry.veil.forge.mixin.compat.imm_ptl;
+
+import com.llamalad7.mixinextras.sugar.Local;
+import foundry.veil.api.client.render.VeilRenderSystem;
+import foundry.veil.forge.compat.immptl.VeilForgeImmersivePortalsCompat;
+import net.minecraft.client.Camera;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.multiplayer.ClientLevel;
+import net.minecraft.world.phys.Vec3;
+import org.joml.Matrix4f;
+import org.joml.Quaternionf;
+import org.spongepowered.asm.mixin.Final;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+import qouteall.imm_ptl.core.render.MyGameRenderer;
+
+import java.util.function.Consumer;
+
+@Mixin(MyGameRenderer.class)
+public class MyGameRendererMixin {
+
+ @Shadow
+ @Final
+ public static Minecraft client;
+
+ @Inject(method = "switchAndRenderTheWorld", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/systems/RenderSystem;applyModelViewMatrix()V", ordinal = 0))
+ private static void veil$prePortalRender(ClientLevel newWorld, Vec3 thisTickCameraPos, Vec3 lastTickCameraPos, Consumer invokeWrapper, int renderDistance, boolean doRenderHand, CallbackInfo ci) {
+ VeilForgeImmersivePortalsCompat.renderingPortal = true;
+ }
+
+ @Inject(method = "switchAndRenderTheWorld", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/systems/RenderSystem;applyModelViewMatrix()V", ordinal = 1))
+ private static void veil$postPortalRender(ClientLevel newWorld, Vec3 thisTickCameraPos, Vec3 lastTickCameraPos, Consumer invokeWrapper, int renderDistance, boolean doRenderHand, CallbackInfo ci, @Local(name = "oldProjectionMatrix") Matrix4f oldProjectionMatrix, @Local(name = "oldCamera") Camera oldCamera) {
+ VeilForgeImmersivePortalsCompat.renderingPortal = false;
+ Quaternionf quaternionf = oldCamera.rotation().conjugate(new Quaternionf());
+ Matrix4f matrix4f2 = (new Matrix4f()).rotation(quaternionf);
+ VeilRenderSystem.renderer().getCameraMatrices().update(oldProjectionMatrix, matrix4f2, oldCamera.getPosition().x(), oldCamera.getPosition().y(), oldCamera.getPosition().z());
+ }
+
+}
diff --git a/neoforge/src/main/resources/META-INF/neoforge.mods.toml b/neoforge/src/main/resources/META-INF/neoforge.mods.toml
index 430bab293..f0d2cad01 100644
--- a/neoforge/src/main/resources/META-INF/neoforge.mods.toml
+++ b/neoforge/src/main/resources/META-INF/neoforge.mods.toml
@@ -44,6 +44,8 @@ description='''${description}'''
config = "${mod_id}.resource.mixins.json"
[[mixins]]
config = "${mod_id}.scheduler.mixins.json"
+[[mixins]]
+ config = "${mod_id}.screenshake.mixins.json"
[[mixins]]
config = "${mod_id}.shader.mixins.json"
[[mixins]]
diff --git a/neoforge/src/main/resources/META-INF/services/foundry.veil.api.compat.ImmersivePortalsCompat b/neoforge/src/main/resources/META-INF/services/foundry.veil.api.compat.ImmersivePortalsCompat
new file mode 100644
index 000000000..64e9badb4
--- /dev/null
+++ b/neoforge/src/main/resources/META-INF/services/foundry.veil.api.compat.ImmersivePortalsCompat
@@ -0,0 +1 @@
+foundry.veil.forge.compat.immptl.VeilForgeImmersivePortalsCompat
\ No newline at end of file
diff --git a/neoforge/src/main/resources/veil.neoforge.mixins.json b/neoforge/src/main/resources/veil.neoforge.mixins.json
index 70d47e599..9ed5a16bf 100644
--- a/neoforge/src/main/resources/veil.neoforge.mixins.json
+++ b/neoforge/src/main/resources/veil.neoforge.mixins.json
@@ -1,48 +1,49 @@
{
- "required": true,
- "minVersion": "0.8",
- "package": "foundry.veil.forge.mixin",
- "compatibilityLevel": "JAVA_21",
- "plugin": "foundry.veil.VeilMixinPluginImpl",
- "mixins": [
- "DeferredRegisterMixin",
- "RegistriesMixin",
- "resources.PathPackResourcesMixin"
- ],
- "client": [
- "client.MinecraftMixin",
- "client.RenderBuffersMixin",
- "client.RenderTypeMixin",
- "client.command.ClientCommandSourceStackMixin",
- "client.debug.vanilla.DebugLevelRendererMixin",
- "client.dynamicbuffer.LevelRendererMixin",
- "client.perspective.iris.PipelineManagerMixin",
- "client.perspective.sodium.OcclusionCullerMixin",
- "client.perspective.sodium.RenderRegionMixin",
- "client.perspective.sodium.RenderSectionManagerAccessor",
- "client.perspective.sodium.RenderSectionManagerMixin",
- "client.perspective.sodium.RenderSectionMixin",
- "client.perspective.sodium.SodiumWorldRendererMixin",
- "client.perspective.vanilla.LevelRendererMixin",
- "compat.iris.IrisRenderingPipelineAccessor",
- "compat.iris.IrisRenderingPipelineMixin",
- "compat.iris.IrisRenderTargetMixin",
- "compat.iris.ShaderWrapperMixin",
- "compat.sodium.BlockRendererMixin",
- "compat.sodium.ChunkMeshFormatsMixin",
- "compat.sodium.ChunkShaderOptionsMixin",
- "compat.sodium.ChunkVertexConsumerMixin",
- "compat.sodium.ChunkVertexEncoderVertexMixin",
- "compat.sodium.DefaultFluidRendererMixin",
- "compat.sodium.DefaultShaderInterfaceMixin",
- "compat.sodium.RenderSectionManagerAccessor",
- "compat.sodium.ShaderChunkRendererMixin",
- "compat.sodium.ShaderLoaderMixin",
- "compat.sodium.ShaderParserMixin",
- "compat.sodium.SodiumWorldRendererAccessor",
- "compat.sodium.SortedRenderListsAccessor"
- ],
- "injectors": {
- "defaultRequire": 1
+ "required": true,
+ "minVersion": "0.8",
+ "package": "foundry.veil.forge.mixin",
+ "compatibilityLevel": "JAVA_21",
+ "plugin": "foundry.veil.VeilMixinPluginImpl",
+ "mixins": [
+ "DeferredRegisterMixin",
+ "RegistriesMixin",
+ "resources.PathPackResourcesMixin"
+ ],
+ "client": [
+ "client.MinecraftMixin",
+ "client.RenderBuffersMixin",
+ "client.RenderTypeMixin",
+ "client.command.ClientCommandSourceStackMixin",
+ "client.debug.vanilla.DebugLevelRendererMixin",
+ "client.dynamicbuffer.LevelRendererMixin",
+ "client.perspective.iris.PipelineManagerMixin",
+ "client.perspective.sodium.OcclusionCullerMixin",
+ "client.perspective.sodium.RenderRegionMixin",
+ "client.perspective.sodium.RenderSectionManagerAccessor",
+ "client.perspective.sodium.RenderSectionManagerMixin",
+ "client.perspective.sodium.RenderSectionMixin",
+ "client.perspective.sodium.SodiumWorldRendererMixin",
+ "client.perspective.vanilla.LevelRendererMixin",
+ "compat.iris.IrisRenderingPipelineAccessor",
+ "compat.iris.IrisRenderingPipelineMixin",
+ "compat.iris.IrisRenderTargetMixin",
+ "compat.iris.ShaderWrapperMixin",
+ "compat.sodium.BlockRendererMixin",
+ "compat.sodium.ChunkMeshFormatsMixin",
+ "compat.sodium.ChunkShaderOptionsMixin",
+ "compat.sodium.ChunkVertexConsumerMixin",
+ "compat.sodium.ChunkVertexEncoderVertexMixin",
+ "compat.sodium.DefaultFluidRendererMixin",
+ "compat.sodium.DefaultShaderInterfaceMixin",
+ "compat.sodium.RenderSectionManagerAccessor",
+ "compat.sodium.ShaderChunkRendererMixin",
+ "compat.sodium.ShaderLoaderMixin",
+ "compat.sodium.ShaderParserMixin",
+ "compat.sodium.SodiumWorldRendererAccessor",
+ "compat.sodium.SortedRenderListsAccessor",
+ "compat.imm_ptl.MyGameRendererMixin"
+ ],
+ "injectors": {
+ "defaultRequire": 1
}
}
diff --git a/wiki/Home.md b/wiki/Home.md
index 3e766096e..ec0c139fb 100644
--- a/wiki/Home.md
+++ b/wiki/Home.md
@@ -111,6 +111,7 @@ dependencies {
- [Post-Processing](PostProcessing)
- [Quasar (Particles)](Quasar)
- [Render Type Stages](RenderTypeStage)
+- [Screenshake](Screenshake)
- [Custom Shaders](Shader)
- [Shader Injections](ShaderInject)
- [Better Vertex Buffers](VertexArray)
diff --git a/wiki/Screenshake.md b/wiki/Screenshake.md
new file mode 100644
index 000000000..6e9bad773
--- /dev/null
+++ b/wiki/Screenshake.md
@@ -0,0 +1,6 @@
+Veil comes with a built-in system to shake the screen. To access the manager, simply call `VeilRenderer#getScreenShakeManager`.
+
+Currently, there are two types of screen shake implemented: local and global. The `GlobalScreenShakeType` offsets the camera regardless of distance; the `LocalScreenShakeType` tapers its strength based on distance to the center.
+Both currently implemented types allow for a Molang expression to be used, allowing for more complex effects.
+
+To implement your own screen shake type, simply extend the abstract class `ScreenShakeType`.
\ No newline at end of file