avoid floating point accuracy errors at high coordinate values.

refactor contraption model translation to be a method in AbstractContraptionEntity.
 - this simplifies the setup required for the fast rendering.
This commit is contained in:
JozsefA 2021-02-14 23:09:17 -08:00
parent 11616a0b16
commit 1e95fe4c7b
36 changed files with 431 additions and 351 deletions

View file

@ -1,11 +1,7 @@
package com.simibubi.create;
import com.simibubi.create.content.contraptions.components.actors.SeatEntity;
import com.simibubi.create.content.contraptions.components.structureMovement.AbstractContraptionEntity;
import com.simibubi.create.content.contraptions.components.structureMovement.ControlledContraptionEntity;
import com.simibubi.create.content.contraptions.components.structureMovement.ControlledContraptionEntityRenderer;
import com.simibubi.create.content.contraptions.components.structureMovement.OrientedContraptionEntity;
import com.simibubi.create.content.contraptions.components.structureMovement.OrientedContraptionEntityRenderer;
import com.simibubi.create.content.contraptions.components.structureMovement.*;
import com.simibubi.create.content.contraptions.components.structureMovement.glue.SuperGlueEntity;
import com.simibubi.create.content.contraptions.components.structureMovement.glue.SuperGlueRenderer;
import com.simibubi.create.foundation.utility.Lang;
@ -51,7 +47,7 @@ public class AllEntityTypes {
@OnlyIn(value = Dist.CLIENT)
public static void registerRenderers() {
RenderingRegistry.registerEntityRenderingHandler(CONTROLLED_CONTRAPTION.get(),
ControlledContraptionEntityRenderer::new);
ContraptionEntityRenderer::new);
RenderingRegistry.registerEntityRenderingHandler(ORIENTED_CONTRAPTION.get(),
OrientedContraptionEntityRenderer::new);
RenderingRegistry.registerEntityRenderingHandler(SUPER_GLUE.get(), SuperGlueRenderer::new);

View file

@ -1,6 +1,7 @@
package com.simibubi.create.content.contraptions.base;
import com.simibubi.create.foundation.render.backend.instancing.InstanceData;
import com.simibubi.create.foundation.render.backend.instancing.InstancedModel;
import com.simibubi.create.foundation.utility.ColorHelper;
import net.minecraft.client.renderer.Vector3f;
import net.minecraft.util.math.BlockPos;
@ -19,6 +20,10 @@ public class KineticData<D extends KineticData<D>> extends InstanceData {
private float rotationalSpeed;
private float rotationOffset;
protected KineticData(InstancedModel<?> owner) {
super(owner);
}
public D setTileEntity(KineticTileEntity te) {
setPosition(te.getPos());
if (te.hasSource()) {
@ -37,6 +42,14 @@ public class KineticData<D extends KineticData<D>> extends InstanceData {
return setPosition(pos.getX(), pos.getY(), pos.getZ());
}
public D setPosition(int x, int y, int z) {
BlockPos origin = owner.renderer.getOriginCoordinate();
return setPosition((float) (x - origin.getX()),
(float) (y - origin.getY()),
(float) (z - origin.getZ()));
}
public D setPosition(float x, float y, float z) {
this.x = x;
this.y = y;

View file

@ -1,6 +1,7 @@
package com.simibubi.create.content.contraptions.base;
import com.simibubi.create.foundation.render.backend.gl.attrib.VertexFormat;
import com.simibubi.create.foundation.render.backend.instancing.InstancedModel;
import net.minecraft.client.renderer.Vector3f;
import net.minecraft.util.Direction;
@ -16,6 +17,10 @@ public class RotatingData extends KineticData<RotatingData> {
private byte rotationAxisY;
private byte rotationAxisZ;
protected RotatingData(InstancedModel<?> owner) {
super(owner);
}
public RotatingData setRotationAxis(Direction.Axis axis) {
Direction orientation = Direction.getFacingFromAxis(Direction.AxisDirection.POSITIVE, axis);
setRotationAxis(orientation.getUnitVector());

View file

@ -2,16 +2,17 @@ package com.simibubi.create.content.contraptions.base;
import com.simibubi.create.foundation.render.backend.gl.attrib.VertexFormat;
import com.simibubi.create.foundation.render.backend.instancing.InstancedModel;
import com.simibubi.create.foundation.render.backend.instancing.InstancedTileRenderer;
import net.minecraft.client.renderer.BufferBuilder;
public class RotatingInstancedModel extends InstancedModel<RotatingData> {
public RotatingInstancedModel(BufferBuilder buf) {
super(buf);
public RotatingInstancedModel(InstancedTileRenderer<?> renderer, BufferBuilder buf) {
super(renderer, buf);
}
@Override
protected RotatingData newInstance() {
return new RotatingData();
return new RotatingData(this);
}
@Override

View file

@ -2,6 +2,7 @@ package com.simibubi.create.content.contraptions.components.actors;
import com.simibubi.create.foundation.render.backend.gl.attrib.VertexFormat;
import com.simibubi.create.foundation.render.backend.instancing.InstanceData;
import com.simibubi.create.foundation.render.backend.instancing.InstancedModel;
import net.minecraft.client.renderer.Vector3f;
import net.minecraft.util.math.BlockPos;
@ -28,6 +29,11 @@ public class ContraptionActorData extends InstanceData {
private byte rotationCenterY = 64;
private byte rotationCenterZ = 64;
protected ContraptionActorData(InstancedModel<?> owner) {
super(owner);
}
public ContraptionActorData setPosition(BlockPos pos) {
this.x = pos.getX();
this.y = pos.getY();

View file

@ -2,11 +2,12 @@ package com.simibubi.create.content.contraptions.components.actors;
import com.simibubi.create.foundation.render.backend.gl.attrib.VertexFormat;
import com.simibubi.create.foundation.render.backend.instancing.InstancedModel;
import com.simibubi.create.foundation.render.backend.instancing.InstancedTileRenderer;
import net.minecraft.client.renderer.BufferBuilder;
public class RotatingActorModel extends InstancedModel<ContraptionActorData> {
public RotatingActorModel(BufferBuilder buf) {
super(buf);
public RotatingActorModel(InstancedTileRenderer<?> renderer, BufferBuilder buf) {
super(renderer, buf);
}
@Override
@ -16,6 +17,6 @@ public class RotatingActorModel extends InstancedModel<ContraptionActorData> {
@Override
protected ContraptionActorData newInstance() {
return new ContraptionActorData();
return new ContraptionActorData(this);
}
}

View file

@ -7,6 +7,7 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.UUID;
import com.mojang.blaze3d.matrix.MatrixStack;
import org.apache.commons.lang3.mutable.MutableInt;
import org.apache.commons.lang3.tuple.MutablePair;
@ -593,6 +594,9 @@ public abstract class AbstractContraptionEntity extends Entity implements IEntit
return false;
}
@OnlyIn(Dist.CLIENT)
public abstract void doLocalTransforms(float partialTicks, MatrixStack[] matrixStacks);
public static class ContraptionRotationState {
static final ContraptionRotationState NONE = new ContraptionRotationState();

View file

@ -10,37 +10,26 @@ import net.minecraft.client.renderer.entity.EntityRendererManager;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.MathHelper;
public abstract class AbstractContraptionEntityRenderer<C extends AbstractContraptionEntity> extends EntityRenderer<C> {
public class ContraptionEntityRenderer<C extends AbstractContraptionEntity> extends EntityRenderer<C> {
protected AbstractContraptionEntityRenderer(EntityRendererManager p_i46179_1_) {
public ContraptionEntityRenderer(EntityRendererManager p_i46179_1_) {
super(p_i46179_1_);
}
@Override
public ResourceLocation getEntityTexture(C p_110775_1_) {
public ResourceLocation getEntityTexture(C entity) {
return null;
}
protected abstract void transform(C contraptionEntity, float partialTicks, MatrixStack[] matrixStacks);
public MatrixStack makeTransformMatrix(C contraptionEntity, float partialTicks) {
MatrixStack stack = getLocalTransform(contraptionEntity, partialTicks);
transform(contraptionEntity, partialTicks, new MatrixStack[]{ stack });
return stack;
}
@Override
public boolean shouldRender(C entity, ClippingHelperImpl p_225626_2_, double p_225626_3_, double p_225626_5_,
public boolean shouldRender(C entity, ClippingHelperImpl clippingHelper, double p_225626_3_, double p_225626_5_,
double p_225626_7_) {
if (!super.shouldRender(entity, p_225626_2_, p_225626_3_, p_225626_5_, p_225626_7_))
if (entity.getContraption() == null)
return false;
if (!entity.isAlive())
return false;
if (entity.getContraption() == null)
return false;
return true;
return super.shouldRender(entity, clippingHelper, p_225626_3_, p_225626_5_, p_225626_7_);
}
@Override
@ -49,11 +38,11 @@ public abstract class AbstractContraptionEntityRenderer<C extends AbstractContra
super.render(entity, yaw, partialTicks, ms, buffers, overlay);
// Keep a copy of the transforms in order to determine correct lighting
MatrixStack msLocal = getLocalTransform(entity, AnimationTickHolder.getRenderTick());
MatrixStack msLocal = translateTo(entity, AnimationTickHolder.getRenderTick());
MatrixStack[] matrixStacks = new MatrixStack[] { ms, msLocal };
ms.push();
transform(entity, partialTicks, matrixStacks);
entity.doLocalTransforms(partialTicks, matrixStacks);
Contraption contraption = entity.getContraption();
if (contraption != null) {
ContraptionRenderDispatcher.render(entity, ms, buffers, msLocal, contraption);
@ -62,7 +51,7 @@ public abstract class AbstractContraptionEntityRenderer<C extends AbstractContra
}
protected MatrixStack getLocalTransform(AbstractContraptionEntity entity, float pt) {
protected MatrixStack translateTo(AbstractContraptionEntity entity, float pt) {
MatrixStack matrixStack = new MatrixStack();
double x = MathHelper.lerp(pt, entity.lastTickPosX, entity.getX());
double y = MathHelper.lerp(pt, entity.lastTickPosY, entity.getY());

View file

@ -2,8 +2,10 @@ package com.simibubi.create.content.contraptions.components.structureMovement;
import static com.simibubi.create.foundation.utility.AngleHelper.angleLerp;
import com.mojang.blaze3d.matrix.MatrixStack;
import com.simibubi.create.AllEntityTypes;
import com.simibubi.create.content.contraptions.components.structureMovement.bearing.BearingContraption;
import com.simibubi.create.foundation.utility.MatrixStacker;
import com.simibubi.create.foundation.utility.NBTHelper;
import com.simibubi.create.foundation.utility.VecHelper;
@ -225,4 +227,18 @@ public class ControlledContraptionEntity extends AbstractContraptionEntity {
setPos(x, y, z);
this.angle = angle;
}
@Override
@OnlyIn(Dist.CLIENT)
public void doLocalTransforms(float partialTicks, MatrixStack[] matrixStacks) {
float angle = getAngle(partialTicks);
Axis axis = getRotationAxis();
for (MatrixStack stack : matrixStacks)
MatrixStacker.of(stack)
.nudge(getEntityId())
.centre()
.rotate(angle, axis)
.unCentre();
}
}

View file

@ -1,30 +0,0 @@
package com.simibubi.create.content.contraptions.components.structureMovement;
import com.mojang.blaze3d.matrix.MatrixStack;
import com.simibubi.create.foundation.utility.MatrixStacker;
import net.minecraft.client.renderer.entity.EntityRendererManager;
import net.minecraft.util.Direction.Axis;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
public class ControlledContraptionEntityRenderer extends AbstractContraptionEntityRenderer<ControlledContraptionEntity> {
public ControlledContraptionEntityRenderer(EntityRendererManager p_i46179_1_) {
super(p_i46179_1_);
}
@Override
protected void transform(ControlledContraptionEntity entity, float partialTicks,
MatrixStack[] matrixStacks) {
float angle = entity.getAngle(partialTicks);
Axis axis = entity.getRotationAxis();
for (MatrixStack stack : matrixStacks)
MatrixStacker.of(stack)
.nudge(entity.getEntityId())
.centre()
.rotate(angle, axis)
.unCentre();
}
}

View file

@ -7,6 +7,7 @@ import java.util.UUID;
import javax.annotation.Nullable;
import com.mojang.blaze3d.matrix.MatrixStack;
import com.simibubi.create.AllEntityTypes;
import com.simibubi.create.content.contraptions.components.structureMovement.bearing.StabilizedContraption;
import com.simibubi.create.content.contraptions.components.structureMovement.mounted.CartAssemblerTileEntity.CartMovementMode;
@ -14,10 +15,7 @@ import com.simibubi.create.content.contraptions.components.structureMovement.mou
import com.simibubi.create.content.contraptions.components.structureMovement.train.capability.CapabilityMinecartController;
import com.simibubi.create.content.contraptions.components.structureMovement.train.capability.MinecartController;
import com.simibubi.create.foundation.item.ItemHelper;
import com.simibubi.create.foundation.utility.AngleHelper;
import com.simibubi.create.foundation.utility.Couple;
import com.simibubi.create.foundation.utility.NBTHelper;
import com.simibubi.create.foundation.utility.VecHelper;
import com.simibubi.create.foundation.utility.*;
import net.minecraft.block.BlockState;
import net.minecraft.entity.Entity;
@ -40,6 +38,8 @@ import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.common.util.LazyOptional;
/**
@ -494,4 +494,89 @@ public class OrientedContraptionEntity extends AbstractContraptionEntity {
yaw = angle;
}
@Override
@OnlyIn(Dist.CLIENT)
public void doLocalTransforms(float partialTicks, MatrixStack[] matrixStacks) {
float angleInitialYaw = getInitialYaw();
float angleYaw = getYaw(partialTicks);
float anglePitch = getPitch(partialTicks);
for (MatrixStack stack : matrixStacks)
stack.translate(-.5f, 0, -.5f);
Entity ridingEntity = getRidingEntity();
if (ridingEntity instanceof AbstractMinecartEntity)
repositionOnCart(partialTicks, matrixStacks, ridingEntity);
else if (ridingEntity instanceof AbstractContraptionEntity) {
if (ridingEntity.getRidingEntity() instanceof AbstractMinecartEntity)
repositionOnCart(partialTicks, matrixStacks, ridingEntity.getRidingEntity());
else
repositionOnContraption(partialTicks, matrixStacks, ridingEntity);
}
for (MatrixStack stack : matrixStacks)
MatrixStacker.of(stack)
.nudge(getEntityId())
.centre()
.rotateY(angleYaw)
.rotateZ(anglePitch)
.rotateY(angleInitialYaw)
.unCentre();
}
@OnlyIn(Dist.CLIENT)
private void repositionOnContraption(float partialTicks, MatrixStack[] matrixStacks, Entity ridingEntity) {
Vec3d pos = getContraptionOffset(partialTicks, ridingEntity);
for (MatrixStack stack : matrixStacks)
stack.translate(pos.x, pos.y, pos.z);
}
// Minecarts do not always render at their exact location, so the contraption
// has to adjust aswell
@OnlyIn(Dist.CLIENT)
private void repositionOnCart(float partialTicks, MatrixStack[] matrixStacks, Entity ridingEntity) {
Vec3d cartPos = getCartOffset(partialTicks, ridingEntity);
if (cartPos == Vec3d.ZERO) return;
for (MatrixStack stack : matrixStacks)
stack.translate(cartPos.x, cartPos.y, cartPos.z);
}
@OnlyIn(Dist.CLIENT)
private Vec3d getContraptionOffset(float partialTicks, Entity ridingEntity) {
AbstractContraptionEntity parent = (AbstractContraptionEntity) ridingEntity;
Vec3d passengerPosition = parent.getPassengerPosition(this, partialTicks);
double x = passengerPosition.x - MathHelper.lerp(partialTicks, this.lastTickPosX, this.getX());
double y = passengerPosition.y - MathHelper.lerp(partialTicks, this.lastTickPosY, this.getY());
double z = passengerPosition.z - MathHelper.lerp(partialTicks, this.lastTickPosZ, this.getZ());
return new Vec3d(x, y, z);
}
@OnlyIn(Dist.CLIENT)
private Vec3d getCartOffset(float partialTicks, Entity ridingEntity) {
AbstractMinecartEntity cart = (AbstractMinecartEntity) ridingEntity;
double cartX = MathHelper.lerp(partialTicks, cart.lastTickPosX, cart.getX());
double cartY = MathHelper.lerp(partialTicks, cart.lastTickPosY, cart.getY());
double cartZ = MathHelper.lerp(partialTicks, cart.lastTickPosZ, cart.getZ());
Vec3d cartPos = cart.getPos(cartX, cartY, cartZ);
if (cartPos != null) {
Vec3d cartPosFront = cart.getPosOffset(cartX, cartY, cartZ, (double) 0.3F);
Vec3d cartPosBack = cart.getPosOffset(cartX, cartY, cartZ, (double) -0.3F);
if (cartPosFront == null)
cartPosFront = cartPos;
if (cartPosBack == null)
cartPosBack = cartPos;
cartX = cartPos.x - cartX;
cartY = (cartPosFront.y + cartPosBack.y) / 2.0D - cartY;
cartZ = cartPos.z - cartZ;
return new Vec3d(cartX, cartY, cartZ);
}
return Vec3d.ZERO;
}
}

View file

@ -1,113 +1,20 @@
package com.simibubi.create.content.contraptions.components.structureMovement;
import com.mojang.blaze3d.matrix.MatrixStack;
import com.simibubi.create.foundation.utility.MatrixStacker;
import net.minecraft.client.renderer.culling.ClippingHelperImpl;
import net.minecraft.client.renderer.entity.EntityRendererManager;
import net.minecraft.entity.Entity;
import net.minecraft.entity.item.minecart.AbstractMinecartEntity;
import net.minecraft.util.Direction;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
public class OrientedContraptionEntityRenderer extends AbstractContraptionEntityRenderer<OrientedContraptionEntity> {
public class OrientedContraptionEntityRenderer extends ContraptionEntityRenderer<OrientedContraptionEntity> {
public OrientedContraptionEntityRenderer(EntityRendererManager p_i46179_1_) {
super(p_i46179_1_);
}
@Override
public boolean shouldRender(OrientedContraptionEntity entity, ClippingHelperImpl p_225626_2_, double p_225626_3_,
public boolean shouldRender(OrientedContraptionEntity entity, ClippingHelperImpl clippingHelper, double p_225626_3_,
double p_225626_5_, double p_225626_7_) {
if (!super.shouldRender(entity, p_225626_2_, p_225626_3_, p_225626_5_, p_225626_7_))
if (!super.shouldRender(entity, clippingHelper, p_225626_3_, p_225626_5_, p_225626_7_))
return false;
if (entity.getContraption()
.getType() == AllContraptionTypes.MOUNTED && entity.getRidingEntity() == null)
return false;
return true;
return entity.getContraption().getType() != AllContraptionTypes.MOUNTED || entity.getRidingEntity() != null;
}
@Override
protected void transform(OrientedContraptionEntity entity, float partialTicks, MatrixStack[] matrixStacks) {
float angleInitialYaw = entity.getInitialYaw();
float angleYaw = entity.getYaw(partialTicks);
float anglePitch = entity.getPitch(partialTicks);
for (MatrixStack stack : matrixStacks)
stack.translate(-.5f, 0, -.5f);
Entity ridingEntity = entity.getRidingEntity();
if (ridingEntity instanceof AbstractMinecartEntity)
repositionOnCart(partialTicks, matrixStacks, ridingEntity);
else if (ridingEntity instanceof AbstractContraptionEntity) {
if (ridingEntity.getRidingEntity() instanceof AbstractMinecartEntity)
repositionOnCart(partialTicks, matrixStacks, ridingEntity.getRidingEntity());
else
repositionOnContraption(entity, partialTicks, matrixStacks, ridingEntity);
}
for (MatrixStack stack : matrixStacks)
MatrixStacker.of(stack)
.nudge(entity.getEntityId())
.centre()
.rotateY(angleYaw)
.rotateZ(anglePitch)
.rotateY(angleInitialYaw)
.unCentre();
}
private void repositionOnContraption(OrientedContraptionEntity entity, float partialTicks,
MatrixStack[] matrixStacks, Entity ridingEntity) {
Vec3d pos = getContraptionOffset(entity, partialTicks, ridingEntity);
for (MatrixStack stack : matrixStacks)
stack.translate(pos.x, pos.y, pos.z);
}
// Minecarts do not always render at their exact location, so the contraption
// has to adjust aswell
private void repositionOnCart(float partialTicks, MatrixStack[] matrixStacks, Entity ridingEntity) {
Vec3d cartPos = getCartOffset(partialTicks, ridingEntity);
if (cartPos == Vec3d.ZERO) return;
for (MatrixStack stack : matrixStacks)
stack.translate(cartPos.x, cartPos.y, cartPos.z);
}
private Vec3d getContraptionOffset(OrientedContraptionEntity entity, float partialTicks, Entity ridingEntity) {
AbstractContraptionEntity parent = (AbstractContraptionEntity) ridingEntity;
Vec3d passengerPosition = parent.getPassengerPosition(entity, partialTicks);
double x = passengerPosition.x - MathHelper.lerp(partialTicks, entity.lastTickPosX, entity.getX());
double y = passengerPosition.y - MathHelper.lerp(partialTicks, entity.lastTickPosY, entity.getY());
double z = passengerPosition.z - MathHelper.lerp(partialTicks, entity.lastTickPosZ, entity.getZ());
return new Vec3d(x, y, z);
}
private Vec3d getCartOffset(float partialTicks, Entity ridingEntity) {
AbstractMinecartEntity cart = (AbstractMinecartEntity) ridingEntity;
double cartX = MathHelper.lerp(partialTicks, cart.lastTickPosX, cart.getX());
double cartY = MathHelper.lerp(partialTicks, cart.lastTickPosY, cart.getY());
double cartZ = MathHelper.lerp(partialTicks, cart.lastTickPosZ, cart.getZ());
Vec3d cartPos = cart.getPos(cartX, cartY, cartZ);
if (cartPos != null) {
Vec3d cartPosFront = cart.getPosOffset(cartX, cartY, cartZ, (double) 0.3F);
Vec3d cartPosBack = cart.getPosOffset(cartX, cartY, cartZ, (double) -0.3F);
if (cartPosFront == null)
cartPosFront = cartPos;
if (cartPosBack == null)
cartPosBack = cartPos;
cartX = cartPos.x - cartX;
cartY = (cartPosFront.y + cartPosBack.y) / 2.0D - cartY;
cartZ = cartPos.z - cartZ;
return new Vec3d(cartX, cartY, cartZ);
}
return Vec3d.ZERO;
}
}

View file

@ -7,13 +7,20 @@ import com.simibubi.create.content.contraptions.base.KineticRenderMaterials;
import com.simibubi.create.foundation.render.backend.instancing.RenderMaterial;
import com.simibubi.create.content.contraptions.base.RotatingInstancedModel;
import com.simibubi.create.content.contraptions.components.actors.RotatingActorModel;
import net.minecraft.util.math.BlockPos;
public class ContraptionKineticRenderer extends InstancedTileRenderer<ContraptionProgram> {
@Override
public void registerMaterials() {
materials.put(KineticRenderMaterials.BELTS, new RenderMaterial<>(AllProgramSpecs.CONTRAPTION_BELT, BeltInstancedModel::new));
materials.put(KineticRenderMaterials.ROTATING, new RenderMaterial<>(AllProgramSpecs.CONTRAPTION_ROTATING, RotatingInstancedModel::new));
materials.put(KineticRenderMaterials.ACTORS, new RenderMaterial<>(AllProgramSpecs.CONTRAPTION_ACTOR, RotatingActorModel::new));
materials.put(KineticRenderMaterials.BELTS, new RenderMaterial<>(this, AllProgramSpecs.CONTRAPTION_BELT, BeltInstancedModel::new));
materials.put(KineticRenderMaterials.ROTATING, new RenderMaterial<>(this, AllProgramSpecs.CONTRAPTION_ROTATING, RotatingInstancedModel::new));
materials.put(KineticRenderMaterials.ACTORS, new RenderMaterial<>(this, AllProgramSpecs.CONTRAPTION_ACTOR, RotatingActorModel::new));
}
@Override
public BlockPos getOriginCoordinate() {
return BlockPos.ZERO;
}
}

View file

@ -4,6 +4,7 @@ import com.simibubi.create.foundation.render.backend.gl.BasicProgram;
import com.simibubi.create.foundation.render.backend.light.GridAlignedBB;
import net.minecraft.client.renderer.Matrix4f;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.AxisAlignedBB;
import org.lwjgl.opengl.GL20;
public class ContraptionProgram extends BasicProgram {
@ -26,9 +27,12 @@ public class ContraptionProgram extends BasicProgram {
uLightVolume = setSamplerBinding("uLightVolume", 4);
}
public void bind(Matrix4f model, GridAlignedBB lightVolume) {
GL20.glUniform3f(uLightBoxSize, lightVolume.sizeX(), lightVolume.sizeY(), lightVolume.sizeZ());
GL20.glUniform3f(uLightBoxMin, lightVolume.minX, lightVolume.minY, lightVolume.minZ);
public void bind(Matrix4f model, AxisAlignedBB lightVolume) {
double sizeX = lightVolume.maxX - lightVolume.minX;
double sizeY = lightVolume.maxY - lightVolume.minY;
double sizeZ = lightVolume.maxZ - lightVolume.minZ;
GL20.glUniform3f(uLightBoxSize, (float) sizeX, (float) sizeY, (float) sizeZ);
GL20.glUniform3f(uLightBoxMin, (float) lightVolume.minX, (float) lightVolume.minY, (float) lightVolume.minZ);
uploadMatrixUniform(uModel, model);
}
}

View file

@ -38,60 +38,34 @@ import org.lwjgl.opengl.GL40;
import java.lang.ref.WeakReference;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ForkJoinPool;
public class ContraptionRenderDispatcher {
public static final Int2ObjectMap<RenderedContraption> renderers = new Int2ObjectOpenHashMap<>();
public static final Compartment<Pair<Contraption, Integer>> CONTRAPTION = new Compartment<>();
protected static PlacementSimulationWorld renderWorld;
private static boolean firstLayer = true;
public static void notifyLightUpdate(ILightReader world, LightType type, SectionPos pos) {
for (RenderedContraption renderer : renderers.values()) {
renderer.getLighter().lightVolume.notifyLightUpdate(world, type, pos);
}
}
public static void renderTick(TickEvent.RenderTickEvent event) {
if (!Backend.canUseVBOs()) return;
ClientWorld world = Minecraft.getInstance().world;
if (event.phase == TickEvent.Phase.START && world != null) {
Map<Integer, WeakReference<AbstractContraptionEntity>> map = ContraptionHandler.loadedContraptions.get(world);
for (WeakReference<AbstractContraptionEntity> weakReference : map.values()) {
AbstractContraptionEntity entity = weakReference.get();
if (entity == null) continue;
EntityRendererManager renderManager = Minecraft.getInstance().getRenderManager();
EntityRenderer<?> renderer = renderManager.getRenderer(entity);
if (renderer instanceof AbstractContraptionEntityRenderer) {
updateTransform(entity, (AbstractContraptionEntityRenderer<? super AbstractContraptionEntity>) renderer);
}
}
}
public static void renderTick() {
firstLayer = true;
}
public static void renderTileEntities(World world, Contraption c, MatrixStack ms, MatrixStack msLocal,
IRenderTypeBuffer buffer) {
if (Backend.canUseInstancing()) {
PlacementSimulationWorld renderWorld = null;
if (Backend.canUseVBOs()) {
RenderedContraption renderer = getRenderer(world, c);
TileEntityRenderHelper.renderTileEntities(world, renderer.renderWorld, c.specialRenderedTileEntities, ms, msLocal, buffer);
} else {
TileEntityRenderHelper.renderTileEntities(world, c.specialRenderedTileEntities, ms, msLocal, buffer);
renderWorld = renderer.renderWorld;
}
}
TileEntityRenderHelper.renderTileEntities(world, renderWorld, c.specialRenderedTileEntities, ms, msLocal, buffer);
private static <C extends AbstractContraptionEntity> void updateTransform(C c, AbstractContraptionEntityRenderer<C> entityRenderer) {
MatrixStack stack = entityRenderer.makeTransformMatrix(c, AnimationTickHolder.getPartialTicks());
Contraption c1 = c.getContraption();
getRenderer(c1.entity.world, c1).setRenderSettings(stack.peek().getModel());
}
public static void tick() {
@ -112,11 +86,20 @@ public class ContraptionRenderDispatcher {
return contraption;
}
public static void renderLayer(RenderType layer, Matrix4f viewProjection, float camX, float camY, float camZ) {
public static void renderLayer(RenderType layer, Matrix4f viewProjection, double camX, double camY, double camZ) {
removeDeadContraptions();
if (renderers.isEmpty()) return;
if (firstLayer) {
for (RenderedContraption renderer : renderers.values()) {
renderer.beginFrame(camX, camY, camZ);
}
firstLayer = false;
}
layer.startDrawing();
GL11.glEnable(GL13.GL_TEXTURE_3D);
GL13.glActiveTexture(GL40.GL_TEXTURE4); // the shaders expect light volumes to be in texture 4
@ -142,19 +125,13 @@ public class ContraptionRenderDispatcher {
}
public static void removeDeadContraptions() {
ArrayList<Integer> toRemove = new ArrayList<>();
for (RenderedContraption renderer : renderers.values()) {
renderers.values().removeIf(renderer -> {
if (renderer.isDead()) {
toRemove.add(renderer.getEntityId());
renderer.invalidate();
return true;
}
}
for (Integer id : toRemove) {
renderers.remove(id);
}
return false;
});
}
public static void invalidateAll() {

View file

@ -3,23 +3,26 @@ package com.simibubi.create.content.contraptions.components.structureMovement.re
import com.mojang.blaze3d.matrix.MatrixStack;
import com.simibubi.create.AllMovementBehaviours;
import com.simibubi.create.content.contraptions.base.KineticRenderMaterials;
import com.simibubi.create.content.contraptions.components.structureMovement.Contraption;
import com.simibubi.create.content.contraptions.components.structureMovement.MovementBehaviour;
import com.simibubi.create.content.contraptions.components.structureMovement.MovementContext;
import com.simibubi.create.content.contraptions.components.structureMovement.*;
import com.simibubi.create.foundation.render.backend.Backend;
import com.simibubi.create.foundation.render.backend.instancing.*;
import com.simibubi.create.content.contraptions.components.actors.ContraptionActorData;
import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionLighter;
import com.simibubi.create.foundation.render.backend.light.GridAlignedBB;
import com.simibubi.create.foundation.utility.AnimationTickHolder;
import com.simibubi.create.foundation.utility.worldWrappers.PlacementSimulationWorld;
import net.minecraft.block.BlockRenderType;
import net.minecraft.block.BlockState;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.*;
import net.minecraft.client.renderer.entity.EntityRenderer;
import net.minecraft.client.renderer.entity.EntityRendererManager;
import net.minecraft.client.renderer.model.IBakedModel;
import net.minecraft.client.renderer.texture.OverlayTexture;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.world.World;
import net.minecraft.world.gen.feature.template.Template;
import net.minecraft.world.lighting.WorldLightManager;
@ -45,6 +48,7 @@ public class RenderedContraption {
public Contraption contraption;
private Matrix4f model;
private AxisAlignedBB lightBox;
public RenderedContraption(World world, Contraption contraption) {
this.contraption = contraption;
@ -76,14 +80,55 @@ public class RenderedContraption {
}
public void doRenderLayer(RenderType layer, ContraptionProgram shader) {
ContraptionModel buffer = renderLayers.get(layer);
if (buffer != null) {
ContraptionModel structure = renderLayers.get(layer);
if (structure != null) {
setup(shader);
buffer.render();
structure.render();
teardown();
}
}
public void beginFrame(double camX, double camY, double camZ) {
AbstractContraptionEntity entity = contraption.entity;
float pt = AnimationTickHolder.getPartialTicks();
MatrixStack stack = new MatrixStack();
double x = MathHelper.lerp(pt, entity.lastTickPosX, entity.getX()) - camX;
double y = MathHelper.lerp(pt, entity.lastTickPosY, entity.getY()) - camY;
double z = MathHelper.lerp(pt, entity.lastTickPosZ, entity.getZ()) - camZ;
stack.translate(x, y, z);
entity.doLocalTransforms(pt, new MatrixStack[]{ stack });
model = stack.peek().getModel();
AxisAlignedBB lightBox = GridAlignedBB.toAABB(lighter.lightVolume.getTextureVolume());
this.lightBox = lightBox.offset(-camX, -camY, -camZ);
}
void setup(ContraptionProgram shader) {
if (model == null || lightBox == null) return;
shader.bind(model, lightBox);
lighter.lightVolume.bind();
}
void teardown() {
lighter.lightVolume.unbind();
}
void invalidate() {
for (ContraptionModel buffer : renderLayers.values()) {
buffer.delete();
}
renderLayers.clear();
lighter.lightVolume.delete();
kinetics.invalidate();
}
private void buildLayers() {
for (ContraptionModel buffer : renderLayers.values()) {
buffer.delete();
@ -128,37 +173,12 @@ public class RenderedContraption {
}
}
void setRenderSettings(Matrix4f model) {
this.model = model;
}
void setup(ContraptionProgram shader) {
if (model == null) return;
shader.bind(model, lighter.lightVolume.getTextureVolume());
lighter.lightVolume.use();
}
void teardown() {
lighter.lightVolume.release();
}
void invalidate() {
for (ContraptionModel buffer : renderLayers.values()) {
buffer.delete();
}
renderLayers.clear();
lighter.lightVolume.delete();
kinetics.invalidate();
}
private static ContraptionModel buildStructureModel(PlacementSimulationWorld renderWorld, Contraption c, RenderType layer) {
BufferBuilder builder = buildStructure(renderWorld, c, layer);
return new ContraptionModel(builder);
}
public static PlacementSimulationWorld setupRenderWorld(World world, Contraption c) {
private static PlacementSimulationWorld setupRenderWorld(World world, Contraption c) {
PlacementSimulationWorld renderWorld = new PlacementSimulationWorld(world);
renderWorld.setTileEntities(c.presentTileEntities.values());
@ -176,7 +196,7 @@ public class RenderedContraption {
return renderWorld;
}
public static BufferBuilder buildStructure(PlacementSimulationWorld renderWorld, Contraption c, RenderType layer) {
private static BufferBuilder buildStructure(PlacementSimulationWorld renderWorld, Contraption c, RenderType layer) {
ForgeHooksClient.setRenderLayer(layer);
MatrixStack ms = new MatrixStack();

View file

@ -4,6 +4,7 @@ import com.simibubi.create.foundation.block.render.SpriteShiftEntry;
import com.simibubi.create.foundation.render.backend.gl.attrib.VertexFormat;
import com.simibubi.create.content.contraptions.base.KineticVertexAttributes;
import com.simibubi.create.content.contraptions.base.KineticData;
import com.simibubi.create.foundation.render.backend.instancing.InstancedModel;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import java.nio.ByteBuffer;
@ -25,6 +26,10 @@ public class BeltData extends KineticData<BeltData> {
private float maxV;
private byte scrollMult;
protected BeltData(InstancedModel<?> owner) {
super(owner);
}
public BeltData setRotation(float rotX, float rotY, float rotZ) {
this.rotX = rotX;
this.rotY = rotY;
@ -32,7 +37,6 @@ public class BeltData extends KineticData<BeltData> {
return this;
}
public BeltData setScrollTexture(SpriteShiftEntry spriteShift) {
TextureAtlasSprite source = spriteShift.getOriginal();
TextureAtlasSprite target = spriteShift.getTarget();

View file

@ -2,16 +2,17 @@ package com.simibubi.create.content.contraptions.relays.belt;
import com.simibubi.create.foundation.render.backend.gl.attrib.VertexFormat;
import com.simibubi.create.foundation.render.backend.instancing.InstancedModel;
import com.simibubi.create.foundation.render.backend.instancing.InstancedTileRenderer;
import net.minecraft.client.renderer.BufferBuilder;
public class BeltInstancedModel extends InstancedModel<BeltData> {
public BeltInstancedModel(BufferBuilder buf) {
super(buf);
public BeltInstancedModel(InstancedTileRenderer<?> renderer, BufferBuilder buf) {
super(renderer, buf);
}
@Override
protected BeltData newInstance() {
return new BeltData();
return new BeltData(this);
}
@Override

View file

@ -1,30 +0,0 @@
package com.simibubi.create.content.schematics.client;
import com.mojang.blaze3d.matrix.MatrixStack;
import com.simibubi.create.content.contraptions.components.structureMovement.render.ContraptionKineticRenderer;
import com.simibubi.create.foundation.renderState.SuperRenderTypeBuffer;
import net.minecraft.client.Minecraft;
public class SchematicRendererWithInstancing extends SchematicRenderer {
public final ContraptionKineticRenderer tiles;
public SchematicRendererWithInstancing() {
this.tiles = new ContraptionKineticRenderer();
}
@Override
protected void redraw(Minecraft minecraft) {
super.redraw(minecraft);
tiles.invalidate();
schematic.getRenderedTileEntities().forEach(tiles::add);
}
@Override
public void render(MatrixStack ms, SuperRenderTypeBuffer buffer) {
super.render(ms, buffer);
//tiles.render(RenderType.getCutoutMipped(), FastRenderDispatcher.getProjectionMatrix(), );
}
}

View file

@ -195,7 +195,7 @@ public class ClientEvents {
if (!isGameActive())
return;
TurntableHandler.gameRenderTick();
ContraptionRenderDispatcher.renderTick(event);
ContraptionRenderDispatcher.renderTick();
}
protected static boolean isGameActive() {

View file

@ -1,6 +1,7 @@
package com.simibubi.create.foundation.mixin;
import com.mojang.blaze3d.matrix.MatrixStack;
import com.mojang.blaze3d.platform.GlStateManager;
import com.simibubi.create.CreateClient;
import com.simibubi.create.content.contraptions.components.structureMovement.render.ContraptionRenderDispatcher;
import com.simibubi.create.foundation.render.backend.Backend;
@ -38,17 +39,12 @@ public class RenderHooksMixin {
private void renderLayer(RenderType type, MatrixStack stack, double camX, double camY, double camZ, CallbackInfo ci) {
if (!Backend.available()) return;
float cameraX = (float) camX;
float cameraY = (float) camY;
float cameraZ = (float) camZ;
Matrix4f viewProjection = Matrix4f.translate(-cameraX, -cameraY, -cameraZ);
viewProjection.multiplyBackward(stack.peek().getModel());
Matrix4f viewProjection = stack.peek().getModel().copy();
viewProjection.multiplyBackward(FastRenderDispatcher.getProjectionMatrix());
FastRenderDispatcher.renderLayer(type, viewProjection, cameraX, cameraY, cameraZ);
FastRenderDispatcher.renderLayer(type, viewProjection, camX, camY, camZ);
ContraptionRenderDispatcher.renderLayer(type, viewProjection, cameraX, cameraY, cameraZ);
ContraptionRenderDispatcher.renderLayer(type, viewProjection, camX, camY, camZ);
GL20.glUseProgram(0);
}
@ -60,6 +56,6 @@ public class RenderHooksMixin {
OptifineHandler.refresh();
Backend.refresh();
if (world != null) world.loadedTileEntityList.forEach(CreateClient.kineticRenderer::add);
if (Backend.canUseInstancing() && world != null) world.loadedTileEntityList.forEach(CreateClient.kineticRenderer::add);
}
}

View file

@ -4,12 +4,73 @@ import com.simibubi.create.content.contraptions.base.KineticRenderMaterials;
import com.simibubi.create.content.contraptions.base.RotatingInstancedModel;
import com.simibubi.create.content.contraptions.relays.belt.BeltInstancedModel;
import com.simibubi.create.foundation.render.backend.gl.BasicProgram;
import com.simibubi.create.foundation.render.backend.gl.shader.ShaderCallback;
import com.simibubi.create.foundation.render.backend.instancing.*;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.Matrix4f;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.entity.Entity;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.math.BlockPos;
import java.util.ArrayList;
public class KineticRenderer extends InstancedTileRenderer<BasicProgram> {
public static int MAX_ORIGIN_DISTANCE = 1000;
public BlockPos originCoordinate = BlockPos.ZERO;
@Override
public void registerMaterials() {
materials.put(KineticRenderMaterials.BELTS, new RenderMaterial<>(AllProgramSpecs.BELT, BeltInstancedModel::new));
materials.put(KineticRenderMaterials.ROTATING, new RenderMaterial<>(AllProgramSpecs.ROTATING, RotatingInstancedModel::new));
materials.put(KineticRenderMaterials.BELTS, new RenderMaterial<>(this, AllProgramSpecs.BELT, BeltInstancedModel::new));
materials.put(KineticRenderMaterials.ROTATING, new RenderMaterial<>(this, AllProgramSpecs.ROTATING, RotatingInstancedModel::new));
}
@Override
public BlockPos getOriginCoordinate() {
return originCoordinate;
}
@Override
public void tick() {
super.tick();
Minecraft mc = Minecraft.getInstance();
Entity renderViewEntity = mc.renderViewEntity;
if (renderViewEntity == null) return;
BlockPos renderViewPosition = renderViewEntity.getPosition();
int dX = Math.abs(renderViewPosition.getX() - originCoordinate.getX());
int dY = Math.abs(renderViewPosition.getY() - originCoordinate.getY());
int dZ = Math.abs(renderViewPosition.getZ() - originCoordinate.getZ());
if (dX > MAX_ORIGIN_DISTANCE ||
dY > MAX_ORIGIN_DISTANCE ||
dZ > MAX_ORIGIN_DISTANCE) {
originCoordinate = renderViewPosition;
ArrayList<TileEntity> instancedTiles = new ArrayList<>(instances.keySet());
invalidate();
instancedTiles.forEach(this::add);
}
}
@Override
public void render(RenderType layer, Matrix4f viewProjection, double camX, double camY, double camZ, ShaderCallback<BasicProgram> callback) {
BlockPos originCoordinate = getOriginCoordinate();
camX -= originCoordinate.getX();
camY -= originCoordinate.getY();
camZ -= originCoordinate.getZ();
Matrix4f translate = Matrix4f.translate((float) -camX, (float) -camY, (float) -camZ);
translate.multiplyBackward(viewProjection);
super.render(layer, translate, camX, camY, camZ, callback);
}
}

View file

@ -34,8 +34,6 @@ import java.util.function.Predicate;
public class Backend {
public static final Logger log = LogManager.getLogger(Backend.class);
public static final FloatBuffer FLOAT_BUFFER = MemoryUtil.memAllocFloat(1); // TODO: these leak 80 bytes of memory per program launch
public static final FloatBuffer VEC4_BUFFER = MemoryUtil.memAllocFloat(4);
public static final FloatBuffer MATRIX_BUFFER = MemoryUtil.memAllocFloat(16);
private static final Map<ResourceLocation, ProgramSpec<?>> registry = new HashMap<>();

View file

@ -29,7 +29,6 @@ import java.util.function.Consumer;
public class FastRenderDispatcher {
public static WorldAttached<ConcurrentHashMap.KeySetView<TileEntity, Boolean>> queuedUpdates = new WorldAttached<>(ConcurrentHashMap::newKeySet);
public static WorldAttached<ConcurrentHashMap<TileEntity, Integer>> addedLastTick = new WorldAttached<>(ConcurrentHashMap::new);
private static Matrix4f projectionMatrixThisFrame = null;
@ -44,26 +43,7 @@ public class FastRenderDispatcher {
public static void tick() {
ClientWorld world = Minecraft.getInstance().world;
// Clean up twice a second. This doesn't have to happen every tick,
// but this does need to be run to ensure we don't miss anything.
int ticks = AnimationTickHolder.getTicks();
ConcurrentHashMap<TileEntity, Integer> map = addedLastTick.get(world);
map
.entrySet()
.stream()
.filter(it -> ticks - it.getValue() > 10)
.map(Map.Entry::getKey)
.forEach(te -> {
map.remove(te);
CreateClient.kineticRenderer.onLightUpdate(te);
});
if (ticks % 10 == 0) {
CreateClient.kineticRenderer.clean();
}
CreateClient.kineticRenderer.tick();
runQueue(queuedUpdates.get(world), CreateClient.kineticRenderer::update);
}
@ -98,7 +78,7 @@ public class FastRenderDispatcher {
}
}
public static void renderLayer(RenderType layer, Matrix4f viewProjection, float cameraX, float cameraY, float cameraZ) {
public static void renderLayer(RenderType layer, Matrix4f viewProjection, double cameraX, double cameraY, double cameraZ) {
if (!Backend.canUseInstancing()) return;
layer.startDrawing();

View file

@ -37,14 +37,14 @@ public class BasicProgram extends GlProgram {
uLightMap = setSamplerBinding("uLightMap", 2);
}
public void bind(Matrix4f viewProjection, float camX, float camY, float camZ, int debugMode) {
public void bind(Matrix4f viewProjection, double camX, double camY, double camZ, int debugMode) {
super.bind();
GL20.glUniform1i(uDebug, debugMode);
GL20.glUniform1f(uTime, AnimationTickHolder.getRenderTick());
uploadMatrixUniform(uViewProjection, viewProjection);
GL20.glUniform3f(uCameraPos, camX, camY, camZ);
GL20.glUniform3f(uCameraPos, (float) camX, (float) camY, (float) camZ);
GL20.glUniform2f(uFogRange, GlFog.getFogStart(), GlFog.getFogEnd());
GL20.glUniform4fv(uFogColor, GlFog.FOG_COLOR);

View file

@ -4,6 +4,12 @@ import java.nio.ByteBuffer;
public abstract class InstanceData {
protected final InstancedModel<?> owner;
protected InstanceData(InstancedModel<?> owner) {
this.owner = owner;
}
public abstract void write(ByteBuffer buf);
public void putVec4(ByteBuffer buf, float x, float y, float z, float w) {

View file

@ -17,6 +17,8 @@ import java.util.function.Consumer;
public abstract class InstancedModel<D extends InstanceData> extends BufferedModel {
public static final VertexFormat FORMAT = VertexFormat.builder().addAttributes(ModelVertexAttributes.class).build();
public final InstancedTileRenderer<?> renderer;
protected GlVertexArray vao;
protected GlBuffer instanceVBO;
protected int glBufferSize = -1;
@ -27,8 +29,9 @@ public abstract class InstancedModel<D extends InstanceData> extends BufferedMod
protected int minIndexChanged = -1;
protected int maxIndexChanged = -1;
public InstancedModel(BufferBuilder buf) {
public InstancedModel(InstancedTileRenderer<?> renderer, BufferBuilder buf) {
super(buf);
this.renderer = renderer;
}
@Override

View file

@ -1,19 +1,27 @@
package com.simibubi.create.foundation.render.backend.instancing;
import com.simibubi.create.CreateClient;
import com.simibubi.create.foundation.render.backend.FastRenderDispatcher;
import com.simibubi.create.foundation.render.backend.gl.BasicProgram;
import com.simibubi.create.foundation.render.backend.Backend;
import com.simibubi.create.foundation.render.backend.gl.shader.ShaderCallback;
import com.simibubi.create.foundation.utility.AnimationTickHolder;
import com.simibubi.create.foundation.utility.WorldAttached;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.Matrix4f;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.math.BlockPos;
import javax.annotation.Nullable;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public abstract class InstancedTileRenderer<P extends BasicProgram> {
public static WorldAttached<ConcurrentHashMap<TileEntity, Integer>> addedLastTick = new WorldAttached<>(ConcurrentHashMap::new);
protected Map<TileEntity, TileEntityInstance<?>> instances = new HashMap<>();
protected Map<MaterialType<?>, RenderMaterial<P, ?>> materials = new HashMap<>();
@ -22,8 +30,35 @@ public abstract class InstancedTileRenderer<P extends BasicProgram> {
registerMaterials();
}
public abstract BlockPos getOriginCoordinate();
public abstract void registerMaterials();
public void tick() {
ClientWorld world = Minecraft.getInstance().world;
int ticks = AnimationTickHolder.getTicks();
ConcurrentHashMap<TileEntity, Integer> map = addedLastTick.get(world);
map
.entrySet()
.stream()
.filter(it -> ticks - it.getValue() > 10)
.map(Map.Entry::getKey)
.forEach(te -> {
map.remove(te);
onLightUpdate(te);
});
// Clean up twice a second. This doesn't have to happen every tick,
// but this does need to be run to ensure we don't miss anything.
if (ticks % 10 == 0) {
clean();
}
}
@SuppressWarnings("unchecked")
public <M extends InstancedModel<?>> RenderMaterial<P, M> getMaterial(MaterialType<M> materialType) {
return (RenderMaterial<P, M>) materials.get(materialType);
@ -47,7 +82,7 @@ public abstract class InstancedTileRenderer<P extends BasicProgram> {
TileEntityInstance<? super T> renderer = InstancedTileRenderRegistry.instance.create(this, tile);
if (renderer != null) {
FastRenderDispatcher.addedLastTick.get(tile.getWorld()).put(tile, AnimationTickHolder.getTicks());
addedLastTick.get(tile.getWorld()).put(tile, AnimationTickHolder.getTicks());
instances.put(tile, renderer);
}
@ -58,6 +93,8 @@ public abstract class InstancedTileRenderer<P extends BasicProgram> {
}
public <T extends TileEntity> void onLightUpdate(T tile) {
if (!Backend.canUseInstancing()) return;
if (tile instanceof IInstanceRendered) {
TileEntityInstance<? super T> instance = getInstance(tile, false);
@ -67,12 +104,16 @@ public abstract class InstancedTileRenderer<P extends BasicProgram> {
}
public <T extends TileEntity> void add(T tile) {
if (!Backend.canUseInstancing()) return;
if (tile instanceof IInstanceRendered) {
getInstance(tile);
}
}
public <T extends TileEntity> void update(T tile) {
if (!Backend.canUseInstancing()) return;
if (tile instanceof IInstanceRendered) {
TileEntityInstance<? super T> instance = getInstance(tile, false);
@ -82,6 +123,8 @@ public abstract class InstancedTileRenderer<P extends BasicProgram> {
}
public <T extends TileEntity> void remove(T tile) {
if (!Backend.canUseInstancing()) return;
if (tile instanceof IInstanceRendered) {
TileEntityInstance<? super T> instance = getInstance(tile, false);
@ -103,11 +146,11 @@ public abstract class InstancedTileRenderer<P extends BasicProgram> {
instances.clear();
}
public void render(RenderType layer, Matrix4f viewProjection, float camX, float camY, float camZ) {
public void render(RenderType layer, Matrix4f viewProjection, double camX, double camY, double camZ) {
render(layer, viewProjection, camX, camY, camZ, null);
}
public void render(RenderType layer, Matrix4f viewProjection, float camX, float camY, float camZ, ShaderCallback<P> callback) {
public void render(RenderType layer, Matrix4f viewProjection, double camX, double camY, double camZ, ShaderCallback<P> callback) {
for (RenderMaterial<P, ?> material : materials.values()) {
if (material.canRenderInLayer(layer))
material.render(layer, viewProjection, camX, camY, camZ, callback);

View file

@ -4,5 +4,5 @@ import net.minecraft.client.renderer.BufferBuilder;
@FunctionalInterface
public interface ModelFactory<B extends InstancedModel<?>> {
B convert(BufferBuilder buf);
B makeModel(InstancedTileRenderer<?> renderer, BufferBuilder buf);
}

View file

@ -33,6 +33,7 @@ import static com.simibubi.create.foundation.render.Compartment.PARTIAL;
public class RenderMaterial<P extends BasicProgram, MODEL extends InstancedModel<?>> {
protected final InstancedTileRenderer<?> renderer;
protected final Map<Compartment<?>, Cache<Object, MODEL>> models;
protected final ModelFactory<MODEL> factory;
protected final ProgramSpec<P> programSpec;
@ -41,11 +42,12 @@ public class RenderMaterial<P extends BasicProgram, MODEL extends InstancedModel
/**
* Creates a material that renders in the default layer (CUTOUT_MIPPED)
*/
public RenderMaterial(ProgramSpec<P> programSpec, ModelFactory<MODEL> factory) {
this(programSpec, factory, type -> type == RenderType.getCutoutMipped());
public RenderMaterial(InstancedTileRenderer<?> renderer, ProgramSpec<P> programSpec, ModelFactory<MODEL> factory) {
this(renderer, programSpec, factory, type -> type == RenderType.getCutoutMipped());
}
public RenderMaterial(ProgramSpec<P> programSpec, ModelFactory<MODEL> factory, Predicate<RenderType> layerPredicate) {
public RenderMaterial(InstancedTileRenderer<?> renderer, ProgramSpec<P> programSpec, ModelFactory<MODEL> factory, Predicate<RenderType> layerPredicate) {
this.renderer = renderer;
this.models = new HashMap<>();
this.factory = factory;
this.programSpec = programSpec;
@ -59,11 +61,11 @@ public class RenderMaterial<P extends BasicProgram, MODEL extends InstancedModel
return layerPredicate.test(layer);
}
public void render(RenderType layer, Matrix4f projection, float camX, float camY, float camZ) {
public void render(RenderType layer, Matrix4f projection, double camX, double camY, double camZ) {
render(layer, projection, camX, camY, camZ, null);
}
public void render(RenderType layer, Matrix4f viewProjection, float camX, float camY, float camZ, ShaderCallback<P> setup) {
public void render(RenderType layer, Matrix4f viewProjection, double camX, double camY, double camZ, ShaderCallback<P> setup) {
P program = Backend.getProgram(programSpec);
program.bind(viewProjection, camX, camY, camZ, FastRenderDispatcher.getDebugMode());
@ -142,7 +144,7 @@ public class RenderMaterial<P extends BasicProgram, MODEL extends InstancedModel
private MODEL buildModel(IBakedModel model, BlockState referenceState, MatrixStack ms) {
BufferBuilder builder = SuperByteBufferCache.getBufferBuilder(model, referenceState, ms);
return factory.convert(builder);
return factory.makeModel(renderer, builder);
}
}

View file

@ -22,8 +22,6 @@ public abstract class TileEntityInstance<T extends TileEntity> {
init();
}
protected abstract void init();
public final void update() {
BlockState currentState = tile.getBlockState();
if (lastState == currentState) {
@ -35,9 +33,24 @@ public abstract class TileEntityInstance<T extends TileEntity> {
}
}
/**
* Acquire all {@link InstanceKey}s and initialize any data you may need to calculate the instance properties.
*/
protected abstract void init();
/**
* Update changed instance data using the {@link InstanceKey}s you got in {@link #init()}.
* You don't have to update light data. That should be done in {@link #updateLight()}
*/
protected abstract void onUpdate();
public abstract void updateLight();
/**
* Called when a light update occurs in the world. If your model needs it, update light here.
*/
public void updateLight() { }
/**
* Call {@link InstanceKey#delete()} on all acquired keys.
*/
public abstract void remove();
}

View file

@ -212,7 +212,7 @@ public class LightVolume {
bufferDirty = true;
}
public void use() {
public void bind() {
// just in case something goes wrong or we accidentally call this before this volume is properly disposed of.
if (lightData == null || removed) return;
@ -246,7 +246,7 @@ public class LightVolume {
}
}
public void release() {
public void unbind() {
glTexture.unbind();
}

View file

@ -67,13 +67,15 @@ void main() {
vec4 worldPos = localRotation * vec4(aPos - .5, 1.) + vec4(aInstancePos + .5, 0.);
#ifdef CONTRAPTION
worldPos = uModel * worldPos;
BoxCoord = (worldPos.xyz - uLightBoxMin) / uLightBoxSize;
mat4 normalMat = uModel * localRotation;
BoxCoord = (worldPos.xyz - uLightBoxMin) / uLightBoxSize;
FragDistance = length(worldPos.xyz);
#else
mat4 normalMat = localRotation;
FragDistance = length(worldPos.xyz - uCameraPos);
#endif
vec3 norm = normalize(normalMat * vec4(aNormal, 0.)).xyz;
@ -84,7 +86,6 @@ void main() {
Diffuse = diffuse(norm);
TexCoords = aTexCoords - aSourceTexture + aScrollTexture.xy + vec2(0, scroll);
Light = aLight;
FragDistance = length(worldPos.xyz - uCameraPos);
gl_Position = uViewProjection * worldPos;
#ifdef CONTRAPTION

View file

@ -77,7 +77,7 @@ void main() {
Diffuse = diffuse(norm);
TexCoords = aTexCoords;
Light = aModelLight;
FragDistance = length(worldPos.xyz - uCameraPos);
FragDistance = length(worldPos.xyz);
gl_Position = uViewProjection * worldPos;
if (uDebug == 2) {

View file

@ -43,17 +43,17 @@ float diffuse(vec3 normal) {
}
void main() {
vec4 worldPos = uModel * vec4(aPos, 1.);
vec4 viewPos = uModel * vec4(aPos, 1.);
vec3 norm = (uModel * vec4(aNormal, 0.)).xyz;
BoxCoord = (worldPos.xyz - uLightBoxMin) / uLightBoxSize;
BoxCoord = (viewPos.xyz - uLightBoxMin) / uLightBoxSize;
Diffuse = diffuse(norm);
Color = aColor / diffuse(aNormal);
TexCoords = aTexCoords;
Light = aModelLight;
FragDistance = length(worldPos.xyz - uCameraPos);
gl_Position = uViewProjection * worldPos;
FragDistance = length(viewPos.xyz);
gl_Position = uViewProjection * viewPos;
if (uDebug == 2) {
Color = vec4(norm, 1.);

View file

@ -65,13 +65,15 @@ void main() {
vec4 worldPos = kineticRotation * vec4(aPos - .5, 1.) + vec4(aInstancePos + .5, 0.);
#ifdef CONTRAPTION
worldPos = uModel * worldPos;
BoxCoord = (worldPos.xyz - uLightBoxMin) / uLightBoxSize;
mat4 normalMat = uModel * kineticRotation;
BoxCoord = (worldPos.xyz - uLightBoxMin) / uLightBoxSize;
FragDistance = length(worldPos.xyz);
#else
mat4 normalMat = kineticRotation;
FragDistance = length(worldPos.xyz - uCameraPos);
#endif
vec3 norm = normalize(normalMat * vec4(aNormal, 0.)).xyz;
@ -79,7 +81,6 @@ void main() {
Diffuse = diffuse(norm);
TexCoords = aTexCoords;
Light = aLight;
FragDistance = length(worldPos.xyz - uCameraPos);
gl_Position = uViewProjection * worldPos;
#ifdef CONTRAPTION