test Skyblock
This commit is contained in:
@@ -33,8 +33,10 @@ import net.neoforged.neoforge.registries.DeferredItem;
|
|||||||
import net.neoforged.neoforge.registries.DeferredRegister;
|
import net.neoforged.neoforge.registries.DeferredRegister;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import ovh.glitchlabs.ornamentum.blocks.ModBlocks;
|
import ovh.glitchlabs.ornamentum.blocks.ModBlocks;
|
||||||
|
import ovh.glitchlabs.ornamentum.blocks.entity.ModBlockEntities;
|
||||||
import ovh.glitchlabs.ornamentum.items.ModCreativeModeTabs;
|
import ovh.glitchlabs.ornamentum.items.ModCreativeModeTabs;
|
||||||
import ovh.glitchlabs.ornamentum.items.ModItems;
|
import ovh.glitchlabs.ornamentum.items.ModItems;
|
||||||
|
import ovh.glitchlabs.ornamentum.menu.ModMenuTypes;
|
||||||
|
|
||||||
// The value here should match an entry in the META-INF/neoforge.mods.toml file
|
// The value here should match an entry in the META-INF/neoforge.mods.toml file
|
||||||
@Mod(Ornamentum.MODID)
|
@Mod(Ornamentum.MODID)
|
||||||
@@ -53,6 +55,9 @@ public class Ornamentum {
|
|||||||
ModItems.register(modEventBus);
|
ModItems.register(modEventBus);
|
||||||
ModCreativeModeTabs.register(modEventBus);
|
ModCreativeModeTabs.register(modEventBus);
|
||||||
|
|
||||||
|
ModBlockEntities.register(modEventBus);
|
||||||
|
ModMenuTypes.register(modEventBus);
|
||||||
|
|
||||||
// Register the commonSetup method for modloading
|
// Register the commonSetup method for modloading
|
||||||
modEventBus.addListener(this::commonSetup);
|
modEventBus.addListener(this::commonSetup);
|
||||||
|
|
||||||
@@ -97,6 +102,12 @@ public class Ornamentum {
|
|||||||
// Register render types for glow glass blocks
|
// Register render types for glow glass blocks
|
||||||
registerGlassRenderTypes();
|
registerGlassRenderTypes();
|
||||||
registerGrateRenderTypes();
|
registerGrateRenderTypes();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public static void onRegisterScreens(net.neoforged.neoforge.client.event.RegisterMenuScreensEvent event) {
|
||||||
|
event.register(ModMenuTypes.SKYBOX_MENU.get(), ovh.glitchlabs.ornamentum.client.gui.SkyboxScreen::new);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void registerGlassRenderTypes() {
|
private static void registerGlassRenderTypes() {
|
||||||
|
|||||||
@@ -2,16 +2,15 @@ package ovh.glitchlabs.ornamentum.blocks;
|
|||||||
|
|
||||||
import net.minecraft.world.item.BlockItem;
|
import net.minecraft.world.item.BlockItem;
|
||||||
import net.minecraft.world.item.Item;
|
import net.minecraft.world.item.Item;
|
||||||
import net.minecraft.world.level.block.Block;
|
import net.minecraft.world.level.block.*;
|
||||||
import net.minecraft.world.level.block.Blocks;
|
|
||||||
import net.minecraft.world.level.block.SlabBlock;
|
|
||||||
import net.minecraft.world.level.block.StairBlock;
|
|
||||||
import net.minecraft.world.level.block.state.BlockBehaviour;
|
import net.minecraft.world.level.block.state.BlockBehaviour;
|
||||||
import net.neoforged.bus.api.IEventBus;
|
import net.neoforged.bus.api.IEventBus;
|
||||||
import net.neoforged.neoforge.registries.DeferredBlock;
|
import net.neoforged.neoforge.registries.DeferredBlock;
|
||||||
import net.neoforged.neoforge.registries.DeferredRegister;
|
import net.neoforged.neoforge.registries.DeferredRegister;
|
||||||
import ovh.glitchlabs.ornamentum.Ornamentum;
|
import ovh.glitchlabs.ornamentum.Ornamentum;
|
||||||
import ovh.glitchlabs.ornamentum.items.ModItems;
|
import ovh.glitchlabs.ornamentum.items.ModItems;
|
||||||
|
import ovh.glitchlabs.ornamentum.blocks.SkyboxBlock;
|
||||||
|
|
||||||
|
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
@@ -19,6 +18,12 @@ public class ModBlocks {
|
|||||||
public static final DeferredRegister.Blocks BLOCKS =
|
public static final DeferredRegister.Blocks BLOCKS =
|
||||||
DeferredRegister.createBlocks(Ornamentum.MODID);
|
DeferredRegister.createBlocks(Ornamentum.MODID);
|
||||||
|
|
||||||
|
public static final DeferredBlock<SkyboxBlock> SKYBOX_BLOCK = registerBlock("skybox_block",
|
||||||
|
() -> new SkyboxBlock(BlockBehaviour.Properties.of()
|
||||||
|
.strength(3.0f)
|
||||||
|
.sound(SoundType.AMETHYST)
|
||||||
|
.lightLevel(state -> 7)));
|
||||||
|
|
||||||
|
|
||||||
//more bricks
|
//more bricks
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
// Zielpfad: src/main/java/ovh/glitchlabs/ornamentum/block/SkyboxBlock.java
|
||||||
|
package ovh.glitchlabs.ornamentum.blocks;
|
||||||
|
|
||||||
|
import com.mojang.serialization.MapCodec;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.world.InteractionResult;
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraft.world.level.block.BaseEntityBlock;
|
||||||
|
import net.minecraft.world.level.block.RenderShape;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
|
import net.minecraft.world.level.block.state.BlockBehaviour;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraft.world.phys.BlockHitResult;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import ovh.glitchlabs.ornamentum.blocks.entity.SkyboxBlockEntity;
|
||||||
|
|
||||||
|
public class SkyboxBlock extends BaseEntityBlock {
|
||||||
|
|
||||||
|
public static final MapCodec<SkyboxBlock> CODEC = simpleCodec(SkyboxBlock::new);
|
||||||
|
|
||||||
|
public SkyboxBlock(BlockBehaviour.Properties properties) {
|
||||||
|
super(properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected MapCodec<? extends BaseEntityBlock> codec() {
|
||||||
|
return CODEC;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RenderShape getRenderShape(BlockState state) {
|
||||||
|
return RenderShape.MODEL;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public BlockEntity newBlockEntity(BlockPos pos, BlockState state) {
|
||||||
|
return new SkyboxBlockEntity(pos, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult hit) {
|
||||||
|
if (!level.isClientSide() && level.getBlockEntity(pos) instanceof SkyboxBlockEntity be) {
|
||||||
|
player.openMenu(be, buf -> buf.writeBlockPos(pos));
|
||||||
|
}
|
||||||
|
return InteractionResult.SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
// Zielpfad: src/main/java/ovh/glitchlabs/ornamentum/block/entity/ModBlockEntities.java
|
||||||
|
package ovh.glitchlabs.ornamentum.blocks.entity;
|
||||||
|
|
||||||
|
import net.minecraft.core.registries.Registries;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||||
|
import net.neoforged.bus.api.IEventBus;
|
||||||
|
import net.neoforged.neoforge.registries.DeferredHolder;
|
||||||
|
import net.neoforged.neoforge.registries.DeferredRegister;
|
||||||
|
import ovh.glitchlabs.ornamentum.Ornamentum;
|
||||||
|
import ovh.glitchlabs.ornamentum.blocks.ModBlocks;
|
||||||
|
|
||||||
|
public class ModBlockEntities {
|
||||||
|
public static final DeferredRegister<BlockEntityType<?>> BLOCK_ENTITY_TYPES =
|
||||||
|
DeferredRegister.create(Registries.BLOCK_ENTITY_TYPE, Ornamentum.MODID);
|
||||||
|
|
||||||
|
public static final DeferredHolder<BlockEntityType<?>, BlockEntityType<SkyboxBlockEntity>> SKYBOX_BLOCK_ENTITY =
|
||||||
|
BLOCK_ENTITY_TYPES.register("skybox_block_entity",
|
||||||
|
() -> BlockEntityType.Builder.of(SkyboxBlockEntity::new, ModBlocks.SKYBOX_BLOCK.get()).build(null));
|
||||||
|
|
||||||
|
public static void register(IEventBus bus) {
|
||||||
|
BLOCK_ENTITY_TYPES.register(bus);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,160 @@
|
|||||||
|
// Zielpfad: src/main/java/ovh/glitchlabs/ornamentum/block/entity/SkyboxBlockEntity.java
|
||||||
|
package ovh.glitchlabs.ornamentum.blocks.entity;
|
||||||
|
|
||||||
|
import net.minecraft.core.HolderLookup;
|
||||||
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
|
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;
|
||||||
|
import net.minecraft.util.Mth;
|
||||||
|
import net.minecraft.world.MenuProvider;
|
||||||
|
import net.minecraft.world.entity.player.Inventory;
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||||
|
import net.minecraft.world.inventory.ContainerData;
|
||||||
|
import net.minecraft.world.level.block.Block;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import ovh.glitchlabs.ornamentum.client.ClientSkyboxTracker;
|
||||||
|
import ovh.glitchlabs.ornamentum.menu.SkyboxMenu;
|
||||||
|
|
||||||
|
public class SkyboxBlockEntity extends BlockEntity implements MenuProvider {
|
||||||
|
|
||||||
|
private int range = 48;
|
||||||
|
private int colorIndex = 0;
|
||||||
|
private boolean starsEnabled = true;
|
||||||
|
private int texturePreset = 0;
|
||||||
|
|
||||||
|
private final ContainerData data = new ContainerData() {
|
||||||
|
@Override
|
||||||
|
public int get(int index) {
|
||||||
|
return switch (index) {
|
||||||
|
case 0 -> range;
|
||||||
|
case 1 -> colorIndex;
|
||||||
|
case 2 -> starsEnabled ? 1 : 0;
|
||||||
|
case 3 -> texturePreset;
|
||||||
|
default -> 0;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void set(int index, int value) {
|
||||||
|
switch (index) {
|
||||||
|
case 0 -> range = value;
|
||||||
|
case 1 -> colorIndex = value;
|
||||||
|
case 2 -> starsEnabled = value != 0;
|
||||||
|
case 3 -> texturePreset = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getCount() {
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public SkyboxBlockEntity(BlockPos pos, BlockState state) {
|
||||||
|
super(ModBlockEntities.SKYBOX_BLOCK_ENTITY.get(), pos, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getRange() {
|
||||||
|
return range;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getColorIndex() {
|
||||||
|
return colorIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isStarsEnabled() {
|
||||||
|
return starsEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTexturePreset() {
|
||||||
|
return texturePreset;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRange(int value) {
|
||||||
|
this.range = Mth.clamp(value, 8, 128);
|
||||||
|
syncToClient();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setColorIndex(int value) {
|
||||||
|
this.colorIndex = value;
|
||||||
|
syncToClient();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStarsEnabled(boolean value) {
|
||||||
|
this.starsEnabled = value;
|
||||||
|
syncToClient();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTexturePreset(int value) {
|
||||||
|
this.texturePreset = value;
|
||||||
|
syncToClient();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void syncToClient() {
|
||||||
|
setChanged();
|
||||||
|
if (level != null && !level.isClientSide()) {
|
||||||
|
level.sendBlockUpdated(getBlockPos(), getBlockState(), getBlockState(), Block.UPDATE_CLIENTS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void saveAdditional(CompoundTag tag, HolderLookup.Provider registries) {
|
||||||
|
super.saveAdditional(tag, registries);
|
||||||
|
tag.putInt("Range", range);
|
||||||
|
tag.putInt("ColorIndex", colorIndex);
|
||||||
|
tag.putBoolean("StarsEnabled", starsEnabled);
|
||||||
|
tag.putInt("TexturePreset", texturePreset);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void loadAdditional(CompoundTag tag, HolderLookup.Provider registries) {
|
||||||
|
super.loadAdditional(tag, registries);
|
||||||
|
range = tag.getInt("Range");
|
||||||
|
colorIndex = tag.getInt("ColorIndex");
|
||||||
|
starsEnabled = tag.getBoolean("StarsEnabled");
|
||||||
|
texturePreset = tag.getInt("TexturePreset");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompoundTag getUpdateTag(HolderLookup.Provider registries) {
|
||||||
|
CompoundTag tag = new CompoundTag();
|
||||||
|
saveAdditional(tag, registries);
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ClientboundBlockEntityDataPacket getUpdatePacket() {
|
||||||
|
return ClientboundBlockEntityDataPacket.create(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Component getDisplayName() {
|
||||||
|
return Component.translatable("block.ornamentum.skybox_block");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AbstractContainerMenu createMenu(int containerId, Inventory playerInventory, Player player) {
|
||||||
|
return new SkyboxMenu(containerId, playerInventory, this, this.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLoad() {
|
||||||
|
super.onLoad();
|
||||||
|
// Nur der physische Client betritt diesen Zweig, da level.isClientSide() auf dem
|
||||||
|
// dedizierten Server immer false ist -> ClientSkyboxTracker wird dort nie geladen.
|
||||||
|
if (level != null && level.isClientSide()) {
|
||||||
|
ClientSkyboxTracker.register(getBlockPos());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setRemoved() {
|
||||||
|
super.setRemoved();
|
||||||
|
if (level != null && level.isClientSide()) {
|
||||||
|
ClientSkyboxTracker.unregister(getBlockPos());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
// Zielpfad: src/main/java/ovh/glitchlabs/ornamentum/client/ClientSkyboxTracker.java
|
||||||
|
package ovh.glitchlabs.ornamentum.client;
|
||||||
|
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.player.LocalPlayer;
|
||||||
|
import net.minecraft.client.multiplayer.ClientLevel;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import ovh.glitchlabs.ornamentum.blocks.entity.SkyboxBlockEntity;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
public class ClientSkyboxTracker {
|
||||||
|
|
||||||
|
private static final Set<BlockPos> POSITIONS = ConcurrentHashMap.newKeySet();
|
||||||
|
private static final int FADE_DISTANCE = 12;
|
||||||
|
|
||||||
|
public static void register(BlockPos pos) {
|
||||||
|
POSITIONS.add(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void unregister(BlockPos pos) {
|
||||||
|
POSITIONS.remove(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bündelt alle für das Rendering relevanten Werte des aktuell "gewinnenden"
|
||||||
|
* (nächstgelegenen, aktivsten) Skybox-Blocks.
|
||||||
|
*/
|
||||||
|
public record ActiveSkybox(float blend, int colorIndex, boolean starsEnabled, int texturePreset) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return null, wenn kein Skybox-Block in Reichweite ist, sonst die Werte
|
||||||
|
* des Blocks mit dem stärksten Überblend-Faktor.
|
||||||
|
*/
|
||||||
|
public static ActiveSkybox getActive() {
|
||||||
|
LocalPlayer player = Minecraft.getInstance().player;
|
||||||
|
ClientLevel level = Minecraft.getInstance().level;
|
||||||
|
if (player == null || level == null || POSITIONS.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
ActiveSkybox best = null;
|
||||||
|
float bestBlend = 0f;
|
||||||
|
|
||||||
|
for (BlockPos pos : POSITIONS) {
|
||||||
|
if (!(level.getBlockEntity(pos) instanceof SkyboxBlockEntity be)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
double dist = Math.sqrt(player.blockPosition().distSqr(pos));
|
||||||
|
int range = be.getRange();
|
||||||
|
if (dist >= range) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
float blend = dist <= range - FADE_DISTANCE
|
||||||
|
? 1f
|
||||||
|
: (float) ((range - dist) / FADE_DISTANCE);
|
||||||
|
|
||||||
|
if (blend > bestBlend) {
|
||||||
|
bestBlend = blend;
|
||||||
|
best = new ActiveSkybox(blend, be.getColorIndex(), be.isStarsEnabled(), be.getTexturePreset());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return best;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
// Zielpfad: src/main/java/ovh/glitchlabs/ornamentum/client/ModSkyboxRenderer.java
|
||||||
|
package ovh.glitchlabs.ornamentum.client;
|
||||||
|
|
||||||
|
import com.mojang.blaze3d.systems.RenderSystem;
|
||||||
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
|
import com.mojang.blaze3d.vertex.VertexBuffer;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.multiplayer.ClientLevel;
|
||||||
|
import net.minecraft.client.renderer.GameRenderer;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.neoforged.api.distmarker.Dist;
|
||||||
|
import net.neoforged.bus.api.SubscribeEvent;
|
||||||
|
import net.neoforged.fml.common.EventBusSubscriber;
|
||||||
|
import net.neoforged.neoforge.client.event.RenderLevelStageEvent;
|
||||||
|
import org.joml.Matrix4f;
|
||||||
|
import ovh.glitchlabs.ornamentum.Ornamentum;
|
||||||
|
|
||||||
|
@EventBusSubscriber(modid = Ornamentum.MODID, value = Dist.CLIENT)
|
||||||
|
public class ModSkyboxRenderer {
|
||||||
|
|
||||||
|
// Ein Preset pro Index aus dem GUI ("Textur wechseln"). Lege die passenden
|
||||||
|
// PNGs unter assets/ornamentum/textures/sky/ ab (equirektangulare Panoramen).
|
||||||
|
private static final ResourceLocation[] SKY_TEXTURES = {
|
||||||
|
ResourceLocation.fromNamespaceAndPath(Ornamentum.MODID, "textures/sky/custom_sky_0.png"),
|
||||||
|
ResourceLocation.fromNamespaceAndPath(Ornamentum.MODID, "textures/sky/custom_sky_1.png"),
|
||||||
|
ResourceLocation.fromNamespaceAndPath(Ornamentum.MODID, "textures/sky/custom_sky_2.png"),
|
||||||
|
ResourceLocation.fromNamespaceAndPath(Ornamentum.MODID, "textures/sky/custom_sky_3.png")
|
||||||
|
};
|
||||||
|
|
||||||
|
private static final ResourceLocation STARS_TEXTURE =
|
||||||
|
ResourceLocation.fromNamespaceAndPath(Ornamentum.MODID, "textures/sky/custom_stars.png");
|
||||||
|
|
||||||
|
// r, g, b pro Farb-Preset (Index = colorIndex aus dem GUI, "Farbe wechseln")
|
||||||
|
private static final float[][] COLOR_PRESETS = {
|
||||||
|
{0.55f, 0.75f, 1.00f}, // Blau
|
||||||
|
{0.65f, 0.45f, 0.85f}, // Lila
|
||||||
|
{1.00f, 0.60f, 0.30f}, // Orange
|
||||||
|
{0.40f, 0.85f, 0.45f}, // Grün
|
||||||
|
{0.90f, 0.30f, 0.30f}, // Rot
|
||||||
|
{0.30f, 0.80f, 0.80f} // Türkis
|
||||||
|
};
|
||||||
|
|
||||||
|
private static VertexBuffer skySphere;
|
||||||
|
private static VertexBuffer starSphere;
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public static void onRenderSky(RenderLevelStageEvent event) {
|
||||||
|
if (event.getStage() != RenderLevelStageEvent.Stage.AFTER_SKY) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClientSkyboxTracker.ActiveSkybox active = ClientSkyboxTracker.getActive();
|
||||||
|
if (active == null || active.blend() <= 0f) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (skySphere == null) {
|
||||||
|
skySphere = SkyboxSphereBuilder.buildSphere(16, 16, 100f);
|
||||||
|
}
|
||||||
|
if (starSphere == null) {
|
||||||
|
starSphere = SkyboxSphereBuilder.buildSphere(16, 16, 90f);
|
||||||
|
}
|
||||||
|
|
||||||
|
ClientLevel level = Minecraft.getInstance().level;
|
||||||
|
if (level == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
long dayTime = level.getDayTime() % 24000;
|
||||||
|
boolean isNight = dayTime > 13000 && dayTime < 23000;
|
||||||
|
float partialTick = event.getPartialTick().getGameTimeDeltaPartialTick(true);
|
||||||
|
float starVisibility = level.getStarBrightness(partialTick);
|
||||||
|
|
||||||
|
float[] color = COLOR_PRESETS[active.colorIndex() % COLOR_PRESETS.length];
|
||||||
|
float darken = isNight ? 0.25f : 1.0f;
|
||||||
|
|
||||||
|
PoseStack poseStack = event.getPoseStack();
|
||||||
|
if (poseStack == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
poseStack.pushPose();
|
||||||
|
|
||||||
|
RenderSystem.enableBlend();
|
||||||
|
RenderSystem.defaultBlendFunc();
|
||||||
|
RenderSystem.depthMask(false);
|
||||||
|
RenderSystem.setShader(GameRenderer::getPositionTexShader);
|
||||||
|
|
||||||
|
Matrix4f mvMatrix = new Matrix4f(poseStack.last().pose());
|
||||||
|
Matrix4f projMatrix = event.getProjectionMatrix();
|
||||||
|
|
||||||
|
// 1) Basis-Himmelskugel mit gewählter Textur + Farbton
|
||||||
|
RenderSystem.setShaderColor(color[0] * darken, color[1] * darken, color[2] * darken, active.blend());
|
||||||
|
RenderSystem.setShaderTexture(0, SKY_TEXTURES[active.texturePreset() % SKY_TEXTURES.length]);
|
||||||
|
skySphere.bind();
|
||||||
|
skySphere.drawWithShader(mvMatrix, projMatrix, RenderSystem.getShader());
|
||||||
|
|
||||||
|
// 2) Sterne-Overlay, nur wenn im GUI aktiviert + es dunkel genug ist
|
||||||
|
if (active.starsEnabled()) {
|
||||||
|
float starAlpha = starVisibility * active.blend();
|
||||||
|
if (starAlpha > 0.01f) {
|
||||||
|
RenderSystem.setShaderColor(1f, 1f, 1f, starAlpha);
|
||||||
|
RenderSystem.setShaderTexture(0, STARS_TEXTURE);
|
||||||
|
starSphere.bind();
|
||||||
|
starSphere.drawWithShader(mvMatrix, projMatrix, RenderSystem.getShader());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VertexBuffer.unbind();
|
||||||
|
RenderSystem.setShaderColor(1f, 1f, 1f, 1f);
|
||||||
|
RenderSystem.depthMask(true);
|
||||||
|
RenderSystem.disableBlend();
|
||||||
|
|
||||||
|
poseStack.popPose();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
// Zielpfad: src/main/java/ovh/glitchlabs/ornamentum/client/SkyboxSphereBuilder.java
|
||||||
|
package ovh.glitchlabs.ornamentum.client;
|
||||||
|
|
||||||
|
import com.mojang.blaze3d.vertex.BufferBuilder;
|
||||||
|
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
|
||||||
|
import com.mojang.blaze3d.vertex.MeshData;
|
||||||
|
import com.mojang.blaze3d.vertex.Tesselator;
|
||||||
|
import com.mojang.blaze3d.vertex.VertexBuffer;
|
||||||
|
import com.mojang.blaze3d.vertex.VertexFormat;
|
||||||
|
import org.joml.Vector3f;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Baut eine einfache UV-gemappte Kugel als VertexBuffer, die per RenderSystem
|
||||||
|
* mit einer equirektangularen Panorama-Textur bezogen werden kann.
|
||||||
|
*
|
||||||
|
* Hinweis: Die genaue BufferBuilder/Tesselator-API kann sich zwischen NeoForge-
|
||||||
|
* Patch-Versionen leicht unterscheiden (1.21.x hat die Vertex-Pipeline mehrfach
|
||||||
|
* überarbeitet). Falls dein Compiler hier meckert, sag Bescheid mit der genauen
|
||||||
|
* Fehlermeldung - meist reicht eine kleine Anpassung der Methodennamen.
|
||||||
|
*/
|
||||||
|
public class SkyboxSphereBuilder {
|
||||||
|
|
||||||
|
public static VertexBuffer buildSphere(int rings, int segments, float radius) {
|
||||||
|
BufferBuilder builder = Tesselator.getInstance().begin(
|
||||||
|
VertexFormat.Mode.TRIANGLES, DefaultVertexFormat.POSITION_TEX);
|
||||||
|
|
||||||
|
for (int r = 0; r < rings; r++) {
|
||||||
|
float theta1 = (float) (r * Math.PI / rings);
|
||||||
|
float theta2 = (float) ((r + 1) * Math.PI / rings);
|
||||||
|
|
||||||
|
for (int s = 0; s < segments; s++) {
|
||||||
|
float phi1 = (float) (s * 2 * Math.PI / segments);
|
||||||
|
float phi2 = (float) ((s + 1) * 2 * Math.PI / segments);
|
||||||
|
|
||||||
|
Vector3f p1 = sphericalToCartesian(theta1, phi1, radius);
|
||||||
|
Vector3f p2 = sphericalToCartesian(theta2, phi1, radius);
|
||||||
|
Vector3f p3 = sphericalToCartesian(theta2, phi2, radius);
|
||||||
|
Vector3f p4 = sphericalToCartesian(theta1, phi2, radius);
|
||||||
|
|
||||||
|
float u1 = (float) s / segments;
|
||||||
|
float u2 = (float) (s + 1) / segments;
|
||||||
|
float v1 = (float) r / rings;
|
||||||
|
float v2 = (float) (r + 1) / rings;
|
||||||
|
|
||||||
|
builder.addVertex(p1.x, p1.y, p1.z).setUv(u1, v1);
|
||||||
|
builder.addVertex(p2.x, p2.y, p2.z).setUv(u1, v2);
|
||||||
|
builder.addVertex(p3.x, p3.y, p3.z).setUv(u2, v2);
|
||||||
|
|
||||||
|
builder.addVertex(p1.x, p1.y, p1.z).setUv(u1, v1);
|
||||||
|
builder.addVertex(p3.x, p3.y, p3.z).setUv(u2, v2);
|
||||||
|
builder.addVertex(p4.x, p4.y, p4.z).setUv(u2, v1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MeshData mesh = builder.buildOrThrow();
|
||||||
|
VertexBuffer buffer = new VertexBuffer(VertexBuffer.Usage.STATIC);
|
||||||
|
buffer.bind();
|
||||||
|
buffer.upload(mesh);
|
||||||
|
VertexBuffer.unbind();
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Vector3f sphericalToCartesian(float theta, float phi, float radius) {
|
||||||
|
float x = (float) (radius * Math.sin(theta) * Math.cos(phi));
|
||||||
|
float y = (float) (radius * Math.cos(theta));
|
||||||
|
float z = (float) (radius * Math.sin(theta) * Math.sin(phi));
|
||||||
|
return new Vector3f(x, y, z);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
// Zielpfad: src/main/java/ovh/glitchlabs/ornamentum/client/gui/SkyboxScreen.java
|
||||||
|
package ovh.glitchlabs.ornamentum.client.gui;
|
||||||
|
|
||||||
|
import net.minecraft.client.gui.GuiGraphics;
|
||||||
|
import net.minecraft.client.gui.components.Button;
|
||||||
|
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.neoforged.neoforge.network.PacketDistributor;
|
||||||
|
import ovh.glitchlabs.ornamentum.Ornamentum;
|
||||||
|
import ovh.glitchlabs.ornamentum.menu.SkyboxMenu;
|
||||||
|
import ovh.glitchlabs.ornamentum.network.SkyboxSettingsPayload;
|
||||||
|
import net.minecraft.world.entity.player.Inventory;
|
||||||
|
|
||||||
|
public class SkyboxScreen extends AbstractContainerScreen<SkyboxMenu> {
|
||||||
|
|
||||||
|
private static final ResourceLocation BG_TEXTURE =
|
||||||
|
ResourceLocation.fromNamespaceAndPath(Ornamentum.MODID, "textures/gui/skybox_gui.png");
|
||||||
|
|
||||||
|
private static final String[] COLOR_NAMES = {"Blau", "Lila", "Orange", "Grün", "Rot", "Türkis"};
|
||||||
|
private static final String[] TEXTURE_NAMES = {"Standard", "Nebel", "Sterne+", "Aurora"};
|
||||||
|
|
||||||
|
public SkyboxScreen(SkyboxMenu menu, Inventory inventory, Component title) {
|
||||||
|
super(menu, inventory, title);
|
||||||
|
this.imageWidth = 176;
|
||||||
|
this.imageHeight = 166;
|
||||||
|
this.inventoryLabelY = 10000; // Standard-Inventar-Label ausblenden (kein Spieler-Inventar im GUI)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void init() {
|
||||||
|
super.init();
|
||||||
|
int x = leftPos;
|
||||||
|
int y = topPos;
|
||||||
|
|
||||||
|
addRenderableWidget(Button.builder(Component.literal("-8"), b -> sendChange(0, -8))
|
||||||
|
.bounds(x + 20, y + 20, 30, 20).build());
|
||||||
|
addRenderableWidget(Button.builder(Component.literal("+8"), b -> sendChange(0, 8))
|
||||||
|
.bounds(x + 126, y + 20, 30, 20).build());
|
||||||
|
|
||||||
|
addRenderableWidget(Button.builder(Component.literal("Farbe wechseln"), b -> sendChange(1, 1))
|
||||||
|
.bounds(x + 20, y + 50, 136, 20).build());
|
||||||
|
|
||||||
|
addRenderableWidget(Button.builder(Component.literal("Sterne umschalten"), b -> sendChange(2, 0))
|
||||||
|
.bounds(x + 20, y + 80, 136, 20).build());
|
||||||
|
|
||||||
|
addRenderableWidget(Button.builder(Component.literal("Textur wechseln"), b -> sendChange(3, 1))
|
||||||
|
.bounds(x + 20, y + 110, 136, 20).build());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendChange(int field, int delta) {
|
||||||
|
PacketDistributor.sendToServer(new SkyboxSettingsPayload(
|
||||||
|
menu.getBlockEntity().getBlockPos(), field, delta));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void renderBg(GuiGraphics guiGraphics, float partialTick, int mouseX, int mouseY) {
|
||||||
|
guiGraphics.blit(BG_TEXTURE, leftPos, topPos, 0, 0, imageWidth, imageHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void renderLabels(GuiGraphics guiGraphics, int mouseX, int mouseY) {
|
||||||
|
var data = menu.getData();
|
||||||
|
guiGraphics.drawString(font, "Reichweite: " + data.get(0), 20, 8, 0x404040, false);
|
||||||
|
guiGraphics.drawString(font, "Farbe: " + COLOR_NAMES[data.get(1) % COLOR_NAMES.length], 20, 38, 0x404040, false);
|
||||||
|
guiGraphics.drawString(font, "Sterne: " + (data.get(2) != 0 ? "An" : "Aus"), 20, 68, 0x404040, false);
|
||||||
|
guiGraphics.drawString(font, "Textur: " + TEXTURE_NAMES[data.get(3) % TEXTURE_NAMES.length], 20, 98, 0x404040, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,6 +16,9 @@ public class ModBlockLootTableProvider extends BlockLootSubProvider {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void generate() {
|
protected void generate() {
|
||||||
|
|
||||||
|
dropSelf(ModBlocks.SKYBOX_BLOCK.get());
|
||||||
|
|
||||||
dropSelf(ModBlocks.POOL_TILES.get());
|
dropSelf(ModBlocks.POOL_TILES.get());
|
||||||
dropSelf(ModBlocks.POOL_TILES1.get());
|
dropSelf(ModBlocks.POOL_TILES1.get());
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,9 @@ public class ModBlockStateProvider extends BlockStateProvider {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void registerStatesAndModels() {
|
protected void registerStatesAndModels() {
|
||||||
|
|
||||||
|
blockWithItem(ModBlocks.SKYBOX_BLOCK);
|
||||||
|
|
||||||
blockWithItem(ModBlocks.POOL_TILES);
|
blockWithItem(ModBlocks.POOL_TILES);
|
||||||
blockWithItem(ModBlocks.POOL_TILES1);
|
blockWithItem(ModBlocks.POOL_TILES1);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
// Zielpfad: src/main/java/ovh/glitchlabs/ornamentum/menu/ModMenuTypes.java
|
||||||
|
package ovh.glitchlabs.ornamentum.menu;
|
||||||
|
|
||||||
|
import net.minecraft.core.registries.Registries;
|
||||||
|
import net.minecraft.world.inventory.MenuType;
|
||||||
|
import net.neoforged.bus.api.IEventBus;
|
||||||
|
import net.neoforged.neoforge.common.extensions.IMenuTypeExtension;
|
||||||
|
import net.neoforged.neoforge.registries.DeferredHolder;
|
||||||
|
import net.neoforged.neoforge.registries.DeferredRegister;
|
||||||
|
import ovh.glitchlabs.ornamentum.Ornamentum;
|
||||||
|
|
||||||
|
public class ModMenuTypes {
|
||||||
|
public static final DeferredRegister<MenuType<?>> MENU_TYPES =
|
||||||
|
DeferredRegister.create(Registries.MENU, Ornamentum.MODID);
|
||||||
|
|
||||||
|
public static final DeferredHolder<MenuType<?>, MenuType<SkyboxMenu>> SKYBOX_MENU =
|
||||||
|
MENU_TYPES.register("skybox_menu", () -> IMenuTypeExtension.create(SkyboxMenu::new));
|
||||||
|
|
||||||
|
public static void register(IEventBus bus) {
|
||||||
|
MENU_TYPES.register(bus);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
// Zielpfad: src/main/java/ovh/glitchlabs/ornamentum/menu/SkyboxMenu.java
|
||||||
|
package ovh.glitchlabs.ornamentum.menu;
|
||||||
|
|
||||||
|
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||||
|
import net.minecraft.world.entity.player.Inventory;
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||||
|
import net.minecraft.world.inventory.ContainerData;
|
||||||
|
import net.minecraft.world.inventory.SimpleContainerData;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraft.world.phys.Vec3;
|
||||||
|
import ovh.glitchlabs.ornamentum.blocks.entity.SkyboxBlockEntity;
|
||||||
|
|
||||||
|
public class SkyboxMenu extends AbstractContainerMenu {
|
||||||
|
|
||||||
|
private final SkyboxBlockEntity blockEntity;
|
||||||
|
private final ContainerData data;
|
||||||
|
|
||||||
|
// Wird beim Client-seitigen Öffnen des Menüs aufgerufen (über ModMenuTypes / IMenuTypeExtension)
|
||||||
|
public SkyboxMenu(int containerId, Inventory inv, RegistryFriendlyByteBuf buf) {
|
||||||
|
this(containerId, inv,
|
||||||
|
(SkyboxBlockEntity) inv.player.level().getBlockEntity(buf.readBlockPos()),
|
||||||
|
new SimpleContainerData(4));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wird server- und clientseitig verwendet (die echte BlockEntity liefert die Daten)
|
||||||
|
public SkyboxMenu(int containerId, Inventory inv, SkyboxBlockEntity blockEntity, ContainerData data) {
|
||||||
|
super(ModMenuTypes.SKYBOX_MENU.get(), containerId);
|
||||||
|
this.blockEntity = blockEntity;
|
||||||
|
this.data = data;
|
||||||
|
addDataSlots(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SkyboxBlockEntity getBlockEntity() {
|
||||||
|
return blockEntity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ContainerData getData() {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean stillValid(Player player) {
|
||||||
|
return blockEntity != null && !blockEntity.isRemoved()
|
||||||
|
&& player.distanceToSqr(Vec3.atCenterOf(blockEntity.getBlockPos())) < 64.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ItemStack quickMoveStack(Player player, int index) {
|
||||||
|
// Kein Inventar in diesem Menü - keine Item-Slots zu verschieben.
|
||||||
|
return ItemStack.EMPTY;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
// Zielpfad: src/main/java/ovh/glitchlabs/ornamentum/network/ModNetworking.java
|
||||||
|
package ovh.glitchlabs.ornamentum.network;
|
||||||
|
|
||||||
|
import net.neoforged.bus.api.SubscribeEvent;
|
||||||
|
import net.neoforged.fml.common.EventBusSubscriber;
|
||||||
|
import net.neoforged.neoforge.network.event.RegisterPayloadHandlersEvent;
|
||||||
|
import net.neoforged.neoforge.network.handling.IPayloadContext;
|
||||||
|
import net.neoforged.neoforge.network.registration.PayloadRegistrar;
|
||||||
|
import ovh.glitchlabs.ornamentum.Ornamentum;
|
||||||
|
import ovh.glitchlabs.ornamentum.blocks.entity.SkyboxBlockEntity;
|
||||||
|
|
||||||
|
@EventBusSubscriber(modid = Ornamentum.MODID, bus = EventBusSubscriber.Bus.MOD)
|
||||||
|
public class ModNetworking {
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public static void register(RegisterPayloadHandlersEvent event) {
|
||||||
|
PayloadRegistrar registrar = event.registrar("1");
|
||||||
|
registrar.playToServer(SkyboxSettingsPayload.TYPE, SkyboxSettingsPayload.STREAM_CODEC,
|
||||||
|
ModNetworking::handleSkyboxSettings);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void handleSkyboxSettings(SkyboxSettingsPayload payload, IPayloadContext context) {
|
||||||
|
context.enqueueWork(() -> {
|
||||||
|
if (!(context.player().level().getBlockEntity(payload.pos()) instanceof SkyboxBlockEntity be)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (payload.field()) {
|
||||||
|
case 0 -> be.setRange(be.getRange() + payload.delta());
|
||||||
|
case 1 -> be.setColorIndex((be.getColorIndex() + 1) % 6);
|
||||||
|
case 2 -> be.setStarsEnabled(!be.isStarsEnabled());
|
||||||
|
case 3 -> be.setTexturePreset((be.getTexturePreset() + 1) % 4);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
// Zielpfad: src/main/java/ovh/glitchlabs/ornamentum/network/SkyboxSettingsPayload.java
|
||||||
|
package ovh.glitchlabs.ornamentum.network;
|
||||||
|
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.network.FriendlyByteBuf;
|
||||||
|
import net.minecraft.network.codec.ByteBufCodecs;
|
||||||
|
import net.minecraft.network.codec.StreamCodec;
|
||||||
|
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import ovh.glitchlabs.ornamentum.Ornamentum;
|
||||||
|
|
||||||
|
// field: 0 = range (+delta), 1 = colorIndex (+1 zum Weiterschalten), 2 = starsEnabled (toggle, delta ignoriert),
|
||||||
|
// 3 = texturePreset (+1 zum Weiterschalten)
|
||||||
|
public record SkyboxSettingsPayload(BlockPos pos, int field, int delta) implements CustomPacketPayload {
|
||||||
|
|
||||||
|
public static final Type<SkyboxSettingsPayload> TYPE =
|
||||||
|
new Type<>(ResourceLocation.fromNamespaceAndPath(Ornamentum.MODID, "skybox_settings"));
|
||||||
|
|
||||||
|
public static final StreamCodec<FriendlyByteBuf, SkyboxSettingsPayload> STREAM_CODEC = StreamCodec.composite(
|
||||||
|
BlockPos.STREAM_CODEC, SkyboxSettingsPayload::pos,
|
||||||
|
ByteBufCodecs.VAR_INT, SkyboxSettingsPayload::field,
|
||||||
|
ByteBufCodecs.VAR_INT, SkyboxSettingsPayload::delta,
|
||||||
|
SkyboxSettingsPayload::new
|
||||||
|
);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type<? extends CustomPacketPayload> type() {
|
||||||
|
return TYPE;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user