mirror of
https://github.com/Creators-of-Create/Create.git
synced 2024-11-11 04:54:28 +01:00
on our way to super fancy contraption lighting
This commit is contained in:
parent
0cb18c532d
commit
22a90c8e5d
@ -16,7 +16,8 @@ import com.simibubi.create.foundation.block.render.CustomBlockModels;
|
||||
import com.simibubi.create.foundation.block.render.SpriteShifter;
|
||||
import com.simibubi.create.foundation.item.CustomItemModels;
|
||||
import com.simibubi.create.foundation.item.CustomRenderedItems;
|
||||
import com.simibubi.create.foundation.render.FastContraptionRenderer;
|
||||
import com.simibubi.create.foundation.render.ContraptionRenderDispatcher;
|
||||
import com.simibubi.create.foundation.render.RenderedContraption;
|
||||
import com.simibubi.create.foundation.render.FastKineticRenderer;
|
||||
import com.simibubi.create.foundation.render.SuperByteBufferCache;
|
||||
import com.simibubi.create.foundation.utility.outliner.Outliner;
|
||||
@ -182,6 +183,6 @@ public class CreateClient {
|
||||
public static void invalidateRenderers() {
|
||||
CreateClient.bufferCache.invalidate();
|
||||
CreateClient.kineticRenderer.invalidate();
|
||||
FastContraptionRenderer.invalidateAll();
|
||||
ContraptionRenderDispatcher.invalidateAll();
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,9 @@ package com.simibubi.create.content.contraptions.components.structureMovement;
|
||||
|
||||
import com.mojang.blaze3d.matrix.MatrixStack;
|
||||
|
||||
import com.simibubi.create.foundation.render.FastContraptionRenderer;
|
||||
import com.simibubi.create.foundation.render.ContraptionRenderDispatcher;
|
||||
import com.simibubi.create.foundation.render.RenderedContraption;
|
||||
import net.java.games.input.Controller;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.IRenderTypeBuffer;
|
||||
import net.minecraft.client.renderer.culling.ClippingHelperImpl;
|
||||
@ -50,7 +52,7 @@ public abstract class AbstractContraptionEntityRenderer<C extends AbstractContra
|
||||
Contraption contraption = entity.getContraption();
|
||||
if (contraption != null) {
|
||||
ContraptionRenderer.renderDynamic(entity.world, contraption, ms, msLocal, buffers);
|
||||
FastContraptionRenderer.markForRendering(entity.world, contraption, msLocal);
|
||||
ContraptionRenderDispatcher.markForRendering(entity.world, contraption, msLocal);
|
||||
}
|
||||
ms.pop();
|
||||
|
||||
|
@ -18,6 +18,10 @@ import java.util.stream.Collectors;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.simibubi.create.foundation.render.light.ContraptionLighter;
|
||||
import com.simibubi.create.foundation.render.light.EmptyLighter;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
import org.apache.commons.lang3.tuple.MutablePair;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
@ -956,4 +960,9 @@ public abstract class Contraption {
|
||||
mountedFluidStorage.updateFluid(containedFluid);
|
||||
}
|
||||
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
public ContraptionLighter<?> makeLighter() {
|
||||
return new EmptyLighter(this);
|
||||
}
|
||||
|
||||
}
|
@ -84,7 +84,7 @@ public class ContraptionRenderer {
|
||||
return new SuperByteBuffer(builder);
|
||||
}
|
||||
|
||||
protected static BufferBuilder buildStructure(Contraption c, RenderType layer) {
|
||||
public static BufferBuilder buildStructure(Contraption c, RenderType layer) {
|
||||
if (renderWorld == null || renderWorld.getWorld() != Minecraft.getInstance().world)
|
||||
renderWorld = new PlacementSimulationWorld(Minecraft.getInstance().world);
|
||||
|
||||
|
@ -1,5 +1,8 @@
|
||||
package com.simibubi.create.content.contraptions.components.structureMovement.bearing;
|
||||
|
||||
import com.simibubi.create.foundation.render.light.ContraptionLighter;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import com.simibubi.create.AllTags.AllBlockTags;
|
||||
@ -88,4 +91,9 @@ public class BearingContraption extends Contraption {
|
||||
return axis == facing.getAxis();
|
||||
}
|
||||
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
@Override
|
||||
public ContraptionLighter<?> makeLighter() {
|
||||
return new BearingLighter(this);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,19 @@
|
||||
package com.simibubi.create.content.contraptions.components.structureMovement.bearing;
|
||||
|
||||
import com.simibubi.create.foundation.render.light.ContraptionLighter;
|
||||
import com.simibubi.create.foundation.render.light.GridAlignedBB;
|
||||
import net.minecraft.util.math.AxisAlignedBB;
|
||||
|
||||
public class BearingLighter extends ContraptionLighter<BearingContraption> {
|
||||
|
||||
public BearingLighter(BearingContraption contraption) {
|
||||
super(contraption);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GridAlignedBB getContraptionBounds() {
|
||||
GridAlignedBB localBounds = GridAlignedBB.fromAABB(contraption.bounds);
|
||||
localBounds.translate(contraption.anchor);
|
||||
return localBounds;
|
||||
}
|
||||
}
|
@ -26,6 +26,7 @@ import com.simibubi.create.foundation.networking.AllPackets;
|
||||
import com.simibubi.create.foundation.networking.LeftClickPacket;
|
||||
import com.simibubi.create.foundation.render.FastRenderDispatcher;
|
||||
import com.simibubi.create.foundation.render.RenderWork;
|
||||
import com.simibubi.create.foundation.render.light.LightVolumeDebugger;
|
||||
import com.simibubi.create.foundation.renderState.SuperRenderTypeBuffer;
|
||||
import com.simibubi.create.foundation.tileEntity.behaviour.edgeInteraction.EdgeInteractionRenderer;
|
||||
import com.simibubi.create.foundation.tileEntity.behaviour.filtering.FilteringRenderer;
|
||||
@ -118,7 +119,6 @@ public class ClientEvents {
|
||||
Vec3d cameraPos = Minecraft.getInstance().gameRenderer.getActiveRenderInfo().getProjectedView();
|
||||
|
||||
MatrixStack ms = event.getMatrixStack();
|
||||
ActiveRenderInfo info = Minecraft.getInstance().gameRenderer.getActiveRenderInfo();
|
||||
ms.push();
|
||||
ms.translate(-cameraPos.getX(), -cameraPos.getY(), -cameraPos.getZ());
|
||||
SuperRenderTypeBuffer buffer = SuperRenderTypeBuffer.getInstance();
|
||||
@ -126,6 +126,7 @@ public class ClientEvents {
|
||||
CouplingRenderer.renderAll(ms, buffer);
|
||||
CreateClient.schematicHandler.render(ms, buffer);
|
||||
CreateClient.outliner.renderOutlines(ms, buffer);
|
||||
LightVolumeDebugger.render(ms, buffer);
|
||||
// CollisionDebugger.render(ms, buffer);
|
||||
buffer.draw();
|
||||
|
||||
|
@ -1,149 +0,0 @@
|
||||
package com.simibubi.create.foundation.render;
|
||||
|
||||
import com.simibubi.create.content.contraptions.components.structureMovement.Contraption;
|
||||
import net.minecraft.util.math.AxisAlignedBB;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.LightType;
|
||||
import net.minecraft.world.World;
|
||||
import org.lwjgl.opengl.*;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import static com.simibubi.create.foundation.render.RenderMath.nextPowerOf2;
|
||||
|
||||
public class ContraptionLighter {
|
||||
|
||||
private int minX;
|
||||
private int minY;
|
||||
private int minZ;
|
||||
|
||||
private final int sizeX;
|
||||
private final int sizeY;
|
||||
private final int sizeZ;
|
||||
|
||||
private SafeDirectBuffer lightVolume;
|
||||
|
||||
private boolean dirty;
|
||||
|
||||
private int texture;
|
||||
|
||||
public ContraptionLighter(Contraption contraption) {
|
||||
texture = GL11.glGenTextures();
|
||||
|
||||
AxisAlignedBB bounds = contraption.bounds;
|
||||
|
||||
int minX = (int) Math.floor(bounds.minX) - 1;
|
||||
int minY = (int) Math.floor(bounds.minY) - 1;
|
||||
int minZ = (int) Math.floor(bounds.minZ) - 1;
|
||||
int maxX = (int) Math.ceil(bounds.maxX) + 1;
|
||||
int maxY = (int) Math.ceil(bounds.maxY) + 1;
|
||||
int maxZ = (int) Math.ceil(bounds.maxZ) + 1;
|
||||
|
||||
sizeX = nextPowerOf2(maxX - minX);
|
||||
sizeY = nextPowerOf2(maxY - minY);
|
||||
sizeZ = nextPowerOf2(maxZ - minZ);
|
||||
|
||||
lightVolume = new SafeDirectBuffer(sizeX * sizeY * sizeZ * 2);
|
||||
|
||||
update(contraption);
|
||||
}
|
||||
|
||||
public int getSizeX() {
|
||||
return sizeX;
|
||||
}
|
||||
|
||||
public int getSizeY() {
|
||||
return sizeY;
|
||||
}
|
||||
|
||||
public int getSizeZ() {
|
||||
return sizeZ;
|
||||
}
|
||||
|
||||
public int getMinX() {
|
||||
return minX;
|
||||
}
|
||||
|
||||
public int getMinY() {
|
||||
return minY;
|
||||
}
|
||||
|
||||
public int getMinZ() {
|
||||
return minZ;
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
RenderWork.enqueue(() -> {
|
||||
GL15.glDeleteTextures(texture);
|
||||
texture = 0;
|
||||
try {
|
||||
lightVolume.close();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
lightVolume = null;
|
||||
});
|
||||
}
|
||||
|
||||
private void setupPosition(Contraption c) {
|
||||
Vec3d positionVec = c.entity.getPositionVec();
|
||||
minX = (int) (Math.floor(positionVec.x) - sizeX / 2);
|
||||
minY = (int) (Math.floor(positionVec.y) - 1);
|
||||
minZ = (int) (Math.floor(positionVec.z) - sizeZ / 2);
|
||||
}
|
||||
|
||||
public void update(Contraption c) {
|
||||
if (lightVolume == null) return;
|
||||
|
||||
setupPosition(c);
|
||||
|
||||
World world = c.entity.world;
|
||||
|
||||
BlockPos.Mutable pos = new BlockPos.Mutable();
|
||||
|
||||
for (int x = 0; x < sizeX; x++) {
|
||||
for (int y = 0; y < sizeY; y++) {
|
||||
for (int z = 0; z < sizeZ; z++) {
|
||||
pos.setPos(minX + x, minY + y, minZ + z);
|
||||
|
||||
int blockLight = world.getLightLevel(LightType.BLOCK, pos);
|
||||
int skyLight = world.getLightLevel(LightType.SKY, pos);
|
||||
|
||||
writeLight(x, y, z, blockLight, skyLight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
private void writeLight(int x, int y, int z, int block, int sky) {
|
||||
int i = (x + sizeX * (y + z * sizeY)) * 2;
|
||||
|
||||
byte b = (byte) ((block & 0xF) << 4);
|
||||
byte s = (byte) ((sky & 0xF) << 4);
|
||||
|
||||
lightVolume.put(i, b);
|
||||
lightVolume.put(i + 1, s);
|
||||
}
|
||||
|
||||
public void use() {
|
||||
if (texture == 0 || lightVolume == null) return;
|
||||
|
||||
GL12.glBindTexture(GL12.GL_TEXTURE_3D, texture);
|
||||
GL11.glTexParameteri(GL13.GL_TEXTURE_3D, GL13.GL_TEXTURE_MIN_FILTER, GL13.GL_LINEAR);
|
||||
GL11.glTexParameteri(GL13.GL_TEXTURE_3D, GL13.GL_TEXTURE_MAG_FILTER, GL13.GL_LINEAR);
|
||||
GL11.glTexParameteri(GL13.GL_TEXTURE_3D, GL13.GL_TEXTURE_WRAP_S, GL13.GL_CLAMP);
|
||||
GL11.glTexParameteri(GL13.GL_TEXTURE_3D, GL13.GL_TEXTURE_WRAP_R, GL13.GL_CLAMP);
|
||||
GL11.glTexParameteri(GL13.GL_TEXTURE_3D, GL13.GL_TEXTURE_WRAP_T, GL13.GL_CLAMP);
|
||||
if (dirty) {
|
||||
GL12.glTexImage3D(GL12.GL_TEXTURE_3D, 0, GL40.GL_RG8, sizeX, sizeY, sizeZ, 0, GL40.GL_RG, GL40.GL_UNSIGNED_BYTE, lightVolume.getBacking());
|
||||
dirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void release() {
|
||||
GL12.glBindTexture(GL12.GL_TEXTURE_3D, 0);
|
||||
}
|
||||
}
|
@ -0,0 +1,111 @@
|
||||
package com.simibubi.create.foundation.render;
|
||||
|
||||
import com.mojang.blaze3d.matrix.MatrixStack;
|
||||
import com.simibubi.create.content.contraptions.components.structureMovement.Contraption;
|
||||
import com.simibubi.create.foundation.render.light.LightVolume;
|
||||
import com.simibubi.create.foundation.render.shader.Shader;
|
||||
import com.simibubi.create.foundation.render.shader.ShaderCallback;
|
||||
import com.simibubi.create.foundation.render.shader.ShaderHelper;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.Matrix4f;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.util.math.AxisAlignedBB;
|
||||
import net.minecraft.util.math.SectionPos;
|
||||
import net.minecraft.world.ILightReader;
|
||||
import net.minecraft.world.LightType;
|
||||
import net.minecraft.world.World;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
import org.lwjgl.opengl.GL40;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
||||
public class ContraptionRenderDispatcher {
|
||||
public static final HashMap<Integer, RenderedContraption> renderers = new HashMap<>();
|
||||
|
||||
public static void markForRendering(World world, Contraption c, MatrixStack model) {
|
||||
getRenderer(world, c).setRenderSettings(model.peek().getModel());
|
||||
}
|
||||
|
||||
public static void notifyLightUpdate(ILightReader world, LightType type, SectionPos pos) {
|
||||
for (RenderedContraption renderer : renderers.values()) {
|
||||
renderer.getLighter().lightVolume.notifyLightUpdate(world, type, pos);
|
||||
}
|
||||
}
|
||||
|
||||
private static RenderedContraption getRenderer(World world, Contraption c) {
|
||||
RenderedContraption renderer;
|
||||
int entityId = c.entity.getEntityId();
|
||||
if (renderers.containsKey(entityId)) {
|
||||
renderer = renderers.get(entityId);
|
||||
} else {
|
||||
renderer = new RenderedContraption(world, c);
|
||||
renderers.put(entityId, renderer);
|
||||
}
|
||||
|
||||
return renderer;
|
||||
}
|
||||
|
||||
public static void renderLayer(RenderType renderType, Matrix4f projectionMat, Matrix4f viewMat) {
|
||||
removeDeadContraptions();
|
||||
|
||||
if (renderers.isEmpty()) return;
|
||||
|
||||
FastKineticRenderer.setup(Minecraft.getInstance().gameRenderer);
|
||||
GL11.glEnable(GL13.GL_TEXTURE_3D);
|
||||
GL13.glActiveTexture(GL40.GL_TEXTURE4); // the shaders expect light volumes to be in texture 4
|
||||
|
||||
ShaderCallback callback = ShaderHelper.getViewProjectionCallback(projectionMat, viewMat);
|
||||
|
||||
int structureShader = ShaderHelper.useShader(Shader.CONTRAPTION_STRUCTURE, callback);
|
||||
for (RenderedContraption renderer : renderers.values()) {
|
||||
renderer.doRenderLayer(renderType, structureShader);
|
||||
}
|
||||
|
||||
if (renderType == FastKineticRenderer.getKineticRenderLayer()) {
|
||||
int rotatingShader = ShaderHelper.useShader(Shader.CONTRAPTION_ROTATING, callback);
|
||||
for (RenderedContraption renderer : renderers.values()) {
|
||||
renderer.setup(rotatingShader);
|
||||
renderer.kinetics.renderRotating();
|
||||
renderer.teardown();
|
||||
}
|
||||
|
||||
int beltShader = ShaderHelper.useShader(Shader.CONTRAPTION_BELT, callback);
|
||||
for (RenderedContraption renderer : renderers.values()) {
|
||||
renderer.setup(beltShader);
|
||||
renderer.kinetics.renderBelts();
|
||||
renderer.teardown();
|
||||
}
|
||||
}
|
||||
|
||||
ShaderHelper.releaseShader();
|
||||
|
||||
GL11.glDisable(GL13.GL_TEXTURE_3D);
|
||||
FastKineticRenderer.teardown();
|
||||
GL13.glActiveTexture(GL40.GL_TEXTURE0);
|
||||
}
|
||||
|
||||
public static void removeDeadContraptions() {
|
||||
ArrayList<Integer> toRemove = new ArrayList<>();
|
||||
|
||||
for (RenderedContraption renderer : renderers.values()) {
|
||||
if (renderer.isDead()) {
|
||||
toRemove.add(renderer.getEntityId());
|
||||
renderer.invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
for (Integer id : toRemove) {
|
||||
renderers.remove(id);
|
||||
}
|
||||
}
|
||||
|
||||
public static void invalidateAll() {
|
||||
for (RenderedContraption renderer : renderers.values()) {
|
||||
renderer.invalidate();
|
||||
}
|
||||
|
||||
renderers.clear();
|
||||
}
|
||||
}
|
@ -1,226 +0,0 @@
|
||||
package com.simibubi.create.foundation.render;
|
||||
|
||||
import com.mojang.blaze3d.matrix.MatrixStack;
|
||||
import com.mojang.blaze3d.platform.GlStateManager;
|
||||
import com.simibubi.create.content.contraptions.components.structureMovement.Contraption;
|
||||
import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionRenderer;
|
||||
import com.simibubi.create.foundation.render.instancing.IInstanceRendered;
|
||||
import com.simibubi.create.foundation.render.instancing.IInstancedTileEntityRenderer;
|
||||
import com.simibubi.create.foundation.render.shader.Shader;
|
||||
import com.simibubi.create.foundation.render.shader.ShaderCallback;
|
||||
import com.simibubi.create.foundation.render.shader.ShaderHelper;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.BufferBuilder;
|
||||
import net.minecraft.client.renderer.Matrix4f;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.client.renderer.tileentity.TileEntityRenderer;
|
||||
import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.world.World;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
import org.lwjgl.opengl.GL40;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
public class FastContraptionRenderer extends ContraptionRenderer {
|
||||
|
||||
private static final HashMap<Integer, FastContraptionRenderer> renderers = new HashMap<>();
|
||||
|
||||
private HashMap<RenderType, ContraptionBuffer> renderLayers = new HashMap<>();
|
||||
|
||||
private ContraptionLighter lighter;
|
||||
|
||||
public final FastKineticRenderer kinetics;
|
||||
|
||||
private Contraption c;
|
||||
|
||||
private Matrix4f model;
|
||||
|
||||
public FastContraptionRenderer(World world, Contraption c) {
|
||||
this.c = c;
|
||||
this.lighter = new ContraptionLighter(c);
|
||||
this.kinetics = new FastKineticRenderer();
|
||||
|
||||
buildLayers(c);
|
||||
buildInstancedTiles(c);
|
||||
}
|
||||
|
||||
private void buildLayers(Contraption c) {
|
||||
for (ContraptionBuffer buffer : renderLayers.values()) {
|
||||
buffer.delete();
|
||||
}
|
||||
|
||||
renderLayers.clear();
|
||||
|
||||
List<RenderType> blockLayers = RenderType.getBlockLayers();
|
||||
|
||||
for (RenderType layer : blockLayers) {
|
||||
renderLayers.put(layer, buildStructureBuffer(c, layer));
|
||||
}
|
||||
}
|
||||
|
||||
private void buildInstancedTiles(Contraption c) {
|
||||
List<TileEntity> tileEntities = c.renderedTileEntities;
|
||||
if (!tileEntities.isEmpty()) {
|
||||
for (TileEntity te : tileEntities) {
|
||||
if (te instanceof IInstanceRendered) {
|
||||
TileEntityRenderer<TileEntity> renderer = TileEntityRendererDispatcher.instance.getRenderer(te);
|
||||
|
||||
if (renderer instanceof IInstancedTileEntityRenderer) {
|
||||
kinetics.addInstancedData(this, te, (IInstancedTileEntityRenderer<? super TileEntity>) renderer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
kinetics.markAllDirty();
|
||||
}
|
||||
|
||||
public static void tick() {
|
||||
if (Minecraft.getInstance().isGamePaused()) return;
|
||||
|
||||
for (FastContraptionRenderer renderer : renderers.values()) {
|
||||
renderer.lighter.update(renderer.c);
|
||||
}
|
||||
}
|
||||
|
||||
private void setRenderSettings(Matrix4f model) {
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
private void setup(int shader) {
|
||||
setupShaderUniforms(shader);
|
||||
lighter.use();
|
||||
}
|
||||
|
||||
private void teardown() {
|
||||
lighter.release();
|
||||
}
|
||||
|
||||
private void setupShaderUniforms(int shader) {
|
||||
FloatBuffer buf = ShaderHelper.VEC3_BUFFER;
|
||||
|
||||
int lightBoxSize = GlStateManager.getUniformLocation(shader, "lightBoxSize");
|
||||
buf.put(0, (float) lighter.getSizeX());
|
||||
buf.put(1, (float) lighter.getSizeY());
|
||||
buf.put(2, (float) lighter.getSizeZ());
|
||||
buf.rewind();
|
||||
GlStateManager.uniform3(lightBoxSize, buf);
|
||||
|
||||
int lightBoxMin = GlStateManager.getUniformLocation(shader, "lightBoxMin");
|
||||
buf.put(0, (float) lighter.getMinX());
|
||||
buf.put(1, (float) lighter.getMinY());
|
||||
buf.put(2, (float) lighter.getMinZ());
|
||||
buf.rewind();
|
||||
GlStateManager.uniform3(lightBoxMin, buf);
|
||||
|
||||
int model = GlStateManager.getUniformLocation(shader, "model");
|
||||
this.model.write(ShaderHelper.MATRIX_BUFFER);
|
||||
ShaderHelper.MATRIX_BUFFER.rewind();
|
||||
GlStateManager.uniformMatrix4(model, false, ShaderHelper.MATRIX_BUFFER);
|
||||
}
|
||||
|
||||
private void invalidate() {
|
||||
for (ContraptionBuffer buffer : renderLayers.values()) {
|
||||
buffer.delete();
|
||||
}
|
||||
renderLayers.clear();
|
||||
|
||||
lighter.delete();
|
||||
|
||||
kinetics.invalidate();
|
||||
}
|
||||
|
||||
public static void markForRendering(World world, Contraption c, MatrixStack model) {
|
||||
getRenderer(world, c).setRenderSettings(model.peek().getModel());
|
||||
}
|
||||
|
||||
private static FastContraptionRenderer getRenderer(World world, Contraption c) {
|
||||
FastContraptionRenderer renderer;
|
||||
int entityId = c.entity.getEntityId();
|
||||
if (renderers.containsKey(entityId)) {
|
||||
renderer = renderers.get(entityId);
|
||||
} else {
|
||||
renderer = new FastContraptionRenderer(world, c);
|
||||
renderers.put(entityId, renderer);
|
||||
}
|
||||
|
||||
return renderer;
|
||||
}
|
||||
|
||||
public static void renderLayer(RenderType renderType, Matrix4f projectionMat, Matrix4f viewMat) {
|
||||
removeDeadContraptions();
|
||||
|
||||
if (renderers.isEmpty()) return;
|
||||
|
||||
FastKineticRenderer.setup(Minecraft.getInstance().gameRenderer);
|
||||
GL11.glEnable(GL13.GL_TEXTURE_3D);
|
||||
GL13.glActiveTexture(GL40.GL_TEXTURE4); // the shaders expect light volumes to be in texture 4
|
||||
|
||||
ShaderCallback callback = ShaderHelper.getViewProjectionCallback(projectionMat, viewMat);
|
||||
|
||||
int structureShader = ShaderHelper.useShader(Shader.CONTRAPTION_STRUCTURE, callback);
|
||||
for (FastContraptionRenderer renderer : renderers.values()) {
|
||||
ContraptionBuffer buffer = renderer.renderLayers.get(renderType);
|
||||
if (buffer != null) {
|
||||
renderer.setup(structureShader);
|
||||
buffer.render();
|
||||
renderer.teardown();
|
||||
}
|
||||
}
|
||||
|
||||
if (renderType == FastKineticRenderer.getKineticRenderLayer()) {
|
||||
int rotatingShader = ShaderHelper.useShader(Shader.CONTRAPTION_ROTATING, callback);
|
||||
for (FastContraptionRenderer renderer : renderers.values()) {
|
||||
renderer.setup(rotatingShader);
|
||||
renderer.kinetics.renderRotating();
|
||||
renderer.teardown();
|
||||
}
|
||||
|
||||
int beltShader = ShaderHelper.useShader(Shader.CONTRAPTION_BELT, callback);
|
||||
for (FastContraptionRenderer renderer : renderers.values()) {
|
||||
renderer.setup(beltShader);
|
||||
renderer.kinetics.renderBelts();
|
||||
renderer.teardown();
|
||||
}
|
||||
}
|
||||
|
||||
ShaderHelper.releaseShader();
|
||||
|
||||
GL11.glDisable(GL13.GL_TEXTURE_3D);
|
||||
FastKineticRenderer.teardown();
|
||||
GL13.glActiveTexture(GL40.GL_TEXTURE0);
|
||||
}
|
||||
|
||||
public static void removeDeadContraptions() {
|
||||
ArrayList<Integer> toRemove = new ArrayList<>();
|
||||
|
||||
for (FastContraptionRenderer renderer : renderers.values()) {
|
||||
if (!renderer.c.entity.isAlive()) {
|
||||
toRemove.add(renderer.c.entity.getEntityId());
|
||||
renderer.invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
for (Integer id : toRemove) {
|
||||
renderers.remove(id);
|
||||
}
|
||||
}
|
||||
|
||||
public static void invalidateAll() {
|
||||
for (FastContraptionRenderer renderer : renderers.values()) {
|
||||
renderer.invalidate();
|
||||
}
|
||||
|
||||
renderers.clear();
|
||||
}
|
||||
|
||||
private static ContraptionBuffer buildStructureBuffer(Contraption c, RenderType layer) {
|
||||
BufferBuilder builder = buildStructure(c, layer);
|
||||
return new ContraptionBuffer(builder);
|
||||
}
|
||||
}
|
@ -70,7 +70,7 @@ public class FastKineticRenderer {
|
||||
renderer.addInstanceData(new InstanceContext.World<>(te));
|
||||
}
|
||||
|
||||
<T extends TileEntity> void addInstancedData(FastContraptionRenderer c, T te, IInstancedTileEntityRenderer<T> renderer) {
|
||||
<T extends TileEntity> void addInstancedData(RenderedContraption c, T te, IInstancedTileEntityRenderer<T> renderer) {
|
||||
renderer.addInstanceData(new InstanceContext.Contraption<>(te, c));
|
||||
}
|
||||
|
||||
@ -87,6 +87,7 @@ public class FastKineticRenderer {
|
||||
runOnAll(InstanceBuffer::delete);
|
||||
belts.values().forEach(Cache::invalidateAll);
|
||||
rotating.values().forEach(Cache::invalidateAll);
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
private void runOnAll(Consumer<InstanceBuffer<?>> f) {
|
||||
|
@ -19,6 +19,7 @@ import net.minecraft.potion.Effects;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.util.math.SectionPos;
|
||||
import net.minecraft.world.ILightReader;
|
||||
import net.minecraft.world.LightType;
|
||||
import net.minecraft.world.chunk.Chunk;
|
||||
|
||||
@ -42,11 +43,11 @@ public class FastRenderDispatcher {
|
||||
CreateClient.kineticRenderer.renderInstancesAsWorld(type, projection, view);
|
||||
}
|
||||
|
||||
FastContraptionRenderer.renderLayer(type, projection, view);
|
||||
ContraptionRenderDispatcher.renderLayer(type, projection, view);
|
||||
}
|
||||
|
||||
public static void notifyLightUpdate(ClientChunkProvider world, LightType type, SectionPos pos) {
|
||||
FastContraptionRenderer.tick();
|
||||
ContraptionRenderDispatcher.notifyLightUpdate((ILightReader) world.getWorld(), type, pos);
|
||||
|
||||
Chunk chunk = world.getChunk(pos.getSectionX(), pos.getSectionZ(), false);
|
||||
|
||||
|
@ -5,4 +5,9 @@ public class RenderMath {
|
||||
int h = Integer.highestOneBit(a);
|
||||
return (h == a) ? h : (h << 1);
|
||||
}
|
||||
|
||||
public static boolean isPowerOf2(int n) {
|
||||
int b = n & (n - 1);
|
||||
return b == 0 && n != 0;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,152 @@
|
||||
package com.simibubi.create.foundation.render;
|
||||
|
||||
import com.mojang.blaze3d.platform.GlStateManager;
|
||||
import com.simibubi.create.content.contraptions.components.structureMovement.Contraption;
|
||||
import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionRenderer;
|
||||
import com.simibubi.create.foundation.render.instancing.IInstanceRendered;
|
||||
import com.simibubi.create.foundation.render.instancing.IInstancedTileEntityRenderer;
|
||||
import com.simibubi.create.foundation.render.light.ContraptionLighter;
|
||||
import com.simibubi.create.foundation.render.light.LightVolume;
|
||||
import com.simibubi.create.foundation.render.shader.ShaderHelper;
|
||||
import net.minecraft.client.renderer.BufferBuilder;
|
||||
import net.minecraft.client.renderer.Matrix4f;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.client.renderer.tileentity.TileEntityRenderer;
|
||||
import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.util.math.AxisAlignedBB;
|
||||
import net.minecraft.util.math.SectionPos;
|
||||
import net.minecraft.world.ILightReader;
|
||||
import net.minecraft.world.LightType;
|
||||
import net.minecraft.world.World;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
public class RenderedContraption {
|
||||
private HashMap<RenderType, ContraptionBuffer> renderLayers = new HashMap<>();
|
||||
|
||||
private final ContraptionLighter<?> lighter;
|
||||
|
||||
public final FastKineticRenderer kinetics;
|
||||
|
||||
private Contraption contraption;
|
||||
|
||||
private Matrix4f model;
|
||||
|
||||
public RenderedContraption(World world, Contraption contraption) {
|
||||
this.contraption = contraption;
|
||||
this.lighter = contraption.makeLighter();
|
||||
this.kinetics = new FastKineticRenderer();
|
||||
|
||||
buildLayers(contraption);
|
||||
buildInstancedTiles(contraption);
|
||||
}
|
||||
|
||||
public int getEntityId() {
|
||||
return contraption.entity.getEntityId();
|
||||
}
|
||||
|
||||
public boolean isDead() {
|
||||
return !contraption.entity.isAlive();
|
||||
}
|
||||
|
||||
public ContraptionLighter<?> getLighter() {
|
||||
return lighter;
|
||||
}
|
||||
|
||||
public void doRenderLayer(RenderType layer, int shader) {
|
||||
ContraptionBuffer buffer = renderLayers.get(layer);
|
||||
if (buffer != null) {
|
||||
setup(shader);
|
||||
buffer.render();
|
||||
teardown();
|
||||
}
|
||||
}
|
||||
|
||||
private void buildLayers(Contraption c) {
|
||||
for (ContraptionBuffer buffer : renderLayers.values()) {
|
||||
buffer.delete();
|
||||
}
|
||||
|
||||
renderLayers.clear();
|
||||
|
||||
List<RenderType> blockLayers = RenderType.getBlockLayers();
|
||||
|
||||
for (RenderType layer : blockLayers) {
|
||||
renderLayers.put(layer, buildStructureBuffer(c, layer));
|
||||
}
|
||||
}
|
||||
|
||||
private void buildInstancedTiles(Contraption c) {
|
||||
List<TileEntity> tileEntities = c.renderedTileEntities;
|
||||
if (!tileEntities.isEmpty()) {
|
||||
for (TileEntity te : tileEntities) {
|
||||
if (te instanceof IInstanceRendered) {
|
||||
TileEntityRenderer<TileEntity> renderer = TileEntityRendererDispatcher.instance.getRenderer(te);
|
||||
|
||||
if (renderer instanceof IInstancedTileEntityRenderer) {
|
||||
kinetics.addInstancedData(this, te, (IInstancedTileEntityRenderer<? super TileEntity>) renderer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
kinetics.markAllDirty();
|
||||
}
|
||||
|
||||
void setRenderSettings(Matrix4f model) {
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
void setup(int shader) {
|
||||
setupShaderUniforms(shader);
|
||||
lighter.lightVolume.use();
|
||||
}
|
||||
|
||||
void teardown() {
|
||||
lighter.lightVolume.release();
|
||||
}
|
||||
|
||||
void setupShaderUniforms(int shader) {
|
||||
FloatBuffer buf = ShaderHelper.VEC3_BUFFER;
|
||||
|
||||
int lightBoxSize = GlStateManager.getUniformLocation(shader, "lightBoxSize");
|
||||
buf.put(0, (float) lighter.lightVolume.getSizeX());
|
||||
buf.put(1, (float) lighter.lightVolume.getSizeY());
|
||||
buf.put(2, (float) lighter.lightVolume.getSizeZ());
|
||||
buf.rewind();
|
||||
GlStateManager.uniform3(lightBoxSize, buf);
|
||||
|
||||
int lightBoxMin = GlStateManager.getUniformLocation(shader, "lightBoxMin");
|
||||
buf.put(0, (float) lighter.lightVolume.getMinX());
|
||||
buf.put(1, (float) lighter.lightVolume.getMinY());
|
||||
buf.put(2, (float) lighter.lightVolume.getMinZ());
|
||||
buf.rewind();
|
||||
GlStateManager.uniform3(lightBoxMin, buf);
|
||||
|
||||
int model = GlStateManager.getUniformLocation(shader, "model");
|
||||
this.model.write(ShaderHelper.MATRIX_BUFFER);
|
||||
ShaderHelper.MATRIX_BUFFER.rewind();
|
||||
GlStateManager.uniformMatrix4(model, false, ShaderHelper.MATRIX_BUFFER);
|
||||
}
|
||||
|
||||
void invalidate() {
|
||||
for (ContraptionBuffer buffer : renderLayers.values()) {
|
||||
buffer.delete();
|
||||
}
|
||||
renderLayers.clear();
|
||||
|
||||
lighter.lightVolume.delete();
|
||||
|
||||
kinetics.invalidate();
|
||||
}
|
||||
|
||||
private static ContraptionBuffer buildStructureBuffer(Contraption c, RenderType layer) {
|
||||
BufferBuilder builder = ContraptionRenderer.buildStructure(c, layer);
|
||||
return new ContraptionBuffer(builder);
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
package com.simibubi.create.foundation.render.instancing;
|
||||
|
||||
import com.simibubi.create.CreateClient;
|
||||
import com.simibubi.create.foundation.render.FastContraptionRenderer;
|
||||
import com.simibubi.create.foundation.render.RenderedContraption;
|
||||
import com.simibubi.create.foundation.render.FastKineticRenderer;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
|
||||
@ -19,9 +19,9 @@ public abstract class InstanceContext<T extends TileEntity> {
|
||||
|
||||
public static class Contraption<T extends TileEntity> extends InstanceContext<T> {
|
||||
|
||||
public final FastContraptionRenderer c;
|
||||
public final RenderedContraption c;
|
||||
|
||||
public Contraption(T te, FastContraptionRenderer c) {
|
||||
public Contraption(T te, RenderedContraption c) {
|
||||
super(te);
|
||||
this.c = c;
|
||||
}
|
||||
|
@ -0,0 +1,27 @@
|
||||
package com.simibubi.create.foundation.render.light;
|
||||
|
||||
import com.simibubi.create.content.contraptions.components.structureMovement.Contraption;
|
||||
import net.minecraft.util.math.SectionPos;
|
||||
import net.minecraft.world.ILightReader;
|
||||
import net.minecraft.world.LightType;
|
||||
|
||||
import static com.simibubi.create.foundation.render.RenderMath.nextPowerOf2;
|
||||
|
||||
public abstract class ContraptionLighter<C extends Contraption> {
|
||||
protected final C contraption;
|
||||
public final LightVolume lightVolume;
|
||||
|
||||
protected ContraptionLighter(C contraption) {
|
||||
this.contraption = contraption;
|
||||
|
||||
GridAlignedBB bounds = getContraptionBounds();
|
||||
bounds.grow(1); // so we have at least enough data on the edges to avoid artifacts
|
||||
bounds.nextPowerOf2Centered();
|
||||
|
||||
lightVolume = new LightVolume(bounds);
|
||||
|
||||
lightVolume.initialize(contraption.entity.world);
|
||||
}
|
||||
|
||||
public abstract GridAlignedBB getContraptionBounds();
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
package com.simibubi.create.foundation.render.light;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface CoordinateConsumer {
|
||||
void consume(int x, int y, int z);
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package com.simibubi.create.foundation.render.light;
|
||||
|
||||
import com.simibubi.create.content.contraptions.components.structureMovement.Contraption;
|
||||
|
||||
// so other contraptions don't crash before they have a lighter
|
||||
public class EmptyLighter extends ContraptionLighter<Contraption> {
|
||||
public EmptyLighter(Contraption contraption) {
|
||||
super(contraption);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GridAlignedBB getContraptionBounds() {
|
||||
return new GridAlignedBB(0, 0, 0, 1, 1, 1);
|
||||
}
|
||||
}
|
@ -0,0 +1,203 @@
|
||||
package com.simibubi.create.foundation.render.light;
|
||||
|
||||
import com.simibubi.create.foundation.render.RenderMath;
|
||||
import net.minecraft.util.math.AxisAlignedBB;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.SectionPos;
|
||||
import net.minecraft.util.math.Vec3i;
|
||||
import net.minecraft.world.LightType;
|
||||
|
||||
import java.util.function.IntFunction;
|
||||
|
||||
import static com.simibubi.create.foundation.render.RenderMath.isPowerOf2;
|
||||
|
||||
public class GridAlignedBB {
|
||||
public int minX;
|
||||
public int minY;
|
||||
public int minZ;
|
||||
public int maxX;
|
||||
public int maxY;
|
||||
public int maxZ;
|
||||
|
||||
public GridAlignedBB(int minX, int minY, int minZ, int maxX, int maxY, int maxZ) {
|
||||
this.minX = minX;
|
||||
this.minY = minY;
|
||||
this.minZ = minZ;
|
||||
this.maxX = maxX;
|
||||
this.maxY = maxY;
|
||||
this.maxZ = maxZ;
|
||||
}
|
||||
|
||||
public static GridAlignedBB copy(GridAlignedBB bb) {
|
||||
return new GridAlignedBB(bb.minX, bb.minY, bb.minZ, bb.maxX, bb.maxY, bb.maxZ);
|
||||
}
|
||||
|
||||
public static GridAlignedBB fromAABB(AxisAlignedBB aabb) {
|
||||
int minX = (int) Math.floor(aabb.minX);
|
||||
int minY = (int) Math.floor(aabb.minY);
|
||||
int minZ = (int) Math.floor(aabb.minZ);
|
||||
int maxX = (int) Math.ceil(aabb.maxX);
|
||||
int maxY = (int) Math.ceil(aabb.maxY);
|
||||
int maxZ = (int) Math.ceil(aabb.maxZ);
|
||||
return new GridAlignedBB(minX, minY, minZ, maxX, maxY, maxZ);
|
||||
}
|
||||
|
||||
public static GridAlignedBB fromSection(SectionPos pos) {
|
||||
return new GridAlignedBB(pos.getWorldStartX(),
|
||||
pos.getWorldStartY(),
|
||||
pos.getWorldStartZ(),
|
||||
pos.getWorldEndX() + 1,
|
||||
pos.getWorldEndY() + 1,
|
||||
pos.getWorldEndZ() + 1);
|
||||
}
|
||||
|
||||
public static AxisAlignedBB toAABB(GridAlignedBB bb) {
|
||||
return new AxisAlignedBB(bb.minX, bb.minY, bb.minZ, bb.maxX, bb.maxY, bb.maxZ);
|
||||
}
|
||||
|
||||
public int sizeX() {
|
||||
return maxX - minX;
|
||||
}
|
||||
|
||||
public int sizeY() {
|
||||
return maxY - minY;
|
||||
}
|
||||
|
||||
public int sizeZ() {
|
||||
return maxZ - minZ;
|
||||
}
|
||||
|
||||
public int volume() {
|
||||
return sizeX() * sizeY() * sizeZ();
|
||||
}
|
||||
|
||||
public boolean empty() {
|
||||
// if any dimension has side length 0 this box contains no volume
|
||||
return minX == maxX ||
|
||||
minY == maxY ||
|
||||
minZ == maxZ;
|
||||
}
|
||||
|
||||
public void translate(Vec3i by) {
|
||||
this.minX += by.getX();
|
||||
this.minY += by.getY();
|
||||
this.minZ += by.getZ();
|
||||
this.maxX += by.getX();
|
||||
this.maxY += by.getY();
|
||||
this.maxZ += by.getZ();
|
||||
}
|
||||
|
||||
/**
|
||||
* Grow this bounding box to have power of 2 side length, scaling from the center.
|
||||
*/
|
||||
public void nextPowerOf2Centered() {
|
||||
int sizeX = sizeX();
|
||||
int sizeY = sizeY();
|
||||
int sizeZ = sizeZ();
|
||||
|
||||
int newSizeX = RenderMath.nextPowerOf2(sizeX);
|
||||
int newSizeY = RenderMath.nextPowerOf2(sizeY);
|
||||
int newSizeZ = RenderMath.nextPowerOf2(sizeZ);
|
||||
|
||||
int diffX = newSizeX - sizeX;
|
||||
int diffY = newSizeY - sizeY;
|
||||
int diffZ = newSizeZ - sizeZ;
|
||||
|
||||
this.minX -= diffX / 2; // floor division for the minimums
|
||||
this.minY -= diffY / 2;
|
||||
this.minZ -= diffZ / 2;
|
||||
this.maxX += (diffX + 1) / 2; // ceiling divison for the maximums
|
||||
this.maxY += (diffY + 1) / 2;
|
||||
this.maxZ += (diffZ + 1) / 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Grow this bounding box to have power of 2 side lengths, scaling from the minimum coords.
|
||||
*/
|
||||
public void nextPowerOf2() {
|
||||
int sizeX = RenderMath.nextPowerOf2(sizeX());
|
||||
int sizeY = RenderMath.nextPowerOf2(sizeY());
|
||||
int sizeZ = RenderMath.nextPowerOf2(sizeZ());
|
||||
|
||||
this.maxX = this.minX + sizeX;
|
||||
this.maxY = this.minY + sizeY;
|
||||
this.maxZ = this.minZ + sizeZ;
|
||||
}
|
||||
|
||||
public boolean hasPowerOf2Sides() {
|
||||
// this is only true if all individual side lengths are powers of 2
|
||||
return isPowerOf2(volume());
|
||||
}
|
||||
|
||||
public void grow(int s) {
|
||||
this.grow(s, s, s);
|
||||
}
|
||||
|
||||
public void grow(int x, int y, int z) {
|
||||
this.minX -= x;
|
||||
this.minY -= y;
|
||||
this.minZ -= z;
|
||||
this.maxX += x;
|
||||
this.maxY += y;
|
||||
this.maxZ += z;
|
||||
}
|
||||
|
||||
public GridAlignedBB intersect(GridAlignedBB other) {
|
||||
int minX = Math.max(this.minX, other.minX);
|
||||
int minY = Math.max(this.minY, other.minY);
|
||||
int minZ = Math.max(this.minZ, other.minZ);
|
||||
int maxX = Math.min(this.maxX, other.maxX);
|
||||
int maxY = Math.min(this.maxY, other.maxY);
|
||||
int maxZ = Math.min(this.maxZ, other.maxZ);
|
||||
return new GridAlignedBB(minX, minY, minZ, maxX, maxY, maxZ);
|
||||
}
|
||||
|
||||
public void intersectAssign(GridAlignedBB other) {
|
||||
this.minX = Math.max(this.minX, other.minX);
|
||||
this.minY = Math.max(this.minY, other.minY);
|
||||
this.minZ = Math.max(this.minZ, other.minZ);
|
||||
this.maxX = Math.min(this.maxX, other.maxX);
|
||||
this.maxY = Math.min(this.maxY, other.maxY);
|
||||
this.maxZ = Math.min(this.maxZ, other.maxZ);
|
||||
}
|
||||
|
||||
public GridAlignedBB union(GridAlignedBB other) {
|
||||
int minX = Math.min(this.minX, other.minX);
|
||||
int minY = Math.min(this.minY, other.minY);
|
||||
int minZ = Math.min(this.minZ, other.minZ);
|
||||
int maxX = Math.max(this.maxX, other.maxX);
|
||||
int maxY = Math.max(this.maxY, other.maxY);
|
||||
int maxZ = Math.max(this.maxZ, other.maxZ);
|
||||
return new GridAlignedBB(minX, minY, minZ, maxX, maxY, maxZ);
|
||||
}
|
||||
|
||||
public void unionAssign(GridAlignedBB other) {
|
||||
this.minX = Math.min(this.minX, other.minX);
|
||||
this.minY = Math.min(this.minY, other.minY);
|
||||
this.minZ = Math.min(this.minZ, other.minZ);
|
||||
this.maxX = Math.max(this.maxX, other.maxX);
|
||||
this.maxY = Math.max(this.maxY, other.maxY);
|
||||
this.maxZ = Math.max(this.maxZ, other.maxZ);
|
||||
}
|
||||
|
||||
public boolean intersects(GridAlignedBB other) {
|
||||
return this.intersects(other.minX, other.minY, other.minZ, other.maxX, other.maxY, other.maxZ);
|
||||
}
|
||||
|
||||
public boolean intersects(int minX, int minY, int minZ, int maxX, int maxY, int maxZ) {
|
||||
return this.minX < maxX && this.maxX > minX && this.minY < maxY && this.maxY > minY && this.minZ < maxZ && this.maxZ > minZ;
|
||||
}
|
||||
|
||||
public void forEachContained(CoordinateConsumer func) {
|
||||
if (empty()) return;
|
||||
|
||||
for (int x = minX; x < maxX; x++) {
|
||||
for (int y = Math.max(minY, 0); y < Math.min(maxY, 255); y++) { // clamp to world height limits
|
||||
for (int z = minZ; z < maxZ; z++) {
|
||||
func.consume(x, y, z);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,204 @@
|
||||
package com.simibubi.create.foundation.render.light;
|
||||
|
||||
import com.simibubi.create.foundation.render.RenderWork;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.SectionPos;
|
||||
import net.minecraft.world.ILightReader;
|
||||
import net.minecraft.world.LightType;
|
||||
import org.lwjgl.opengl.*;
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public class LightVolume {
|
||||
|
||||
private final GridAlignedBB volume;
|
||||
private ByteBuffer lightData;
|
||||
|
||||
private boolean bufferDirty;
|
||||
|
||||
private int glTexture;
|
||||
|
||||
public LightVolume(GridAlignedBB volume) {
|
||||
// the gpu requires that all textures have power of 2 side lengths
|
||||
if (!volume.hasPowerOf2Sides())
|
||||
throw new IllegalArgumentException("LightVolume must have power of 2 side lengths");
|
||||
|
||||
this.volume = volume;
|
||||
|
||||
this.glTexture = GL11.glGenTextures();
|
||||
this.lightData = MemoryUtil.memAlloc(this.volume.volume() * 2); // TODO: maybe figure out how to pack light coords into a single byte
|
||||
}
|
||||
|
||||
public GridAlignedBB getBox() {
|
||||
return GridAlignedBB.copy(volume);
|
||||
}
|
||||
|
||||
public int getMinX() {
|
||||
return volume.minX;
|
||||
}
|
||||
|
||||
public int getMinY() {
|
||||
return volume.minY;
|
||||
}
|
||||
|
||||
public int getMinZ() {
|
||||
return volume.minZ;
|
||||
}
|
||||
|
||||
public int getMaxX() {
|
||||
return volume.maxX;
|
||||
}
|
||||
|
||||
public int getMaxY() {
|
||||
return volume.maxY;
|
||||
}
|
||||
|
||||
public int getMaxZ() {
|
||||
return volume.maxZ;
|
||||
}
|
||||
|
||||
public int getSizeX() {
|
||||
return volume.sizeX();
|
||||
}
|
||||
|
||||
public int getSizeY() {
|
||||
return volume.sizeY();
|
||||
}
|
||||
|
||||
public int getSizeZ() {
|
||||
return volume.sizeZ();
|
||||
}
|
||||
|
||||
|
||||
public void notifyLightUpdate(ILightReader world, LightType type, SectionPos location) {
|
||||
GridAlignedBB changedVolume = GridAlignedBB.fromSection(location);
|
||||
changedVolume.intersectAssign(volume); // compute the region contained by us that has dirty lighting data.
|
||||
|
||||
if (!changedVolume.empty()) {
|
||||
if (type == LightType.BLOCK) copyBlock(world, changedVolume);
|
||||
else if (type == LightType.SKY) copySky(world, changedVolume);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Completely (re)populate this volume with block and sky lighting data.
|
||||
* This is expensive and should be avoided.
|
||||
*/
|
||||
public void initialize(ILightReader world) {
|
||||
BlockPos.Mutable pos = new BlockPos.Mutable();
|
||||
|
||||
int shiftX = volume.minX;
|
||||
int shiftY = volume.minY;
|
||||
int shiftZ = volume.minZ;
|
||||
|
||||
volume.forEachContained((x, y, z) -> {
|
||||
pos.setPos(x, y, z);
|
||||
|
||||
int blockLight = world.getLightLevel(LightType.BLOCK, pos);
|
||||
int skyLight = world.getLightLevel(LightType.SKY, pos);
|
||||
|
||||
writeLight(x - shiftX, y - shiftY, z - shiftZ, blockLight, skyLight);
|
||||
});
|
||||
|
||||
bufferDirty = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy block light from the world into this volume.
|
||||
* @param worldVolume the region in the world to copy data from.
|
||||
*/
|
||||
public void copyBlock(ILightReader world, GridAlignedBB worldVolume) {
|
||||
BlockPos.Mutable pos = new BlockPos.Mutable();
|
||||
|
||||
int xShift = volume.minX;
|
||||
int yShift = volume.minY;
|
||||
int zShift = volume.minZ;
|
||||
|
||||
worldVolume.forEachContained((x, y, z) -> {
|
||||
pos.setPos(x, y, z);
|
||||
|
||||
int light = world.getLightLevel(LightType.BLOCK, pos);
|
||||
|
||||
writeBlock(x - xShift, y - yShift, z - zShift, light);
|
||||
});
|
||||
|
||||
bufferDirty = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy sky light from the world into this volume.
|
||||
* @param worldVolume the region in the world to copy data from.
|
||||
*/
|
||||
public void copySky(ILightReader world, GridAlignedBB worldVolume) {
|
||||
BlockPos.Mutable pos = new BlockPos.Mutable();
|
||||
|
||||
int xShift = volume.minX;
|
||||
int yShift = volume.minY;
|
||||
int zShift = volume.minZ;
|
||||
|
||||
worldVolume.forEachContained((x, y, z) -> {
|
||||
pos.setPos(x, y, z);
|
||||
|
||||
int light = world.getLightLevel(LightType.SKY, pos);
|
||||
|
||||
writeSky(x - xShift, y - yShift, z - zShift, light);
|
||||
});
|
||||
|
||||
bufferDirty = true;
|
||||
}
|
||||
|
||||
public void use() {
|
||||
// just in case something goes wrong or we accidentally call this before this volume is properly disposed of.
|
||||
if (glTexture == 0 || lightData == null) return;
|
||||
|
||||
GL12.glBindTexture(GL12.GL_TEXTURE_3D, glTexture);
|
||||
GL11.glTexParameteri(GL13.GL_TEXTURE_3D, GL13.GL_TEXTURE_MIN_FILTER, GL13.GL_LINEAR);
|
||||
GL11.glTexParameteri(GL13.GL_TEXTURE_3D, GL13.GL_TEXTURE_MAG_FILTER, GL13.GL_LINEAR);
|
||||
GL11.glTexParameteri(GL13.GL_TEXTURE_3D, GL13.GL_TEXTURE_WRAP_S, GL20.GL_MIRRORED_REPEAT);
|
||||
GL11.glTexParameteri(GL13.GL_TEXTURE_3D, GL13.GL_TEXTURE_WRAP_R, GL20.GL_MIRRORED_REPEAT);
|
||||
GL11.glTexParameteri(GL13.GL_TEXTURE_3D, GL13.GL_TEXTURE_WRAP_T, GL20.GL_MIRRORED_REPEAT);
|
||||
if (bufferDirty) {
|
||||
GL12.glTexImage3D(GL12.GL_TEXTURE_3D, 0, GL40.GL_RG8, volume.sizeX(), volume.sizeY(), volume.sizeZ(), 0, GL40.GL_RG, GL40.GL_UNSIGNED_BYTE, lightData);
|
||||
bufferDirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void release() {
|
||||
GL12.glBindTexture(GL12.GL_TEXTURE_3D, 0);
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
RenderWork.enqueue(() -> {
|
||||
GL15.glDeleteTextures(glTexture);
|
||||
glTexture = 0;
|
||||
MemoryUtil.memFree(lightData);
|
||||
lightData = null;
|
||||
});
|
||||
}
|
||||
|
||||
private void writeLight(int x, int y, int z, int block, int sky) {
|
||||
byte b = (byte) ((block & 0xF) << 4);
|
||||
byte s = (byte) ((sky & 0xF) << 4);
|
||||
|
||||
int i = index(x, y, z);
|
||||
lightData.put(i, b);
|
||||
lightData.put(i + 1, s);
|
||||
}
|
||||
|
||||
private void writeBlock(int x, int y, int z, int block) {
|
||||
byte b = (byte) ((block & 0xF) << 4);
|
||||
|
||||
lightData.put(index(x, y, z), b);
|
||||
}
|
||||
|
||||
private void writeSky(int x, int y, int z, int sky) {
|
||||
byte b = (byte) ((sky & 0xF) << 4);
|
||||
|
||||
lightData.put(index(x, y, z) + 1, b);
|
||||
}
|
||||
|
||||
private int index(int x, int y, int z) {
|
||||
return (x + volume.sizeX() * (y + z * volume.sizeY())) * 2;
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package com.simibubi.create.foundation.render.light;
|
||||
|
||||
import com.mojang.blaze3d.matrix.MatrixStack;
|
||||
import com.simibubi.create.foundation.render.ContraptionRenderDispatcher;
|
||||
import com.simibubi.create.foundation.render.RenderedContraption;
|
||||
import com.simibubi.create.foundation.renderState.SuperRenderTypeBuffer;
|
||||
import com.simibubi.create.foundation.utility.outliner.AABBOutline;
|
||||
import com.simibubi.create.foundation.utility.outliner.Outline;
|
||||
|
||||
public class LightVolumeDebugger {
|
||||
public static void render(MatrixStack ms, SuperRenderTypeBuffer buffer) {
|
||||
ContraptionRenderDispatcher.renderers.values()
|
||||
.stream()
|
||||
.map(r -> r.getLighter().lightVolume.getBox())
|
||||
.map(volume -> new AABBOutline(GridAlignedBB.toAABB(volume)))
|
||||
.forEach(outline -> outline.render(ms, buffer));
|
||||
}
|
||||
}
|
@ -13,6 +13,8 @@ import net.minecraft.resources.IReloadableResourceManager;
|
||||
import net.minecraft.resources.IResourceManager;
|
||||
import net.minecraft.resources.IResourceManagerReloadListener;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.resource.ISelectiveResourceReloadListener;
|
||||
import net.minecraftforge.resource.VanillaResourceType;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
@ -41,11 +43,13 @@ public class ShaderHelper {
|
||||
if (Minecraft.getInstance() != null
|
||||
&& Minecraft.getInstance().getResourceManager() instanceof IReloadableResourceManager) {
|
||||
((IReloadableResourceManager) Minecraft.getInstance().getResourceManager()).addReloadListener(
|
||||
(IResourceManagerReloadListener) manager -> {
|
||||
PROGRAMS.values().forEach(ShaderLinkHelper::deleteShader);
|
||||
PROGRAMS.clear();
|
||||
for (Shader shader : Shader.values()) {
|
||||
createProgram(manager, shader);
|
||||
(ISelectiveResourceReloadListener) (manager, predicate) -> {
|
||||
if (predicate.test(VanillaResourceType.SHADERS)) {
|
||||
PROGRAMS.values().forEach(ShaderLinkHelper::deleteShader);
|
||||
PROGRAMS.clear();
|
||||
for (Shader shader : Shader.values()) {
|
||||
createProgram(manager, shader);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ Technology that empowers the player.'''
|
||||
[[dependencies.create]]
|
||||
modId="forge"
|
||||
mandatory=true
|
||||
versionRange="[31.2.0,)"
|
||||
versionRange="[31.2.44,)"
|
||||
ordering="NONE"
|
||||
side="BOTH"
|
||||
|
||||
|
@ -55,13 +55,17 @@ void main() {
|
||||
|
||||
float scroll = fract(speed * time / (36 * 16.)) * scrollSize * scrollMult;
|
||||
|
||||
Diffuse = diffuse(normalize((rotation * vec4(aNormal, 0.)).xyz));
|
||||
vec3 norm = (rotation * vec4(aNormal, 0.)).xyz;
|
||||
|
||||
Diffuse = diffuse(norm);
|
||||
Light = light;
|
||||
TexCoords = aTexCoords - sourceUV + scrollTexture.xy + vec2(0., scroll);
|
||||
gl_Position = projection * view * renderPos;
|
||||
|
||||
if (debug == 1) {
|
||||
Color = vec4(networkTint, 1);
|
||||
} else if (debug == 2) {
|
||||
Color = vec4(norm, 1);
|
||||
} else {
|
||||
Color = vec4(1);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user