Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/20.1/forge' into 21.0/neoforge
Browse files Browse the repository at this point in the history
  • Loading branch information
embeddedt committed Sep 29, 2024
2 parents c94d1c1 + 170a1c1 commit c04eb1f
Show file tree
Hide file tree
Showing 12 changed files with 161 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -61,5 +61,6 @@ public static class Option {
public static final ResourceLocation CPU_FRAMES_AHEAD = ResourceLocation.fromNamespaceAndPath(Embeddium.MODID, "cpu_render_ahead_limit");
public static final ResourceLocation TRANSLUCENT_FACE_SORTING = ResourceLocation.fromNamespaceAndPath(Embeddium.MODID, "translucent_face_sorting");
public static final ResourceLocation USE_QUAD_NORMALS_FOR_LIGHTING = ResourceLocation.fromNamespaceAndPath(Embeddium.MODID, "use_quad_normals_for_lighting");
public static final ResourceLocation RENDER_PASS_OPTIMIZATION = ResourceLocation.fromNamespaceAndPath(Embeddium.MODID, "render_pass_optimization");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,15 @@ public static OptionPage performance() {
.setFlags(OptionFlag.REQUIRES_RENDERER_UPDATE)
.build()
)
.add(OptionImpl.createBuilder(boolean.class, sodiumOpts)
.setId(StandardOptions.Option.RENDER_PASS_OPTIMIZATION)
.setName(Component.translatable("embeddium.options.use_render_pass_optimization.name"))
.setTooltip(Component.translatable("embeddium.options.use_render_pass_optimization.tooltip"))
.setControl(TickBoxControl::new)
.setImpact(OptionImpact.LOW)
.setBinding((opts, value) -> opts.performance.useRenderPassOptimization = value, opts -> opts.performance.useRenderPassOptimization)
.setFlags(OptionFlag.REQUIRES_RENDERER_RELOAD)
.build())
.add(OptionImpl.createBuilder(boolean.class, sodiumOpts)
.setId(StandardOptions.Option.NO_ERROR_CONTEXT)
.setName(Component.translatable("sodium.options.use_no_error_context.name"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ public static class PerformanceSettings {
public boolean useCompactVertexFormat = true;
@SerializedName("use_translucent_face_sorting_v2")
public boolean useTranslucentFaceSorting = true;
public boolean useRenderPassOptimization = true;
public boolean useNoErrorGLContext = true;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package org.embeddedt.embeddium.impl.mixin.core.model.quad;

import com.llamalad7.mixinextras.injector.ModifyReturnValue;
import com.llamalad7.mixinextras.sugar.Local;
import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.client.renderer.block.model.BlockElementFace;
import net.minecraft.client.renderer.block.model.FaceBakery;
import net.minecraft.client.renderer.texture.SpriteContents;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import org.embeddedt.embeddium.impl.model.quad.BakedQuadView;
import org.embeddedt.embeddium.impl.model.quad.properties.ModelQuadFlags;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;

@Mixin(FaceBakery.class)
public class BakedQuadFactoryMixin {
/**
* @author embeddedt
* @reason Check if quad's UVs are contained within the sprite's boundaries; if so, mark it as having a trusted sprite
* (meaning the particle sprite matches the encoded UVs)
*/
@ModifyReturnValue(method = "bakeQuad", at = @At("RETURN"))
private BakedQuad setMaterialClassification(BakedQuad quad, @Local(ordinal = 0, argsOnly = true) BlockElementFace face, @Local(ordinal = 0, argsOnly = true) TextureAtlasSprite sprite) {
if (sprite.getClass() == TextureAtlasSprite.class && sprite.contents().getClass() == SpriteContents.class) {
float[] uvs = face.uv().uvs;
float minUV = Float.MAX_VALUE, maxUV = Float.MIN_VALUE;

for (float uv : uvs) {
minUV = Math.min(minUV, uv);
maxUV = Math.max(maxUV, uv);
}

if (minUV >= 0 && maxUV <= 16) {
// Quad UVs do not extend outside texture boundary, we can trust the given sprite
BakedQuadView view = (BakedQuadView)quad;
view.setFlags(view.getFlags() | ModelQuadFlags.IS_TRUSTED_SPRITE);
}

}

return quad;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,8 @@ public int getForgeNormal(int idx) {
@Override
public int getFlags() {
int f = this.flags;
if (f == 0) {
this.flags = f = ModelQuadFlags.getQuadFlags(this, direction);
if ((f & ModelQuadFlags.IS_POPULATED) == 0) {
this.flags = f = (f | ModelQuadFlags.getQuadFlags(this, direction));
}
return f;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import net.minecraft.client.renderer.texture.SpriteContents;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.FastColor;
import org.embeddedt.embeddium.impl.render.chunk.sprite.SpriteTransparencyLevel;
import org.embeddedt.embeddium.impl.render.chunk.sprite.SpriteTransparencyLevelHolder;
import org.lwjgl.system.MemoryUtil;
import org.objectweb.asm.Opcodes;
import org.spongepowered.asm.mixin.Final;
Expand All @@ -18,10 +20,10 @@
import org.spongepowered.asm.mixin.injection.Redirect;

/**
* This Mixin is ported from Iris at <a href="https://github.com/IrisShaders/Iris/blob/41095ac23ea0add664afd1b85c414d1f1ed94066/src/main/java/net/coderbot/iris/mixin/bettermipmaps/MixinTextureAtlasSprite.java">MixinTextureAtlasSprite</a>.
* This Mixin is partially ported from Iris at <a href="https://github.com/IrisShaders/Iris/blob/41095ac23ea0add664afd1b85c414d1f1ed94066/src/main/java/net/coderbot/iris/mixin/bettermipmaps/MixinTextureAtlasSprite.java">MixinTextureAtlasSprite</a>.
*/
@Mixin(SpriteContents.class)
public class SpriteContentsMixin {
public class SpriteContentsMixin implements SpriteTransparencyLevelHolder {
@Mutable
@Shadow
@Final
Expand All @@ -31,6 +33,9 @@ public class SpriteContentsMixin {
@Final
private ResourceLocation name;

@Unique
private SpriteTransparencyLevel embeddium$transparencyLevel;

// While Fabric allows us to @Inject into the constructor here, that's just a specific detail of FabricMC's mixin
// fork. Upstream Mixin doesn't allow arbitrary @Inject usage in constructor. However, we can use @ModifyVariable
// just fine, in a way that hopefully doesn't conflict with other mods.
Expand All @@ -41,11 +46,9 @@ public class SpriteContentsMixin {
// cross-platform.
@Redirect(method = "<init>", at = @At(value = "FIELD", target = "Lnet/minecraft/client/renderer/texture/SpriteContents;originalImage:Lcom/mojang/blaze3d/platform/NativeImage;", opcode = Opcodes.PUTFIELD))
private void sodium$beforeGenerateMipLevels(SpriteContents instance, NativeImage nativeImage, ResourceLocation identifier) {
// Embeddium: Only fill in transparent colors if mipmaps are on and the texture name does not contain "leaves".
// Only fill in transparent colors if mipmaps are on and the texture name does not contain "leaves".
// We're injecting after the "name" field has been set, so this is safe even though we're in a constructor.
if (Minecraft.getInstance().options.mipmapLevels().get() > 0 && !this.name.getPath().contains("leaves")) {
sodium$fillInTransparentPixelColors(nativeImage);
}
embeddium$processTransparentImages(nativeImage, Minecraft.getInstance().options.mipmapLevels().get() > 0 && !this.name.getPath().contains("leaves"));

this.originalImage = nativeImage;
}
Expand All @@ -59,7 +62,7 @@ public class SpriteContentsMixin {
* black color does not leak over into sampling.
*/
@Unique
private static void sodium$fillInTransparentPixelColors(NativeImage nativeImage) {
private void embeddium$processTransparentImages(NativeImage nativeImage, boolean shouldRewriteColors) {
final long ppPixel = NativeImageHelper.getPointerRGBA(nativeImage);
final int pixelCount = nativeImage.getHeight() * nativeImage.getWidth();

Expand All @@ -71,27 +74,41 @@ public class SpriteContentsMixin {

float totalWeight = 0.0f;

SpriteTransparencyLevel level = SpriteTransparencyLevel.OPAQUE;

for (int pixelIndex = 0; pixelIndex < pixelCount; pixelIndex++) {
long pPixel = ppPixel + (pixelIndex * 4);

int color = MemoryUtil.memGetInt(pPixel);
int alpha = FastColor.ABGR32.alpha(color);

// Ignore all fully-transparent pixels for the purposes of computing an average color.
if (alpha != 0) {
float weight = (float) alpha;

// Make sure to convert to linear space so that we don't lose brightness.
r += ColorSRGB.srgbToLinear(FastColor.ABGR32.red(color)) * weight;
g += ColorSRGB.srgbToLinear(FastColor.ABGR32.green(color)) * weight;
b += ColorSRGB.srgbToLinear(FastColor.ABGR32.blue(color)) * weight;

totalWeight += weight;
if (alpha > 0) {
if(alpha < 255) {
level = level.chooseNextLevel(SpriteTransparencyLevel.TRANSLUCENT);
} else {
level = level.chooseNextLevel(SpriteTransparencyLevel.OPAQUE);
}

if (shouldRewriteColors) {
float weight = (float) alpha;

// Make sure to convert to linear space so that we don't lose brightness.
r += ColorSRGB.srgbToLinear(FastColor.ABGR32.red(color)) * weight;
g += ColorSRGB.srgbToLinear(FastColor.ABGR32.green(color)) * weight;
b += ColorSRGB.srgbToLinear(FastColor.ABGR32.blue(color)) * weight;

totalWeight += weight;
}
} else {
level = level.chooseNextLevel(SpriteTransparencyLevel.TRANSPARENT);
}
}

// Bail if none of the pixels are semi-transparent.
if (totalWeight == 0.0f) {
this.embeddium$transparencyLevel = level;

// Bail if none of the pixels are semi-transparent or we aren't supposed to rewrite colors.
if (!shouldRewriteColors || totalWeight == 0.0f) {
return;
}

Expand All @@ -115,4 +132,9 @@ public class SpriteContentsMixin {
}
}
}

@Override
public SpriteTransparencyLevel embeddium$getTransparencyLevel() {
return this.embeddium$transparencyLevel;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ public class ModelQuadFlags {
* the normals of each vertex.
*/
public static final int IS_VANILLA_SHADED = 0b1000;
/**
* Indicates that the particle sprite on this quad can be trusted to be the only sprite it shows.
*/
public static final int IS_TRUSTED_SPRITE = (1 << 4);
/**
* Indicates that the flags are populated for the quad.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.mojang.blaze3d.vertex.PoseStack;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import org.embeddedt.embeddium.api.render.chunk.BlockRenderContext;
import org.embeddedt.embeddium.impl.Embeddium;
import org.embeddedt.embeddium.impl.model.color.ColorProvider;
import org.embeddedt.embeddium.impl.model.color.ColorProviderRegistry;
import org.embeddedt.embeddium.impl.model.light.LightMode;
Expand All @@ -11,9 +12,13 @@
import org.embeddedt.embeddium.impl.model.light.data.QuadLightData;
import org.embeddedt.embeddium.impl.model.quad.BakedQuadView;
import org.embeddedt.embeddium.impl.model.quad.properties.ModelQuadFacing;
import org.embeddedt.embeddium.impl.model.quad.properties.ModelQuadFlags;
import org.embeddedt.embeddium.impl.model.quad.properties.ModelQuadOrientation;
import org.embeddedt.embeddium.impl.render.ShaderModBridge;
import org.embeddedt.embeddium.impl.render.chunk.compile.ChunkBuildBuffers;
import org.embeddedt.embeddium.impl.render.chunk.compile.buffers.ChunkModelBuilder;
import org.embeddedt.embeddium.impl.render.chunk.sprite.SpriteTransparencyLevel;
import org.embeddedt.embeddium.impl.render.chunk.sprite.SpriteTransparencyLevelHolder;
import org.embeddedt.embeddium.impl.render.chunk.terrain.material.DefaultMaterials;
import org.embeddedt.embeddium.impl.render.chunk.terrain.material.Material;
import org.embeddedt.embeddium.impl.render.chunk.vertex.format.ChunkVertexEncoder;
Expand Down Expand Up @@ -76,13 +81,16 @@ public class BlockRenderer {

private final ChunkColorWriter colorEncoder = ChunkColorWriter.get();

private final boolean useRenderPassOptimization;

public BlockRenderer(ColorProviderRegistry colorRegistry, LightPipelineProvider lighters) {
this.colorProviderRegistry = colorRegistry;
this.lighters = lighters;

this.occlusionCache = new BlockOcclusionCache();
this.useAmbientOcclusion = Minecraft.useAmbientOcclusion();
this.fabricModelRenderingHandler = FRAPIRenderHandler.INDIGO_PRESENT ? new IndigoBlockRenderContext(this.occlusionCache, lighters.getLightData()) : null;
this.useRenderPassOptimization = Embeddium.options().performance.useRenderPassOptimization && !ShaderModBridge.areShadersEnabled();
}

/**
Expand Down Expand Up @@ -136,7 +144,7 @@ public void renderModel(BlockRenderContext ctx, ChunkBuildBuffers buffers) {

if (!quads.isEmpty() && this.isFaceVisible(ctx, face)) {
this.useReorienting = true;
this.renderQuadList(ctx, material, lighter, colorizer, renderOffset, meshBuilder, quads, face);
this.renderQuadList(ctx, material, lighter, colorizer, renderOffset, buffers, meshBuilder, quads, face);
if (!this.useReorienting) {
// Reorienting was disabled on this side, make sure it's disabled for the null cullface too, in case
// a mod layers textures in different lists
Expand All @@ -149,7 +157,7 @@ public void renderModel(BlockRenderContext ctx, ChunkBuildBuffers buffers) {

if (!all.isEmpty()) {
this.useReorienting = canReorientNullCullface;
this.renderQuadList(ctx, material, lighter, colorizer, renderOffset, meshBuilder, all, null);
this.renderQuadList(ctx, material, lighter, colorizer, renderOffset, buffers, meshBuilder, all, null);
}
}

Expand Down Expand Up @@ -202,8 +210,28 @@ private static boolean checkQuadsHaveSameLightingConfig(List<BakedQuad> quads) {
return true;
}

private ChunkModelBuilder chooseOptimalBuilder(Material defaultMaterial, ChunkBuildBuffers buffers, ChunkModelBuilder defaultBuilder, BakedQuadView quad) {
if (defaultMaterial == DefaultMaterials.SOLID || !this.useRenderPassOptimization || (quad.getFlags() & ModelQuadFlags.IS_TRUSTED_SPRITE) == 0 || quad.getSprite() == null) {
// No improvement possible
return defaultBuilder;
}

SpriteTransparencyLevel level = SpriteTransparencyLevelHolder.getTransparencyLevel(quad.getSprite().contents());

if (level == SpriteTransparencyLevel.OPAQUE && defaultMaterial.pass.supportsFragmentDiscard()) {
// Can use solid with no visual difference
return buffers.get(DefaultMaterials.SOLID);
} else if (level == SpriteTransparencyLevel.TRANSPARENT && defaultMaterial == DefaultMaterials.TRANSLUCENT) {
// Can use cutout_mipped with no visual difference
return buffers.get(DefaultMaterials.CUTOUT_MIPPED);
} else {
// Have to use default
return defaultBuilder;
}
}

private void renderQuadList(BlockRenderContext ctx, Material material, LightPipeline lighter, ColorProvider<BlockState> colorizer, Vec3 offset,
ChunkModelBuilder builder, List<BakedQuad> quads, Direction cullFace) {
ChunkBuildBuffers buffers, ChunkModelBuilder defaultBuilder, List<BakedQuad> quads, Direction cullFace) {

if(!checkQuadsHaveSameLightingConfig(quads)) {
// Disable reorienting if quads use different light configurations, as otherwise layered quads
Expand All @@ -219,6 +247,8 @@ private void renderQuadList(BlockRenderContext ctx, Material material, LightPipe
final var lightData = this.getVertexLight(ctx, quad.hasAmbientOcclusion() ? lighter : this.lighters.getLighter(LightMode.FLAT), cullFace, quad);
final var vertexColors = this.getVertexColors(ctx, colorizer, quad);

ChunkModelBuilder builder = this.chooseOptimalBuilder(material, buffers, defaultBuilder, quad);

this.writeGeometry(ctx, builder, offset, material, quad, vertexColors, lightData);

TextureAtlasSprite sprite = quad.getSprite();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package org.embeddedt.embeddium.impl.render.chunk.sprite;

public enum SpriteTransparencyLevel {
OPAQUE,
TRANSPARENT,
TRANSLUCENT;

/**
* {@return whichever level has a higher ordinal, i.e. requires a better render pass}
*/
public SpriteTransparencyLevel chooseNextLevel(SpriteTransparencyLevel level) {
return level.ordinal() >= this.ordinal() ? level : this;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package org.embeddedt.embeddium.impl.render.chunk.sprite;

import net.minecraft.client.renderer.texture.SpriteContents;

public interface SpriteTransparencyLevelHolder {
SpriteTransparencyLevel embeddium$getTransparencyLevel();

static SpriteTransparencyLevel getTransparencyLevel(SpriteContents contents) {
return ((SpriteTransparencyLevelHolder)contents).embeddium$getTransparencyLevel();
}
}
4 changes: 3 additions & 1 deletion src/main/resources/assets/embeddium/lang/en_us.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,5 +70,7 @@
"embeddium.options.added_by_mod_string": "Added by mod: %s",
"embeddium.options.fullscreen.resolution.tooltip": "Controls the resolution of the game in fullscreen mode.",
"embeddium.options.use_quad_normals_for_lighting.name": "Use Accurate Quad Shading",
"embeddium.options.use_quad_normals_for_lighting.tooltip": "When enabled, Embeddium will apply shading to non-vanilla block faces based on the true direction they are facing, not their axis-aligned direction. This can improve lighting quality when the Forge experimental light pipeline is disabled (which is recommended for best performance).\n\nIt has no effect if the experimental light pipeline is enabled."
"embeddium.options.use_quad_normals_for_lighting.tooltip": "When enabled, Embeddium will apply shading to non-vanilla block faces based on the true direction they are facing, not their axis-aligned direction. This can improve lighting quality when the Forge experimental light pipeline is disabled (which is recommended for best performance).\n\nIt has no effect if the experimental light pipeline is enabled.",
"embeddium.options.use_render_pass_optimization.name": "Use Render Pass Optimization",
"embeddium.options.use_render_pass_optimization.tooltip": "When enabled, Embeddium will detect block model faces that are marked as transparent (or translucent) when the texture is actually opaque (or transparent) and automatically use a more optimal render pass for them.\n\nThis optimization has no effect when a shader pack is active."
}
Binary file modified src/main/resources/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit c04eb1f

Please sign in to comment.