added oxygen test block

This commit is contained in:
TechnoDraconic
2026-01-28 16:52:49 -08:00
parent 5e512cae1c
commit b41741d578
12 changed files with 240 additions and 3 deletions

View File

@@ -1,6 +1,5 @@
package net.xevianlight.aphelion;
import net.minecraft.client.renderer.entity.ThrownItemRenderer;
import net.minecraft.resources.ResourceLocation;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.fml.common.EventBusSubscriber;
@@ -10,6 +9,7 @@ import net.neoforged.neoforge.client.event.RegisterMenuScreensEvent;
import net.neoforged.neoforge.client.extensions.common.RegisterClientExtensionsEvent;
import net.neoforged.neoforge.event.AddReloadListenerEvent;
import net.xevianlight.aphelion.block.dummy.renderer.MultiblockDummyRenderer;
import net.xevianlight.aphelion.block.entity.custom.renderer.OxygenTestRenderer;
import net.xevianlight.aphelion.client.AphelionConfig;
import net.xevianlight.aphelion.planet.AphelionPlanetJSONLoader;
import net.xevianlight.aphelion.core.init.*;
@@ -132,6 +132,7 @@ public class Aphelion {
@SubscribeEvent
public static void registerBER(EntityRenderersEvent.RegisterRenderers event) {
event.registerBlockEntityRenderer(ModBlockEntities.VAF_MULTIBLOCK_DUMMY_ENTITY.get(), MultiblockDummyRenderer::new);
event.registerBlockEntityRenderer(ModBlockEntities.OXYGEN_TEST_BLOCK_ENTITY.get(), OxygenTestRenderer::new);
}
@SubscribeEvent

View File

@@ -0,0 +1,25 @@
package net.xevianlight.aphelion.block.custom;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.EntityBlock;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.xevianlight.aphelion.block.entity.custom.OxygenTestBlockEntity;
import org.jetbrains.annotations.Nullable;
public class OxygenTestBlock extends Block implements EntityBlock {
public OxygenTestBlock(Properties properties) {
super(properties);
}
@Override
public @Nullable BlockEntity newBlockEntity(BlockPos blockPos, BlockState blockState) {
return new OxygenTestBlockEntity(blockPos, blockState);
}
public static Properties getProperties() {
return Properties.of();
}
}

View File

@@ -0,0 +1,81 @@
package net.xevianlight.aphelion.block.entity.custom;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.Vec3i;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import static net.xevianlight.aphelion.Aphelion.LOGGER;
import net.xevianlight.aphelion.core.init.ModBlockEntities;
import java.util.*;
public class OxygenTestBlockEntity extends BlockEntity {
public OxygenTestBlockEntity(BlockPos pos, BlockState blockState) {
super(ModBlockEntities.OXYGEN_TEST_BLOCK_ENTITY.get(), pos, blockState);
}
public boolean canSpreadTo(BlockPos pos) {
return level != null && level.getBlockState(pos).isAir();
}
public static final int MAX_RANGE = 8;
public boolean isInRange(BlockPos pos, int radius) {
return radius <= MAX_RANGE;
}
private List<BlockPos> enclosedCache;
public List<BlockPos> getEnclosedBlocks() {
if (level == null) return List.of();
if (enclosedCache != null) return enclosedCache;
List<BlockPos> enclosedBlocks = new ArrayList<>();
Set<BlockPos> seen = new HashSet<>();
// It's... a reasonable assumption that we won't have to include more blocks at once than the area of a sphere?
// maybe a bit more, IDK how exactly it scales to blocks.
Deque<BlockPos> queue = new ArrayDeque<>((int)(4 * Math.PI * MAX_RANGE * MAX_RANGE));
Deque<Integer> radiusQueue = new ArrayDeque<>((int)(4 * Math.PI * MAX_RANGE * MAX_RANGE));
queue.add(this.getBlockPos());
radiusQueue.add(0);
// Do flood fill out from this block
// Push on the top of the stack (newest), pop from the bottom of the stack (oldest).
// Ends up being breadth-first; if you wanted, you could label each position pushed to the queue
// by how many "layers deep" it is (i.e. how many steps it took to get there),
// and you'd see that every pos of layer 1 is together, then layer 2, then layer 3...
long start = System.nanoTime();
while (!queue.isEmpty()) {
BlockPos spreadFromPos = queue.removeFirst();
int radius = radiusQueue.removeFirst();
for (Direction d : Direction.values()) {
BlockPos relativePos = spreadFromPos.relative(d);
if (seen.contains(relativePos)) continue;
if (canSpreadTo(relativePos) && isInRange(relativePos, radius)) {
seen.add(relativePos);
enclosedBlocks.add(relativePos);
queue.add(relativePos);
radiusQueue.add(radius + 1);
}
}
}
long durationNanos = System.nanoTime() - start;
double durationMicros = durationNanos / 1000.0d;
LOGGER.info("Flood fill completed in {}µs, {}µs/block", durationMicros, durationMicros / enclosedBlocks.size());
enclosedCache = enclosedBlocks;
return enclosedBlocks;
}
private void helper() {
var myVar = new BlockPos(1, 1, 1).hashCode();
}
}

View File

@@ -0,0 +1,115 @@
package net.xevianlight.aphelion.block.entity.custom.renderer;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer;
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
import net.minecraft.client.renderer.texture.OverlayTexture;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.Vec3i;
import net.minecraft.world.phys.AABB;
import net.xevianlight.aphelion.block.entity.custom.OxygenTestBlockEntity;
import org.joml.Matrix4f;
import org.joml.Vector3f;
import java.util.*;
import java.util.stream.Collectors;
public class OxygenTestRenderer implements BlockEntityRenderer<OxygenTestBlockEntity> {
public OxygenTestRenderer (BlockEntityRendererProvider.Context context) {}
private List<BlockPos> toBlockPositions(OxygenTestBlockEntity be) {
return be.getEnclosedBlocks();
}
@Override
public AABB getRenderBoundingBox(OxygenTestBlockEntity blockEntity) {
return AABB.ofSize(blockEntity.getBlockPos().getCenter(), OxygenTestBlockEntity.MAX_RANGE*2, OxygenTestBlockEntity.MAX_RANGE*2, OxygenTestBlockEntity.MAX_RANGE*2);
}
private Set<BlockPos> relativePositionsCache;
@Override
// If in debug mode, renders a model made from the blocks
// that are currently returned by toBlockPositions(OxygenTestBlockEntity).
public void render(OxygenTestBlockEntity be, float partialTick, PoseStack poseStack, MultiBufferSource buffer, int packedLight, int packedOverlay) {
// This bit's debug only, folks!
if (!Minecraft.getInstance().gui.getDebugOverlay().showDebugScreen()) return;
// Renderers are relative to our block pos, so transform all points to be relative to block pos as well
List<BlockPos> positionsToRender = toBlockPositions(be);
BlockPos originPos = be.getBlockPos();
Set<BlockPos> relativePositions;
if (relativePositionsCache != null) relativePositions = relativePositionsCache;
else relativePositions = positionsToRender.stream().map(bp -> bp.offset(new Vec3i(-originPos.getX(), -originPos.getY(), -originPos.getZ()))).collect(Collectors.toSet());
poseStack.pushPose();
Matrix4f mat = poseStack.last().pose();
VertexConsumer buf = buffer.getBuffer(RenderType.LIGHTNING); // for slightly transparent, lighting-ignoring textures (?), from what I already tested
for (BlockPos p1 : relativePositions) {
// Render a face for every side this block cannot find a neighbor in
boolean upUncovered = !relativePositions.contains(p1.relative(Direction.UP));
boolean downUncovered = !relativePositions.contains(p1.relative(Direction.DOWN));
boolean northUncovered = !relativePositions.contains(p1.relative(Direction.NORTH));
boolean southUncovered = !relativePositions.contains(p1.relative(Direction.SOUTH));
boolean eastUncovered = !relativePositions.contains(p1.relative(Direction.EAST));
boolean westUncovered = !relativePositions.contains(p1.relative(Direction.WEST));
Vector3f centerPos = p1.getCenter().toVector3f();
final Vector3f c000 = new Vector3f(centerPos).add(-0.500001f,-0.500001f,-0.500001f);
final Vector3f c001 = new Vector3f(centerPos).add(0.500001f,-0.500001f,-0.500001f);
final Vector3f c010 = new Vector3f(centerPos).add(-0.500001f,0.500001f,-0.500001f);
final Vector3f c011 = new Vector3f(centerPos).add(0.500001f,0.500001f,-0.500001f);
final Vector3f c100 = new Vector3f(centerPos).add(-0.500001f,-0.500001f,0.500001f);
final Vector3f c101 = new Vector3f(centerPos).add(0.500001f,-0.500001f,0.500001f);
final Vector3f c110 = new Vector3f(centerPos).add(-0.500001f,0.500001f,0.500001f);
final Vector3f c111 = new Vector3f(centerPos).add(0.500001f,0.500001f,0.500001f);
if (upUncovered) {
addQuad(buf, mat, c110, c111, c011, c010, 0x6666FF);
}
if (downUncovered) {
addQuad(buf, mat, c000, c001, c101, c100, 0x6666FF);
}
if (northUncovered) {
addQuad(buf, mat, c001, c000, c010, c011, 0x6666FF);
}
if (eastUncovered) {
addQuad(buf, mat, c101, c001, c011, c111, 0x6666FF);
}
if (southUncovered) {
addQuad(buf, mat, c101, c111, c110, c100, 0x6666FF);
}
if (westUncovered) {
addQuad(buf, mat, c100, c110, c010, c000, 0x6666FF);
}
}
poseStack.popPose();
}
private void addQuad(VertexConsumer buf, Matrix4f matrix, Vector3f p0, Vector3f p1, Vector3f p2, Vector3f p3, int hexColor) {
addVertex(buf, matrix, p0, hexColor);
addVertex(buf, matrix, p1, hexColor);
addVertex(buf, matrix, p2, hexColor);
addVertex(buf, matrix, p3, hexColor);
}
private void addVertex(VertexConsumer buf, Matrix4f matrix, Vector3f vec, int hexColor) {
float x = vec.x();
float y = vec.y();
float z = vec.z();
int r = (hexColor & 0xFF0000) >> 16;
int g = (hexColor & 0x00FF00) >> 8;
int b = hexColor & 0x0000FF;
buf.addVertex(matrix, x, y, z)
.setColor(r, g, b, 255)
.setUv(0, 0) // No texture, so this gets ignored
.setOverlay(OverlayTexture.NO_OVERLAY)
.setLight(15728880) //0xF000F0, or fully bright(?)
.setNormal(0, 1, 0); // Facing upwards
}
}

View File

@@ -2,6 +2,7 @@ package net.xevianlight.aphelion.core.init;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.neoforged.fml.common.Mod;
import net.neoforged.neoforge.registries.DeferredRegister;
import net.xevianlight.aphelion.Aphelion;
import net.xevianlight.aphelion.block.dummy.entity.VAFMultiblockDummyBlockEntity;
@@ -41,4 +42,9 @@ public class ModBlockEntities {
BLOCK_ENTITIES.register("vaf_multiblock_dummy_entity", () -> BlockEntityType.Builder.of(
VAFMultiblockDummyBlockEntity::new, ModBlocks.VAF_MULTIBLOCK_DUMMY_BLOCK.get()).build(null)
);
public static final Supplier<BlockEntityType<OxygenTestBlockEntity>> OXYGEN_TEST_BLOCK_ENTITY =
BLOCK_ENTITIES.register("oxygen_test_block_entity", () -> BlockEntityType.Builder.of(
OxygenTestBlockEntity::new, ModBlocks.OXYGEN_TEST_BLOCK.get()).build(null)
);
}

View File

@@ -1,6 +1,7 @@
package net.xevianlight.aphelion.core.init;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.neoforged.neoforge.registries.DeferredBlock;
import net.neoforged.neoforge.registries.DeferredRegister;
import net.xevianlight.aphelion.Aphelion;
@@ -17,4 +18,5 @@ public class ModBlocks {
public static final DeferredBlock<Block> ARC_FURNACE_CASING_BLOCK = BLOCKS.register("arc_furnace_casing", () -> new ArcFurnaceCasingBlock(ArcFurnaceCasingBlock.getProperties()));
public static final DeferredBlock<Block> VACUUM_ARC_FURNACE_CONTROLLER = BLOCKS.register("vacuum_arc_furnace_controller", () -> new VacuumArcFurnaceController(VacuumArcFurnaceController.getProperties()));
public static final DeferredBlock<Block> VAF_MULTIBLOCK_DUMMY_BLOCK = BLOCKS.register("vaf_dummy_block", () -> new VAFMultiblockDummyBlock(VAFMultiblockDummyBlock.getProperties()));
public static final DeferredBlock<Block> OXYGEN_TEST_BLOCK = BLOCKS.register("oxygen_test_block", () -> new OxygenTestBlock(OxygenTestBlock.getProperties()));
}

View File

@@ -35,5 +35,6 @@ public static final DeferredItem<Item> MUSIC_DISC_BIT_SHIFT = ITEMS.register("mu
public static final DeferredItem<BlockItem> BLOCK_STEEL = ITEMS.register("block_steel", () -> new BlockItem(ModBlocks.BLOCK_STEEL.get(), BlockSteel.getItemProperties()));
public static final DeferredItem<BlockItem> ARC_FURNACE_CASING_BLOCK = ITEMS.register("arc_furnace_casing", () -> new BlockItem(ModBlocks.ARC_FURNACE_CASING_BLOCK.get(), ArcFurnaceCasingBlock.getItemProperties()));
public static final DeferredItem<BlockItem> VACUUM_ARC_FURNACE_CONTROLLER = ITEMS.register("vacuum_arc_furnace_controller", () -> new BlockItem(ModBlocks.VACUUM_ARC_FURNACE_CONTROLLER.get(), VacuumArcFurnaceController.getItemProperties()));
public static final DeferredItem<BlockItem> OXYGEN_TEST_BLOCK = ITEMS.register("oxygen_test_block", () -> new BlockItem(ModBlocks.OXYGEN_TEST_BLOCK.get(), new Item.Properties()));
// public static final DeferredItem<BlockItem> VAF_MULTIBLOCK_DUMMY_BLOCK = ITEMS.register("vaf_multiblock_dummy_block", () -> new BlockItem(ModBlocks.VAF_MULTIBLOCK_DUMMY_BLOCK.get(), VAFMultiblockDummyBlock.getItemProperties()));
}

View File

@@ -23,6 +23,7 @@ public class ModBlockLootTableProvider extends BlockLootSubProvider {
dropSelf(ModBlocks.ELECTRIC_ARC_FURNACE.get());
dropSelf(ModBlocks.ARC_FURNACE_CASING_BLOCK.get());
dropSelf(ModBlocks.VACUUM_ARC_FURNACE_CONTROLLER.get());
dropSelf(ModBlocks.OXYGEN_TEST_BLOCK.get());
dropOther(ModBlocks.VAF_MULTIBLOCK_DUMMY_BLOCK.get(), ItemStack.EMPTY.getItem());
}

View File

@@ -28,6 +28,7 @@ public class ModBlockStateProvider extends BlockStateProvider {
blockWithItem(ModBlocks.BLOCK_STEEL);
blockWithItem(ModBlocks.DIMENSION_CHANGER);
blockItem(ModBlocks.ARC_FURNACE_CASING_BLOCK);
blockWithItem(ModBlocks.OXYGEN_TEST_BLOCK);
}
private void blockWithItem(DeferredBlock<?> deferredBlock) {