nosingleplayer 1.1.6 : fond du menu titre enfin affiché
Trois bugs cumulés, tous vérifiés contre le jar client 26.3 : 1. blit() : l'overload 11 args réutilise w/h comme région source (u0 = u/tw, u1 = (u+w)/tw) -> on n'affichait que le coin haut-gauche de l'image (noir). On utilise l'overload 13 args avec une région source explicite + recadrage "cover" (pas de déformation). 2. Texture introuvable : en MC 26.3 le ResourceManager ne lit plus les packs depuis les jars du classpath (seulement des dossiers), donc les assets d'un mod ne sont plus exposés -> placeholder magenta. Vérifié : "Reloading ResourceManager: vanilla, vanilla" et namespaces=[minecraft, realms]. Le PNG est donc lu dans le jar du mod (getResourceAsStream) et uploadé en DynamicTexture, enregistrée dans le TextureManager. 3. Filtrage : sampler NEAREST par défaut (aliasing visible) -> sampler linéaire + clamp-to-edge. Asset : PNG RGBA 3840x2400.
This commit is contained in:
@@ -5,7 +5,7 @@ loader_version=0.20.0
|
|||||||
loom_version=1.19.1
|
loom_version=1.19.1
|
||||||
|
|
||||||
# Mod Properties
|
# Mod Properties
|
||||||
mod_version=1.1.5
|
mod_version=1.1.6
|
||||||
maven_group=fr.surviepote
|
maven_group=fr.surviepote
|
||||||
archives_base_name=nosingleplayer
|
archives_base_name=nosingleplayer
|
||||||
|
|
||||||
|
|||||||
+93
-5
@@ -1,17 +1,26 @@
|
|||||||
package fr.surviepote.nosingleplayer.mixin;
|
package fr.surviepote.nosingleplayer.mixin;
|
||||||
|
|
||||||
|
import com.mojang.blaze3d.platform.NativeImage;
|
||||||
|
import com.mojang.blaze3d.systems.RenderSystem;
|
||||||
import com.mojang.realmsclient.gui.screens.RealmsNotificationsScreen;
|
import com.mojang.realmsclient.gui.screens.RealmsNotificationsScreen;
|
||||||
|
import com.mojang.renderpearl.api.textures.FilterMode;
|
||||||
|
import fr.surviepote.nosingleplayer.NoSingleplayer;
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.client.gui.GuiGraphicsExtractor;
|
import net.minecraft.client.gui.GuiGraphicsExtractor;
|
||||||
import net.minecraft.client.gui.components.LogoRenderer;
|
import net.minecraft.client.gui.components.LogoRenderer;
|
||||||
import net.minecraft.client.gui.screens.TitleScreen;
|
import net.minecraft.client.gui.screens.TitleScreen;
|
||||||
import net.minecraft.client.renderer.RenderPipelines;
|
import net.minecraft.client.renderer.RenderPipelines;
|
||||||
|
import net.minecraft.client.renderer.texture.DynamicTexture;
|
||||||
import net.minecraft.resources.Identifier;
|
import net.minecraft.resources.Identifier;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.Unique;
|
import org.spongepowered.asm.mixin.Unique;
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
@Mixin(TitleScreen.class)
|
@Mixin(TitleScreen.class)
|
||||||
public abstract class TitleScreenRenderMixin {
|
public abstract class TitleScreenRenderMixin {
|
||||||
|
|
||||||
@@ -19,23 +28,102 @@ public abstract class TitleScreenRenderMixin {
|
|||||||
private static final Identifier SURVIEPOTE_BACKGROUND = Identifier.fromNamespaceAndPath(
|
private static final Identifier SURVIEPOTE_BACKGROUND = Identifier.fromNamespaceAndPath(
|
||||||
"nosingleplayer", "textures/gui/background.png");
|
"nosingleplayer", "textures/gui/background.png");
|
||||||
|
|
||||||
// Taille réelle de background.png (fond plein écran étiré).
|
// Le PNG est lu directement dans le jar du mod (voir surviepote$loadBackground).
|
||||||
@Unique
|
@Unique
|
||||||
private static final int BACKGROUND_TEX_W = 3840;
|
private static final String SURVIEPOTE_BACKGROUND_RESOURCE =
|
||||||
|
"/assets/nosingleplayer/textures/gui/background.png";
|
||||||
|
|
||||||
@Unique
|
@Unique
|
||||||
private static final int BACKGROUND_TEX_H = 2400;
|
private static DynamicTexture surviepote$background;
|
||||||
|
|
||||||
|
@Unique
|
||||||
|
private static int surviepote$backgroundWidth;
|
||||||
|
|
||||||
|
@Unique
|
||||||
|
private static int surviepote$backgroundHeight;
|
||||||
|
|
||||||
// Pas de panorama dynamique : fond statique custom à la place.
|
// Pas de panorama dynamique : fond statique custom à la place.
|
||||||
// Note : extractPanorama est déclarée dans Screen (héritée), on redirige donc
|
// Note : extractPanorama est déclarée dans Screen (héritée), on redirige donc
|
||||||
// l'APPEL dans TitleScreen plutôt que la méthode elle-même.
|
// l'APPEL dans TitleScreen plutôt que la méthode elle-même.
|
||||||
|
// Le fond couvre l'écran sans déformation : recadrage centré type "cover".
|
||||||
@Redirect(method = "extractRenderState",
|
@Redirect(method = "extractRenderState",
|
||||||
at = @At(value = "INVOKE",
|
at = @At(value = "INVOKE",
|
||||||
target = "Lnet/minecraft/client/gui/screens/TitleScreen;extractPanorama(Lnet/minecraft/client/gui/GuiGraphicsExtractor;F)V"))
|
target = "Lnet/minecraft/client/gui/screens/TitleScreen;extractPanorama(Lnet/minecraft/client/gui/GuiGraphicsExtractor;F)V"))
|
||||||
private void surviepote$customBackground(TitleScreen self, GuiGraphicsExtractor extractor, float partialTick) {
|
private void surviepote$customBackground(TitleScreen self, GuiGraphicsExtractor extractor, float partialTick) {
|
||||||
int w = Minecraft.getInstance().getWindow().getGuiScaledWidth();
|
int w = Minecraft.getInstance().getWindow().getGuiScaledWidth();
|
||||||
int h = Minecraft.getInstance().getWindow().getGuiScaledHeight();
|
int h = Minecraft.getInstance().getWindow().getGuiScaledHeight();
|
||||||
extractor.blit(RenderPipelines.GUI_TEXTURED, SURVIEPOTE_BACKGROUND, 0, 0, 0.0F, 0.0F,
|
|
||||||
w, h, BACKGROUND_TEX_W, BACKGROUND_TEX_H, -1);
|
surviepote$loadBackground();
|
||||||
|
if (surviepote$background == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
float screenAspect = (float) w / (float) h;
|
||||||
|
float texAspect = (float) surviepote$backgroundWidth / (float) surviepote$backgroundHeight;
|
||||||
|
|
||||||
|
int u, v, srcW, srcH;
|
||||||
|
if (screenAspect > texAspect) {
|
||||||
|
// Écran plus large que l'image : on rogne en haut/bas.
|
||||||
|
srcW = surviepote$backgroundWidth;
|
||||||
|
srcH = Math.round(surviepote$backgroundWidth / screenAspect);
|
||||||
|
u = 0;
|
||||||
|
v = (surviepote$backgroundHeight - srcH) / 2;
|
||||||
|
} else {
|
||||||
|
// Écran plus haut que l'image : on rogne à gauche/droite.
|
||||||
|
srcH = surviepote$backgroundHeight;
|
||||||
|
srcW = Math.round(surviepote$backgroundHeight * screenAspect);
|
||||||
|
u = (surviepote$backgroundWidth - srcW) / 2;
|
||||||
|
v = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Surcharge complète : (u, v) = origine dans la texture, (w, h) = taille à
|
||||||
|
// l'écran, (srcW, srcH) = taille de la région source dans la texture.
|
||||||
|
// L'overload court réutilise w/h comme région source (=> zoom sur un coin).
|
||||||
|
extractor.blit(RenderPipelines.GUI_TEXTURED, SURVIEPOTE_BACKGROUND,
|
||||||
|
0, 0, (float) u, (float) v,
|
||||||
|
w, h, srcW, srcH,
|
||||||
|
surviepote$backgroundWidth, surviepote$backgroundHeight, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MC 26.3 n'expose plus les ressources des mods au ResourceManager : les packs
|
||||||
|
* ne sont lus que depuis des dossiers, pas depuis les jars du classpath. Une
|
||||||
|
* texture référencée par son Identifier est donc introuvable (placeholder
|
||||||
|
* magenta). On charge donc le PNG depuis le jar du mod et on enregistre une
|
||||||
|
* DynamicTexture dans le TextureManager.
|
||||||
|
*/
|
||||||
|
@Unique
|
||||||
|
private static void surviepote$loadBackground() {
|
||||||
|
var textures = Minecraft.getInstance().getTextureManager();
|
||||||
|
if (surviepote$background != null
|
||||||
|
&& textures.getTexture(SURVIEPOTE_BACKGROUND) == surviepote$background) {
|
||||||
|
return; // déjà chargé et toujours enregistré
|
||||||
|
}
|
||||||
|
try (InputStream in = NoSingleplayer.class.getResourceAsStream(SURVIEPOTE_BACKGROUND_RESOURCE)) {
|
||||||
|
if (in == null) {
|
||||||
|
NoSingleplayer.LOGGER.error("[SurviePote] Fond introuvable dans le jar : {}",
|
||||||
|
SURVIEPOTE_BACKGROUND_RESOURCE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
NativeImage image = NativeImage.read(in);
|
||||||
|
surviepote$backgroundWidth = image.getWidth();
|
||||||
|
surviepote$backgroundHeight = image.getHeight();
|
||||||
|
surviepote$background = new LinearDynamicTexture(() -> "SurviePote Background", image);
|
||||||
|
textures.register(SURVIEPOTE_BACKGROUND, surviepote$background);
|
||||||
|
NoSingleplayer.LOGGER.info("[SurviePote] Fond chargé : {}x{}",
|
||||||
|
surviepote$backgroundWidth, surviepote$backgroundHeight);
|
||||||
|
} catch (IOException e) {
|
||||||
|
NoSingleplayer.LOGGER.error("[SurviePote] Échec de chargement du fond", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** DynamicTexture avec filtrage linéaire : le fond est réduit à la taille de l'écran. */
|
||||||
|
@Unique
|
||||||
|
private static final class LinearDynamicTexture extends DynamicTexture {
|
||||||
|
LinearDynamicTexture(Supplier<String> name, NativeImage image) {
|
||||||
|
super(name, image);
|
||||||
|
this.sampler = RenderSystem.getSamplerCache().getClampToEdge(FilterMode.LINEAR);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pas de logo Minecraft.
|
// Pas de logo Minecraft.
|
||||||
|
|||||||
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 1.7 MiB After Width: | Height: | Size: 1.9 MiB |
Reference in New Issue
Block a user