Refactor bogey rendering

This commit is contained in:
PepperCode1 2024-10-19 16:18:54 -07:00
parent eb2f1fecdc
commit 03a94dd74d
19 changed files with 544 additions and 863 deletions

View file

@ -1,132 +1,42 @@
package com.simibubi.create;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.function.Supplier;
import com.google.common.collect.ImmutableMap;
import com.simibubi.create.content.trains.bogey.AbstractBogeyBlock;
import com.simibubi.create.content.trains.bogey.BogeyRenderer;
import com.simibubi.create.content.trains.bogey.BogeyRenderer.CommonRenderer;
import org.jetbrains.annotations.ApiStatus;
import com.simibubi.create.content.trains.bogey.BogeySizes;
import com.simibubi.create.content.trains.bogey.BogeyStyle;
import com.simibubi.create.content.trains.bogey.StandardBogeyRenderer.CommonStandardBogeyRenderer;
import com.simibubi.create.content.trains.bogey.StandardBogeyRenderer.LargeStandardBogeyRenderer;
import com.simibubi.create.content.trains.bogey.StandardBogeyRenderer.SmallStandardBogeyRenderer;
import com.simibubi.create.content.trains.bogey.BogeyStyle.SizeRenderer;
import com.simibubi.create.content.trains.bogey.StandardBogeyRenderer;
import com.simibubi.create.content.trains.bogey.StandardBogeyVisual;
import com.simibubi.create.foundation.utility.Components;
import com.simibubi.create.foundation.utility.Lang;
import com.tterrag.registrate.util.entry.BlockEntry;
import net.minecraft.core.particles.ParticleOptions;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.fml.DistExecutor;
public class AllBogeyStyles {
public static final Map<ResourceLocation, BogeyStyle> BOGEY_STYLES = new HashMap<>();
public static final Map<ResourceLocation, Map<ResourceLocation, BogeyStyle>> CYCLE_GROUPS = new HashMap<>();
private static final Map<ResourceLocation, BogeyStyle> EMPTY_GROUP = ImmutableMap.of();
private static final Map<ResourceLocation, BogeyStyle> EMPTY_GROUP = Collections.emptyMap();
public static final ResourceLocation STANDARD_CYCLE_GROUP = Create.asResource("standard");
public static final BogeyStyle STANDARD =
builder("standard", STANDARD_CYCLE_GROUP).displayName(Components.translatable("create.bogey.style.standard"))
.size(BogeySizes.SMALL, AllBlocks.SMALL_BOGEY, () -> new SizeRenderer(new StandardBogeyRenderer.Small(), StandardBogeyVisual.Small::new))
.size(BogeySizes.LARGE, AllBlocks.LARGE_BOGEY, () -> new SizeRenderer(new StandardBogeyRenderer.Large(), StandardBogeyVisual.Large::new))
.build();
public static Map<ResourceLocation, BogeyStyle> getCycleGroup(ResourceLocation cycleGroup) {
return CYCLE_GROUPS.getOrDefault(cycleGroup, EMPTY_GROUP);
}
public static final String STANDARD_CYCLE_GROUP = "standard";
public static final BogeyStyle STANDARD =
create("standard", STANDARD_CYCLE_GROUP).commonRenderer(() -> CommonStandardBogeyRenderer::new)
.displayName(Components.translatable("create.bogey.style.standard"))
.size(BogeySizes.SMALL, () -> SmallStandardBogeyRenderer::new, AllBlocks.SMALL_BOGEY)
.size(BogeySizes.LARGE, () -> LargeStandardBogeyRenderer::new, AllBlocks.LARGE_BOGEY)
.build();
private static BogeyStyleBuilder create(String name, String cycleGroup) {
return create(Create.asResource(name), Create.asResource(cycleGroup));
private static BogeyStyle.Builder builder(String name, ResourceLocation cycleGroup) {
return new BogeyStyle.Builder(Create.asResource(name), cycleGroup);
}
public static BogeyStyleBuilder create(ResourceLocation name, ResourceLocation cycleGroup) {
return new BogeyStyleBuilder(name, cycleGroup);
}
public static void register() {}
public static class BogeyStyleBuilder {
protected final Map<BogeySizes.BogeySize, Supplier<BogeyStyle.SizeRenderData>> sizeRenderers = new HashMap<>();
protected final Map<BogeySizes.BogeySize, ResourceLocation> sizes = new HashMap<>();
protected final ResourceLocation name;
protected final ResourceLocation cycleGroup;
protected Component displayName = Lang.translateDirect("bogey.style.invalid");
protected ResourceLocation soundType = AllSoundEvents.TRAIN2.getId();
protected CompoundTag defaultData = new CompoundTag();
protected ParticleOptions contactParticle = ParticleTypes.CRIT;
protected ParticleOptions smokeParticle = ParticleTypes.POOF;
protected Optional<Supplier<? extends CommonRenderer>> commonRenderer = Optional.empty();
public BogeyStyleBuilder(ResourceLocation name, ResourceLocation cycleGroup) {
this.name = name;
this.cycleGroup = cycleGroup;
}
public BogeyStyleBuilder displayName(Component displayName) {
this.displayName = displayName;
return this;
}
public BogeyStyleBuilder soundType(ResourceLocation soundType) {
this.soundType = soundType;
return this;
}
public BogeyStyleBuilder defaultData(CompoundTag defaultData) {
this.defaultData = defaultData;
return this;
}
public BogeyStyleBuilder size(BogeySizes.BogeySize size, Supplier<Supplier<? extends BogeyRenderer>> renderer,
BlockEntry<? extends AbstractBogeyBlock<?>> blockEntry) {
this.size(size, renderer, blockEntry.getId());
return this;
}
public BogeyStyleBuilder size(BogeySizes.BogeySize size, Supplier<Supplier<? extends BogeyRenderer>> renderer,
ResourceLocation location) {
this.sizes.put(size, location);
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> {
this.sizeRenderers.put(size, () -> new BogeyStyle.SizeRenderData(renderer.get(), renderer.get()
.get()));
});
return this;
}
public BogeyStyleBuilder contactParticle(ParticleOptions contactParticle) {
this.contactParticle = contactParticle;
return this;
}
public BogeyStyleBuilder smokeParticle(ParticleOptions smokeParticle) {
this.smokeParticle = smokeParticle;
return this;
}
public BogeyStyleBuilder commonRenderer(Supplier<Supplier<? extends CommonRenderer>> commonRenderer) {
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> {
this.commonRenderer = Optional.of(commonRenderer.get());
});
return this;
}
public BogeyStyle build() {
BogeyStyle entry = new BogeyStyle(name, cycleGroup, displayName, soundType, contactParticle, smokeParticle,
defaultData, sizes, sizeRenderers, commonRenderer);
BOGEY_STYLES.put(name, entry);
CYCLE_GROUPS.computeIfAbsent(cycleGroup, l -> new HashMap<>())
.put(name, entry);
return entry;
}
@ApiStatus.Internal
public static void init() {
}
}

View file

@ -136,7 +136,7 @@ public class Create {
AllFanProcessingTypes.register();
BlockSpoutingBehaviour.registerDefaults();
BogeySizes.init();
AllBogeyStyles.register();
AllBogeyStyles.init();
// ----
ComputerCraftProxy.register();

View file

@ -4,18 +4,15 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import javax.annotation.Nullable;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.mojang.math.Axis;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllBogeyStyles;
import com.simibubi.create.AllItems;
@ -33,8 +30,6 @@ import com.simibubi.create.foundation.utility.Iterate;
import com.simibubi.create.foundation.utility.Lang;
import com.simibubi.create.foundation.utility.RegisteredObjects;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
@ -57,8 +52,6 @@ import net.minecraft.world.level.block.state.properties.Property;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.registries.ForgeRegistries;
public abstract class AbstractBogeyBlock<T extends AbstractBogeyBlockEntity> extends Block implements IBE<T>, ProperWaterloggedBlock, ISpecialBlockItemRequirement, IWrenchable {
@ -66,7 +59,6 @@ public abstract class AbstractBogeyBlock<T extends AbstractBogeyBlockEntity> ext
static final List<ResourceLocation> BOGEYS = new ArrayList<>();
public BogeySizes.BogeySize size;
public AbstractBogeyBlock(Properties pProperties, BogeySizes.BogeySize size) {
super(pProperties);
registerDefaultState(defaultBlockState().setValue(WATERLOGGED, false));
@ -91,7 +83,7 @@ public abstract class AbstractBogeyBlock<T extends AbstractBogeyBlockEntity> ext
/**
* Only for internal Create use. If you have your own style set, do not call this method
*/
@Deprecated
@ApiStatus.Internal
public static void registerStandardBogey(ResourceLocation block) {
BOGEYS.add(block);
}
@ -147,30 +139,6 @@ public abstract class AbstractBogeyBlock<T extends AbstractBogeyBlockEntity> ext
return false;
}
@OnlyIn(Dist.CLIENT)
public void render(@Nullable BlockState state, float wheelAngle, PoseStack ms, float partialTicks,
MultiBufferSource buffers, int light, int overlay, BogeyStyle style, CompoundTag bogeyData) {
if (style == null)
style = getDefaultStyle();
final Optional<BogeyRenderer.CommonRenderer> commonRenderer
= style.getInWorldCommonRenderInstance();
final BogeyRenderer renderer = style.getInWorldRenderInstance(this.getSize());
if (state != null) {
ms.translate(.5f, .5f, .5f);
if (state.getValue(AXIS) == Direction.Axis.X)
ms.mulPose(Axis.YP.rotationDegrees(90));
}
ms.translate(0, -1.5 - 1 / 128f, 0);
VertexConsumer vb = buffers.getBuffer(RenderType.cutoutMipped());
if (bogeyData == null)
bogeyData = new CompoundTag();
renderer.render(bogeyData, wheelAngle, ms, light, vb, state == null);
CompoundTag finalBogeyData = bogeyData;
commonRenderer.ifPresent(common ->
common.render(finalBogeyData, wheelAngle, ms, light, vb, state == null));
}
public BogeySizes.BogeySize getSize() {
return this.size;
}
@ -216,9 +184,9 @@ public abstract class AbstractBogeyBlock<T extends AbstractBogeyBlockEntity> ext
Set<BogeySizes.BogeySize> validSizes = style.validSizes();
for (int i = 0; i < BogeySizes.count(); i++) {
for (int i = 0; i < BogeySizes.all().size(); i++) {
if (validSizes.contains(size)) break;
size = size.increment();
size = size.nextBySize();
}
sbbe.setBogeyStyle(style);
@ -227,7 +195,7 @@ public abstract class AbstractBogeyBlock<T extends AbstractBogeyBlockEntity> ext
sbbe.setBogeyData(sbbe.getBogeyData().merge(defaultData));
if (size == getSize()) {
if (state.getBlock() != style.getBlockOfSize(size)) {
if (state.getBlock() != style.getBlockForSize(size)) {
CompoundTag oldData = sbbe.getBogeyData();
level.setBlock(pos, copyProperties(state, getStateOfSize(sbbe, size)), Block.UPDATE_ALL);
if (!(level.getBlockEntity(pos) instanceof AbstractBogeyBlockEntity bogeyBlockEntity))
@ -328,7 +296,7 @@ public abstract class AbstractBogeyBlock<T extends AbstractBogeyBlockEntity> ext
public BlockState getStateOfSize(AbstractBogeyBlockEntity sbbe, BogeySizes.BogeySize size) {
BogeyStyle style = sbbe.getStyle();
BlockState state = style.getBlockOfSize(size).defaultBlockState();
BlockState state = style.getBlockForSize(size).defaultBlockState();
return copyProperties(sbbe.getBlockState(), state);
}

View file

@ -37,14 +37,14 @@ public abstract class AbstractBogeyBlockEntity extends CachedRenderBBBlockEntity
public void setBogeyData(@NotNull CompoundTag newData) {
if (!newData.contains(BOGEY_STYLE_KEY)) {
ResourceLocation style = getDefaultStyle().name;
ResourceLocation style = getDefaultStyle().id;
NBTHelper.writeResourceLocation(newData, BOGEY_STYLE_KEY, style);
}
this.bogeyData = newData;
}
public void setBogeyStyle(@NotNull BogeyStyle style) {
ResourceLocation location = style.name;
ResourceLocation location = style.id;
CompoundTag data = this.getBogeyData();
NBTHelper.writeResourceLocation(data, BOGEY_STYLE_KEY, location);
markUpdated();
@ -80,7 +80,7 @@ public abstract class AbstractBogeyBlockEntity extends CachedRenderBBBlockEntity
private CompoundTag createBogeyData() {
CompoundTag nbt = new CompoundTag();
NBTHelper.writeResourceLocation(nbt, BOGEY_STYLE_KEY, getDefaultStyle().name);
NBTHelper.writeResourceLocation(nbt, BOGEY_STYLE_KEY, getDefaultStyle().id);
boolean upsideDown = false;
if (getBlockState().getBlock() instanceof AbstractBogeyBlock<?> bogeyBlock)
upsideDown = bogeyBlock.isUpsideDown(getBlockState());

View file

@ -1,22 +0,0 @@
package com.simibubi.create.content.trains.bogey;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.simibubi.create.content.trains.entity.CarriageBogey;
import dev.engine_room.flywheel.api.visualization.VisualizationContext;
import net.minecraft.nbt.CompoundTag;
public class BackupBogeyRenderer extends BogeyRenderer.CommonRenderer {
public static BackupBogeyRenderer INSTANCE = new BackupBogeyRenderer();
@Override
public void render(CompoundTag bogeyData, float wheelAngle, PoseStack ms, int light, VertexConsumer vb, boolean inContraption) {
}
@Override
public void initialiseContraptionModelData(VisualizationContext context, CarriageBogey carriageBogey) {
}
}

View file

@ -1,16 +1,18 @@
package com.simibubi.create.content.trains.bogey;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.math.Axis;
import com.simibubi.create.foundation.blockEntity.renderer.SafeBlockEntityRenderer;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
import net.minecraft.core.Direction;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
public class BogeyBlockEntityRenderer<T extends BlockEntity> extends SafeBlockEntityRenderer<T> {
public BogeyBlockEntityRenderer(BlockEntityRendererProvider.Context context) {}
public BogeyBlockEntityRenderer(BlockEntityRendererProvider.Context context) {
}
@Override
protected void renderSafe(T be, float partialTicks, PoseStack ms, MultiBufferSource buffer, int light,
@ -18,9 +20,12 @@ public class BogeyBlockEntityRenderer<T extends BlockEntity> extends SafeBlockEn
BlockState blockState = be.getBlockState();
if (be instanceof AbstractBogeyBlockEntity sbbe) {
float angle = sbbe.getVirtualAngle(partialTicks);
if (blockState.getBlock() instanceof AbstractBogeyBlock<?> bogey)
bogey.render(blockState, angle, ms, partialTicks, buffer, light, overlay, sbbe.getStyle(), sbbe.getBogeyData());
if (blockState.getBlock() instanceof AbstractBogeyBlock<?> bogey) {
ms.translate(.5f, .5f, .5f);
if (blockState.getValue(AbstractBogeyBlock.AXIS) == Direction.Axis.X)
ms.mulPose(Axis.YP.rotationDegrees(90));
sbbe.getStyle().render(bogey.getSize(), partialTicks, ms, buffer, light, overlay, angle, sbbe.getBogeyData(), false);
}
}
}
}

View file

@ -1,399 +1,10 @@
package com.simibubi.create.content.trains.bogey;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.IntStream;
import org.jetbrains.annotations.Nullable;
import org.joml.Matrix3fc;
import org.joml.Matrix4fc;
import org.joml.Quaternionfc;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.simibubi.create.content.trains.entity.CarriageBogey;
import com.simibubi.create.foundation.render.CachedBufferer;
import com.simibubi.create.foundation.render.SuperByteBuffer;
import com.simibubi.create.foundation.render.VirtualRenderHelper;
import dev.engine_room.flywheel.api.visualization.VisualizationContext;
import dev.engine_room.flywheel.lib.instance.InstanceTypes;
import dev.engine_room.flywheel.lib.instance.PosedInstance;
import dev.engine_room.flywheel.lib.model.Models;
import dev.engine_room.flywheel.lib.model.baked.PartialModel;
import dev.engine_room.flywheel.lib.transform.Transform;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
/**
* This is a port of the bogey api from Extended Bogeys, If you are looking to implement your own bogeys you can find some helpful resources below:
* <p>
* - <a href="https://github.com/Rabbitminers/Extended-Bogeys/tree/1.18/multiloader/dev/common/src/main/java/com/rabbitminers/extendedbogeys/bogeys/styles">Extended Bogeys (Examples)</a>
* - <a href="https://github.com/Rabbitminers/Extended-Bogeys/blob/1.18/multiloader/dev/API_DOCS.md">Extended Bogeys (API documentation)</a>
* - <a href="https://github.com/Layers-of-Railways/Railway/tree/93e318d1e922b1e992b89b0aceef85a2d545f370/common/src/main/java/com/railwayteam/railways/content/custom_bogeys">Steam n' Rails (Examples)</a>
*/
public abstract class BogeyRenderer {
Map<String, BogeyModelData[]> contraptionModelData = new HashMap<>();
/**
* A common interface for getting transform data for both in-world and in-contraption model data safely from a
* partial model
*
* @param model The key for the model data to instantiate or retrieve
* @param ms The posestack used for contraption model data
* @param inInstancedContraption The type of model needed
* @param size The amount of models needed
* @return A generic transform which can be used for both in-world and in-contraption models
*/
public BogeyModelData[] getTransform(PartialModel model, PoseStack ms, boolean inInstancedContraption, int size) {
return (inInstancedContraption) ? transformContraptionModelData(keyFromModel(model), ms) : createModelData(model, size);
}
/**
* A common interface for getting transform data for both in-world and in-contraption model data safely from a
* blockstate
*
* @param state The key for the model data to instantiate or retrieve
* @param ms The posestack used for contraption model data
* @param inContraption The type of model needed
* @param size The amount of models needed
* @return A generic transform which can be used for both in-world and in-contraption models
*/
public BogeyModelData[] getTransform(BlockState state, PoseStack ms, boolean inContraption, int size) {
return inContraption ? transformContraptionModelData(keyFromModel(state), ms) : createModelData(state, size);
}
/**
* Helper function to collect or create a single model from a partial model used for both in-world and
* in-contraption rendering
*
* @param model The key of the model to be collected or instantiated
* @param ms Posestack to bind the model to if it is within a contraption
* @param inInstancedContraption Type of rendering required
* @return A generic transform which can be used for both in-world and in-contraption models
*/
public BogeyModelData getTransform(PartialModel model, PoseStack ms, boolean inInstancedContraption) {
return inInstancedContraption ? contraptionModelData.get(keyFromModel(model))[0].setTransform(ms)
: BogeyModelData.from(model);
}
/**
* A common interface for getting transform data for blockstates, for a single model
*
* @param state The state of the model to be collected or instantiated
* @param ms Posestack to bind the model to if it is within a contraption
* @param inContraption Type of model required
* @return A generic transform which can be used for both in-world and in-contraption models
*/
public BogeyModelData getTransform(BlockState state, PoseStack ms, boolean inContraption) {
return (inContraption) ? contraptionModelData.get(keyFromModel(state))[0].setTransform(ms)
: BogeyModelData.from(state);
}
/**
* Used for calling both in-world and in-contraption rendering
*
* @param bogeyData Custom data stored on the bogey able to be used for rendering
* @param wheelAngle The angle of the wheel
* @param ms The posestack to render to
* @param light (Optional) Light used for in-world rendering
* @param vb (Optional) Vertex Consumer used for in-world rendering
*/
@OnlyIn(Dist.CLIENT)
public abstract void render(CompoundTag bogeyData, float wheelAngle, PoseStack ms, int light,
VertexConsumer vb, boolean inContraption);
/**
* Used for calling in-contraption rendering ensuring that falsey data is handled correctly
*
* @param bogeyData Custom data stored on the bogey able to be used for rendering
* @param wheelAngle The angle of the wheel
* @param ms The posestack to render to
*/
@OnlyIn(Dist.CLIENT)
public void render(CompoundTag bogeyData, float wheelAngle, PoseStack ms) {
this.render(bogeyData, wheelAngle, ms, 0, null, true);
}
public abstract BogeySizes.BogeySize getSize();
/**
* Used to collect Contraption Model Data for in-contraption rendering, should not be utilised directly when
* rendering to prevent render type mismatch
*
* @param key The key used to access the model
* @param ms Posestack of the contraption to bind the model data to
* @return A generic transform which can be used for both in-world and in-contraption models
*/
private BogeyModelData[] transformContraptionModelData(String key, PoseStack ms) {
BogeyModelData[] modelData = contraptionModelData.get(key);
Arrays.stream(modelData).forEach(modelDataElement -> modelDataElement.setTransform(ms));
return modelData;
}
/**
* Used for in world rendering, creates a set count of model data to be rendered, allowing for a generic response
* when rendering multiple models both in-world and in-contraption for example, with wheels
*
* @param model The partial model of the model data ot be made
* @param size The Amount of models needed
* @return A generic transform which can be used for both in-world and in-contraption models
*/
private BogeyModelData[] createModelData(PartialModel model, int size) {
BogeyModelData[] data = { BogeyModelData.from(model) };
return expandArrayToLength(data, size);
}
/**
* Used for in world rendering, creates a set count of model data to be rendered, allowing for a generic response
* when rendering multiple models both in-world and in-contraption for example, with wheels
*
* @param state The state of the model data to be made
* @param size Amount of models needed
* @return A generic transform which can be used for both in-world and in-contraption models
*/
private BogeyModelData[] createModelData(BlockState state, int size) {
BogeyModelData[] data = { BogeyModelData.from(state) };
return expandArrayToLength(data, size);
}
/**
* Utility function to clone in-world models to a set size to allow for common handling of rendering with multiple
* instances of the same model for example with wheels
*
* @param data An in-world model to be replicated
* @param size Amount of models needed
* @return A generic transform which can be used for both in-world and in-contraption models
*/
private BogeyModelData[] expandArrayToLength(BogeyModelData[] data, int size) {
return Arrays.stream(Collections.nCopies(size, data).toArray())
.flatMap(inner -> Arrays.stream((BogeyModelData[]) inner))
.toArray(BogeyModelData[]::new);
}
/**
* Provides render implementations a point in setup to instantiate all model data to be needed
*
* @param context The visualization context
* @param carriageBogey The bogey to create data for
*/
@OnlyIn(Dist.CLIENT)
public abstract void initialiseContraptionModelData(VisualizationContext context, CarriageBogey carriageBogey);
/**
* Creates instances of models for in-world rendering to a set length from a provided partial model
*
* @param context The visualization context
* @param model Partial model to be instanced
* @param count Amount of models neeeded
*/
public void createModelInstance(VisualizationContext context, PartialModel model, int count) {
var instancer = context.instancerProvider()
.instancer(InstanceTypes.POSED, Models.partial(model));
BogeyModelData[] modelData = IntStream.range(0, count)
.mapToObj(i -> instancer.createInstance())
.map(BogeyModelData::new)
.toArray(BogeyModelData[]::new);
contraptionModelData.put(keyFromModel(model), modelData);
}
/**
* Creates instances of models for in-contraption rendering to a set length from a provided blockstate
*
* @param context The visualization context
* @param state Blockstate of the model to be created
* @param count Amount of models needed
*/
public void createModelInstance(VisualizationContext context, BlockState state, int count) {
var instancer = context.instancerProvider()
.instancer(InstanceTypes.POSED, VirtualRenderHelper.blockModel(state));
BogeyModelData[] modelData = IntStream.range(0, count)
.mapToObj(i -> instancer.createInstance())
.map(BogeyModelData::new)
.toArray(BogeyModelData[]::new);
contraptionModelData.put(keyFromModel(state), modelData);
}
/**
* Creates a single instance of models for in-contraption rendering from a provided blockstate
*
* @param context The visualization context
* @param states Blockstates of the models to be created
*/
public void createModelInstance(VisualizationContext context, BlockState... states) {
for (BlockState state : states)
this.createModelInstance(context, state, 1);
}
/**
* Helper function to create a single model instance for in-contraption rendering
*
* @param context The visualization context
* @param models The type of model to create instances of
*/
public void createModelInstance(VisualizationContext context, PartialModel... models) {
for (PartialModel model : models)
createModelInstance(context, model, 1);
}
/**
* This method is deprecated, use BogeyModelData#render instead, left in
* to avoid existing usages from crashing
*
* @param b The model data itself
* @param ms Pose stack to render to
* @param light light level of the scene
* @param vb Vertex Consumber to render to
* @param <B> Generic alias for both contraption and in-world model data
*/
@Deprecated
public static <B extends Transform<?>> void finalize(B b, PoseStack ms, int light, @Nullable VertexConsumer vb) {
b.scale(1 - 1/512f);
if (b instanceof SuperByteBuffer byteBuf && vb != null)
byteBuf.light(light).renderInto(ms, vb);
}
/**
* Automatic handling for setting empty transforms for all model data
*
*/
public void emptyTransforms() {
for (BogeyModelData[] data : contraptionModelData.values())
for (BogeyModelData model : data)
model.setEmptyTransform();
}
/**
* Automatic handling for updating all model data's light
*
* @param blockLight the blocklight to be applied
* @param skyLight the skylight to be applied
*/
public void updateLight(int blockLight, int skyLight) {
for (BogeyModelData[] data : contraptionModelData.values())
for (BogeyModelData model : data)
model.updateLight(blockLight, skyLight);
}
/**
* Automatic handling for clearing all model data of a contraption
*
*/
public void remove() {
for (BogeyModelData[] data : contraptionModelData.values())
for (BogeyModelData model : data)
model.delete();
contraptionModelData.clear();
}
/**
* Create a model key from a partial model, so it can be easily accessed
*
* @param partialModel the model we want a unique key for
* @return Key of the model
*/
private String keyFromModel(PartialModel partialModel) {
return partialModel.modelLocation().toString();
}
/**
* Create a model key from a blockstate, so it can be easily accessed
*
* @param state Blockstate of the model
* @return Key of the model
*/
private String keyFromModel(BlockState state) {
return state.toString();
}
public static abstract class CommonRenderer extends BogeyRenderer {
@Override
public BogeySizes.BogeySize getSize() {
return null;
}
}
public record BogeyModelData(Transform<?> transform) implements Transform<BogeyModelData> {
public static BogeyModelData from(PartialModel model) {
BlockState air = Blocks.AIR.defaultBlockState();
return new BogeyModelData(CachedBufferer.partial(model, air));
}
public static BogeyModelData from(BlockState model) {
return new BogeyModelData(CachedBufferer.block(model));
}
public void render(PoseStack ms, int light, @Nullable VertexConsumer vb) {
transform.scale(1 - 1/512f);
if (transform instanceof SuperByteBuffer byteBuf && vb != null)
byteBuf.light(light).renderInto(ms, vb);
}
public BogeyModelData setTransform(PoseStack ms) {
if (this.transform instanceof PosedInstance model)
model.setTransform(ms)
.setChanged();
return this;
}
public BogeyModelData setEmptyTransform() {
if (this.transform instanceof PosedInstance model)
model.setZeroTransform()
.setChanged();
return this;
}
public BogeyModelData delete() {
if (this.transform instanceof PosedInstance model)
model.delete();
return this;
}
public BogeyModelData updateLight(int blockLight, int skyLight) {
if (this.transform instanceof PosedInstance model)
model.light(blockLight, skyLight)
.setChanged();
return this;
}
@Override
public BogeyModelData mulPose(Matrix4fc pose) {
this.transform.mulPose(pose);
return this;
}
@Override
public BogeyModelData mulNormal(Matrix3fc normal) {
this.transform.mulNormal(normal);
return this;
}
@Override
public BogeyModelData rotate(Quaternionfc quaternion) {
this.transform.rotate(quaternion);
return this;
}
@Override
public BogeyModelData scale(float factorX, float factorY, float factorZ) {
this.transform.scale(factorX, factorY, factorZ);
return this;
}
@Override
public BogeyModelData translate(float x, float y, float z) {
this.transform.translate(x, y, z);
return this;
}
}
public interface BogeyRenderer {
void render(CompoundTag bogeyData, float wheelAngle, float partialTick, PoseStack poseStack, MultiBufferSource bufferSource, int packedLight, int packedOverlay, boolean inContraption);
}

View file

@ -1,70 +1,78 @@
package com.simibubi.create.content.trains.bogey;
import java.util.Collection;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;
import java.util.Map;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.UnmodifiableView;
import com.simibubi.create.Create;
import net.minecraft.resources.ResourceLocation;
public class BogeySizes {
private static final Collection<BogeySize> BOGEY_SIZES = new HashSet<>();
public static final BogeySize SMALL = new BogeySize(Create.ID, "small", 6.5f / 16f);
public static final BogeySize LARGE = new BogeySize(Create.ID, "large", 12.5f / 16f);
public final class BogeySizes {
private static final Map<ResourceLocation, BogeySize> BOGEY_SIZES = new HashMap<>();
private static final List<BogeySize> SORTED_INCREASING = new ArrayList<>();
private static final List<BogeySize> SORTED_DECREASING = new ArrayList<>();
@UnmodifiableView
private static final Map<ResourceLocation, BogeySize> BOGEY_SIZES_VIEW = Collections.unmodifiableMap(BOGEY_SIZES);
@UnmodifiableView
private static final List<BogeySize> SORTED_INCREASING_VIEW = Collections.unmodifiableList(SORTED_INCREASING);
@UnmodifiableView
private static final List<BogeySize> SORTED_DECREASING_VIEW = Collections.unmodifiableList(SORTED_DECREASING);
public static final BogeySize SMALL = new BogeySize(Create.asResource("small"), 6.5f / 16f);
public static final BogeySize LARGE = new BogeySize(Create.asResource("large"), 12.5f / 16f);
static {
BOGEY_SIZES.add(SMALL);
BOGEY_SIZES.add(LARGE);
register(SMALL);
register(LARGE);
}
public static BogeySize addSize(String modId, String name, float size) {
ResourceLocation location = new ResourceLocation(modId, name);
return addSize(location, size);
private BogeySizes() {
}
public static BogeySize addSize(ResourceLocation location, float size) {
BogeySize customSize = new BogeySize(location, size);
BOGEY_SIZES.add(customSize);
return customSize;
public static void register(BogeySize size) {
ResourceLocation id = size.id();
if (BOGEY_SIZES.containsKey(id)) {
throw new IllegalArgumentException();
}
BOGEY_SIZES.put(id, size);
SORTED_INCREASING.add(size);
SORTED_DECREASING.add(size);
SORTED_INCREASING.sort(Comparator.comparing(BogeySize::wheelRadius));
SORTED_DECREASING.sort(Comparator.comparing(BogeySize::wheelRadius).reversed());
}
public static List<BogeySize> getAllSizesSmallToLarge() {
return BOGEY_SIZES.stream()
.sorted(Comparator.comparing(BogeySize::wheelRadius))
.collect(Collectors.toList());
@UnmodifiableView
public static Map<ResourceLocation, BogeySize> all() {
return BOGEY_SIZES_VIEW;
}
public static List<BogeySize> getAllSizesLargeToSmall() {
List<BogeySize> sizes = getAllSizesSmallToLarge();
Collections.reverse(sizes);
return sizes;
@UnmodifiableView
public static List<BogeySize> allSortedIncreasing() {
return SORTED_INCREASING_VIEW;
}
public static int count() {
return BOGEY_SIZES.size();
@UnmodifiableView
public static List<BogeySize> allSortedDecreasing() {
return SORTED_DECREASING_VIEW;
}
public record BogeySize(ResourceLocation location, float wheelRadius) {
public BogeySize(String modId, String name, float wheelRadius) {
this(new ResourceLocation(modId, name), wheelRadius);
@ApiStatus.Internal
public static void init() {
}
public BogeySize increment() {
List<BogeySize> values = getAllSizesSmallToLarge();
public record BogeySize(ResourceLocation id, float wheelRadius) {
public BogeySize nextBySize() {
List<BogeySize> values = allSortedIncreasing();
int ordinal = values.indexOf(this);
return values.get((ordinal + 1) % values.size());
}
public boolean is(BogeySize size) {
return size.location == this.location;
}
}
public static void init() {
}
}

View file

@ -2,60 +2,53 @@ package com.simibubi.create.content.trains.bogey;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Supplier;
import java.util.stream.Stream;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import com.mojang.blaze3d.vertex.PoseStack;
import com.simibubi.create.AllBogeyStyles;
import com.simibubi.create.AllSoundEvents;
import com.simibubi.create.content.trains.bogey.BogeyRenderer.CommonRenderer;
import com.simibubi.create.content.trains.bogey.BogeySizes.BogeySize;
import com.simibubi.create.content.trains.entity.CarriageBogey;
import com.simibubi.create.foundation.utility.Lang;
import dev.engine_room.flywheel.api.visualization.VisualizationContext;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.core.particles.ParticleOptions;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.world.level.block.Block;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.fml.DistExecutor;
import net.minecraftforge.registries.ForgeRegistries;
public class BogeyStyle {
public final ResourceLocation name;
public final ResourceLocation id;
public final ResourceLocation cycleGroup;
public final Component displayName;
public final ResourceLocation soundType;
public final Supplier<SoundEvent> soundEvent;
public final ParticleOptions contactParticle;
public final ParticleOptions smokeParticle;
public final CompoundTag defaultData;
private Optional<Supplier<? extends CommonRenderer>> commonRendererFactory;
private Map<BogeySizes.BogeySize, ResourceLocation> sizes;
private final Map<BogeySizes.BogeySize, Supplier<? extends AbstractBogeyBlock<?>>> sizes;
@OnlyIn(Dist.CLIENT)
private Map<BogeySizes.BogeySize, SizeRenderData> sizeRenderers;
private Map<BogeySizes.BogeySize, SizeRenderer> sizeRenderers;
@OnlyIn(Dist.CLIENT)
private Optional<CommonRenderer> commonRenderer;
public BogeyStyle(ResourceLocation id, ResourceLocation cycleGroup, Component displayName,
Supplier<SoundEvent> soundEvent, ParticleOptions contactParticle, ParticleOptions smokeParticle,
CompoundTag defaultData, Map<BogeySizes.BogeySize, Supplier<? extends AbstractBogeyBlock<?>>> sizes,
Map<BogeySizes.BogeySize, Supplier<? extends SizeRenderer>> sizeRenderers) {
public BogeyStyle(ResourceLocation name, ResourceLocation cycleGroup, Component displayName,
ResourceLocation soundType, ParticleOptions contactParticle, ParticleOptions smokeParticle,
CompoundTag defaultData, Map<BogeySizes.BogeySize, ResourceLocation> sizes,
Map<BogeySizes.BogeySize, Supplier<SizeRenderData>> sizeRenderers,
Optional<Supplier<? extends CommonRenderer>> commonRenderer) {
this.name = name;
this.id = id;
this.cycleGroup = cycleGroup;
this.displayName = displayName;
this.soundType = soundType;
this.soundEvent = soundEvent;
this.contactParticle = contactParticle;
this.smokeParticle = smokeParticle;
this.defaultData = defaultData;
@ -64,9 +57,6 @@ public class BogeyStyle {
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> {
this.sizeRenderers = new HashMap<>();
sizeRenderers.forEach((k, v) -> this.sizeRenderers.put(k, v.get()));
this.commonRendererFactory = commonRenderer;
this.commonRenderer = commonRenderer.map(Supplier::get);
});
}
@ -74,60 +64,109 @@ public class BogeyStyle {
return AllBogeyStyles.getCycleGroup(cycleGroup);
}
public Block getNextBlock(BogeySizes.BogeySize currentSize) {
return Stream.iterate(currentSize.increment(), BogeySizes.BogeySize::increment)
.filter(sizes::containsKey)
.findFirst()
.map(this::getBlockOfSize)
.orElse(getBlockOfSize(currentSize));
}
public Block getBlockOfSize(BogeySizes.BogeySize size) {
return ForgeRegistries.BLOCKS.getValue(sizes.get(size));
}
public Set<BogeySizes.BogeySize> validSizes() {
return sizes.keySet();
}
@NotNull
public SoundEvent getSoundType() {
AllSoundEvents.SoundEntry entry = AllSoundEvents.ALL.get(this.soundType);
if (entry == null || entry.getMainEvent() == null) entry = AllSoundEvents.TRAIN2;
return entry.getMainEvent();
public AbstractBogeyBlock<?> getBlockForSize(BogeySizes.BogeySize size) {
return sizes.get(size).get();
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public AbstractBogeyBlock<?> getNextBlock(BogeySizes.BogeySize currentSize) {
return Stream.iterate(currentSize.nextBySize(), BogeySizes.BogeySize::nextBySize)
.filter(sizes::containsKey)
.findFirst()
.map(this::getBlockForSize)
.orElse((AbstractBogeyBlock) getBlockForSize(currentSize));
}
@OnlyIn(Dist.CLIENT)
public BogeyRenderer createRendererInstance(BogeySizes.BogeySize size) {
return this.sizeRenderers.get(size).createRenderInstance();
public void render(BogeySize size, float partialTick, PoseStack poseStack, MultiBufferSource buffers, int light, int overlay, float wheelAngle, @Nullable CompoundTag bogeyData, boolean inContraption) {
if (bogeyData == null)
bogeyData = new CompoundTag();
poseStack.translate(0, -1.5 - 1 / 128f, 0);
SizeRenderer renderer = sizeRenderers.get(size);
if (renderer != null) {
renderer.renderer.render(bogeyData, wheelAngle, partialTick, poseStack, buffers, light, overlay, inContraption);
}
}
@OnlyIn(Dist.CLIENT)
public BogeyRenderer getInWorldRenderInstance(BogeySizes.BogeySize size) {
SizeRenderData sizeData = this.sizeRenderers.get(size);
return sizeData != null ? sizeData.getInWorldInstance() : BackupBogeyRenderer.INSTANCE;
@Nullable
public BogeyVisual createVisual(VisualizationContext ctx, CarriageBogey bogey, float partialTick) {
SizeRenderer renderer = sizeRenderers.get(bogey.getSize());
if (renderer != null) {
return renderer.visualizer.createVisual(ctx, bogey, partialTick);
}
public Optional<CommonRenderer> getInWorldCommonRenderInstance() {
return this.commonRenderer;
}
public Optional<CommonRenderer> getNewCommonRenderInstance() {
return this.commonRendererFactory.map(Supplier::get);
}
public BogeyVisual createVisual(CarriageBogey bogey, BogeySizes.BogeySize size, VisualizationContext context) {
return new BogeyVisual(bogey, this, size, context);
return null;
}
@OnlyIn(Dist.CLIENT)
public record SizeRenderData(Supplier<? extends BogeyRenderer> rendererFactory, BogeyRenderer instance) {
public BogeyRenderer createRenderInstance() {
return rendererFactory.get();
public record SizeRenderer(BogeyRenderer renderer, BogeyVisualizer visualizer) {
}
public BogeyRenderer getInWorldInstance() {
return instance;
public static class Builder {
protected final ResourceLocation id;
protected final ResourceLocation cycleGroup;
protected final Map<BogeySizes.BogeySize, Supplier<? extends AbstractBogeyBlock<?>>> sizes = new HashMap<>();
protected Component displayName = Lang.translateDirect("bogey.style.invalid");
protected Supplier<SoundEvent> soundEvent = AllSoundEvents.TRAIN2::getMainEvent;
protected ParticleOptions contactParticle = ParticleTypes.CRIT;
protected ParticleOptions smokeParticle = ParticleTypes.POOF;
protected CompoundTag defaultData = new CompoundTag();
protected final Map<BogeySizes.BogeySize, Supplier<? extends SizeRenderer>> sizeRenderers = new HashMap<>();
public Builder(ResourceLocation id, ResourceLocation cycleGroup) {
this.id = id;
this.cycleGroup = cycleGroup;
}
public Builder displayName(Component displayName) {
this.displayName = displayName;
return this;
}
public Builder soundEvent(Supplier<SoundEvent> soundEvent) {
this.soundEvent = soundEvent;
return this;
}
public Builder contactParticle(ParticleOptions contactParticle) {
this.contactParticle = contactParticle;
return this;
}
public Builder smokeParticle(ParticleOptions smokeParticle) {
this.smokeParticle = smokeParticle;
return this;
}
public Builder defaultData(CompoundTag defaultData) {
this.defaultData = defaultData;
return this;
}
public Builder size(BogeySizes.BogeySize size, Supplier<? extends AbstractBogeyBlock<?>> block,
Supplier<? extends SizeRenderer> renderer) {
this.sizes.put(size, block);
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> {
this.sizeRenderers.put(size, renderer);
});
return this;
}
public BogeyStyle build() {
BogeyStyle entry = new BogeyStyle(id, cycleGroup, displayName, soundEvent, contactParticle, smokeParticle,
defaultData, sizes, sizeRenderers);
AllBogeyStyles.BOGEY_STYLES.put(id, entry);
AllBogeyStyles.CYCLE_GROUPS.computeIfAbsent(cycleGroup, l -> new HashMap<>())
.put(id, entry);
return entry;
}
}
}

View file

@ -1,65 +1,13 @@
package com.simibubi.create.content.trains.bogey;
import java.util.Optional;
import com.mojang.blaze3d.vertex.PoseStack;
import com.simibubi.create.content.trains.entity.CarriageBogey;
import com.simibubi.create.content.trains.entity.CarriageContraptionEntity;
import com.simibubi.create.foundation.utility.AnimationTickHolder;
import dev.engine_room.flywheel.api.visualization.VisualizationContext;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.BlockAndTintGetter;
import net.minecraft.world.level.LightLayer;
import net.minecraft.world.phys.Vec3;
public interface BogeyVisual {
void update(float wheelAngle, PoseStack poseStack);
public final class BogeyVisual {
private final BogeySizes.BogeySize size;
private final BogeyStyle style;
void hide();
public final CarriageBogey bogey;
public final BogeyRenderer renderer;
public final Optional<BogeyRenderer.CommonRenderer> commonRenderer;
void updateLight(int packedLight);
public BogeyVisual(CarriageBogey bogey, BogeyStyle style, BogeySizes.BogeySize size,
VisualizationContext context) {
this.bogey = bogey;
this.size = size;
this.style = style;
this.renderer = this.style.createRendererInstance(this.size);
this.commonRenderer = this.style.getNewCommonRenderInstance();
commonRenderer.ifPresent(bogeyRenderer -> bogeyRenderer.initialiseContraptionModelData(context, bogey));
renderer.initialiseContraptionModelData(context, bogey);
}
public void beginFrame(float wheelAngle, PoseStack ms) {
if (ms == null) {
renderer.emptyTransforms();
return;
}
commonRenderer.ifPresent(bogeyRenderer -> bogeyRenderer.render(bogey.bogeyData, wheelAngle, ms));
renderer.render(bogey.bogeyData, wheelAngle, ms);
}
public void updateLight(BlockAndTintGetter world, CarriageContraptionEntity entity) {
var lightPos = BlockPos.containing(getLightPos(entity));
commonRenderer
.ifPresent(bogeyRenderer -> bogeyRenderer.updateLight(world.getBrightness(LightLayer.BLOCK, lightPos),
world.getBrightness(LightLayer.SKY, lightPos)));
renderer.updateLight(world.getBrightness(LightLayer.BLOCK, lightPos),
world.getBrightness(LightLayer.SKY, lightPos));
}
private Vec3 getLightPos(CarriageContraptionEntity entity) {
return bogey.getAnchorPosition() != null ? bogey.getAnchorPosition()
: entity.getLightProbePosition(AnimationTickHolder.getPartialTicks());
}
@FunctionalInterface
interface BogeyVisualFactory {
BogeyVisual create(CarriageBogey bogey, BogeySizes.BogeySize size, VisualizationContext context);
}
void delete();
}

View file

@ -0,0 +1,10 @@
package com.simibubi.create.content.trains.bogey;
import com.simibubi.create.content.trains.entity.CarriageBogey;
import dev.engine_room.flywheel.api.visualization.VisualizationContext;
@FunctionalInterface
public interface BogeyVisualizer {
BogeyVisual createVisual(VisualizationContext ctx, CarriageBogey bogey, float partialTick);
}

View file

@ -1,134 +1,109 @@
package com.simibubi.create.content.trains.bogey;
import static com.simibubi.create.AllPartialModels.BOGEY_DRIVE;
import static com.simibubi.create.AllPartialModels.BOGEY_FRAME;
import static com.simibubi.create.AllPartialModels.BOGEY_PIN;
import static com.simibubi.create.AllPartialModels.BOGEY_PISTON;
import static com.simibubi.create.AllPartialModels.LARGE_BOGEY_WHEELS;
import static com.simibubi.create.AllPartialModels.SMALL_BOGEY_WHEELS;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllPartialModels;
import com.simibubi.create.content.kinetics.simpleRelays.ShaftBlock;
import com.simibubi.create.content.trains.entity.CarriageBogey;
import com.simibubi.create.foundation.render.CachedBufferer;
import com.simibubi.create.foundation.render.SuperByteBuffer;
import com.simibubi.create.foundation.utility.AngleHelper;
import com.simibubi.create.foundation.utility.Iterate;
import dev.engine_room.flywheel.api.visualization.VisualizationContext;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.level.block.Blocks;
public class StandardBogeyRenderer {
public static class CommonStandardBogeyRenderer extends BogeyRenderer.CommonRenderer {
public class StandardBogeyRenderer implements BogeyRenderer {
@Override
public void initialiseContraptionModelData(VisualizationContext context, CarriageBogey carriageBogey) {
createModelInstance(context, AllBlocks.SHAFT.getDefaultState()
.setValue(ShaftBlock.AXIS, Direction.Axis.Z), 2);
}
public void render(CompoundTag bogeyData, float wheelAngle, float partialTick, PoseStack poseStack, MultiBufferSource bufferSource, int light, int overlay, boolean inContraption) {
VertexConsumer buffer = bufferSource.getBuffer(RenderType.cutoutMipped());
@Override
public void render(CompoundTag bogeyData, float wheelAngle, PoseStack ms, int light, VertexConsumer vb, boolean inContraption) {
boolean inInstancedContraption = vb == null;
BogeyModelData[] shafts = getTransform(AllBlocks.SHAFT.getDefaultState()
.setValue(ShaftBlock.AXIS, Direction.Axis.Z), ms, inInstancedContraption, 2);
SuperByteBuffer shaft = CachedBufferer.block(AllBlocks.SHAFT.getDefaultState()
.setValue(ShaftBlock.AXIS, Direction.Axis.Z));
for (int i : Iterate.zeroAndOne) {
shafts[i].translate(-.5f, .25f, i * -1)
shaft.translate(-.5f, .25f, i * -1)
.center()
.rotateZDegrees(wheelAngle)
.uncenter()
.render(ms, light, vb);
}
.light(light)
.overlay(overlay)
.renderInto(poseStack, buffer);
}
}
public static class SmallStandardBogeyRenderer extends BogeyRenderer {
public static class Small extends StandardBogeyRenderer {
@Override
public void initialiseContraptionModelData(VisualizationContext context, CarriageBogey carriageBogey) {
createModelInstance(context, SMALL_BOGEY_WHEELS, 2);
createModelInstance(context, BOGEY_FRAME);
}
public void render(CompoundTag bogeyData, float wheelAngle, float partialTick, PoseStack poseStack, MultiBufferSource bufferSource, int light, int overlay, boolean inContraption) {
super.render(bogeyData, wheelAngle, partialTick, poseStack, bufferSource, light, overlay, inContraption);
VertexConsumer buffer = bufferSource.getBuffer(RenderType.cutoutMipped());
@Override
public BogeySizes.BogeySize getSize() {
return BogeySizes.SMALL;
}
CachedBufferer.partial(AllPartialModels.BOGEY_FRAME, Blocks.AIR.defaultBlockState())
.scale(1 - 1 / 512f)
.light(light)
.overlay(overlay)
.renderInto(poseStack, buffer);
@Override
public void render(CompoundTag bogeyData, float wheelAngle, PoseStack ms, int light, VertexConsumer vb, boolean inContraption) {
boolean inInstancedContraption = vb == null;
getTransform(BOGEY_FRAME, ms, inInstancedContraption)
.render(ms, light, vb);
BogeyModelData[] wheels = getTransform(SMALL_BOGEY_WHEELS, ms, inInstancedContraption, 2);
SuperByteBuffer wheels = CachedBufferer.partial(AllPartialModels.SMALL_BOGEY_WHEELS, Blocks.AIR.defaultBlockState());
for (int side : Iterate.positiveAndNegative) {
if (!inInstancedContraption)
ms.pushPose();
wheels[(side + 1)/2]
.translate(0, 12 / 16f, side)
wheels.translate(0, 12 / 16f, side)
.rotateXDegrees(wheelAngle)
.render(ms, light, vb);
if (!inInstancedContraption)
ms.popPose();
.light(light)
.overlay(overlay)
.renderInto(poseStack, buffer);
}
}
}
public static class LargeStandardBogeyRenderer extends BogeyRenderer {
public static class Large extends StandardBogeyRenderer {
@Override
public void initialiseContraptionModelData(VisualizationContext context, CarriageBogey carriageBogey) {
createModelInstance(context, LARGE_BOGEY_WHEELS, BOGEY_DRIVE, BOGEY_PISTON, BOGEY_PIN);
createModelInstance(context, AllBlocks.SHAFT.getDefaultState()
.setValue(ShaftBlock.AXIS, Direction.Axis.X), 2);
}
public void render(CompoundTag bogeyData, float wheelAngle, float partialTick, PoseStack poseStack, MultiBufferSource bufferSource, int light, int overlay, boolean inContraption) {
super.render(bogeyData, wheelAngle, partialTick, poseStack, bufferSource, light, overlay, inContraption);
@Override
public BogeySizes.BogeySize getSize() {
return BogeySizes.LARGE;
}
@Override
public void render(CompoundTag bogeyData, float wheelAngle, PoseStack ms, int light, VertexConsumer vb, boolean inContraption) {
boolean inInstancedContraption = vb == null;
BogeyModelData[] secondaryShafts = getTransform(AllBlocks.SHAFT.getDefaultState()
.setValue(ShaftBlock.AXIS, Direction.Axis.X), ms, inInstancedContraption, 2);
VertexConsumer buffer = bufferSource.getBuffer(RenderType.cutoutMipped());
SuperByteBuffer secondaryShaft = CachedBufferer.block(AllBlocks.SHAFT.getDefaultState()
.setValue(ShaftBlock.AXIS, Direction.Axis.X));
for (int i : Iterate.zeroAndOne) {
secondaryShafts[i]
.translate(-.5f, .25f, .5f + i * -2)
secondaryShaft.translate(-.5f, .25f, .5f + i * -2)
.center()
.rotateXDegrees(wheelAngle)
.uncenter()
.render(ms, light, vb);
.light(light)
.overlay(overlay)
.renderInto(poseStack, buffer);
}
getTransform(BOGEY_DRIVE, ms, inInstancedContraption)
.render(ms, light, vb);
CachedBufferer.partial(AllPartialModels.BOGEY_DRIVE, Blocks.AIR.defaultBlockState())
.scale(1 - 1 / 512f)
.light(light)
.overlay(overlay)
.renderInto(poseStack, buffer);
getTransform(BOGEY_PISTON, ms, inInstancedContraption)
CachedBufferer.partial(AllPartialModels.BOGEY_PISTON, Blocks.AIR.defaultBlockState())
.translate(0, 0, 1 / 4f * Math.sin(AngleHelper.rad(wheelAngle)))
.render(ms, light, vb);
.light(light)
.overlay(overlay)
.renderInto(poseStack, buffer);
if (!inInstancedContraption)
ms.pushPose();
getTransform(LARGE_BOGEY_WHEELS, ms, inInstancedContraption)
CachedBufferer.partial(AllPartialModels.LARGE_BOGEY_WHEELS, Blocks.AIR.defaultBlockState())
.translate(0, 1, 0)
.rotateXDegrees(wheelAngle)
.render(ms, light, vb);
.light(light)
.overlay(overlay)
.renderInto(poseStack, buffer);
getTransform(BOGEY_PIN, ms, inInstancedContraption)
CachedBufferer.partial(AllPartialModels.BOGEY_PIN, Blocks.AIR.defaultBlockState())
.translate(0, 1, 0)
.rotateXDegrees(wheelAngle)
.translate(0, 1 / 4f, 0)
.rotateXDegrees(-wheelAngle)
.render(ms, light, vb);
if (!inInstancedContraption)
ms.popPose();
.light(light)
.overlay(overlay)
.renderInto(poseStack, buffer);
}
}
}

View file

@ -0,0 +1,215 @@
package com.simibubi.create.content.trains.bogey;
import com.mojang.blaze3d.vertex.PoseStack;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllPartialModels;
import com.simibubi.create.content.kinetics.simpleRelays.ShaftBlock;
import com.simibubi.create.content.trains.entity.CarriageBogey;
import com.simibubi.create.foundation.render.VirtualRenderHelper;
import com.simibubi.create.foundation.utility.AngleHelper;
import dev.engine_room.flywheel.api.visualization.VisualizationContext;
import dev.engine_room.flywheel.lib.instance.InstanceTypes;
import dev.engine_room.flywheel.lib.instance.TransformedInstance;
import dev.engine_room.flywheel.lib.model.Models;
import net.minecraft.core.Direction;
public class StandardBogeyVisual implements BogeyVisual {
private final TransformedInstance shaft1;
private final TransformedInstance shaft2;
public StandardBogeyVisual(VisualizationContext ctx, CarriageBogey bogey, float partialTick) {
var shaftInstancer = ctx.instancerProvider()
.instancer(InstanceTypes.TRANSFORMED, VirtualRenderHelper.blockModel(AllBlocks.SHAFT.getDefaultState()
.setValue(ShaftBlock.AXIS, Direction.Axis.Z)));
shaft1 = shaftInstancer.createInstance();
shaft2 = shaftInstancer.createInstance();
}
@Override
public void update(float wheelAngle, PoseStack poseStack) {
shaft1.setTransform(poseStack)
.translate(-.5f, .25f, 0)
.center()
.rotateZDegrees(wheelAngle)
.uncenter()
.setChanged();
shaft2.setTransform(poseStack)
.translate(-.5f, .25f, -1)
.center()
.rotateZDegrees(wheelAngle)
.uncenter()
.setChanged();
}
@Override
public void hide() {
shaft1.setZeroTransform().setChanged();
shaft2.setZeroTransform().setChanged();
}
@Override
public void updateLight(int packedLight) {
shaft1.light(packedLight).setChanged();
shaft2.light(packedLight).setChanged();
}
@Override
public void delete() {
shaft1.delete();
shaft2.delete();
}
public static class Small extends StandardBogeyVisual {
private final TransformedInstance frame;
private final TransformedInstance wheel1;
private final TransformedInstance wheel2;
public Small(VisualizationContext ctx, CarriageBogey bogey, float partialTick) {
super(ctx, bogey, partialTick);
var wheelInstancer = ctx.instancerProvider()
.instancer(InstanceTypes.TRANSFORMED, Models.partial(AllPartialModels.SMALL_BOGEY_WHEELS));
frame = ctx.instancerProvider()
.instancer(InstanceTypes.TRANSFORMED, Models.partial(AllPartialModels.BOGEY_FRAME))
.createInstance();
wheel1 = wheelInstancer.createInstance();
wheel2 = wheelInstancer.createInstance();
}
@Override
public void update(float wheelAngle, PoseStack poseStack) {
super.update(wheelAngle, poseStack);
wheel1.setTransform(poseStack)
.translate(0, 12 / 16f, -1)
.rotateXDegrees(wheelAngle)
.setChanged();
wheel2.setTransform(poseStack)
.translate(0, 12 / 16f, 1)
.rotateXDegrees(wheelAngle)
.setChanged();
frame.setTransform(poseStack)
.scale(1 - 1 / 512f)
.setChanged();
}
@Override
public void hide() {
super.hide();
frame.setZeroTransform().setChanged();
wheel1.setZeroTransform().setChanged();
wheel2.setZeroTransform().setChanged();
}
@Override
public void updateLight(int packedLight) {
super.updateLight(packedLight);
frame.light(packedLight).setChanged();
wheel1.light(packedLight).setChanged();
wheel2.light(packedLight).setChanged();
}
@Override
public void delete() {
super.delete();
frame.delete();
wheel1.delete();
wheel2.delete();
}
}
public static class Large extends StandardBogeyVisual {
private final TransformedInstance secondaryShaft1;
private final TransformedInstance secondaryShaft2;
private final TransformedInstance drive;
private final TransformedInstance piston;
private final TransformedInstance wheels;
private final TransformedInstance pin;
public Large(VisualizationContext ctx, CarriageBogey bogey, float partialTick) {
super(ctx, bogey, partialTick);
var secondaryShaftInstancer = ctx.instancerProvider()
.instancer(InstanceTypes.TRANSFORMED, VirtualRenderHelper.blockModel(AllBlocks.SHAFT.getDefaultState()
.setValue(ShaftBlock.AXIS, Direction.Axis.X)));
secondaryShaft1 = secondaryShaftInstancer.createInstance();
secondaryShaft2 = secondaryShaftInstancer.createInstance();
drive = ctx.instancerProvider()
.instancer(InstanceTypes.TRANSFORMED, Models.partial(AllPartialModels.BOGEY_DRIVE))
.createInstance();
piston = ctx.instancerProvider()
.instancer(InstanceTypes.TRANSFORMED, Models.partial(AllPartialModels.BOGEY_PISTON))
.createInstance();
wheels = ctx.instancerProvider()
.instancer(InstanceTypes.TRANSFORMED, Models.partial(AllPartialModels.LARGE_BOGEY_WHEELS))
.createInstance();
pin = ctx.instancerProvider()
.instancer(InstanceTypes.TRANSFORMED, Models.partial(AllPartialModels.BOGEY_PIN))
.createInstance();
}
@Override
public void update(float wheelAngle, PoseStack poseStack) {
super.update(wheelAngle, poseStack);
secondaryShaft1.setTransform(poseStack)
.translate(-.5f, .25f, .5f)
.center()
.rotateXDegrees(wheelAngle)
.uncenter()
.setChanged();
secondaryShaft2.setTransform(poseStack)
.translate(-.5f, .25f, -1.5f)
.center()
.rotateXDegrees(wheelAngle)
.uncenter()
.setChanged();
drive.setTransform(poseStack)
.scale(1 - 1/512f)
.setChanged();
piston.setTransform(poseStack)
.translate(0, 0, 1 / 4f * Math.sin(AngleHelper.rad(wheelAngle)))
.setChanged();
wheels.setTransform(poseStack)
.translate(0, 1, 0)
.rotateXDegrees(wheelAngle)
.setChanged();
pin.setTransform(poseStack)
.translate(0, 1, 0)
.rotateXDegrees(wheelAngle)
.translate(0, 1 / 4f, 0)
.rotateXDegrees(-wheelAngle)
.setChanged();
}
@Override
public void hide() {
super.hide();
secondaryShaft1.setZeroTransform().setChanged();
secondaryShaft2.setZeroTransform().setChanged();
wheels.setZeroTransform().setChanged();
drive.setZeroTransform().setChanged();
piston.setZeroTransform().setChanged();
pin.setZeroTransform().setChanged();
}
@Override
public void updateLight(int packedLight) {
super.updateLight(packedLight);
secondaryShaft1.light(packedLight).setChanged();
secondaryShaft2.light(packedLight).setChanged();
wheels.light(packedLight).setChanged();
drive.light(packedLight).setChanged();
piston.light(packedLight).setChanged();
pin.light(packedLight).setChanged();
}
@Override
public void delete() {
super.delete();
secondaryShaft1.delete();
secondaryShaft2.delete();
wheels.delete();
drive.delete();
piston.delete();
pin.delete();
}
}
}

View file

@ -9,8 +9,8 @@ import com.simibubi.create.AllBogeyStyles;
import com.simibubi.create.Create;
import com.simibubi.create.content.trains.bogey.AbstractBogeyBlock;
import com.simibubi.create.content.trains.bogey.AbstractBogeyBlockEntity;
import com.simibubi.create.content.trains.bogey.BogeySizes.BogeySize;
import com.simibubi.create.content.trains.bogey.BogeyStyle;
import com.simibubi.create.content.trains.bogey.BogeyVisual;
import com.simibubi.create.content.trains.graph.DimensionPalette;
import com.simibubi.create.content.trains.graph.TrackGraph;
import com.simibubi.create.foundation.utility.AngleHelper;
@ -21,7 +21,6 @@ import com.simibubi.create.foundation.utility.RegisteredObjects;
import com.simibubi.create.foundation.utility.VecHelper;
import com.simibubi.create.foundation.utility.animation.LerpedFloat;
import dev.engine_room.flywheel.api.visualization.VisualizationContext;
import net.minecraft.core.Direction.Axis;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
@ -184,7 +183,7 @@ public class CarriageBogey {
tag.put("Points", points.serializeEach(tp -> tp.write(dimensions)));
tag.putBoolean("UpsideDown", upsideDown);
bogeyData.putBoolean(UPSIDE_DOWN_KEY, upsideDown);
NBTHelper.writeResourceLocation(bogeyData, BOGEY_STYLE_KEY, getStyle().name);
NBTHelper.writeResourceLocation(bogeyData, BOGEY_STYLE_KEY, getStyle().id);
tag.put(BOGEY_DATA_KEY, bogeyData);
return tag;
}
@ -199,20 +198,20 @@ public class CarriageBogey {
return new CarriageBogey(type, upsideDown, data, points.getFirst(), points.getSecond());
}
public BogeyVisual createVisual(VisualizationContext context) {
return this.getStyle().createVisual(this, type.getSize(), context);
}
public BogeyStyle getStyle() {
ResourceLocation location = NBTHelper.readResourceLocation(this.bogeyData, BOGEY_STYLE_KEY);
BogeyStyle style = AllBogeyStyles.BOGEY_STYLES.get(location);
return style != null ? style : AllBogeyStyles.STANDARD; // just for safety
}
public BogeySize getSize() {
return type.getSize();
}
private CompoundTag createBogeyData() {
BogeyStyle style = type != null ? type.getDefaultStyle() : AllBogeyStyles.STANDARD;
CompoundTag nbt = style.defaultData != null ? style.defaultData : new CompoundTag();
NBTHelper.writeResourceLocation(nbt, BOGEY_STYLE_KEY, style.name);
NBTHelper.writeResourceLocation(nbt, BOGEY_STYLE_KEY, style.id);
nbt.putBoolean(UPSIDE_DOWN_KEY, isUpsideDown());
return nbt;
}

View file

@ -736,6 +736,7 @@ public class CarriageContraptionEntity extends OrientedContraptionEntity {
dimensional.updateRenderedCutoff();
}
// FIXME: entities should not reference their visual in any way
@OnlyIn(Dist.CLIENT)
private WeakReference<CarriageContraptionVisual> instanceHolder;

View file

@ -66,8 +66,8 @@ public class CarriageContraptionEntityRenderer extends ContraptionEntityRenderer
int light = getBogeyLightCoords(entity, bogey, partialTicks);
bogey.type.render(null, bogey.wheelAngle.getValue(partialTicks), ms, partialTicks, buffers, light,
overlay, bogey.getStyle(), bogey.bogeyData);
bogey.getStyle().render(bogey.getSize(), partialTicks, ms, buffers, light,
overlay, bogey.wheelAngle.getValue(partialTicks), bogey.bogeyData, true);
ms.popPose();
}
@ -75,7 +75,6 @@ public class CarriageContraptionEntityRenderer extends ContraptionEntityRenderer
bogey.updateCouplingAnchor(position, viewXRot, viewYRot, bogeySpacing, partialTicks, bogey.isLeading);
if (!carriage.isOnTwoBogeys())
bogey.updateCouplingAnchor(position, viewXRot, viewYRot, bogeySpacing, partialTicks, !bogey.isLeading);
});
}
@ -99,7 +98,6 @@ public class CarriageContraptionEntityRenderer extends ContraptionEntityRenderer
}
public static int getBogeyLightCoords(CarriageContraptionEntity entity, CarriageBogey bogey, float partialTicks) {
var lightPos = BlockPos.containing(
Objects.requireNonNullElseGet(bogey.getAnchorPosition(), () -> entity.getLightProbePosition(partialTicks)));

View file

@ -1,10 +1,10 @@
package com.simibubi.create.content.trains.entity;
import org.jetbrains.annotations.Nullable;
import org.joml.Vector3f;
import com.mojang.blaze3d.vertex.PoseStack;
import com.simibubi.create.content.contraptions.render.ContraptionVisual;
import com.simibubi.create.content.trains.bogey.BogeyRenderer;
import com.simibubi.create.content.trains.bogey.BogeyVisual;
import com.simibubi.create.foundation.utility.Couple;
import com.simibubi.create.foundation.utility.Iterate;
@ -14,16 +14,16 @@ import dev.engine_room.flywheel.api.visualization.VisualizationContext;
import dev.engine_room.flywheel.lib.transform.TransformStack;
public class CarriageContraptionVisual extends ContraptionVisual<CarriageContraptionEntity> {
private final PoseStack ms = new PoseStack();
@Nullable
private Carriage carriage;
private Couple<BogeyVisual> bogeys;
private Couple<Boolean> bogeyHidden;
@Nullable
private Couple<@Nullable VisualizedBogey> bogeys;
private Couple<Boolean> bogeyHidden = Couple.create(() -> false);
public CarriageContraptionVisual(VisualizationContext context, CarriageContraptionEntity entity, float partialTick) {
super(context, entity, partialTick);
bogeyHidden = Couple.create(() -> false);
entity.bindInstance(this);
}
@ -32,8 +32,7 @@ public class CarriageContraptionVisual extends ContraptionVisual<CarriageContrap
carriage = entity.getCarriage();
if (carriage != null) {
bogeys = carriage.bogeys.mapNotNullWithParam((bogey, manager) -> bogey.getStyle()
.createVisual(bogey, bogey.type.getSize(), manager), visualizationContext);
bogeys = carriage.bogeys.mapNotNull(bogey -> VisualizedBogey.of(visualizationContext, bogey, pt));
}
super.init(pt);
@ -62,26 +61,27 @@ public class CarriageContraptionVisual extends ContraptionVisual<CarriageContrap
ms.pushPose();
Vector3f instancePosition = getVisualPosition(partialTick);
Vector3f visualPosition = getVisualPosition(partialTick);
TransformStack.of(ms)
.translate(instancePosition);
.translate(visualPosition);
for (boolean current : Iterate.trueAndFalse) {
BogeyVisual instance = bogeys.get(current);
if (instance == null)
VisualizedBogey visualizedBogey = bogeys.get(current);
if (visualizedBogey == null)
continue;
if (bogeyHidden.get(current)) {
instance.beginFrame(0, null);
visualizedBogey.visual.hide();
continue;
}
ms.pushPose();
CarriageBogey bogey = instance.bogey;
CarriageBogey bogey = visualizedBogey.bogey;
CarriageContraptionEntityRenderer.translateBogey(ms, bogey, bogeySpacing, viewYRot, viewXRot, partialTick);
ms.translate(0, -1.5 - 1 / 128f, 0);
instance.beginFrame(bogey.wheelAngle.getValue(partialTick), ms);
visualizedBogey.visual.update(bogey.wheelAngle.getValue(partialTick), ms);
ms.popPose();
}
@ -95,9 +95,11 @@ public class CarriageContraptionVisual extends ContraptionVisual<CarriageContrap
if (bogeys == null)
return;
bogeys.forEach(instance -> {
if (instance != null)
instance.updateLight(level, entity);
bogeys.forEach(bogey -> {
if (bogey != null) {
int packedLight = CarriageContraptionEntityRenderer.getBogeyLightCoords(entity, bogey.bogey, partialTick);
bogey.visual.updateLight(packedLight);
}
});
}
@ -108,11 +110,21 @@ public class CarriageContraptionVisual extends ContraptionVisual<CarriageContrap
if (bogeys == null)
return;
bogeys.forEach(instance -> {
if (instance != null) {
instance.commonRenderer.ifPresent(BogeyRenderer::remove);
instance.renderer.remove();
bogeys.forEach(bogey -> {
if (bogey != null) {
bogey.visual.delete();
}
});
}
private record VisualizedBogey(CarriageBogey bogey, BogeyVisual visual) {
@Nullable
static VisualizedBogey of(VisualizationContext ctx, CarriageBogey bogey, float partialTick) {
BogeyVisual visual = bogey.getStyle().createVisual(ctx, bogey, partialTick);
if (visual == null) {
return null;
}
return new VisualizedBogey(bogey, visual);
}
}
}

View file

@ -42,7 +42,7 @@ public class CarriageSounds {
public CarriageSounds(CarriageContraptionEntity entity) {
this.entity = entity;
bogeySounds = entity.getCarriage().bogeys.map(bogey ->
bogey != null && bogey.getStyle() != null ? bogey.getStyle().getSoundType()
bogey != null && bogey.getStyle() != null ? bogey.getStyle().soundEvent.get()
: AllSoundEvents.TRAIN2.getMainEvent());
closestBogeySound = bogeySounds.getFirst();
distanceFactor = LerpedFloat.linear();
@ -94,7 +94,7 @@ public class CarriageSounds {
relevantBogey = bogeys.getFirst();
}
if (relevantBogey != null) {
closestBogeySound = relevantBogey.getStyle().getSoundType();
closestBogeySound = relevantBogey.getStyle().soundEvent.get();
}
Vec3 toCarriage = distance1 > distance2 ? toBogey2 : toBogey1;

View file

@ -55,6 +55,10 @@ public class Couple<T> extends Pair<T, T> implements Iterable<T> {
return Couple.create(function.apply(first), function.apply(second));
}
public <S> Couple<S> mapNotNull(Function<T, S> function) {
return Couple.create(first != null ? function.apply(first) : null, second != null ? function.apply(second) : null);
}
public <S> Couple<S> mapWithContext(BiFunction<T, Boolean, S> function) {
return Couple.create(function.apply(first, true), function.apply(second, false));
}