Reformat backend

This commit is contained in:
JozsefA 2021-04-29 17:19:08 -07:00
parent d9b04138df
commit 322496f3b4
66 changed files with 2537 additions and 2539 deletions

View file

@ -9,7 +9,6 @@ import org.lwjgl.opengl.GL;
import org.lwjgl.opengl.GLCapabilities; import org.lwjgl.opengl.GLCapabilities;
import com.simibubi.create.foundation.config.AllConfigs; import com.simibubi.create.foundation.config.AllConfigs;
import com.simibubi.create.foundation.render.backend.gl.GlFog;
import com.simibubi.create.foundation.render.backend.gl.shader.GlProgram; import com.simibubi.create.foundation.render.backend.gl.shader.GlProgram;
import com.simibubi.create.foundation.render.backend.gl.shader.IMultiProgram; import com.simibubi.create.foundation.render.backend.gl.shader.IMultiProgram;
import com.simibubi.create.foundation.render.backend.gl.shader.ProgramSpec; import com.simibubi.create.foundation.render.backend.gl.shader.ProgramSpec;

View file

@ -13,75 +13,75 @@ import net.minecraft.client.renderer.BufferBuilder;
public abstract class BufferedModel extends TemplateBuffer { public abstract class BufferedModel extends TemplateBuffer {
protected GlBuffer modelVBO; protected GlBuffer modelVBO;
protected boolean removed; protected boolean removed;
protected BufferedModel(BufferBuilder buf) { protected BufferedModel(BufferBuilder buf) {
super(buf); super(buf);
if (vertexCount > 0) init(); if (vertexCount > 0) init();
} }
protected void init() { protected void init() {
modelVBO = new GlBuffer(GL20.GL_ARRAY_BUFFER); modelVBO = new GlBuffer(GL20.GL_ARRAY_BUFFER);
modelVBO.with(vbo -> initModel()); modelVBO.with(vbo -> initModel());
} }
protected void initModel() { protected void initModel() {
int stride = getModelFormat().getStride(); int stride = getModelFormat().getStride();
int invariantSize = vertexCount * stride; int invariantSize = vertexCount * stride;
// allocate the buffer on the gpu // allocate the buffer on the gpu
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, invariantSize, GL15.GL_STATIC_DRAW); GL15.glBufferData(GL15.GL_ARRAY_BUFFER, invariantSize, GL15.GL_STATIC_DRAW);
// mirror it in system memory so we can write to it // mirror it in system memory so we can write to it
modelVBO.map(invariantSize, buffer -> { modelVBO.map(invariantSize, buffer -> {
for (int i = 0; i < vertexCount; i++) { for (int i = 0; i < vertexCount; i++) {
copyVertex(buffer, i); copyVertex(buffer, i);
} }
}); });
} }
protected abstract void copyVertex(ByteBuffer to, int index); protected abstract void copyVertex(ByteBuffer to, int index);
protected abstract VertexFormat getModelFormat(); protected abstract VertexFormat getModelFormat();
protected int getTotalShaderAttributeCount() { protected int getTotalShaderAttributeCount() {
return getModelFormat().getShaderAttributeCount(); return getModelFormat().getShaderAttributeCount();
} }
/** /**
* Renders this model, checking first if there is anything to render. * Renders this model, checking first if there is anything to render.
*/ */
public final void render() { public final void render() {
if (vertexCount == 0 || removed) return; if (vertexCount == 0 || removed) return;
doRender(); doRender();
} }
/** /**
* Set up any state and make the draw calls. * Set up any state and make the draw calls.
*/ */
protected abstract void doRender(); protected abstract void doRender();
protected void setupAttributes() { protected void setupAttributes() {
int numAttributes = getTotalShaderAttributeCount(); int numAttributes = getTotalShaderAttributeCount();
for (int i = 0; i <= numAttributes; i++) { for (int i = 0; i <= numAttributes; i++) {
GL20.glEnableVertexAttribArray(i); GL20.glEnableVertexAttribArray(i);
} }
getModelFormat().vertexAttribPointers(0); getModelFormat().vertexAttribPointers(0);
} }
public final void delete() { public final void delete() {
removed = true; removed = true;
if (vertexCount > 0) { if (vertexCount > 0) {
RenderWork.enqueue(this::deleteInternal); RenderWork.enqueue(this::deleteInternal);
} }
} }
protected void deleteInternal() { protected void deleteInternal() {
modelVBO.delete(); modelVBO.delete();
} }
} }

View file

@ -2,78 +2,71 @@ package com.simibubi.create.foundation.render.backend;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import com.mojang.blaze3d.matrix.MatrixStack;
import com.simibubi.create.CreateClient; import com.simibubi.create.CreateClient;
import com.simibubi.create.content.contraptions.KineticDebugger; import com.simibubi.create.content.contraptions.KineticDebugger;
import com.simibubi.create.foundation.render.KineticRenderer; import com.simibubi.create.foundation.render.KineticRenderer;
import com.simibubi.create.foundation.utility.AnimationTickHolder;
import com.simibubi.create.foundation.utility.WorldAttached; import com.simibubi.create.foundation.utility.WorldAttached;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.entity.player.ClientPlayerEntity;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.world.ClientWorld; import net.minecraft.client.world.ClientWorld;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.potion.Effects;
import net.minecraft.tileentity.TileEntity; import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.vector.Matrix4f; import net.minecraft.util.math.vector.Matrix4f;
import net.minecraft.util.math.vector.Vector3f;
import net.minecraft.world.World; import net.minecraft.world.World;
public class FastRenderDispatcher { public class FastRenderDispatcher {
public static WorldAttached<ConcurrentHashMap.KeySetView<TileEntity, Boolean>> queuedUpdates = new WorldAttached<>(ConcurrentHashMap::newKeySet); public static WorldAttached<ConcurrentHashMap.KeySetView<TileEntity, Boolean>> queuedUpdates = new WorldAttached<>(ConcurrentHashMap::newKeySet);
public static void enqueueUpdate(TileEntity te) { public static void enqueueUpdate(TileEntity te) {
queuedUpdates.get(te.getWorld()).add(te); queuedUpdates.get(te.getWorld()).add(te);
} }
public static void tick() { public static void tick() {
Minecraft mc = Minecraft.getInstance(); Minecraft mc = Minecraft.getInstance();
ClientWorld world = mc.world; ClientWorld world = mc.world;
KineticRenderer kineticRenderer = CreateClient.kineticRenderer.get(world); KineticRenderer kineticRenderer = CreateClient.kineticRenderer.get(world);
Entity renderViewEntity = mc.renderViewEntity; Entity renderViewEntity = mc.renderViewEntity;
kineticRenderer.tick(renderViewEntity.getX(), renderViewEntity.getY(), renderViewEntity.getZ()); kineticRenderer.tick(renderViewEntity.getX(), renderViewEntity.getY(), renderViewEntity.getZ());
ConcurrentHashMap.KeySetView<TileEntity, Boolean> map = queuedUpdates.get(world); ConcurrentHashMap.KeySetView<TileEntity, Boolean> map = queuedUpdates.get(world);
map map
.forEach(te -> { .forEach(te -> {
map.remove(te); map.remove(te);
kineticRenderer.update(te); kineticRenderer.update(te);
}); });
} }
public static boolean available() { public static boolean available() {
return Backend.canUseInstancing(); return Backend.canUseInstancing();
} }
public static boolean available(World world) { public static boolean available(World world) {
return Backend.canUseInstancing() && Backend.isFlywheelWorld(world); return Backend.canUseInstancing() && Backend.isFlywheelWorld(world);
} }
public static int getDebugMode() { public static int getDebugMode() {
return KineticDebugger.isActive() ? 1 : 0; return KineticDebugger.isActive() ? 1 : 0;
} }
public static void refresh() { public static void refresh() {
RenderWork.enqueue(Minecraft.getInstance().worldRenderer::loadRenderers); RenderWork.enqueue(Minecraft.getInstance().worldRenderer::loadRenderers);
} }
public static void renderLayer(RenderType layer, Matrix4f viewProjection, double cameraX, double cameraY, double cameraZ) { public static void renderLayer(RenderType layer, Matrix4f viewProjection, double cameraX, double cameraY, double cameraZ) {
if (!Backend.canUseInstancing()) return; if (!Backend.canUseInstancing()) return;
ClientWorld world = Minecraft.getInstance().world; ClientWorld world = Minecraft.getInstance().world;
KineticRenderer kineticRenderer = CreateClient.kineticRenderer.get(world); KineticRenderer kineticRenderer = CreateClient.kineticRenderer.get(world);
layer.startDrawing(); layer.startDrawing();
kineticRenderer.render(layer, viewProjection, cameraX, cameraY, cameraZ); kineticRenderer.render(layer, viewProjection, cameraX, cameraY, cameraZ);
layer.endDrawing(); layer.endDrawing();
} }
} }

View file

@ -5,6 +5,6 @@ import com.simibubi.create.foundation.render.backend.core.OrientedData;
import com.simibubi.create.foundation.render.backend.instancing.InstancedModel; import com.simibubi.create.foundation.render.backend.instancing.InstancedModel;
public class MaterialTypes { public class MaterialTypes {
public static final MaterialType<InstancedModel<ModelData>> TRANSFORMED = new MaterialType<>(); public static final MaterialType<InstancedModel<ModelData>> TRANSFORMED = new MaterialType<>();
public static final MaterialType<InstancedModel<OrientedData>> ORIENTED = new MaterialType<>(); public static final MaterialType<InstancedModel<OrientedData>> ORIENTED = new MaterialType<>();
} }

View file

@ -9,77 +9,78 @@ import java.util.Optional;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
public class OptifineHandler { public class OptifineHandler {
public static final String OPTIFINE_ROOT_PACKAGE = "net.optifine"; public static final String OPTIFINE_ROOT_PACKAGE = "net.optifine";
public static final String SHADER_PACKAGE = "net.optifine.shaders"; public static final String SHADER_PACKAGE = "net.optifine.shaders";
private static Package optifine; private static Package optifine;
private static OptifineHandler handler; private static OptifineHandler handler;
public final boolean usingShaders; public final boolean usingShaders;
public OptifineHandler(boolean usingShaders) { public OptifineHandler(boolean usingShaders) {
this.usingShaders = usingShaders; this.usingShaders = usingShaders;
} }
/** /**
* Get information about the current Optifine configuration. * Get information about the current Optifine configuration.
* @return {@link Optional#empty()} if Optifine is not installed. *
*/ * @return {@link Optional#empty()} if Optifine is not installed.
public static Optional<OptifineHandler> get() { */
return Optional.ofNullable(handler); public static Optional<OptifineHandler> get() {
} return Optional.ofNullable(handler);
}
public static boolean optifineInstalled() { public static boolean optifineInstalled() {
return optifine != null; return optifine != null;
} }
public static boolean usingShaders() { public static boolean usingShaders() {
return OptifineHandler.get() return OptifineHandler.get()
.map(OptifineHandler::isUsingShaders) .map(OptifineHandler::isUsingShaders)
.orElse(false); .orElse(false);
} }
public static void init() { public static void init() {
optifine = Package.getPackage(OPTIFINE_ROOT_PACKAGE); optifine = Package.getPackage(OPTIFINE_ROOT_PACKAGE);
if (optifine == null) { if (optifine == null) {
Backend.log.info("Optifine not detected."); Backend.log.info("Optifine not detected.");
} else { } else {
Backend.log.info("Optifine detected."); Backend.log.info("Optifine detected.");
refresh(); refresh();
} }
} }
public static void refresh() { public static void refresh() {
if (optifine == null) return; if (optifine == null) return;
File dir = Minecraft.getInstance().gameDir; File dir = Minecraft.getInstance().gameDir;
File shaderOptions = new File(dir, "optionsshaders.txt"); File shaderOptions = new File(dir, "optionsshaders.txt");
boolean shadersOff = true; boolean shadersOff = true;
try { try {
BufferedReader reader = new BufferedReader(new FileReader(shaderOptions)); BufferedReader reader = new BufferedReader(new FileReader(shaderOptions));
shadersOff = reader.lines() shadersOff = reader.lines()
.anyMatch(it -> { .anyMatch(it -> {
String line = it.replaceAll("\\s", ""); String line = it.replaceAll("\\s", "");
if (line.startsWith("shaderPack=")) { if (line.startsWith("shaderPack=")) {
String setting = line.substring("shaderPack=".length()); String setting = line.substring("shaderPack=".length());
return setting.equals("OFF") || setting.equals("(internal)"); return setting.equals("OFF") || setting.equals("(internal)");
} }
return false; return false;
}); });
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {
Backend.log.info("No shader config found."); Backend.log.info("No shader config found.");
} }
handler = new OptifineHandler(!shadersOff); handler = new OptifineHandler(!shadersOff);
} }
public boolean isUsingShaders() { public boolean isUsingShaders() {
return usingShaders; return usingShaders;
} }
} }

View file

@ -6,7 +6,7 @@ import net.minecraft.util.math.vector.Matrix3f;
import net.minecraft.util.math.vector.Matrix4f; import net.minecraft.util.math.vector.Matrix4f;
public class RenderUtil { public class RenderUtil {
public static int nextPowerOf2(int a) { public static int nextPowerOf2(int a) {
int h = Integer.highestOneBit(a); int h = Integer.highestOneBit(a);
return (h == a) ? h : (h << 1); return (h == a) ? h : (h << 1);
} }
@ -22,7 +22,7 @@ public class RenderUtil {
// GPUs want matrices in column major order. // GPUs want matrices in column major order.
public static float[] writeMatrixStack(Matrix4f model, Matrix3f normal) { public static float[] writeMatrixStack(Matrix4f model, Matrix3f normal) {
return new float[] { return new float[]{
model.a00, model.a00,
model.a10, model.a10,
model.a20, model.a20,

View file

@ -4,18 +4,18 @@ import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentLinkedQueue;
public class RenderWork { public class RenderWork {
private static final Queue<Runnable> runs = new ConcurrentLinkedQueue<>(); private static final Queue<Runnable> runs = new ConcurrentLinkedQueue<>();
public static void runAll() { public static void runAll() {
while (!runs.isEmpty()) { while (!runs.isEmpty()) {
runs.remove().run(); runs.remove().run();
} }
} }
/** /**
* Queue work to be executed at the end of a frame * Queue work to be executed at the end of a frame
*/ */
public static void enqueue(Runnable run) { public static void enqueue(Runnable run) {
runs.add(run); runs.add(run);
} }
} }

View file

@ -28,14 +28,14 @@ import org.lwjgl.system.MemoryUtil;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.systems.RenderSystem;
import com.simibubi.create.foundation.render.backend.gl.GlFogMode; import com.simibubi.create.foundation.render.backend.gl.GlFogMode;
import com.simibubi.create.foundation.render.backend.gl.shader.SingleProgram; import com.simibubi.create.foundation.render.backend.gl.shader.FogSensitiveProgram;
import com.simibubi.create.foundation.render.backend.gl.shader.GlProgram; import com.simibubi.create.foundation.render.backend.gl.shader.GlProgram;
import com.simibubi.create.foundation.render.backend.gl.shader.GlShader; import com.simibubi.create.foundation.render.backend.gl.shader.GlShader;
import com.simibubi.create.foundation.render.backend.gl.shader.FogSensitiveProgram;
import com.simibubi.create.foundation.render.backend.gl.shader.IMultiProgram; import com.simibubi.create.foundation.render.backend.gl.shader.IMultiProgram;
import com.simibubi.create.foundation.render.backend.gl.shader.ProgramSpec; import com.simibubi.create.foundation.render.backend.gl.shader.ProgramSpec;
import com.simibubi.create.foundation.render.backend.gl.shader.ShaderConstants; import com.simibubi.create.foundation.render.backend.gl.shader.ShaderConstants;
import com.simibubi.create.foundation.render.backend.gl.shader.ShaderType; import com.simibubi.create.foundation.render.backend.gl.shader.ShaderType;
import com.simibubi.create.foundation.render.backend.gl.shader.SingleProgram;
import net.minecraft.resources.IResource; import net.minecraft.resources.IResource;
import net.minecraft.resources.IResourceManager; import net.minecraft.resources.IResourceManager;
@ -70,7 +70,7 @@ public class ShaderLoader {
} }
} }
private void loadShaderSources(IResourceManager manager){ private void loadShaderSources(IResourceManager manager) {
Collection<ResourceLocation> allShaders = manager.getAllResourceLocations(SHADER_DIR, s -> { Collection<ResourceLocation> allShaders = manager.getAllResourceLocations(SHADER_DIR, s -> {
for (String ext : EXTENSIONS) { for (String ext : EXTENSIONS) {
if (s.endsWith(ext)) return true; if (s.endsWith(ext)) return true;
@ -185,7 +185,7 @@ public class ShaderLoader {
try { try {
bytebuffer = readToBuffer(is); bytebuffer = readToBuffer(is);
int i = bytebuffer.position(); int i = bytebuffer.position();
((Buffer)bytebuffer).rewind(); ((Buffer) bytebuffer).rewind();
return MemoryUtil.memASCII(bytebuffer, i); return MemoryUtil.memASCII(bytebuffer, i);
} catch (IOException e) { } catch (IOException e) {
@ -202,11 +202,12 @@ public class ShaderLoader {
public ByteBuffer readToBuffer(InputStream is) throws IOException { public ByteBuffer readToBuffer(InputStream is) throws IOException {
ByteBuffer bytebuffer; ByteBuffer bytebuffer;
if (is instanceof FileInputStream) { if (is instanceof FileInputStream) {
FileInputStream fileinputstream = (FileInputStream)is; FileInputStream fileinputstream = (FileInputStream) is;
FileChannel filechannel = fileinputstream.getChannel(); FileChannel filechannel = fileinputstream.getChannel();
bytebuffer = MemoryUtil.memAlloc((int)filechannel.size() + 1); bytebuffer = MemoryUtil.memAlloc((int) filechannel.size() + 1);
while (filechannel.read(bytebuffer) != -1) { } while (filechannel.read(bytebuffer) != -1) {
}
} else { } else {
bytebuffer = MemoryUtil.memAlloc(8192); bytebuffer = MemoryUtil.memAlloc(8192);
ReadableByteChannel readablebytechannel = Channels.newChannel(is); ReadableByteChannel readablebytechannel = Channels.newChannel(is);

View file

@ -7,72 +7,72 @@ import com.simibubi.create.foundation.render.backend.instancing.InstancedModel;
public class BasicData extends InstanceData implements IFlatLight<BasicData> { public class BasicData extends InstanceData implements IFlatLight<BasicData> {
protected byte blockLight; protected byte blockLight;
protected byte skyLight; protected byte skyLight;
protected byte r = (byte) 0xFF; protected byte r = (byte) 0xFF;
protected byte g = (byte) 0xFF; protected byte g = (byte) 0xFF;
protected byte b = (byte) 0xFF; protected byte b = (byte) 0xFF;
protected byte a = (byte) 0xFF; protected byte a = (byte) 0xFF;
public BasicData(InstancedModel<?> owner) { public BasicData(InstancedModel<?> owner) {
super(owner); super(owner);
} }
@Override @Override
public BasicData setBlockLight(int blockLight) { public BasicData setBlockLight(int blockLight) {
this.blockLight = (byte) (blockLight << 4); this.blockLight = (byte) (blockLight << 4);
markDirty(); markDirty();
return this; return this;
} }
@Override @Override
public BasicData setSkyLight(int skyLight) { public BasicData setSkyLight(int skyLight) {
this.skyLight = (byte) (skyLight << 4); this.skyLight = (byte) (skyLight << 4);
markDirty(); markDirty();
return this; return this;
} }
public BasicData setColor(int color) { public BasicData setColor(int color) {
return setColor(color, false); return setColor(color, false);
} }
public BasicData setColor(int color, boolean alpha) { public BasicData setColor(int color, boolean alpha) {
byte r = (byte) ((color >> 16) & 0xFF); byte r = (byte) ((color >> 16) & 0xFF);
byte g = (byte) ((color >> 8) & 0xFF); byte g = (byte) ((color >> 8) & 0xFF);
byte b = (byte) (color & 0xFF); byte b = (byte) (color & 0xFF);
if (alpha) { if (alpha) {
byte a = (byte) ((color >> 24) & 0xFF); byte a = (byte) ((color >> 24) & 0xFF);
return setColor(r, g, b, a); return setColor(r, g, b, a);
} else { } else {
return setColor(r, g, b); return setColor(r, g, b);
} }
} }
public BasicData setColor(int r, int g, int b) { public BasicData setColor(int r, int g, int b) {
return setColor((byte) r, (byte) g, (byte) b); return setColor((byte) r, (byte) g, (byte) b);
} }
public BasicData setColor(byte r, byte g, byte b) { public BasicData setColor(byte r, byte g, byte b) {
this.r = r; this.r = r;
this.g = g; this.g = g;
this.b = b; this.b = b;
markDirty(); markDirty();
return this; return this;
} }
public BasicData setColor(byte r, byte g, byte b, byte a) { public BasicData setColor(byte r, byte g, byte b, byte a) {
this.r = r; this.r = r;
this.g = g; this.g = g;
this.b = b; this.b = b;
this.a = a; this.a = a;
markDirty(); markDirty();
return this; return this;
} }
@Override @Override
public void write(ByteBuffer buf) { public void write(ByteBuffer buf) {
buf.put(new byte[] { blockLight, skyLight, r, g, b, a }); buf.put(new byte[]{blockLight, skyLight, r, g, b, a});
} }
} }

View file

@ -5,22 +5,23 @@ import com.simibubi.create.foundation.render.backend.instancing.InstanceData;
/** /**
* An interface that implementors of {@link InstanceData} should also implement * An interface that implementors of {@link InstanceData} should also implement
* if they wish to make use of Flywheel's provided light update methods. * if they wish to make use of Flywheel's provided light update methods.
* * <p>
* This only covers flat lighting, smooth lighting is still TODO. * This only covers flat lighting, smooth lighting is still TODO.
*
* @param <D> The name of the class that implements this interface. * @param <D> The name of the class that implements this interface.
*/ */
public interface IFlatLight<D extends InstanceData & IFlatLight<D>> { public interface IFlatLight<D extends InstanceData & IFlatLight<D>> {
/** /**
* @param blockLight An integer in the range [0, 15] representing the * @param blockLight An integer in the range [0, 15] representing the
* amount of block light this instance should receive. * amount of block light this instance should receive.
* @return <code>this</code> * @return <code>this</code>
*/ */
D setBlockLight(int blockLight); D setBlockLight(int blockLight);
/** /**
* @param skyLight An integer in the range [0, 15] representing the * @param skyLight An integer in the range [0, 15] representing the
* amount of sky light this instance should receive. * amount of sky light this instance should receive.
* @return <code>this</code> * @return <code>this</code>
*/ */
D setSkyLight(int skyLight); D setSkyLight(int skyLight);
} }

View file

@ -6,36 +6,36 @@ import com.simibubi.create.foundation.render.backend.gl.attrib.IVertexAttrib;
import com.simibubi.create.foundation.render.backend.gl.attrib.VertexAttribSpec; import com.simibubi.create.foundation.render.backend.gl.attrib.VertexAttribSpec;
public enum ModelAttributes implements IVertexAttrib { public enum ModelAttributes implements IVertexAttrib {
VERTEX_POSITION("aPos", CommonAttributes.VEC3), VERTEX_POSITION("aPos", CommonAttributes.VEC3),
NORMAL("aNormal", CommonAttributes.NORMAL), NORMAL("aNormal", CommonAttributes.NORMAL),
TEXTURE("aTexCoords", CommonAttributes.UV), TEXTURE("aTexCoords", CommonAttributes.UV),
; ;
private final String name; private final String name;
private final VertexAttribSpec spec; private final VertexAttribSpec spec;
ModelAttributes(String name, VertexAttribSpec spec) { ModelAttributes(String name, VertexAttribSpec spec) {
this.name = name; this.name = name;
this.spec = spec; this.spec = spec;
} }
@Override @Override
public String attribName() { public String attribName() {
return name; return name;
} }
@Override @Override
public IAttribSpec attribSpec() { public IAttribSpec attribSpec() {
return spec; return spec;
} }
@Override @Override
public int getDivisor() { public int getDivisor() {
return 0; return 0;
} }
@Override @Override
public int getBufferIndex() { public int getBufferIndex() {
return 0; return 0;
} }
} }

View file

@ -7,24 +7,24 @@ import com.simibubi.create.foundation.render.backend.RenderUtil;
import com.simibubi.create.foundation.render.backend.instancing.InstancedModel; import com.simibubi.create.foundation.render.backend.instancing.InstancedModel;
public class ModelData extends BasicData { public class ModelData extends BasicData {
private static final float[] empty = new float[25]; private static final float[] empty = new float[25];
private float[] matrices = empty; private float[] matrices = empty;
public ModelData(InstancedModel<?> owner) { public ModelData(InstancedModel<?> owner) {
super(owner); super(owner);
} }
public ModelData setTransform(MatrixStack stack) { public ModelData setTransform(MatrixStack stack) {
matrices = RenderUtil.writeMatrixStack(stack); matrices = RenderUtil.writeMatrixStack(stack);
markDirty(); markDirty();
return this; return this;
} }
@Override @Override
public void write(ByteBuffer buf) { public void write(ByteBuffer buf) {
super.write(buf); super.write(buf);
buf.asFloatBuffer().put(matrices); buf.asFloatBuffer().put(matrices);
buf.position(buf.position() + matrices.length * 4); buf.position(buf.position() + matrices.length * 4);
} }
} }

View file

@ -5,36 +5,36 @@ import com.simibubi.create.foundation.render.backend.gl.attrib.IAttribSpec;
import com.simibubi.create.foundation.render.backend.gl.attrib.IVertexAttrib; import com.simibubi.create.foundation.render.backend.gl.attrib.IVertexAttrib;
public enum OrientedAttributes implements IVertexAttrib { public enum OrientedAttributes implements IVertexAttrib {
INSTANCE_POS("aInstancePos", CommonAttributes.VEC3), INSTANCE_POS("aInstancePos", CommonAttributes.VEC3),
PIVOT("aPivot", CommonAttributes.VEC3), PIVOT("aPivot", CommonAttributes.VEC3),
ROTATION("aRotation", CommonAttributes.QUATERNION), ROTATION("aRotation", CommonAttributes.QUATERNION),
; ;
private final String name; private final String name;
private final IAttribSpec spec; private final IAttribSpec spec;
OrientedAttributes(String name, IAttribSpec spec) { OrientedAttributes(String name, IAttribSpec spec) {
this.name = name; this.name = name;
this.spec = spec; this.spec = spec;
} }
@Override @Override
public String attribName() { public String attribName() {
return name; return name;
} }
@Override @Override
public IAttribSpec attribSpec() { public IAttribSpec attribSpec() {
return spec; return spec;
} }
@Override @Override
public int getDivisor() { public int getDivisor() {
return 0; return 0;
} }
@Override @Override
public int getBufferIndex() { public int getBufferIndex() {
return 0; return 0;
} }
} }

View file

@ -85,7 +85,7 @@ public class OrientedData extends BasicData {
public void write(ByteBuffer buf) { public void write(ByteBuffer buf) {
super.write(buf); super.write(buf);
buf.asFloatBuffer().put(new float[] { buf.asFloatBuffer().put(new float[]{
posX, posX,
posY, posY,
posZ, posZ,

View file

@ -7,22 +7,22 @@ import com.simibubi.create.foundation.render.backend.instancing.InstancedTileRen
import net.minecraft.client.renderer.BufferBuilder; import net.minecraft.client.renderer.BufferBuilder;
public class OrientedModel extends InstancedModel<OrientedData> { public class OrientedModel extends InstancedModel<OrientedData> {
public static final VertexFormat INSTANCE_FORMAT = VertexFormat.builder() public static final VertexFormat INSTANCE_FORMAT = VertexFormat.builder()
.addAttributes(BasicAttributes.class) .addAttributes(BasicAttributes.class)
.addAttributes(OrientedAttributes.class) .addAttributes(OrientedAttributes.class)
.build(); .build();
public OrientedModel(InstancedTileRenderer<?> renderer, BufferBuilder buf) { public OrientedModel(InstancedTileRenderer<?> renderer, BufferBuilder buf) {
super(renderer, buf); super(renderer, buf);
} }
@Override @Override
protected OrientedData newInstance() { protected OrientedData newInstance() {
return new OrientedData(this); return new OrientedData(this);
} }
@Override @Override
protected VertexFormat getInstanceFormat() { protected VertexFormat getInstanceFormat() {
return INSTANCE_FORMAT; return INSTANCE_FORMAT;
} }
} }

View file

@ -5,35 +5,35 @@ import com.simibubi.create.foundation.render.backend.gl.attrib.IVertexAttrib;
import com.simibubi.create.foundation.render.backend.gl.attrib.MatrixAttributes; import com.simibubi.create.foundation.render.backend.gl.attrib.MatrixAttributes;
public enum TransformAttributes implements IVertexAttrib { public enum TransformAttributes implements IVertexAttrib {
TRANSFORM("aTransform", MatrixAttributes.MAT4), TRANSFORM("aTransform", MatrixAttributes.MAT4),
NORMAL_MAT("aNormalMat", MatrixAttributes.MAT3), NORMAL_MAT("aNormalMat", MatrixAttributes.MAT3),
; ;
private final String name; private final String name;
private final IAttribSpec spec; private final IAttribSpec spec;
TransformAttributes(String name, IAttribSpec spec) { TransformAttributes(String name, IAttribSpec spec) {
this.name = name; this.name = name;
this.spec = spec; this.spec = spec;
} }
@Override @Override
public String attribName() { public String attribName() {
return name; return name;
} }
@Override @Override
public IAttribSpec attribSpec() { public IAttribSpec attribSpec() {
return spec; return spec;
} }
@Override @Override
public int getDivisor() { public int getDivisor() {
return 0; return 0;
} }
@Override @Override
public int getBufferIndex() { public int getBufferIndex() {
return 0; return 0;
} }
} }

View file

@ -7,22 +7,22 @@ import com.simibubi.create.foundation.render.backend.instancing.InstancedTileRen
import net.minecraft.client.renderer.BufferBuilder; import net.minecraft.client.renderer.BufferBuilder;
public class TransformedModel extends InstancedModel<ModelData> { public class TransformedModel extends InstancedModel<ModelData> {
public static final VertexFormat INSTANCE_FORMAT = VertexFormat.builder() public static final VertexFormat INSTANCE_FORMAT = VertexFormat.builder()
.addAttributes(BasicAttributes.class) .addAttributes(BasicAttributes.class)
.addAttributes(TransformAttributes.class) .addAttributes(TransformAttributes.class)
.build(); .build();
public TransformedModel(InstancedTileRenderer<?> renderer, BufferBuilder buf) { public TransformedModel(InstancedTileRenderer<?> renderer, BufferBuilder buf) {
super(renderer, buf); super(renderer, buf);
} }
@Override @Override
protected ModelData newInstance() { protected ModelData newInstance() {
return new ModelData(this); return new ModelData(this);
} }
@Override @Override
protected VertexFormat getInstanceFormat() { protected VertexFormat getInstanceFormat() {
return INSTANCE_FORMAT; return INSTANCE_FORMAT;
} }
} }

View file

@ -11,48 +11,48 @@ import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.vector.Matrix4f; import net.minecraft.util.math.vector.Matrix4f;
public class BasicProgram extends GlProgram { public class BasicProgram extends GlProgram {
protected final int uTime; protected final int uTime;
protected final int uViewProjection; protected final int uViewProjection;
protected final int uDebug; protected final int uDebug;
protected final int uCameraPos; protected final int uCameraPos;
protected final ProgramFogMode fogMode; protected final ProgramFogMode fogMode;
protected int uBlockAtlas; protected int uBlockAtlas;
protected int uLightMap; protected int uLightMap;
public BasicProgram(ResourceLocation name, int handle, ProgramFogMode.Factory fogFactory) { public BasicProgram(ResourceLocation name, int handle, ProgramFogMode.Factory fogFactory) {
super(name, handle); super(name, handle);
uTime = getUniformLocation("uTime"); uTime = getUniformLocation("uTime");
uViewProjection = getUniformLocation("uViewProjection"); uViewProjection = getUniformLocation("uViewProjection");
uDebug = getUniformLocation("uDebug"); uDebug = getUniformLocation("uDebug");
uCameraPos = getUniformLocation("uCameraPos"); uCameraPos = getUniformLocation("uCameraPos");
fogMode = fogFactory.create(this); fogMode = fogFactory.create(this);
bind(); bind();
registerSamplers(); registerSamplers();
unbind(); unbind();
} }
protected void registerSamplers() { protected void registerSamplers() {
uBlockAtlas = setSamplerBinding("uBlockAtlas", 0); uBlockAtlas = setSamplerBinding("uBlockAtlas", 0);
uLightMap = setSamplerBinding("uLightMap", 2); uLightMap = setSamplerBinding("uLightMap", 2);
} }
public void bind(Matrix4f viewProjection, double camX, double camY, double camZ, int debugMode) { public void bind(Matrix4f viewProjection, double camX, double camY, double camZ, int debugMode) {
super.bind(); super.bind();
GL20.glUniform1i(uDebug, debugMode); GL20.glUniform1i(uDebug, debugMode);
GL20.glUniform1f(uTime, AnimationTickHolder.getRenderTime()); GL20.glUniform1f(uTime, AnimationTickHolder.getRenderTime());
uploadMatrixUniform(uViewProjection, viewProjection); uploadMatrixUniform(uViewProjection, viewProjection);
GL20.glUniform3f(uCameraPos, (float) camX, (float) camY, (float) camZ); GL20.glUniform3f(uCameraPos, (float) camX, (float) camY, (float) camZ);
fogMode.bind(); fogMode.bind();
} }
protected static void uploadMatrixUniform(int uniform, Matrix4f mat) { protected static void uploadMatrixUniform(int uniform, Matrix4f mat) {
GL20.glUniformMatrix4fv(uniform, false, RenderUtil.writeMatrix(mat)); GL20.glUniformMatrix4fv(uniform, false, RenderUtil.writeMatrix(mat));
} }
} }

View file

@ -9,40 +9,40 @@ import com.simibubi.create.foundation.render.backend.Backend;
public class GlBuffer extends GlObject { public class GlBuffer extends GlObject {
protected final int bufferType; protected final int bufferType;
public GlBuffer(int bufferType) { public GlBuffer(int bufferType) {
setHandle(GL20.glGenBuffers()); setHandle(GL20.glGenBuffers());
this.bufferType = bufferType; this.bufferType = bufferType;
} }
public int getBufferType() { public int getBufferType() {
return bufferType; return bufferType;
} }
public void bind() { public void bind() {
GL20.glBindBuffer(bufferType, handle()); GL20.glBindBuffer(bufferType, handle());
} }
public void unbind() { public void unbind() {
GL20.glBindBuffer(bufferType, 0); GL20.glBindBuffer(bufferType, 0);
} }
public void with(Consumer<GlBuffer> action) { public void with(Consumer<GlBuffer> action) {
bind(); bind();
action.accept(this); action.accept(this);
unbind(); unbind();
} }
public void map(int length, Consumer<ByteBuffer> upload) { public void map(int length, Consumer<ByteBuffer> upload) {
Backend.compat.mapBuffer(bufferType, 0, length, upload); Backend.compat.mapBuffer(bufferType, 0, length, upload);
} }
public void map(int offset, int length, Consumer<ByteBuffer> upload) { public void map(int offset, int length, Consumer<ByteBuffer> upload) {
Backend.compat.mapBuffer(bufferType, offset, length, upload); Backend.compat.mapBuffer(bufferType, offset, length, upload);
} }
protected void deleteInternal(int handle) { protected void deleteInternal(int handle) {
GL20.glDeleteBuffers(handle); GL20.glDeleteBuffers(handle);
} }
} }

View file

@ -5,43 +5,43 @@ import org.lwjgl.opengl.GL11;
import com.mojang.blaze3d.platform.GlStateManager; import com.mojang.blaze3d.platform.GlStateManager;
public class GlFog { public class GlFog {
public static float[] FOG_COLOR = new float[] {0, 0, 0, 0}; public static float[] FOG_COLOR = new float[]{0, 0, 0, 0};
public static boolean fogEnabled() { public static boolean fogEnabled() {
return GlStateManager.FOG.field_179049_a.field_179201_b; return GlStateManager.FOG.field_179049_a.field_179201_b;
} }
public static int getFogModeGlEnum() { public static int getFogModeGlEnum() {
return GlStateManager.FOG.field_179047_b; return GlStateManager.FOG.field_179047_b;
} }
public static float getFogDensity() { public static float getFogDensity() {
return GlStateManager.FOG.field_179048_c; return GlStateManager.FOG.field_179048_c;
} }
public static float getFogEnd() { public static float getFogEnd() {
return GlStateManager.FOG.field_179046_e; return GlStateManager.FOG.field_179046_e;
} }
public static float getFogStart() { public static float getFogStart() {
return GlStateManager.FOG.field_179045_d; return GlStateManager.FOG.field_179045_d;
} }
public static GlFogMode getFogMode() { public static GlFogMode getFogMode() {
if (!fogEnabled()) { if (!fogEnabled()) {
return GlFogMode.NONE; return GlFogMode.NONE;
} }
int mode = getFogModeGlEnum(); int mode = getFogModeGlEnum();
switch (mode) { switch (mode) {
case GL11.GL_EXP2: case GL11.GL_EXP2:
case GL11.GL_EXP: case GL11.GL_EXP:
return GlFogMode.EXP2; return GlFogMode.EXP2;
case GL11.GL_LINEAR: case GL11.GL_LINEAR:
return GlFogMode.LINEAR; return GlFogMode.LINEAR;
default: default:
throw new UnsupportedOperationException("Unknown fog mode: " + mode); throw new UnsupportedOperationException("Unknown fog mode: " + mode);
} }
} }
} }

View file

@ -7,31 +7,31 @@ import com.google.common.collect.Lists;
import com.simibubi.create.foundation.render.backend.gl.shader.ProgramFogMode; import com.simibubi.create.foundation.render.backend.gl.shader.ProgramFogMode;
public enum GlFogMode { public enum GlFogMode {
NONE(ProgramFogMode.None::new), NONE(ProgramFogMode.None::new),
LINEAR(ProgramFogMode.Linear::new, "USE_FOG_LINEAR"), LINEAR(ProgramFogMode.Linear::new, "USE_FOG_LINEAR"),
EXP2(ProgramFogMode.Exp2::new, "USE_FOG_EXP2"), EXP2(ProgramFogMode.Exp2::new, "USE_FOG_EXP2"),
; ;
public static final String USE_FOG = "USE_FOG"; public static final String USE_FOG = "USE_FOG";
private final ProgramFogMode.Factory fogFactory; private final ProgramFogMode.Factory fogFactory;
private final List<String> defines; private final List<String> defines;
GlFogMode(ProgramFogMode.Factory fogFactory) { GlFogMode(ProgramFogMode.Factory fogFactory) {
this.fogFactory = fogFactory; this.fogFactory = fogFactory;
this.defines = Collections.emptyList(); this.defines = Collections.emptyList();
} }
GlFogMode(ProgramFogMode.Factory fogFactory, String name) { GlFogMode(ProgramFogMode.Factory fogFactory, String name) {
this.fogFactory = fogFactory; this.fogFactory = fogFactory;
this.defines = Lists.newArrayList(USE_FOG, name); this.defines = Lists.newArrayList(USE_FOG, name);
} }
public List<String> getDefines() { public List<String> getDefines() {
return defines; return defines;
} }
public ProgramFogMode.Factory getFogFactory() { public ProgramFogMode.Factory getFogFactory() {
return fogFactory; return fogFactory;
} }
} }

View file

@ -2,42 +2,42 @@ package com.simibubi.create.foundation.render.backend.gl;
// Utility class for safely dealing with gl object handles. // Utility class for safely dealing with gl object handles.
public abstract class GlObject { public abstract class GlObject {
private static final int INVALID_HANDLE = Integer.MIN_VALUE; private static final int INVALID_HANDLE = Integer.MIN_VALUE;
private int handle = INVALID_HANDLE; private int handle = INVALID_HANDLE;
protected final void setHandle(int handle) { protected final void setHandle(int handle) {
this.handle = handle; this.handle = handle;
} }
public final int handle() { public final int handle() {
this.checkHandle(); this.checkHandle();
return this.handle; return this.handle;
} }
protected final void checkHandle() { protected final void checkHandle() {
if (!this.isHandleValid()) { if (!this.isHandleValid()) {
throw new IllegalStateException("Handle is not valid"); throw new IllegalStateException("Handle is not valid");
} }
} }
protected final boolean isHandleValid() { protected final boolean isHandleValid() {
return this.handle != INVALID_HANDLE; return this.handle != INVALID_HANDLE;
} }
protected final void invalidateHandle() { protected final void invalidateHandle() {
this.handle = INVALID_HANDLE; this.handle = INVALID_HANDLE;
} }
public final void delete() { public final void delete() {
if (!isHandleValid()) { if (!isHandleValid()) {
throw new IllegalStateException("Handle already deleted."); throw new IllegalStateException("Handle already deleted.");
} }
deleteInternal(handle); deleteInternal(handle);
invalidateHandle(); invalidateHandle();
} }
protected abstract void deleteInternal(int handle); protected abstract void deleteInternal(int handle);
} }

View file

@ -7,33 +7,33 @@ import net.minecraftforge.api.distmarker.OnlyIn;
@OnlyIn(Dist.CLIENT) @OnlyIn(Dist.CLIENT)
public enum GlPrimitiveType { public enum GlPrimitiveType {
FLOAT(4, "float", GL11.GL_FLOAT), FLOAT(4, "float", GL11.GL_FLOAT),
UBYTE(1, "ubyte", GL11.GL_UNSIGNED_BYTE), UBYTE(1, "ubyte", GL11.GL_UNSIGNED_BYTE),
BYTE(1, "byte", GL11.GL_BYTE), BYTE(1, "byte", GL11.GL_BYTE),
USHORT(2, "ushort", GL11.GL_UNSIGNED_SHORT), USHORT(2, "ushort", GL11.GL_UNSIGNED_SHORT),
SHORT(2, "short", GL11.GL_SHORT), SHORT(2, "short", GL11.GL_SHORT),
UINT(4, "uint", GL11.GL_UNSIGNED_INT), UINT(4, "uint", GL11.GL_UNSIGNED_INT),
INT(4, "int", GL11.GL_INT); INT(4, "int", GL11.GL_INT);
private final int size; private final int size;
private final String displayName; private final String displayName;
private final int glConstant; private final int glConstant;
GlPrimitiveType(int bytes, String name, int glEnum) { GlPrimitiveType(int bytes, String name, int glEnum) {
this.size = bytes; this.size = bytes;
this.displayName = name; this.displayName = name;
this.glConstant = glEnum; this.glConstant = glEnum;
} }
public int getSize() { public int getSize() {
return this.size; return this.size;
} }
public String getDisplayName() { public String getDisplayName() {
return this.displayName; return this.displayName;
} }
public int getGlConstant() { public int getGlConstant() {
return this.glConstant; return this.glConstant;
} }
} }

View file

@ -3,23 +3,23 @@ package com.simibubi.create.foundation.render.backend.gl;
import org.lwjgl.opengl.GL20; import org.lwjgl.opengl.GL20;
public class GlTexture extends GlObject { public class GlTexture extends GlObject {
private final int textureType; private final int textureType;
public GlTexture(int textureType) { public GlTexture(int textureType) {
this.textureType = textureType; this.textureType = textureType;
setHandle(GL20.glGenTextures()); setHandle(GL20.glGenTextures());
} }
@Override @Override
protected void deleteInternal(int handle) { protected void deleteInternal(int handle) {
GL20.glDeleteTextures(handle); GL20.glDeleteTextures(handle);
} }
public void bind() { public void bind() {
GL20.glBindTexture(textureType, handle()); GL20.glBindTexture(textureType, handle());
} }
public void unbind() { public void unbind() {
GL20.glBindTexture(textureType, 0); GL20.glBindTexture(textureType, 0);
} }
} }

View file

@ -5,25 +5,25 @@ import java.util.function.Consumer;
import com.simibubi.create.foundation.render.backend.Backend; import com.simibubi.create.foundation.render.backend.Backend;
public class GlVertexArray extends GlObject { public class GlVertexArray extends GlObject {
public GlVertexArray() { public GlVertexArray() {
setHandle(Backend.compat.genVertexArrays()); setHandle(Backend.compat.genVertexArrays());
} }
public void bind() { public void bind() {
Backend.compat.bindVertexArray(handle()); Backend.compat.bindVertexArray(handle());
} }
public void unbind() { public void unbind() {
Backend.compat.bindVertexArray(0); Backend.compat.bindVertexArray(0);
} }
public void with(Consumer<GlVertexArray> action) { public void with(Consumer<GlVertexArray> action) {
bind(); bind();
action.accept(this); action.accept(this);
unbind(); unbind();
} }
protected void deleteInternal(int handle) { protected void deleteInternal(int handle) {
Backend.compat.deleteVertexArrays(handle); Backend.compat.deleteVertexArrays(handle);
} }
} }

View file

@ -4,18 +4,18 @@ import com.simibubi.create.foundation.render.backend.gl.GlPrimitiveType;
public class CommonAttributes { public class CommonAttributes {
public static final VertexAttribSpec VEC4 = new VertexAttribSpec(GlPrimitiveType.FLOAT, 4); public static final VertexAttribSpec VEC4 = new VertexAttribSpec(GlPrimitiveType.FLOAT, 4);
public static final VertexAttribSpec VEC3 = new VertexAttribSpec(GlPrimitiveType.FLOAT, 3); public static final VertexAttribSpec VEC3 = new VertexAttribSpec(GlPrimitiveType.FLOAT, 3);
public static final VertexAttribSpec VEC2 = new VertexAttribSpec(GlPrimitiveType.FLOAT, 2); public static final VertexAttribSpec VEC2 = new VertexAttribSpec(GlPrimitiveType.FLOAT, 2);
public static final VertexAttribSpec FLOAT = new VertexAttribSpec(GlPrimitiveType.FLOAT, 1); public static final VertexAttribSpec FLOAT = new VertexAttribSpec(GlPrimitiveType.FLOAT, 1);
public static final VertexAttribSpec QUATERNION = new VertexAttribSpec(GlPrimitiveType.FLOAT, 4); public static final VertexAttribSpec QUATERNION = new VertexAttribSpec(GlPrimitiveType.FLOAT, 4);
public static final VertexAttribSpec NORMAL = new VertexAttribSpec(GlPrimitiveType.BYTE, 3, true); public static final VertexAttribSpec NORMAL = new VertexAttribSpec(GlPrimitiveType.BYTE, 3, true);
public static final VertexAttribSpec UV = new VertexAttribSpec(GlPrimitiveType.FLOAT, 2); public static final VertexAttribSpec UV = new VertexAttribSpec(GlPrimitiveType.FLOAT, 2);
public static final VertexAttribSpec RGBA = new VertexAttribSpec(GlPrimitiveType.UBYTE, 4, true); public static final VertexAttribSpec RGBA = new VertexAttribSpec(GlPrimitiveType.UBYTE, 4, true);
public static final VertexAttribSpec RGB = new VertexAttribSpec(GlPrimitiveType.UBYTE, 3, true); public static final VertexAttribSpec RGB = new VertexAttribSpec(GlPrimitiveType.UBYTE, 3, true);
public static final VertexAttribSpec LIGHT = new VertexAttribSpec(GlPrimitiveType.UBYTE, 2, true); public static final VertexAttribSpec LIGHT = new VertexAttribSpec(GlPrimitiveType.UBYTE, 2, true);
public static final VertexAttribSpec NORMALIZED_BYTE = new VertexAttribSpec(GlPrimitiveType.BYTE, 1, true); public static final VertexAttribSpec NORMALIZED_BYTE = new VertexAttribSpec(GlPrimitiveType.BYTE, 1, true);
} }

View file

@ -2,9 +2,9 @@ package com.simibubi.create.foundation.render.backend.gl.attrib;
public interface IAttribSpec { public interface IAttribSpec {
void vertexAttribPointer(int stride, int index, int pointer); void vertexAttribPointer(int stride, int index, int pointer);
int getSize(); int getSize();
int getAttributeCount(); int getAttributeCount();
} }

View file

@ -2,11 +2,11 @@ package com.simibubi.create.foundation.render.backend.gl.attrib;
public interface IVertexAttrib { public interface IVertexAttrib {
String attribName(); String attribName();
IAttribSpec attribSpec(); IAttribSpec attribSpec();
int getDivisor(); int getDivisor();
int getBufferIndex(); int getBufferIndex();
} }

View file

@ -5,33 +5,33 @@ import org.lwjgl.opengl.GL20;
import com.simibubi.create.foundation.render.backend.gl.GlPrimitiveType; import com.simibubi.create.foundation.render.backend.gl.GlPrimitiveType;
public enum MatrixAttributes implements IAttribSpec { public enum MatrixAttributes implements IAttribSpec {
MAT3(3, 3), MAT3(3, 3),
MAT4(4, 4), MAT4(4, 4),
; ;
private final int rows; private final int rows;
private final int cols; private final int cols;
MatrixAttributes(int rows, int cols) { MatrixAttributes(int rows, int cols) {
this.rows = rows; this.rows = rows;
this.cols = cols; this.cols = cols;
} }
@Override @Override
public void vertexAttribPointer(int stride, int index, int pointer) { public void vertexAttribPointer(int stride, int index, int pointer) {
for (int i = 0; i < rows; i++) { for (int i = 0; i < rows; i++) {
long attribPointer = pointer + (long) i * cols * GlPrimitiveType.FLOAT.getSize(); long attribPointer = pointer + (long) i * cols * GlPrimitiveType.FLOAT.getSize();
GL20.glVertexAttribPointer(index + i, cols, GlPrimitiveType.FLOAT.getGlConstant(), false, stride, attribPointer); GL20.glVertexAttribPointer(index + i, cols, GlPrimitiveType.FLOAT.getGlConstant(), false, stride, attribPointer);
} }
} }
@Override @Override
public int getSize() { public int getSize() {
return GlPrimitiveType.FLOAT.getSize() * rows * cols; return GlPrimitiveType.FLOAT.getSize() * rows * cols;
} }
@Override @Override
public int getAttributeCount() { public int getAttributeCount() {
return rows; return rows;
} }
} }

View file

@ -6,36 +6,36 @@ import com.simibubi.create.foundation.render.backend.gl.GlPrimitiveType;
public class VertexAttribSpec implements IAttribSpec { public class VertexAttribSpec implements IAttribSpec {
private final GlPrimitiveType type; private final GlPrimitiveType type;
private final int count; private final int count;
private final int size; private final int size;
private final int attributeCount; private final int attributeCount;
private final boolean normalized; private final boolean normalized;
public VertexAttribSpec(GlPrimitiveType type, int count) { public VertexAttribSpec(GlPrimitiveType type, int count) {
this(type, count, false); this(type, count, false);
} }
public VertexAttribSpec(GlPrimitiveType type, int count, boolean normalized) { public VertexAttribSpec(GlPrimitiveType type, int count, boolean normalized) {
this.type = type; this.type = type;
this.count = count; this.count = count;
this.size = type.getSize() * count; this.size = type.getSize() * count;
this.attributeCount = (this.size + 15) / 16; // ceiling division. GLSL vertex attributes can only be 16 bytes wide this.attributeCount = (this.size + 15) / 16; // ceiling division. GLSL vertex attributes can only be 16 bytes wide
this.normalized = normalized; this.normalized = normalized;
} }
@Override @Override
public void vertexAttribPointer(int stride, int index, int pointer) { public void vertexAttribPointer(int stride, int index, int pointer) {
GL20.glVertexAttribPointer(index, count, type.getGlConstant(), normalized, stride, pointer); GL20.glVertexAttribPointer(index, count, type.getGlConstant(), normalized, stride, pointer);
} }
@Override @Override
public int getSize() { public int getSize() {
return size; return size;
} }
@Override @Override
public int getAttributeCount() { public int getAttributeCount() {
return attributeCount; return attributeCount;
} }
} }

View file

@ -5,61 +5,61 @@ import java.util.Arrays;
public class VertexFormat { public class VertexFormat {
private final ArrayList<IVertexAttrib> allAttributes; private final ArrayList<IVertexAttrib> allAttributes;
private final int numAttributes; private final int numAttributes;
private final int stride; private final int stride;
public VertexFormat(ArrayList<IVertexAttrib> allAttributes) { public VertexFormat(ArrayList<IVertexAttrib> allAttributes) {
this.allAttributes = allAttributes; this.allAttributes = allAttributes;
int numAttributes = 0, stride = 0; int numAttributes = 0, stride = 0;
for (IVertexAttrib attrib : allAttributes) { for (IVertexAttrib attrib : allAttributes) {
IAttribSpec spec = attrib.attribSpec(); IAttribSpec spec = attrib.attribSpec();
numAttributes += spec.getAttributeCount(); numAttributes += spec.getAttributeCount();
stride += spec.getSize(); stride += spec.getSize();
} }
this.numAttributes = numAttributes; this.numAttributes = numAttributes;
this.stride = stride; this.stride = stride;
} }
public int getShaderAttributeCount() { public int getShaderAttributeCount() {
return numAttributes; return numAttributes;
} }
public int getStride() { public int getStride() {
return stride; return stride;
} }
public void vertexAttribPointers(int index) { public void vertexAttribPointers(int index) {
int offset = 0; int offset = 0;
for (IVertexAttrib attrib : this.allAttributes) { for (IVertexAttrib attrib : this.allAttributes) {
IAttribSpec spec = attrib.attribSpec(); IAttribSpec spec = attrib.attribSpec();
spec.vertexAttribPointer(stride, index, offset); spec.vertexAttribPointer(stride, index, offset);
index += spec.getAttributeCount(); index += spec.getAttributeCount();
offset += spec.getSize(); offset += spec.getSize();
} }
} }
public static Builder builder() { public static Builder builder() {
return new Builder(); return new Builder();
} }
public static class Builder { public static class Builder {
private final ArrayList<IVertexAttrib> allAttributes; private final ArrayList<IVertexAttrib> allAttributes;
public Builder() { public Builder() {
allAttributes = new ArrayList<>(); allAttributes = new ArrayList<>();
} }
public <A extends Enum<A> & IVertexAttrib> Builder addAttributes(Class<A> attribEnum) { public <A extends Enum<A> & IVertexAttrib> Builder addAttributes(Class<A> attribEnum) {
allAttributes.addAll(Arrays.asList(attribEnum.getEnumConstants())); allAttributes.addAll(Arrays.asList(attribEnum.getEnumConstants()));
return this; return this;
} }
public VertexFormat build() { public VertexFormat build() {
return new VertexFormat(allAttributes); return new VertexFormat(allAttributes);
} }
} }
} }

View file

@ -7,20 +7,20 @@ import com.simibubi.create.foundation.render.backend.gl.GlFogMode;
public class FogSensitiveProgram<P extends GlProgram> implements IMultiProgram<P> { public class FogSensitiveProgram<P extends GlProgram> implements IMultiProgram<P> {
private final Map<GlFogMode, P> programs; private final Map<GlFogMode, P> programs;
public FogSensitiveProgram(Map<GlFogMode, P> programs) { public FogSensitiveProgram(Map<GlFogMode, P> programs) {
this.programs = programs; this.programs = programs;
} }
@Override @Override
public P get() { public P get() {
return programs.get(GlFog.getFogMode()); return programs.get(GlFog.getFogMode());
} }
@Override @Override
public void delete() { public void delete() {
programs.values().forEach(GlProgram::delete); programs.values().forEach(GlProgram::delete);
} }
} }

View file

@ -3,43 +3,43 @@ package com.simibubi.create.foundation.render.backend.gl.shader;
import com.simibubi.create.foundation.render.backend.gl.GlPrimitiveType; import com.simibubi.create.foundation.render.backend.gl.GlPrimitiveType;
public class GLSLType { public class GLSLType {
public static final GLSLType FLOAT = new GLSLType("mat4", GlPrimitiveType.FLOAT, 16); public static final GLSLType FLOAT = new GLSLType("mat4", GlPrimitiveType.FLOAT, 16);
public static final GLSLType VEC2 = new GLSLType("vec4", GlPrimitiveType.FLOAT, 4); public static final GLSLType VEC2 = new GLSLType("vec4", GlPrimitiveType.FLOAT, 4);
public static final GLSLType VEC3 = new GLSLType("vec3", GlPrimitiveType.FLOAT, 3); public static final GLSLType VEC3 = new GLSLType("vec3", GlPrimitiveType.FLOAT, 3);
public static final GLSLType VEC4 = new GLSLType("vec2", GlPrimitiveType.FLOAT, 2); public static final GLSLType VEC4 = new GLSLType("vec2", GlPrimitiveType.FLOAT, 2);
public static final GLSLType MAT4 = new GLSLType("float", GlPrimitiveType.FLOAT, 1); public static final GLSLType MAT4 = new GLSLType("float", GlPrimitiveType.FLOAT, 1);
private final String symbol; private final String symbol;
private final GlPrimitiveType base; private final GlPrimitiveType base;
private final int count; private final int count;
private final int size; private final int size;
private final int attributeCount; private final int attributeCount;
public GLSLType(String symbol, GlPrimitiveType base, int count) { public GLSLType(String symbol, GlPrimitiveType base, int count) {
this.symbol = symbol; this.symbol = symbol;
this.base = base; this.base = base;
this.count = count; this.count = count;
this.size = base.getSize() * count; this.size = base.getSize() * count;
this.attributeCount = (this.size + 15) / 16; // ceiling division. GLSL vertex attributes can only be 16 bytes wide this.attributeCount = (this.size + 15) / 16; // ceiling division. GLSL vertex attributes can only be 16 bytes wide
} }
public String getSymbol() { public String getSymbol() {
return symbol; return symbol;
} }
public GlPrimitiveType getBase() { public GlPrimitiveType getBase() {
return base; return base;
} }
public int getCount() { public int getCount() {
return count; return count;
} }
public int getSize() { public int getSize() {
return size; return size;
} }
public int getAttributeCount() { public int getAttributeCount() {
return attributeCount; return attributeCount;
} }
} }

View file

@ -11,117 +11,119 @@ import net.minecraft.util.ResourceLocation;
public abstract class GlProgram extends GlObject { public abstract class GlProgram extends GlObject {
public final ResourceLocation name; public final ResourceLocation name;
protected GlProgram(ResourceLocation name, int handle) { protected GlProgram(ResourceLocation name, int handle) {
setHandle(handle); setHandle(handle);
this.name = name; this.name = name;
} }
public static Builder builder(ResourceLocation name, GlFogMode fogMode) { public static Builder builder(ResourceLocation name, GlFogMode fogMode) {
return new Builder(name, fogMode); return new Builder(name, fogMode);
} }
public void bind() { public void bind() {
GL20.glUseProgram(handle()); GL20.glUseProgram(handle());
} }
public void unbind() { public void unbind() {
GL20.glUseProgram(0); GL20.glUseProgram(0);
} }
/** /**
* Retrieves the index of the uniform with the given name. * Retrieves the index of the uniform with the given name.
* @param uniform The name of the uniform to find the index of *
* @return The uniform's index * @param uniform The name of the uniform to find the index of
*/ * @return The uniform's index
public int getUniformLocation(String uniform) { */
int index = GL20.glGetUniformLocation(this.handle(), uniform); public int getUniformLocation(String uniform) {
int index = GL20.glGetUniformLocation(this.handle(), uniform);
if (index < 0) { if (index < 0) {
Backend.log.debug("No active uniform '{}' exists in program '{}'. Could be unused.", uniform, this.name); Backend.log.debug("No active uniform '{}' exists in program '{}'. Could be unused.", uniform, this.name);
} }
return index; return index;
} }
/** /**
* Binds a sampler uniform to the given texture unit. * Binds a sampler uniform to the given texture unit.
* @param name The name of the sampler uniform. *
* @param binding The index of the texture unit. * @param name The name of the sampler uniform.
* @return The sampler uniform's index. * @param binding The index of the texture unit.
* @throws NullPointerException If no uniform exists with the given name. * @return The sampler uniform's index.
*/ * @throws NullPointerException If no uniform exists with the given name.
public int setSamplerBinding(String name, int binding) { */
int samplerUniform = getUniformLocation(name); public int setSamplerBinding(String name, int binding) {
int samplerUniform = getUniformLocation(name);
if (samplerUniform >= 0) { if (samplerUniform >= 0) {
GL20.glUniform1i(samplerUniform, binding); GL20.glUniform1i(samplerUniform, binding);
} }
return samplerUniform; return samplerUniform;
} }
@Override @Override
protected void deleteInternal(int handle) { protected void deleteInternal(int handle) {
GL20.glDeleteProgram(handle); GL20.glDeleteProgram(handle);
} }
public static class Builder { public static class Builder {
private final ResourceLocation name; private final ResourceLocation name;
private final int program; private final int program;
private final GlFogMode fogMode; private final GlFogMode fogMode;
private int attributeIndex; private int attributeIndex;
public Builder(ResourceLocation name, GlFogMode fogMode) { public Builder(ResourceLocation name, GlFogMode fogMode) {
this.name = name; this.name = name;
this.program = GL20.glCreateProgram(); this.program = GL20.glCreateProgram();
this.fogMode = fogMode; this.fogMode = fogMode;
} }
public Builder attachShader(GlShader shader) { public Builder attachShader(GlShader shader) {
GL20.glAttachShader(this.program, shader.handle()); GL20.glAttachShader(this.program, shader.handle());
return this; return this;
} }
public <A extends IVertexAttrib> Builder addAttribute(A attrib) { public <A extends IVertexAttrib> Builder addAttribute(A attrib) {
GL20.glBindAttribLocation(this.program, attributeIndex, attrib.attribName()); GL20.glBindAttribLocation(this.program, attributeIndex, attrib.attribName());
attributeIndex += attrib.attribSpec().getAttributeCount(); attributeIndex += attrib.attribSpec().getAttributeCount();
return this; return this;
} }
/** /**
* Links the attached shaders to this program and returns a user-defined container which wraps the shader * Links the attached shaders to this program and returns a user-defined container which wraps the shader
* program. This container can, for example, provide methods for updating the specific uniforms of that shader * program. This container can, for example, provide methods for updating the specific uniforms of that shader
* set. * set.
* *
* @param factory The factory which will create the shader program's container * @param factory The factory which will create the shader program's container
* @param <P> The type which should be instantiated with the new program's handle * @param <P> The type which should be instantiated with the new program's handle
* @return An instantiated shader container as provided by the factory * @return An instantiated shader container as provided by the factory
*/ */
public <P extends GlProgram> P build(ProgramFactory<P> factory) { public <P extends GlProgram> P build(ProgramFactory<P> factory) {
GL20.glLinkProgram(this.program); GL20.glLinkProgram(this.program);
String log = GL20.glGetProgramInfoLog(this.program); String log = GL20.glGetProgramInfoLog(this.program);
if (!log.isEmpty()) { if (!log.isEmpty()) {
Backend.log.debug("Program link log for " + this.name + ": " + log); Backend.log.debug("Program link log for " + this.name + ": " + log);
} }
int result = GL20.glGetProgrami(this.program, GL20.GL_LINK_STATUS); int result = GL20.glGetProgrami(this.program, GL20.GL_LINK_STATUS);
if (result != GL20.GL_TRUE) { if (result != GL20.GL_TRUE) {
throw new RuntimeException("Shader program linking failed, see log for details"); throw new RuntimeException("Shader program linking failed, see log for details");
} }
return factory.create(this.name, this.program, this.fogMode.getFogFactory()); return factory.create(this.name, this.program, this.fogMode.getFogFactory());
} }
} }
@FunctionalInterface @FunctionalInterface
public interface ProgramFactory<P extends GlProgram> { public interface ProgramFactory<P extends GlProgram> {
P create(ResourceLocation name, int handle, ProgramFogMode.Factory fogFactory); P create(ResourceLocation name, int handle, ProgramFogMode.Factory fogFactory);
} }
} }

View file

@ -10,41 +10,41 @@ import net.minecraft.util.ResourceLocation;
public class GlShader extends GlObject { public class GlShader extends GlObject {
public final ResourceLocation name; public final ResourceLocation name;
public final ShaderType type; public final ShaderType type;
public GlShader(ShaderType type, ResourceLocation name, String source) { public GlShader(ShaderType type, ResourceLocation name, String source) {
this.type = type; this.type = type;
this.name = name; this.name = name;
int handle = GL20.glCreateShader(type.glEnum); int handle = GL20.glCreateShader(type.glEnum);
GlCompat.safeShaderSource(handle, source); GlCompat.safeShaderSource(handle, source);
GL20.glCompileShader(handle); GL20.glCompileShader(handle);
String log = GL20.glGetShaderInfoLog(handle); String log = GL20.glGetShaderInfoLog(handle);
if (!log.isEmpty()) { if (!log.isEmpty()) {
Backend.log.error("Shader compilation log for " + name + ": " + log); Backend.log.error("Shader compilation log for " + name + ": " + log);
} }
if (GL20.glGetShaderi(handle, GL20.GL_COMPILE_STATUS) != GL20.GL_TRUE) { if (GL20.glGetShaderi(handle, GL20.GL_COMPILE_STATUS) != GL20.GL_TRUE) {
throw new RuntimeException("Could not compile shader. See log for details."); throw new RuntimeException("Could not compile shader. See log for details.");
} }
setHandle(handle); setHandle(handle);
} }
@Override @Override
protected void deleteInternal(int handle) { protected void deleteInternal(int handle) {
GL20.glDeleteShader(handle); GL20.glDeleteShader(handle);
} }
@FunctionalInterface @FunctionalInterface
public interface PreProcessor { public interface PreProcessor {
String process(String source); String process(String source);
default PreProcessor andThen(PreProcessor that) { default PreProcessor andThen(PreProcessor that) {
return source -> that.process(this.process(source)); return source -> that.process(this.process(source));
} }
} }
} }

View file

@ -10,6 +10,7 @@ public interface IMultiProgram<P extends GlProgram> {
/** /**
* Get the shader program most suited for the current game state. * Get the shader program most suited for the current game state.
*
* @return The one true program. * @return The one true program.
*/ */
P get(); P get();

View file

@ -6,53 +6,53 @@ import com.simibubi.create.foundation.render.backend.gl.GlFog;
public abstract class ProgramFogMode { public abstract class ProgramFogMode {
public abstract void bind(); public abstract void bind();
public static class None extends ProgramFogMode { public static class None extends ProgramFogMode {
public None(GlProgram program) { public None(GlProgram program) {
} }
@Override @Override
public void bind() { public void bind() {
} }
} }
public static class Linear extends ProgramFogMode { public static class Linear extends ProgramFogMode {
private final int uFogColor; private final int uFogColor;
private final int uFogRange; private final int uFogRange;
public Linear(GlProgram program) { public Linear(GlProgram program) {
this.uFogColor = program.getUniformLocation("uFogColor"); this.uFogColor = program.getUniformLocation("uFogColor");
this.uFogRange = program.getUniformLocation("uFogRange"); this.uFogRange = program.getUniformLocation("uFogRange");
} }
@Override @Override
public void bind() { public void bind() {
GL20.glUniform2f(uFogRange, GlFog.getFogStart(), GlFog.getFogEnd()); GL20.glUniform2f(uFogRange, GlFog.getFogStart(), GlFog.getFogEnd());
GL20.glUniform4fv(uFogColor, GlFog.FOG_COLOR); GL20.glUniform4fv(uFogColor, GlFog.FOG_COLOR);
} }
} }
public static class Exp2 extends ProgramFogMode { public static class Exp2 extends ProgramFogMode {
private final int uFogColor; private final int uFogColor;
private final int uFogDensity; private final int uFogDensity;
public Exp2(GlProgram program) { public Exp2(GlProgram program) {
this.uFogColor = program.getUniformLocation("uFogColor"); this.uFogColor = program.getUniformLocation("uFogColor");
this.uFogDensity = program.getUniformLocation("uFogDensity"); this.uFogDensity = program.getUniformLocation("uFogDensity");
} }
@Override @Override
public void bind() { public void bind() {
GL20.glUniform1f(uFogDensity, GlFog.getFogDensity()); GL20.glUniform1f(uFogDensity, GlFog.getFogDensity());
GL20.glUniform4fv(uFogColor, GlFog.FOG_COLOR); GL20.glUniform4fv(uFogColor, GlFog.FOG_COLOR);
} }
} }
public interface Factory { public interface Factory {
ProgramFogMode create(GlProgram program); ProgramFogMode create(GlProgram program);
} }
} }

View file

@ -10,75 +10,75 @@ import net.minecraft.util.ResourceLocation;
public class ProgramSpec<P extends GlProgram> { public class ProgramSpec<P extends GlProgram> {
public final ResourceLocation name; public final ResourceLocation name;
public final ResourceLocation vert; public final ResourceLocation vert;
public final ResourceLocation frag; public final ResourceLocation frag;
public final ShaderConstants defines; public final ShaderConstants defines;
public final GlProgram.ProgramFactory<P> factory; public final GlProgram.ProgramFactory<P> factory;
public final ArrayList<IVertexAttrib> attributes; public final ArrayList<IVertexAttrib> attributes;
public final boolean fogSensitive; public final boolean fogSensitive;
public static <P extends GlProgram> Builder<P> builder(String name, GlProgram.ProgramFactory<P> factory) { public static <P extends GlProgram> Builder<P> builder(String name, GlProgram.ProgramFactory<P> factory) {
return builder(new ResourceLocation(Create.ID, name), factory); return builder(new ResourceLocation(Create.ID, name), factory);
} }
public static <P extends GlProgram> Builder<P> builder(ResourceLocation name, GlProgram.ProgramFactory<P> factory) { public static <P extends GlProgram> Builder<P> builder(ResourceLocation name, GlProgram.ProgramFactory<P> factory) {
return new Builder<>(name, factory); return new Builder<>(name, factory);
} }
public ProgramSpec(ResourceLocation name, ResourceLocation vert, ResourceLocation frag, GlProgram.ProgramFactory<P> factory, ShaderConstants defines, ArrayList<IVertexAttrib> attributes, boolean fogSensitive) { public ProgramSpec(ResourceLocation name, ResourceLocation vert, ResourceLocation frag, GlProgram.ProgramFactory<P> factory, ShaderConstants defines, ArrayList<IVertexAttrib> attributes, boolean fogSensitive) {
this.name = name; this.name = name;
this.vert = vert; this.vert = vert;
this.frag = frag; this.frag = frag;
this.defines = defines; this.defines = defines;
this.factory = factory; this.factory = factory;
this.attributes = attributes; this.attributes = attributes;
this.fogSensitive = fogSensitive; this.fogSensitive = fogSensitive;
} }
public ResourceLocation getVert() { public ResourceLocation getVert() {
return vert; return vert;
} }
public ResourceLocation getFrag() { public ResourceLocation getFrag() {
return frag; return frag;
} }
public static class Builder<P extends GlProgram> { public static class Builder<P extends GlProgram> {
private ResourceLocation vert; private ResourceLocation vert;
private ResourceLocation frag; private ResourceLocation frag;
private ShaderConstants defines = ShaderConstants.EMPTY; private ShaderConstants defines = ShaderConstants.EMPTY;
private boolean fogSensitive = true; private boolean fogSensitive = true;
private final ResourceLocation name; private final ResourceLocation name;
private final GlProgram.ProgramFactory<P> factory; private final GlProgram.ProgramFactory<P> factory;
private final ArrayList<IVertexAttrib> attributes; private final ArrayList<IVertexAttrib> attributes;
public Builder(ResourceLocation name, GlProgram.ProgramFactory<P> factory) { public Builder(ResourceLocation name, GlProgram.ProgramFactory<P> factory) {
this.name = name; this.name = name;
this.factory = factory; this.factory = factory;
attributes = new ArrayList<>(); attributes = new ArrayList<>();
} }
public Builder<P> setVert(ResourceLocation vert) { public Builder<P> setVert(ResourceLocation vert) {
this.vert = vert; this.vert = vert;
return this; return this;
} }
public Builder<P> setFrag(ResourceLocation frag) { public Builder<P> setFrag(ResourceLocation frag) {
this.frag = frag; this.frag = frag;
return this; return this;
} }
public Builder<P> setDefines(ShaderConstants defines) { public Builder<P> setDefines(ShaderConstants defines) {
this.defines = defines; this.defines = defines;
return this; return this;
} }
public Builder<P> setFogSensitive(boolean fogSensitive) { public Builder<P> setFogSensitive(boolean fogSensitive) {
this.fogSensitive = fogSensitive; this.fogSensitive = fogSensitive;
@ -86,12 +86,12 @@ public class ProgramSpec<P extends GlProgram> {
} }
public <A extends Enum<A> & IVertexAttrib> Builder<P> addAttributes(Class<A> attributeEnum) { public <A extends Enum<A> & IVertexAttrib> Builder<P> addAttributes(Class<A> attributeEnum) {
attributes.addAll(Arrays.asList(attributeEnum.getEnumConstants())); attributes.addAll(Arrays.asList(attributeEnum.getEnumConstants()));
return this; return this;
} }
public ProgramSpec<P> createProgramSpec() { public ProgramSpec<P> createProgramSpec() {
return new ProgramSpec<>(name, vert, frag, factory, defines, attributes, fogSensitive); return new ProgramSpec<>(name, vert, frag, factory, defines, attributes, fogSensitive);
} }
} }
} }

View file

@ -6,12 +6,12 @@ package com.simibubi.create.foundation.render.backend.gl.shader;
@FunctionalInterface @FunctionalInterface
public interface ShaderCallback<P extends GlProgram> { public interface ShaderCallback<P extends GlProgram> {
void call(P program); void call(P program);
default ShaderCallback<P> andThen(ShaderCallback<P> other) { default ShaderCallback<P> andThen(ShaderCallback<P> other) {
return program -> { return program -> {
call(program); call(program);
other.call(program); other.call(program);
}; };
} }
} }

View file

@ -10,50 +10,50 @@ import java.util.stream.Stream;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
public class ShaderConstants implements GlShader.PreProcessor { public class ShaderConstants implements GlShader.PreProcessor {
public static final ShaderConstants EMPTY = new ShaderConstants(); public static final ShaderConstants EMPTY = new ShaderConstants();
private final ArrayList<String> defines; private final ArrayList<String> defines;
public ShaderConstants() { public ShaderConstants() {
defines = new ArrayList<>(); defines = new ArrayList<>();
} }
public ShaderConstants(ShaderConstants other) { public ShaderConstants(ShaderConstants other) {
this.defines = Lists.newArrayList(other.defines); this.defines = Lists.newArrayList(other.defines);
} }
public static ShaderConstants define(String def) { public static ShaderConstants define(String def) {
return new ShaderConstants().def(def); return new ShaderConstants().def(def);
} }
public ShaderConstants def(String def) { public ShaderConstants def(String def) {
defines.add(def); defines.add(def);
return this; return this;
} }
public ShaderConstants defineAll(Collection<String> defines) { public ShaderConstants defineAll(Collection<String> defines) {
this.defines.addAll(defines); this.defines.addAll(defines);
return this; return this;
} }
public ArrayList<String> getDefines() { public ArrayList<String> getDefines() {
return defines; return defines;
} }
public Stream<String> directives() { public Stream<String> directives() {
return defines.stream().map(it -> "#define " + it); return defines.stream().map(it -> "#define " + it);
} }
@Override @Override
public String process(String source) { public String process(String source) {
return new BufferedReader(new StringReader(source)).lines().flatMap(line -> { return new BufferedReader(new StringReader(source)).lines().flatMap(line -> {
Stream<String> map = Stream.of(line); Stream<String> map = Stream.of(line);
if (line.startsWith("#version")) { if (line.startsWith("#version")) {
map = Stream.concat(map, directives()); map = Stream.concat(map, directives());
} }
return map; return map;
}).collect(Collectors.joining("\n")); }).collect(Collectors.joining("\n"));
} }
} }

View file

@ -3,13 +3,13 @@ package com.simibubi.create.foundation.render.backend.gl.shader;
import org.lwjgl.opengl.GL20; import org.lwjgl.opengl.GL20;
public enum ShaderType { public enum ShaderType {
VERTEX(GL20.GL_VERTEX_SHADER), VERTEX(GL20.GL_VERTEX_SHADER),
FRAGMENT(GL20.GL_FRAGMENT_SHADER), FRAGMENT(GL20.GL_FRAGMENT_SHADER),
; ;
public final int glEnum; public final int glEnum;
ShaderType(int glEnum) { ShaderType(int glEnum) {
this.glEnum = glEnum; this.glEnum = glEnum;
} }
} }

View file

@ -6,52 +6,50 @@ import org.lwjgl.opengl.GL31;
import org.lwjgl.opengl.GLCapabilities; import org.lwjgl.opengl.GLCapabilities;
public enum DrawInstanced implements GlVersioned { public enum DrawInstanced implements GlVersioned {
GL31_DRAW_INSTANCED { GL31_DRAW_INSTANCED {
@Override @Override
public boolean supported(GLCapabilities caps) { public boolean supported(GLCapabilities caps) {
return caps.OpenGL31; return caps.OpenGL31;
} }
@Override @Override
public void drawArraysInstanced(int mode, int first, int count, int primcount) { public void drawArraysInstanced(int mode, int first, int count, int primcount) {
GL31.glDrawArraysInstanced(mode, first, count, primcount); GL31.glDrawArraysInstanced(mode, first, count, primcount);
} }
}, },
ARB_DRAW_INSTANCED { ARB_DRAW_INSTANCED {
@Override @Override
public boolean supported(GLCapabilities caps) { public boolean supported(GLCapabilities caps) {
return caps.GL_ARB_draw_instanced; return caps.GL_ARB_draw_instanced;
} }
@Override @Override
public void drawArraysInstanced(int mode, int first, int count, int primcount) { public void drawArraysInstanced(int mode, int first, int count, int primcount) {
ARBDrawInstanced.glDrawArraysInstancedARB(mode, first, count, primcount); ARBDrawInstanced.glDrawArraysInstancedARB(mode, first, count, primcount);
} }
}, },
EXT_DRAW_INSTANCED { EXT_DRAW_INSTANCED {
@Override @Override
public boolean supported(GLCapabilities caps) { public boolean supported(GLCapabilities caps) {
return caps.GL_EXT_draw_instanced; return caps.GL_EXT_draw_instanced;
} }
@Override @Override
public void drawArraysInstanced(int mode, int first, int count, int primcount) { public void drawArraysInstanced(int mode, int first, int count, int primcount) {
EXTDrawInstanced.glDrawArraysInstancedEXT(mode, first, count, primcount); EXTDrawInstanced.glDrawArraysInstancedEXT(mode, first, count, primcount);
} }
}, },
UNSUPPORTED { UNSUPPORTED {
@Override @Override
public boolean supported(GLCapabilities caps) { public boolean supported(GLCapabilities caps) {
return true; return true;
} }
@Override @Override
public void drawArraysInstanced(int mode, int first, int count, int primcount) { public void drawArraysInstanced(int mode, int first, int count, int primcount) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
} };
; public abstract void drawArraysInstanced(int mode, int first, int count, int primcount);
public abstract void drawArraysInstanced(int mode, int first, int count, int primcount);
} }

View file

@ -13,109 +13,109 @@ import org.lwjgl.system.MemoryUtil;
/** /**
* An instance of this class stores information * An instance of this class stores information
* about what OpenGL features are available. * about what OpenGL features are available.
* * <p>
* Each field stores an enum variant that provides access to the * Each field stores an enum variant that provides access to the
* most appropriate version of a feature for the current system. * most appropriate version of a feature for the current system.
*/ */
public class GlCompat { public class GlCompat {
public final MapBuffer mapBuffer; public final MapBuffer mapBuffer;
public final VertexArrayObject vertexArrayObject; public final VertexArrayObject vertexArrayObject;
public final InstancedArrays instancedArrays; public final InstancedArrays instancedArrays;
public final DrawInstanced drawInstanced; public final DrawInstanced drawInstanced;
public final RGPixelFormat pixelFormat; public final RGPixelFormat pixelFormat;
public GlCompat(GLCapabilities caps) { public GlCompat(GLCapabilities caps) {
mapBuffer = getLatest(MapBuffer.class, caps); mapBuffer = getLatest(MapBuffer.class, caps);
vertexArrayObject = getLatest(VertexArrayObject.class, caps); vertexArrayObject = getLatest(VertexArrayObject.class, caps);
instancedArrays = getLatest(InstancedArrays.class, caps); instancedArrays = getLatest(InstancedArrays.class, caps);
drawInstanced = getLatest(DrawInstanced.class, caps); drawInstanced = getLatest(DrawInstanced.class, caps);
pixelFormat = getLatest(RGPixelFormat.class, caps); pixelFormat = getLatest(RGPixelFormat.class, caps);
} }
public void mapBuffer(int target, int offset, int length, Consumer<ByteBuffer> upload) { public void mapBuffer(int target, int offset, int length, Consumer<ByteBuffer> upload) {
mapBuffer.mapBuffer(target, offset, length, upload); mapBuffer.mapBuffer(target, offset, length, upload);
} }
public void vertexAttribDivisor(int index, int divisor) { public void vertexAttribDivisor(int index, int divisor) {
instancedArrays.vertexAttribDivisor(index, divisor); instancedArrays.vertexAttribDivisor(index, divisor);
} }
public void drawArraysInstanced(int mode, int first, int count, int primcount) { public void drawArraysInstanced(int mode, int first, int count, int primcount) {
drawInstanced.drawArraysInstanced(mode, first, count, primcount); drawInstanced.drawArraysInstanced(mode, first, count, primcount);
} }
public int genVertexArrays() { public int genVertexArrays() {
return vertexArrayObject.genVertexArrays(); return vertexArrayObject.genVertexArrays();
} }
public void deleteVertexArrays(int array) { public void deleteVertexArrays(int array) {
vertexArrayObject.deleteVertexArrays(array); vertexArrayObject.deleteVertexArrays(array);
} }
public void bindVertexArray(int array) { public void bindVertexArray(int array) {
vertexArrayObject.bindVertexArray(array); vertexArrayObject.bindVertexArray(array);
} }
public boolean vertexArrayObjectsSupported() { public boolean vertexArrayObjectsSupported() {
return vertexArrayObject != VertexArrayObject.UNSUPPORTED; return vertexArrayObject != VertexArrayObject.UNSUPPORTED;
} }
public boolean instancedArraysSupported() { public boolean instancedArraysSupported() {
return instancedArrays != InstancedArrays.UNSUPPORTED; return instancedArrays != InstancedArrays.UNSUPPORTED;
} }
public boolean drawInstancedSupported() { public boolean drawInstancedSupported() {
return drawInstanced != DrawInstanced.UNSUPPORTED; return drawInstanced != DrawInstanced.UNSUPPORTED;
} }
/** /**
* Get the most compatible version of a specific OpenGL feature by iterating over enum constants in order. * Get the most compatible version of a specific OpenGL feature by iterating over enum constants in order.
* *
* @param clazz The class of the versioning enum. * @param clazz The class of the versioning enum.
* @param caps The current system's supported features. * @param caps The current system's supported features.
* @param <V> The type of the versioning enum. * @param <V> The type of the versioning enum.
* @return The first defined enum variant to return true. * @return The first defined enum variant to return true.
*/ */
public static <V extends Enum<V> & GlVersioned> V getLatest(Class<V> clazz, GLCapabilities caps) { public static <V extends Enum<V> & GlVersioned> V getLatest(Class<V> clazz, GLCapabilities caps) {
V[] constants = clazz.getEnumConstants(); V[] constants = clazz.getEnumConstants();
V last = constants[constants.length - 1]; V last = constants[constants.length - 1];
if (!last.supported(caps)) { if (!last.supported(caps)) {
throw new IllegalStateException(""); throw new IllegalStateException("");
} }
return Arrays.stream(constants).filter(it -> it.supported(caps)).findFirst().get(); return Arrays.stream(constants).filter(it -> it.supported(caps)).findFirst().get();
} }
/** /**
* Copied from: * Copied from:
* <br> https://github.com/grondag/canvas/commit/820bf754092ccaf8d0c169620c2ff575722d7d96 * <br> https://github.com/grondag/canvas/commit/820bf754092ccaf8d0c169620c2ff575722d7d96
* *
* <p>Identical in function to {@link GL20C#glShaderSource(int, CharSequence)} but * <p>Identical in function to {@link GL20C#glShaderSource(int, CharSequence)} but
* passes a null pointer for string length to force the driver to rely on the null * passes a null pointer for string length to force the driver to rely on the null
* terminator for string length. This is a workaround for an apparent flaw with some * terminator for string length. This is a workaround for an apparent flaw with some
* AMD drivers that don't receive or interpret the length correctly, resulting in * AMD drivers that don't receive or interpret the length correctly, resulting in
* an access violation when the driver tries to read past the string memory. * an access violation when the driver tries to read past the string memory.
* *
* <p>Hat tip to fewizz for the find and the fix. * <p>Hat tip to fewizz for the find and the fix.
*/ */
public static void safeShaderSource(int glId, CharSequence source) { public static void safeShaderSource(int glId, CharSequence source) {
final MemoryStack stack = MemoryStack.stackGet(); final MemoryStack stack = MemoryStack.stackGet();
final int stackPointer = stack.getPointer(); final int stackPointer = stack.getPointer();
try { try {
final ByteBuffer sourceBuffer = MemoryUtil.memUTF8(source, true); final ByteBuffer sourceBuffer = MemoryUtil.memUTF8(source, true);
final PointerBuffer pointers = stack.mallocPointer(1); final PointerBuffer pointers = stack.mallocPointer(1);
pointers.put(sourceBuffer); pointers.put(sourceBuffer);
GL20C.nglShaderSource(glId, 1, pointers.address0(), 0); GL20C.nglShaderSource(glId, 1, pointers.address0(), 0);
org.lwjgl.system.APIUtil.apiArrayFree(pointers.address0(), 1); org.lwjgl.system.APIUtil.apiArrayFree(pointers.address0(), 1);
} finally { } finally {
stack.setPointer(stackPointer); stack.setPointer(stackPointer);
} }
} }
} }

View file

@ -7,10 +7,11 @@ import org.lwjgl.opengl.GLCapabilities;
* last defined variant <em>always</em> returns <code>true</code>. * last defined variant <em>always</em> returns <code>true</code>.
*/ */
public interface GlVersioned { public interface GlVersioned {
/** /**
* Queries whether this variant is supported by the current system. * Queries whether this variant is supported by the current system.
* @param caps The {@link GLCapabilities} reported by the current system. *
* @return <code>true</code> if this variant is supported, or if this is the last defined variant. * @param caps The {@link GLCapabilities} reported by the current system.
*/ * @return <code>true</code> if this variant is supported, or if this is the last defined variant.
boolean supported(GLCapabilities caps); */
boolean supported(GLCapabilities caps);
} }

View file

@ -5,41 +5,39 @@ import org.lwjgl.opengl.GL33;
import org.lwjgl.opengl.GLCapabilities; import org.lwjgl.opengl.GLCapabilities;
public enum InstancedArrays implements GlVersioned { public enum InstancedArrays implements GlVersioned {
GL33_INSTANCED_ARRAYS { GL33_INSTANCED_ARRAYS {
@Override @Override
public boolean supported(GLCapabilities caps) { public boolean supported(GLCapabilities caps) {
return caps.OpenGL33; return caps.OpenGL33;
} }
@Override @Override
public void vertexAttribDivisor(int index, int divisor) { public void vertexAttribDivisor(int index, int divisor) {
GL33.glVertexAttribDivisor(index, divisor); GL33.glVertexAttribDivisor(index, divisor);
} }
}, },
ARB_INSTANCED_ARRAYS { ARB_INSTANCED_ARRAYS {
@Override @Override
public boolean supported(GLCapabilities caps) { public boolean supported(GLCapabilities caps) {
return caps.GL_ARB_instanced_arrays; return caps.GL_ARB_instanced_arrays;
} }
@Override @Override
public void vertexAttribDivisor(int index, int divisor) { public void vertexAttribDivisor(int index, int divisor) {
ARBInstancedArrays.glVertexAttribDivisorARB(index, divisor); ARBInstancedArrays.glVertexAttribDivisorARB(index, divisor);
} }
}, },
UNSUPPORTED { UNSUPPORTED {
@Override @Override
public boolean supported(GLCapabilities caps) { public boolean supported(GLCapabilities caps) {
return true; return true;
} }
@Override @Override
public void vertexAttribDivisor(int index, int divisor) { public void vertexAttribDivisor(int index, int divisor) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
} };
; public abstract void vertexAttribDivisor(int index, int divisor);
public abstract void vertexAttribDivisor(int index, int divisor);
} }

View file

@ -10,66 +10,66 @@ import org.lwjgl.opengl.GLCapabilities;
public enum MapBuffer implements GlVersioned { public enum MapBuffer implements GlVersioned {
GL30_RANGE { GL30_RANGE {
@Override @Override
public boolean supported(GLCapabilities caps) { public boolean supported(GLCapabilities caps) {
return caps.OpenGL30; return caps.OpenGL30;
} }
@Override @Override
public void mapBuffer(int target, int offset, int length, Consumer<ByteBuffer> upload) { public void mapBuffer(int target, int offset, int length, Consumer<ByteBuffer> upload) {
ByteBuffer buffer = GL30.glMapBufferRange(target, offset, length, GL30.GL_MAP_WRITE_BIT); ByteBuffer buffer = GL30.glMapBufferRange(target, offset, length, GL30.GL_MAP_WRITE_BIT);
upload.accept(buffer); upload.accept(buffer);
buffer.rewind(); buffer.rewind();
GL30.glUnmapBuffer(target); GL30.glUnmapBuffer(target);
} }
}, },
ARB_RANGE { ARB_RANGE {
@Override @Override
public boolean supported(GLCapabilities caps) { public boolean supported(GLCapabilities caps) {
return caps.GL_ARB_map_buffer_range; return caps.GL_ARB_map_buffer_range;
} }
@Override @Override
public void mapBuffer(int target, int offset, int length, Consumer<ByteBuffer> upload) { public void mapBuffer(int target, int offset, int length, Consumer<ByteBuffer> upload) {
ByteBuffer buffer = ARBMapBufferRange.glMapBufferRange(target, offset, length, GL30.GL_MAP_WRITE_BIT); ByteBuffer buffer = ARBMapBufferRange.glMapBufferRange(target, offset, length, GL30.GL_MAP_WRITE_BIT);
upload.accept(buffer); upload.accept(buffer);
buffer.rewind(); buffer.rewind();
GL30.glUnmapBuffer(target); GL30.glUnmapBuffer(target);
} }
}, },
GL15_MAP { GL15_MAP {
@Override @Override
public boolean supported(GLCapabilities caps) { public boolean supported(GLCapabilities caps) {
return caps.OpenGL15; return caps.OpenGL15;
} }
@Override @Override
public void mapBuffer(int target, int offset, int length, Consumer<ByteBuffer> upload) { public void mapBuffer(int target, int offset, int length, Consumer<ByteBuffer> upload) {
ByteBuffer buffer = GL15.glMapBuffer(target, GL15.GL_WRITE_ONLY); ByteBuffer buffer = GL15.glMapBuffer(target, GL15.GL_WRITE_ONLY);
buffer.position(offset); buffer.position(offset);
upload.accept(buffer); upload.accept(buffer);
buffer.rewind(); buffer.rewind();
GL15.glUnmapBuffer(target); GL15.glUnmapBuffer(target);
} }
}, },
UNSUPPORTED { UNSUPPORTED {
@Override @Override
public boolean supported(GLCapabilities caps) { public boolean supported(GLCapabilities caps) {
return true; return true;
} }
@Override @Override
public void mapBuffer(int target, int offset, int length, Consumer<ByteBuffer> upload) { public void mapBuffer(int target, int offset, int length, Consumer<ByteBuffer> upload) {
throw new UnsupportedOperationException("glMapBuffer not supported"); throw new UnsupportedOperationException("glMapBuffer not supported");
} }
}; };
public abstract void mapBuffer(int target, int offset, int length, Consumer<ByteBuffer> upload); public abstract void mapBuffer(int target, int offset, int length, Consumer<ByteBuffer> upload);
} }

View file

@ -5,73 +5,73 @@ import org.lwjgl.opengl.GL30;
import org.lwjgl.opengl.GLCapabilities; import org.lwjgl.opengl.GLCapabilities;
public enum RGPixelFormat implements GlVersioned { public enum RGPixelFormat implements GlVersioned {
GL30_RG { GL30_RG {
@Override @Override
public boolean supported(GLCapabilities caps) { public boolean supported(GLCapabilities caps) {
return caps.OpenGL30; return caps.OpenGL30;
} }
@Override @Override
public int internalFormat() { public int internalFormat() {
return GL30.GL_RG8; return GL30.GL_RG8;
} }
@Override @Override
public int format() { public int format() {
return GL30.GL_RG; return GL30.GL_RG;
} }
@Override @Override
public int byteCount() { public int byteCount() {
return 2; return 2;
} }
}, },
GL11_RGB { GL11_RGB {
@Override @Override
public boolean supported(GLCapabilities caps) { public boolean supported(GLCapabilities caps) {
return caps.OpenGL11; return caps.OpenGL11;
} }
@Override @Override
public int internalFormat() { public int internalFormat() {
return GL11.GL_RGB8; return GL11.GL_RGB8;
} }
@Override @Override
public int format() { public int format() {
return GL11.GL_RGB; return GL11.GL_RGB;
} }
@Override @Override
public int byteCount() { public int byteCount() {
return 3; return 3;
} }
}, },
UNSUPPORTED { UNSUPPORTED {
@Override @Override
public boolean supported(GLCapabilities caps) { public boolean supported(GLCapabilities caps) {
return true; return true;
} }
@Override @Override
public int internalFormat() { public int internalFormat() {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override @Override
public int format() { public int format() {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override @Override
public int byteCount() { public int byteCount() {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
} };
; public abstract int internalFormat();
public abstract int internalFormat(); public abstract int format();
public abstract int format();
public abstract int byteCount(); public abstract int byteCount();
} }

View file

@ -5,75 +5,73 @@ import org.lwjgl.opengl.GL30;
import org.lwjgl.opengl.GLCapabilities; import org.lwjgl.opengl.GLCapabilities;
public enum VertexArrayObject implements GlVersioned { public enum VertexArrayObject implements GlVersioned {
GL30_VAO { GL30_VAO {
@Override @Override
public boolean supported(GLCapabilities caps) { public boolean supported(GLCapabilities caps) {
return caps.OpenGL30; return caps.OpenGL30;
} }
@Override @Override
public int genVertexArrays() { public int genVertexArrays() {
return GL30.glGenVertexArrays(); return GL30.glGenVertexArrays();
} }
@Override @Override
public void bindVertexArray(int array) { public void bindVertexArray(int array) {
GL30.glBindVertexArray(array); GL30.glBindVertexArray(array);
} }
@Override @Override
public void deleteVertexArrays(int array) { public void deleteVertexArrays(int array) {
GL30.glDeleteVertexArrays(array); GL30.glDeleteVertexArrays(array);
} }
}, },
ARB_VAO { ARB_VAO {
@Override @Override
public boolean supported(GLCapabilities caps) { public boolean supported(GLCapabilities caps) {
return caps.GL_ARB_vertex_array_object; return caps.GL_ARB_vertex_array_object;
} }
@Override @Override
public int genVertexArrays() { public int genVertexArrays() {
return ARBVertexArrayObject.glGenVertexArrays(); return ARBVertexArrayObject.glGenVertexArrays();
} }
@Override @Override
public void bindVertexArray(int array) { public void bindVertexArray(int array) {
ARBVertexArrayObject.glBindVertexArray(array); ARBVertexArrayObject.glBindVertexArray(array);
} }
@Override @Override
public void deleteVertexArrays(int array) { public void deleteVertexArrays(int array) {
ARBVertexArrayObject.glDeleteVertexArrays(array); ARBVertexArrayObject.glDeleteVertexArrays(array);
} }
}, },
UNSUPPORTED { UNSUPPORTED {
@Override @Override
public boolean supported(GLCapabilities caps) { public boolean supported(GLCapabilities caps) {
return true; return true;
} }
@Override @Override
public int genVertexArrays() { public int genVertexArrays() {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override @Override
public void bindVertexArray(int array) { public void bindVertexArray(int array) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override @Override
public void deleteVertexArrays(int array) { public void deleteVertexArrays(int array) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
} };
; public abstract int genVertexArrays();
public abstract int genVertexArrays(); public abstract void bindVertexArray(int array);
public abstract void bindVertexArray(int array); public abstract void deleteVertexArrays(int array);
public abstract void deleteVertexArrays(int array);
} }

View file

@ -10,21 +10,21 @@ package com.simibubi.create.foundation.render.backend.instancing;
* to parameterize the instances, you're encouraged to implement this for prototyping. * to parameterize the instances, you're encouraged to implement this for prototyping.
*/ */
public interface IDynamicInstance extends IInstance { public interface IDynamicInstance extends IInstance {
/** /**
* Called every frame. * Called every frame.
*/ */
void beginFrame(); void beginFrame();
/** /**
* As a further optimization, dynamic instances that are far away are ticked less often. * As a further optimization, dynamic instances that are far away are ticked less often.
* This behavior can be disabled by returning false. * This behavior can be disabled by returning false.
* *
* <br> You might want to opt out of this if you want your animations to remain smooth * <br> You might want to opt out of this if you want your animations to remain smooth
* even when far away from the camera. It is recommended to keep this as is, however. * even when far away from the camera. It is recommended to keep this as is, however.
* *
* @return <code>true</code> if your instance should be slow ticked. * @return <code>true</code> if your instance should be slow ticked.
*/ */
default boolean decreaseFramerateWithDistance() { default boolean decreaseFramerateWithDistance() {
return true; return true;
} }
} }

View file

@ -7,7 +7,7 @@ package com.simibubi.create.foundation.render.backend.instancing;
* <code>Minecraft.getInstance().world</code> will always support Flywheel. * <code>Minecraft.getInstance().world</code> will always support Flywheel.
*/ */
public interface IFlywheelWorld { public interface IFlywheelWorld {
default boolean supportsFlywheel() { default boolean supportsFlywheel() {
return true; return true;
} }
} }

View file

@ -1,7 +1,7 @@
package com.simibubi.create.foundation.render.backend.instancing; package com.simibubi.create.foundation.render.backend.instancing;
public interface IInstanceRendered { public interface IInstanceRendered {
default boolean shouldRenderAsTE() { default boolean shouldRenderAsTE() {
return false; return false;
} }
} }

View file

@ -4,5 +4,5 @@ import net.minecraft.tileentity.TileEntity;
@FunctionalInterface @FunctionalInterface
public interface IRendererFactory<T extends TileEntity> { public interface IRendererFactory<T extends TileEntity> {
TileEntityInstance<? super T> create(InstancedTileRenderer<?> manager, T te); TileEntityInstance<? super T> create(InstancedTileRenderer<?> manager, T te);
} }

View file

@ -4,61 +4,61 @@ import java.nio.ByteBuffer;
public abstract class InstanceData { public abstract class InstanceData {
protected final InstancedModel<?> owner; protected final InstancedModel<?> owner;
boolean dirty; boolean dirty;
boolean removed; boolean removed;
protected InstanceData(InstancedModel<?> owner) { protected InstanceData(InstancedModel<?> owner) {
this.owner = owner; this.owner = owner;
} }
public abstract void write(ByteBuffer buf); public abstract void write(ByteBuffer buf);
public void markDirty() { public void markDirty() {
owner.anyToUpdate = true; owner.anyToUpdate = true;
dirty = true; dirty = true;
} }
public void delete() { public void delete() {
owner.anyToRemove = true; owner.anyToRemove = true;
removed = true; removed = true;
} }
public void putVec4(ByteBuffer buf, float x, float y, float z, float w) { public void putVec4(ByteBuffer buf, float x, float y, float z, float w) {
put(buf, x); put(buf, x);
put(buf, y); put(buf, y);
put(buf, z); put(buf, z);
put(buf, w); put(buf, w);
} }
public void putVec3(ByteBuffer buf, float x, float y, float z) { public void putVec3(ByteBuffer buf, float x, float y, float z) {
put(buf, x); put(buf, x);
put(buf, y); put(buf, y);
put(buf, z); put(buf, z);
} }
public void putVec2(ByteBuffer buf, float x, float y) { public void putVec2(ByteBuffer buf, float x, float y) {
put(buf, x); put(buf, x);
put(buf, y); put(buf, y);
} }
public void putVec3(ByteBuffer buf, byte x, byte y, byte z) { public void putVec3(ByteBuffer buf, byte x, byte y, byte z) {
put(buf, x); put(buf, x);
put(buf, y); put(buf, y);
put(buf, z); put(buf, z);
} }
public void putVec2(ByteBuffer buf, byte x, byte y) { public void putVec2(ByteBuffer buf, byte x, byte y) {
put(buf, x); put(buf, x);
put(buf, y); put(buf, y);
} }
public void put(ByteBuffer buf, byte b) { public void put(ByteBuffer buf, byte b) {
buf.put(b); buf.put(b);
} }
public void put(ByteBuffer buf, float f) { public void put(ByteBuffer buf, float f) {
buf.putFloat(f); buf.putFloat(f);
} }
} }

View file

@ -19,239 +19,239 @@ import com.simibubi.create.foundation.render.backend.gl.attrib.VertexFormat;
import net.minecraft.client.renderer.BufferBuilder; import net.minecraft.client.renderer.BufferBuilder;
public abstract class InstancedModel<D extends InstanceData> extends BufferedModel { public abstract class InstancedModel<D extends InstanceData> extends BufferedModel {
public static final VertexFormat FORMAT = VertexFormat.builder().addAttributes(ModelAttributes.class).build(); public static final VertexFormat FORMAT = VertexFormat.builder().addAttributes(ModelAttributes.class).build();
public final InstancedTileRenderer<?> renderer; public final InstancedTileRenderer<?> renderer;
protected GlVertexArray vao; protected GlVertexArray vao;
protected GlBuffer instanceVBO; protected GlBuffer instanceVBO;
protected int glBufferSize = -1; protected int glBufferSize = -1;
protected int glInstanceCount = 0; protected int glInstanceCount = 0;
protected final ArrayList<D> data = new ArrayList<>(); protected final ArrayList<D> data = new ArrayList<>();
boolean anyToRemove; boolean anyToRemove;
boolean anyToUpdate; boolean anyToUpdate;
public InstancedModel(InstancedTileRenderer<?> renderer, BufferBuilder buf) { public InstancedModel(InstancedTileRenderer<?> renderer, BufferBuilder buf) {
super(buf); super(buf);
this.renderer = renderer; this.renderer = renderer;
} }
@Override @Override
protected void init() { protected void init() {
vao = new GlVertexArray(); vao = new GlVertexArray();
instanceVBO = new GlBuffer(GL20.GL_ARRAY_BUFFER); instanceVBO = new GlBuffer(GL20.GL_ARRAY_BUFFER);
vao.with(vao -> super.init()); vao.with(vao -> super.init());
} }
@Override @Override
protected void initModel() { protected void initModel() {
super.initModel(); super.initModel();
setupAttributes(); setupAttributes();
} }
public int instanceCount() { public int instanceCount() {
return data.size(); return data.size();
} }
public boolean isEmpty() { public boolean isEmpty() {
return instanceCount() == 0; return instanceCount() == 0;
} }
protected void deleteInternal() { protected void deleteInternal() {
super.deleteInternal(); super.deleteInternal();
instanceVBO.delete(); instanceVBO.delete();
vao.delete(); vao.delete();
} }
public synchronized D createInstance() { public synchronized D createInstance() {
D instanceData = newInstance(); D instanceData = newInstance();
instanceData.dirty = true; instanceData.dirty = true;
anyToUpdate = true; anyToUpdate = true;
data.add(instanceData); data.add(instanceData);
return instanceData; return instanceData;
} }
protected abstract D newInstance(); protected abstract D newInstance();
protected void doRender() { protected void doRender() {
vao.with(vao -> { vao.with(vao -> {
renderSetup(); renderSetup();
if (glInstanceCount > 0) if (glInstanceCount > 0)
Backend.compat.drawArraysInstanced(GL11.GL_QUADS, 0, vertexCount, glInstanceCount); Backend.compat.drawArraysInstanced(GL11.GL_QUADS, 0, vertexCount, glInstanceCount);
}); });
} }
protected void renderSetup() { protected void renderSetup() {
if (anyToRemove) { if (anyToRemove) {
removeDeletedInstances(); removeDeletedInstances();
} }
instanceVBO.bind(); instanceVBO.bind();
if (!realloc()) { if (!realloc()) {
if (anyToRemove) { if (anyToRemove) {
clearBufferTail(); clearBufferTail();
} }
if (anyToUpdate) { if (anyToUpdate) {
updateBuffer(); updateBuffer();
} }
} }
glInstanceCount = data.size(); glInstanceCount = data.size();
informAttribDivisors(); informAttribDivisors();
instanceVBO.unbind(); instanceVBO.unbind();
this.anyToRemove = false; this.anyToRemove = false;
this.anyToUpdate = false; this.anyToUpdate = false;
} }
private void informAttribDivisors() { private void informAttribDivisors() {
int staticAttributes = getModelFormat().getShaderAttributeCount(); int staticAttributes = getModelFormat().getShaderAttributeCount();
getInstanceFormat().vertexAttribPointers(staticAttributes); getInstanceFormat().vertexAttribPointers(staticAttributes);
for (int i = 0; i < getInstanceFormat().getShaderAttributeCount(); i++) { for (int i = 0; i < getInstanceFormat().getShaderAttributeCount(); i++) {
Backend.compat.vertexAttribDivisor(i + staticAttributes, 1); Backend.compat.vertexAttribDivisor(i + staticAttributes, 1);
} }
} }
private void clearBufferTail() { private void clearBufferTail() {
int size = data.size(); int size = data.size();
final int offset = size * getInstanceFormat().getStride(); final int offset = size * getInstanceFormat().getStride();
final int length = glBufferSize - offset; final int length = glBufferSize - offset;
if (length > 0) { if (length > 0) {
instanceVBO.map(offset, length, buffer -> { instanceVBO.map(offset, length, buffer -> {
buffer.put(new byte[length]); buffer.put(new byte[length]);
}); });
} }
} }
private void updateBuffer() { private void updateBuffer() {
final int size = data.size(); final int size = data.size();
if (size <= 0) return; if (size <= 0) return;
final int stride = getInstanceFormat().getStride(); final int stride = getInstanceFormat().getStride();
final BitSet dirtySet = getDirtyBitSet(); final BitSet dirtySet = getDirtyBitSet();
if (dirtySet.isEmpty()) return; if (dirtySet.isEmpty()) return;
final int firstDirty = dirtySet.nextSetBit(0); final int firstDirty = dirtySet.nextSetBit(0);
final int lastDirty = dirtySet.previousSetBit(size); final int lastDirty = dirtySet.previousSetBit(size);
final int offset = firstDirty * stride; final int offset = firstDirty * stride;
final int length = (1 + lastDirty - firstDirty) * stride; final int length = (1 + lastDirty - firstDirty) * stride;
if (length > 0) { if (length > 0) {
instanceVBO.map(offset, length, buffer -> { instanceVBO.map(offset, length, buffer -> {
dirtySet.stream().forEach(i -> { dirtySet.stream().forEach(i -> {
final D d = data.get(i); final D d = data.get(i);
buffer.position(i * stride - offset); buffer.position(i * stride - offset);
d.write(buffer); d.write(buffer);
}); });
}); });
} }
} }
private BitSet getDirtyBitSet() { private BitSet getDirtyBitSet() {
final int size = data.size(); final int size = data.size();
final BitSet dirtySet = new BitSet(size); final BitSet dirtySet = new BitSet(size);
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
D element = data.get(i); D element = data.get(i);
if (element.dirty) { if (element.dirty) {
dirtySet.set(i); dirtySet.set(i);
element.dirty = false; element.dirty = false;
} }
} }
return dirtySet; return dirtySet;
} }
private boolean realloc() { private boolean realloc() {
int size = this.data.size(); int size = this.data.size();
int stride = getInstanceFormat().getStride(); int stride = getInstanceFormat().getStride();
int requiredSize = size * stride; int requiredSize = size * stride;
if (requiredSize > glBufferSize) { if (requiredSize > glBufferSize) {
glBufferSize = requiredSize + stride * 16; glBufferSize = requiredSize + stride * 16;
GL15.glBufferData(instanceVBO.getBufferType(), glBufferSize, GL15.GL_STATIC_DRAW); GL15.glBufferData(instanceVBO.getBufferType(), glBufferSize, GL15.GL_STATIC_DRAW);
instanceVBO.map(glBufferSize, buffer -> { instanceVBO.map(glBufferSize, buffer -> {
for (D datum : data) { for (D datum : data) {
datum.write(buffer); datum.write(buffer);
} }
}); });
glInstanceCount = size; glInstanceCount = size;
return true; return true;
} }
return false; return false;
} }
private void removeDeletedInstances() { private void removeDeletedInstances() {
// figure out which elements are to be removed // figure out which elements are to be removed
// any exception thrown from the filter predicate at this stage // any exception thrown from the filter predicate at this stage
// will leave the collection unmodified // will leave the collection unmodified
final int oldSize = this.data.size(); final int oldSize = this.data.size();
int removeCount = 0; int removeCount = 0;
final BitSet removeSet = new BitSet(oldSize); final BitSet removeSet = new BitSet(oldSize);
for (int i = 0; i < oldSize; i++) { for (int i = 0; i < oldSize; i++) {
final D element = this.data.get(i); final D element = this.data.get(i);
if (element.removed) { if (element.removed) {
removeSet.set(i); removeSet.set(i);
removeCount++; removeCount++;
} }
} }
final int newSize = oldSize - removeCount; final int newSize = oldSize - removeCount;
// shift surviving elements left over the spaces left by removed elements // shift surviving elements left over the spaces left by removed elements
for (int i = 0, j = 0; (i < oldSize) && (j < newSize); i++, j++) { for (int i = 0, j = 0; (i < oldSize) && (j < newSize); i++, j++) {
i = removeSet.nextClearBit(i); i = removeSet.nextClearBit(i);
if (i != j) { if (i != j) {
D element = data.get(i); D element = data.get(i);
data.set(j, element); data.set(j, element);
element.dirty = true; element.dirty = true;
} }
} }
anyToUpdate = true; anyToUpdate = true;
data.subList(newSize, oldSize).clear(); data.subList(newSize, oldSize).clear();
} }
@Override @Override
protected void copyVertex(ByteBuffer constant, int i) { protected void copyVertex(ByteBuffer constant, int i) {
constant.putFloat(getX(template, i)); constant.putFloat(getX(template, i));
constant.putFloat(getY(template, i)); constant.putFloat(getY(template, i));
constant.putFloat(getZ(template, i)); constant.putFloat(getZ(template, i));
constant.put(getNX(template, i)); constant.put(getNX(template, i));
constant.put(getNY(template, i)); constant.put(getNY(template, i));
constant.put(getNZ(template, i)); constant.put(getNZ(template, i));
constant.putFloat(getU(template, i)); constant.putFloat(getU(template, i));
constant.putFloat(getV(template, i)); constant.putFloat(getV(template, i));
} }
@Override @Override
protected VertexFormat getModelFormat() { protected VertexFormat getModelFormat() {
return FORMAT; return FORMAT;
} }
protected abstract VertexFormat getInstanceFormat(); protected abstract VertexFormat getInstanceFormat();
protected int getTotalShaderAttributeCount() { protected int getTotalShaderAttributeCount() {
return getInstanceFormat().getShaderAttributeCount() + super.getTotalShaderAttributeCount(); return getInstanceFormat().getShaderAttributeCount() + super.getTotalShaderAttributeCount();
} }
} }

View file

@ -10,22 +10,22 @@ import net.minecraft.tileentity.TileEntity;
import net.minecraft.tileentity.TileEntityType; import net.minecraft.tileentity.TileEntityType;
public class InstancedTileRenderRegistry { public class InstancedTileRenderRegistry {
public static final InstancedTileRenderRegistry instance = new InstancedTileRenderRegistry(); public static final InstancedTileRenderRegistry instance = new InstancedTileRenderRegistry();
private final Map<TileEntityType<?>, IRendererFactory<?>> renderers = Maps.newHashMap(); private final Map<TileEntityType<?>, IRendererFactory<?>> renderers = Maps.newHashMap();
public <T extends TileEntity> void register(TileEntityType<? extends T> type, IRendererFactory<? super T> rendererFactory) { public <T extends TileEntity> void register(TileEntityType<? extends T> type, IRendererFactory<? super T> rendererFactory) {
this.renderers.put(type, rendererFactory); this.renderers.put(type, rendererFactory);
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Nullable @Nullable
public <T extends TileEntity> TileEntityInstance<? super T> create(InstancedTileRenderer<?> manager, T tile) { public <T extends TileEntity> TileEntityInstance<? super T> create(InstancedTileRenderer<?> manager, T tile) {
TileEntityType<?> type = tile.getType(); TileEntityType<?> type = tile.getType();
IRendererFactory<? super T> factory = (IRendererFactory<? super T>) this.renderers.get(type); IRendererFactory<? super T> factory = (IRendererFactory<? super T>) this.renderers.get(type);
if (factory == null) return null; if (factory == null) return null;
else return factory.create(manager, tile); else return factory.create(manager, tile);
} }
} }

View file

@ -25,259 +25,259 @@ import net.minecraft.world.IBlockReader;
import net.minecraft.world.World; import net.minecraft.world.World;
public abstract class InstancedTileRenderer<P extends BasicProgram> { public abstract class InstancedTileRenderer<P extends BasicProgram> {
protected ArrayList<TileEntity> queuedAdditions = new ArrayList<>(64); protected ArrayList<TileEntity> queuedAdditions = new ArrayList<>(64);
protected Map<TileEntity, TileEntityInstance<?>> instances = new HashMap<>(); protected Map<TileEntity, TileEntityInstance<?>> instances = new HashMap<>();
protected Map<TileEntity, ITickableInstance> tickableInstances = new HashMap<>(); protected Map<TileEntity, ITickableInstance> tickableInstances = new HashMap<>();
protected Map<TileEntity, IDynamicInstance> dynamicInstances = new HashMap<>(); protected Map<TileEntity, IDynamicInstance> dynamicInstances = new HashMap<>();
protected Map<MaterialType<?>, RenderMaterial<P, ?>> materials = new HashMap<>(); protected Map<MaterialType<?>, RenderMaterial<P, ?>> materials = new HashMap<>();
protected int frame; protected int frame;
protected int tick; protected int tick;
protected InstancedTileRenderer() { protected InstancedTileRenderer() {
registerMaterials(); registerMaterials();
} }
public abstract BlockPos getOriginCoordinate(); public abstract BlockPos getOriginCoordinate();
public abstract void registerMaterials(); public abstract void registerMaterials();
public void tick(double cameraX, double cameraY, double cameraZ) { public void tick(double cameraX, double cameraY, double cameraZ) {
tick++; tick++;
// integer camera pos // integer camera pos
int cX = (int) cameraX; int cX = (int) cameraX;
int cY = (int) cameraY; int cY = (int) cameraY;
int cZ = (int) cameraZ; int cZ = (int) cameraZ;
if (tickableInstances.size() > 0) { if (tickableInstances.size() > 0) {
for (ITickableInstance instance : tickableInstances.values()) { for (ITickableInstance instance : tickableInstances.values()) {
if (!instance.decreaseTickRateWithDistance()) { if (!instance.decreaseTickRateWithDistance()) {
instance.tick(); instance.tick();
continue; continue;
} }
BlockPos pos = instance.getWorldPosition(); BlockPos pos = instance.getWorldPosition();
int dX = pos.getX() - cX; int dX = pos.getX() - cX;
int dY = pos.getY() - cY; int dY = pos.getY() - cY;
int dZ = pos.getZ() - cZ; int dZ = pos.getZ() - cZ;
if ((tick % getUpdateDivisor(dX, dY, dZ)) == 0) if ((tick % getUpdateDivisor(dX, dY, dZ)) == 0)
instance.tick(); instance.tick();
} }
} }
} }
public void beginFrame(ActiveRenderInfo info, double cameraX, double cameraY, double cameraZ) { public void beginFrame(ActiveRenderInfo info, double cameraX, double cameraY, double cameraZ) {
frame++; frame++;
processQueuedAdditions(); processQueuedAdditions();
Vector3f look = info.getHorizontalPlane(); Vector3f look = info.getHorizontalPlane();
float lookX = look.getX(); float lookX = look.getX();
float lookY = look.getY(); float lookY = look.getY();
float lookZ = look.getZ(); float lookZ = look.getZ();
// integer camera pos // integer camera pos
int cX = (int) cameraX; int cX = (int) cameraX;
int cY = (int) cameraY; int cY = (int) cameraY;
int cZ = (int) cameraZ; int cZ = (int) cameraZ;
if (dynamicInstances.size() > 0) { if (dynamicInstances.size() > 0) {
for (IDynamicInstance dyn : dynamicInstances.values()) { for (IDynamicInstance dyn : dynamicInstances.values()) {
if (!dyn.decreaseFramerateWithDistance()) { if (!dyn.decreaseFramerateWithDistance()) {
dyn.beginFrame(); dyn.beginFrame();
continue; continue;
} }
if (shouldTick(dyn.getWorldPosition(), lookX, lookY, lookZ, cX, cY, cZ)) if (shouldTick(dyn.getWorldPosition(), lookX, lookY, lookZ, cX, cY, cZ))
dyn.beginFrame(); dyn.beginFrame();
} }
} }
} }
public void render(RenderType layer, Matrix4f viewProjection, double camX, double camY, double camZ) { public void render(RenderType layer, Matrix4f viewProjection, double camX, double camY, double camZ) {
render(layer, viewProjection, camX, camY, camZ, null); render(layer, viewProjection, camX, camY, camZ, null);
} }
public void render(RenderType layer, Matrix4f viewProjection, double camX, double camY, double 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()) { for (RenderMaterial<P, ?> material : materials.values()) {
if (material.canRenderInLayer(layer)) if (material.canRenderInLayer(layer))
material.render(layer, viewProjection, camX, camY, camZ, callback); material.render(layer, viewProjection, camX, camY, camZ, callback);
} }
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <M extends InstancedModel<?>> RenderMaterial<P, M> getMaterial(MaterialType<M> materialType) { public <M extends InstancedModel<?>> RenderMaterial<P, M> getMaterial(MaterialType<M> materialType) {
return (RenderMaterial<P, M>) materials.get(materialType); return (RenderMaterial<P, M>) materials.get(materialType);
} }
public RenderMaterial<P, InstancedModel<ModelData>> getTransformMaterial() { public RenderMaterial<P, InstancedModel<ModelData>> getTransformMaterial() {
return getMaterial(MaterialTypes.TRANSFORMED); return getMaterial(MaterialTypes.TRANSFORMED);
} }
public RenderMaterial<P, InstancedModel<OrientedData>> getOrientedMaterial() { public RenderMaterial<P, InstancedModel<OrientedData>> getOrientedMaterial() {
return getMaterial(MaterialTypes.ORIENTED); return getMaterial(MaterialTypes.ORIENTED);
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Nullable @Nullable
public <T extends TileEntity> TileEntityInstance<? super T> getInstance(T tile, boolean create) { public <T extends TileEntity> TileEntityInstance<? super T> getInstance(T tile, boolean create) {
if (!Backend.canUseInstancing()) return null; if (!Backend.canUseInstancing()) return null;
TileEntityInstance<?> instance = instances.get(tile); TileEntityInstance<?> instance = instances.get(tile);
if (instance != null) { if (instance != null) {
return (TileEntityInstance<? super T>) instance; return (TileEntityInstance<? super T>) instance;
} else if (create && canCreateInstance(tile)) { } else if (create && canCreateInstance(tile)) {
return createInternal(tile); return createInternal(tile);
} else { } else {
return null; return null;
} }
} }
public <T extends TileEntity> void onLightUpdate(T tile) { public <T extends TileEntity> void onLightUpdate(T tile) {
if (!Backend.canUseInstancing()) return; if (!Backend.canUseInstancing()) return;
if (tile instanceof IInstanceRendered) { if (tile instanceof IInstanceRendered) {
TileEntityInstance<? super T> instance = getInstance(tile, false); TileEntityInstance<? super T> instance = getInstance(tile, false);
if (instance != null) if (instance != null)
instance.updateLight(); instance.updateLight();
} }
} }
public <T extends TileEntity> void add(T tile) { public <T extends TileEntity> void add(T tile) {
if (!Backend.canUseInstancing()) return; if (!Backend.canUseInstancing()) return;
if (tile instanceof IInstanceRendered) { if (tile instanceof IInstanceRendered) {
addInternal(tile); addInternal(tile);
} }
} }
public <T extends TileEntity> void update(T tile) { public <T extends TileEntity> void update(T tile) {
if (!Backend.canUseInstancing()) return; if (!Backend.canUseInstancing()) return;
if (tile instanceof IInstanceRendered) { if (tile instanceof IInstanceRendered) {
TileEntityInstance<? super T> instance = getInstance(tile, false); TileEntityInstance<? super T> instance = getInstance(tile, false);
if (instance != null) { if (instance != null) {
if (instance.shouldReset()) { if (instance.shouldReset()) {
removeInternal(tile, instance); removeInternal(tile, instance);
createInternal(tile); createInternal(tile);
} else { } else {
instance.update(); instance.update();
} }
} }
} }
} }
public <T extends TileEntity> void remove(T tile) { public <T extends TileEntity> void remove(T tile) {
if (!Backend.canUseInstancing()) return; if (!Backend.canUseInstancing()) return;
if (tile instanceof IInstanceRendered) { if (tile instanceof IInstanceRendered) {
removeInternal(tile); removeInternal(tile);
} }
} }
public synchronized <T extends TileEntity> void queueAdd(T tile) { public synchronized <T extends TileEntity> void queueAdd(T tile) {
if (!Backend.canUseInstancing()) return; if (!Backend.canUseInstancing()) return;
queuedAdditions.add(tile); queuedAdditions.add(tile);
} }
protected synchronized void processQueuedAdditions() { protected synchronized void processQueuedAdditions() {
if (queuedAdditions.size() > 0) { if (queuedAdditions.size() > 0) {
queuedAdditions.forEach(this::addInternal); queuedAdditions.forEach(this::addInternal);
queuedAdditions.clear(); queuedAdditions.clear();
} }
} }
protected boolean shouldTick(BlockPos worldPos, float lookX, float lookY, float lookZ, int cX, int cY, int cZ) { protected boolean shouldTick(BlockPos worldPos, float lookX, float lookY, float lookZ, int cX, int cY, int cZ) {
int dX = worldPos.getX() - cX; int dX = worldPos.getX() - cX;
int dY = worldPos.getY() - cY; int dY = worldPos.getY() - cY;
int dZ = worldPos.getZ() - cZ; int dZ = worldPos.getZ() - cZ;
float dot = (dX + lookX * 2) * lookX + (dY + lookY * 2) * lookY + (dZ + lookZ * 2) * lookZ; float dot = (dX + lookX * 2) * lookX + (dY + lookY * 2) * lookY + (dZ + lookZ * 2) * lookZ;
if (dot < 0) return false; // is it more than 2 blocks behind the camera? if (dot < 0) return false; // is it more than 2 blocks behind the camera?
return (frame % getUpdateDivisor(dX, dY, dZ)) == 0; return (frame % getUpdateDivisor(dX, dY, dZ)) == 0;
} }
protected int getUpdateDivisor(int dX, int dY, int dZ) { protected int getUpdateDivisor(int dX, int dY, int dZ) {
int dSq = dX * dX + dY * dY + dZ * dZ; int dSq = dX * dX + dY * dY + dZ * dZ;
return (dSq / 1024) + 1; return (dSq / 1024) + 1;
} }
private void addInternal(TileEntity tile) { private void addInternal(TileEntity tile) {
getInstance(tile, true); getInstance(tile, true);
} }
private <T extends TileEntity> void removeInternal(T tile) { private <T extends TileEntity> void removeInternal(T tile) {
TileEntityInstance<? super T> instance = getInstance(tile, false); TileEntityInstance<? super T> instance = getInstance(tile, false);
if (instance != null) { if (instance != null) {
removeInternal(tile, instance); removeInternal(tile, instance);
} }
} }
private void removeInternal(TileEntity tile, TileEntityInstance<?> instance) { private void removeInternal(TileEntity tile, TileEntityInstance<?> instance) {
instance.remove(); instance.remove();
instances.remove(tile); instances.remove(tile);
dynamicInstances.remove(tile); dynamicInstances.remove(tile);
tickableInstances.remove(tile); tickableInstances.remove(tile);
} }
private <T extends TileEntity> TileEntityInstance<? super T> createInternal(T tile) { private <T extends TileEntity> TileEntityInstance<? super T> createInternal(T tile) {
TileEntityInstance<? super T> renderer = InstancedTileRenderRegistry.instance.create(this, tile); TileEntityInstance<? super T> renderer = InstancedTileRenderRegistry.instance.create(this, tile);
if (renderer != null) { if (renderer != null) {
renderer.updateLight(); renderer.updateLight();
instances.put(tile, renderer); instances.put(tile, renderer);
if (renderer instanceof IDynamicInstance) if (renderer instanceof IDynamicInstance)
dynamicInstances.put(tile, (IDynamicInstance) renderer); dynamicInstances.put(tile, (IDynamicInstance) renderer);
if (renderer instanceof ITickableInstance) if (renderer instanceof ITickableInstance)
tickableInstances.put(tile, ((ITickableInstance) renderer)); tickableInstances.put(tile, ((ITickableInstance) renderer));
} }
return renderer; return renderer;
} }
public void invalidate() { public void invalidate() {
for (RenderMaterial<?, ?> material : materials.values()) { for (RenderMaterial<?, ?> material : materials.values()) {
material.delete(); material.delete();
} }
instances.clear(); instances.clear();
dynamicInstances.clear(); dynamicInstances.clear();
tickableInstances.clear(); tickableInstances.clear();
} }
public boolean canCreateInstance(TileEntity tile) { public boolean canCreateInstance(TileEntity tile) {
if (tile.isRemoved()) return false; if (tile.isRemoved()) return false;
World world = tile.getWorld(); World world = tile.getWorld();
if (world == null) return false; if (world == null) return false;
if (world.isAirBlock(tile.getPos())) return false; if (world.isAirBlock(tile.getPos())) return false;
if (world == Minecraft.getInstance().world) { if (world == Minecraft.getInstance().world) {
BlockPos pos = tile.getPos(); BlockPos pos = tile.getPos();
IBlockReader existingChunk = world.getExistingChunk(pos.getX() >> 4, pos.getZ() >> 4); IBlockReader existingChunk = world.getExistingChunk(pos.getX() >> 4, pos.getZ() >> 4);
return existingChunk != null; return existingChunk != null;
} }
return world instanceof IFlywheelWorld && ((IFlywheelWorld) world).supportsFlywheel(); return world instanceof IFlywheelWorld && ((IFlywheelWorld) world).supportsFlywheel();
} }
} }

View file

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

View file

@ -36,102 +36,103 @@ import net.minecraft.util.math.vector.Matrix4f;
public class RenderMaterial<P extends BasicProgram, MODEL extends InstancedModel<?>> { public class RenderMaterial<P extends BasicProgram, MODEL extends InstancedModel<?>> {
protected final InstancedTileRenderer<?> renderer; protected final InstancedTileRenderer<?> renderer;
protected final Map<Compartment<?>, Cache<Object, MODEL>> models; protected final Map<Compartment<?>, Cache<Object, MODEL>> models;
protected final ModelFactory<MODEL> factory; protected final ModelFactory<MODEL> factory;
protected final ProgramSpec<P> programSpec; protected final ProgramSpec<P> programSpec;
protected final Predicate<RenderType> layerPredicate; protected final Predicate<RenderType> layerPredicate;
/** /**
* Creates a material that renders in the default layer (CUTOUT_MIPPED) * Creates a material that renders in the default layer (CUTOUT_MIPPED)
*/ */
public RenderMaterial(InstancedTileRenderer<?> renderer, ProgramSpec<P> programSpec, ModelFactory<MODEL> factory) { public RenderMaterial(InstancedTileRenderer<?> renderer, ProgramSpec<P> programSpec, ModelFactory<MODEL> factory) {
this(renderer, programSpec, factory, type -> type == RenderType.getCutoutMipped()); this(renderer, programSpec, factory, type -> type == RenderType.getCutoutMipped());
} }
public RenderMaterial(InstancedTileRenderer<?> renderer, 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.renderer = renderer;
this.models = new HashMap<>(); this.models = new HashMap<>();
this.factory = factory; this.factory = factory;
this.programSpec = programSpec; this.programSpec = programSpec;
this.layerPredicate = layerPredicate; this.layerPredicate = layerPredicate;
registerCompartment(Compartment.PARTIAL); registerCompartment(Compartment.PARTIAL);
registerCompartment(Compartment.DIRECTIONAL_PARTIAL); registerCompartment(Compartment.DIRECTIONAL_PARTIAL);
registerCompartment(Compartment.GENERIC_TILE); registerCompartment(Compartment.GENERIC_TILE);
} }
public boolean canRenderInLayer(RenderType layer) { public boolean canRenderInLayer(RenderType layer) {
return layerPredicate.test(layer); return layerPredicate.test(layer);
} }
public void render(RenderType layer, Matrix4f projection, double camX, double camY, double camZ) { public void render(RenderType layer, Matrix4f projection, double camX, double camY, double camZ) {
render(layer, projection, camX, camY, camZ, null); render(layer, projection, camX, camY, camZ, null);
} }
public void render(RenderType layer, Matrix4f viewProjection, double camX, double camY, double 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); P program = Backend.getProgram(programSpec);
program.bind(viewProjection, camX, camY, camZ, FastRenderDispatcher.getDebugMode()); program.bind(viewProjection, camX, camY, camZ, FastRenderDispatcher.getDebugMode());
if (setup != null) setup.call(program); if (setup != null) setup.call(program);
makeRenderCalls(); makeRenderCalls();
teardown(); teardown();
} }
public void teardown() {} public void teardown() {
}
public void delete() { public void delete() {
runOnAll(InstancedModel::delete); runOnAll(InstancedModel::delete);
models.values().forEach(Cache::invalidateAll); models.values().forEach(Cache::invalidateAll);
} }
protected void makeRenderCalls() { protected void makeRenderCalls() {
runOnAll(InstancedModel::render); runOnAll(InstancedModel::render);
} }
public void runOnAll(Consumer<MODEL> f) { public void runOnAll(Consumer<MODEL> f) {
for (Cache<Object, MODEL> cache : models.values()) { for (Cache<Object, MODEL> cache : models.values()) {
for (MODEL model : cache.asMap().values()) { for (MODEL model : cache.asMap().values()) {
f.accept(model); f.accept(model);
} }
} }
} }
public void registerCompartment(Compartment<?> instance) { public void registerCompartment(Compartment<?> instance) {
models.put(instance, CacheBuilder.newBuilder().build()); models.put(instance, CacheBuilder.newBuilder().build());
} }
public MODEL getModel(PartialModel partial, BlockState referenceState) { public MODEL getModel(PartialModel partial, BlockState referenceState) {
return get(PARTIAL, partial, () -> buildModel(partial.get(), referenceState)); return get(PARTIAL, partial, () -> buildModel(partial.get(), referenceState));
} }
public MODEL getModel(PartialModel partial, BlockState referenceState, Direction dir) { public MODEL getModel(PartialModel partial, BlockState referenceState, Direction dir) {
return getModel(partial, referenceState, dir, rotateToFace(dir)); return getModel(partial, referenceState, dir, rotateToFace(dir));
} }
public MODEL getModel(PartialModel partial, BlockState referenceState, Direction dir, Supplier<MatrixStack> modelTransform) { public MODEL getModel(PartialModel partial, BlockState referenceState, Direction dir, Supplier<MatrixStack> modelTransform) {
return get(Compartment.DIRECTIONAL_PARTIAL, Pair.of(dir, partial), return get(Compartment.DIRECTIONAL_PARTIAL, Pair.of(dir, partial),
() -> buildModel(partial.get(), referenceState, modelTransform.get())); () -> buildModel(partial.get(), referenceState, modelTransform.get()));
} }
public MODEL getModel(BlockState toRender) { public MODEL getModel(BlockState toRender) {
return get(Compartment.GENERIC_TILE, toRender, () -> buildModel(toRender)); return get(Compartment.GENERIC_TILE, toRender, () -> buildModel(toRender));
} }
public <T> MODEL get(Compartment<T> compartment, T key, Supplier<MODEL> supplier) { public <T> MODEL get(Compartment<T> compartment, T key, Supplier<MODEL> supplier) {
Cache<Object, MODEL> compartmentCache = models.get(compartment); Cache<Object, MODEL> compartmentCache = models.get(compartment);
try { try {
return compartmentCache.get(key, supplier::get); return compartmentCache.get(key, supplier::get);
} catch (ExecutionException e) { } catch (ExecutionException e) {
e.printStackTrace(); e.printStackTrace();
return null; return null;
} }
} }
private MODEL buildModel(BlockState renderedState) { private MODEL buildModel(BlockState renderedState) {
BlockRendererDispatcher dispatcher = Minecraft.getInstance().getBlockRendererDispatcher(); BlockRendererDispatcher dispatcher = Minecraft.getInstance().getBlockRendererDispatcher();
return buildModel(dispatcher.getModelForState(renderedState), renderedState); return buildModel(dispatcher.getModelForState(renderedState), renderedState);
} }
private MODEL buildModel(IBakedModel model, BlockState renderedState) { private MODEL buildModel(IBakedModel model, BlockState renderedState) {
return buildModel(model, renderedState, new MatrixStack()); return buildModel(model, renderedState, new MatrixStack());

View file

@ -32,94 +32,96 @@ import net.minecraft.world.World;
*/ */
public abstract class TileEntityInstance<T extends TileEntity> implements IInstance { public abstract class TileEntityInstance<T extends TileEntity> implements IInstance {
protected final InstancedTileRenderer<?> renderer; protected final InstancedTileRenderer<?> renderer;
protected final T tile; protected final T tile;
protected final World world; protected final World world;
protected final BlockPos pos; protected final BlockPos pos;
protected final BlockPos instancePos; protected final BlockPos instancePos;
protected final BlockState blockState; protected final BlockState blockState;
public TileEntityInstance(InstancedTileRenderer<?> renderer, T tile) { public TileEntityInstance(InstancedTileRenderer<?> renderer, T tile) {
this.renderer = renderer; this.renderer = renderer;
this.tile = tile; this.tile = tile;
this.world = tile.getWorld(); this.world = tile.getWorld();
this.pos = tile.getPos(); this.pos = tile.getPos();
this.blockState = tile.getBlockState(); this.blockState = tile.getBlockState();
this.instancePos = pos.subtract(renderer.getOriginCoordinate()); this.instancePos = pos.subtract(renderer.getOriginCoordinate());
} }
/** /**
* Update instance data here. Good for when data doesn't change very often and when animations are GPU based. * Update instance data here. Good for when data doesn't change very often and when animations are GPU based.
* Don't query lighting data here, that's handled separately in {@link #updateLight()}. * Don't query lighting data here, that's handled separately in {@link #updateLight()}.
* *
* <br><br> If your animations are complex or more CPU driven, see {@link IDynamicInstance} or {@link ITickableInstance}. * <br><br> If your animations are complex or more CPU driven, see {@link IDynamicInstance} or {@link ITickableInstance}.
*/ */
protected void update() { } protected void update() {
}
/** /**
* Called after construction and when a light update occurs in the world. * Called after construction and when a light update occurs in the world.
* *
* <br> If your model needs it, update light here. * <br> If your model needs it, update light here.
*/ */
public void updateLight() { } public void updateLight() {
}
/** /**
* Free any acquired resources. * Free any acquired resources.
* *
* <br> eg. call {@link InstanceKey#delete()}. * <br> eg. call {@link InstanceKey#delete()}.
*/ */
public abstract void remove(); public abstract void remove();
/** /**
* Just before {@link #update()} would be called, <code>shouldReset()</code> is checked. * Just before {@link #update()} would be called, <code>shouldReset()</code> is checked.
* If this function returns <code>true</code>, then this instance will be {@link #remove}d, * If this function returns <code>true</code>, then this instance will be {@link #remove}d,
* and another instance will be constructed to replace it. This allows for more sane resource * and another instance will be constructed to replace it. This allows for more sane resource
* acquisition compared to trying to update everything within the lifetime of an instance. * acquisition compared to trying to update everything within the lifetime of an instance.
* *
* @return <code>true</code> if this instance should be discarded and refreshed. * @return <code>true</code> if this instance should be discarded and refreshed.
*/ */
public boolean shouldReset() { public boolean shouldReset() {
return tile.getBlockState() != blockState; return tile.getBlockState() != blockState;
} }
/** /**
* In order to accommodate for floating point precision errors at high coordinates, * In order to accommodate for floating point precision errors at high coordinates,
* {@link InstancedTileRenderer}s are allowed to arbitrarily adjust the origin, and * {@link InstancedTileRenderer}s are allowed to arbitrarily adjust the origin, and
* shift the world matrix provided as a shader uniform accordingly. * shift the world matrix provided as a shader uniform accordingly.
* *
* @return The {@link BlockPos} at which the {@link TileEntity} this instance * @return The {@link BlockPos} at which the {@link TileEntity} this instance
* represents should be rendered at to appear in the correct location. * represents should be rendered at to appear in the correct location.
*/ */
public BlockPos getInstancePosition() { public BlockPos getInstancePosition() {
return instancePos; return instancePos;
} }
@Override @Override
public BlockPos getWorldPosition() { public BlockPos getWorldPosition() {
return pos; return pos;
} }
protected void relight(BlockPos pos, IFlatLight<?>... models) { protected void relight(BlockPos pos, IFlatLight<?>... models) {
relight(world.getLightLevel(LightType.BLOCK, pos), world.getLightLevel(LightType.SKY, pos), models); relight(world.getLightLevel(LightType.BLOCK, pos), world.getLightLevel(LightType.SKY, pos), models);
} }
protected <L extends IFlatLight<?>> void relight(BlockPos pos, Stream<L> models) { protected <L extends IFlatLight<?>> void relight(BlockPos pos, Stream<L> models) {
relight(world.getLightLevel(LightType.BLOCK, pos), world.getLightLevel(LightType.SKY, pos), models); relight(world.getLightLevel(LightType.BLOCK, pos), world.getLightLevel(LightType.SKY, pos), models);
} }
protected void relight(int block, int sky, IFlatLight<?>... models) { protected void relight(int block, int sky, IFlatLight<?>... models) {
relight(block, sky, Arrays.stream(models)); relight(block, sky, Arrays.stream(models));
} }
protected <L extends IFlatLight<?>> void relight(int block, int sky, Stream<L> models) { protected <L extends IFlatLight<?>> void relight(int block, int sky, Stream<L> models) {
models.forEach(model -> model.setBlockLight(block).setSkyLight(sky)); models.forEach(model -> model.setBlockLight(block).setSkyLight(sky));
} }
protected RenderMaterial<?, InstancedModel<ModelData>> getTransformMaterial() { protected RenderMaterial<?, InstancedModel<ModelData>> getTransformMaterial() {
return renderer.getTransformMaterial(); return renderer.getTransformMaterial();
} }
protected RenderMaterial<?, InstancedModel<OrientedData>> getOrientedMaterial() { protected RenderMaterial<?, InstancedModel<OrientedData>> getOrientedMaterial() {
return renderer.getOrientedMaterial(); return renderer.getOrientedMaterial();
} }
} }

View file

@ -8,7 +8,7 @@ import java.util.List;
import com.simibubi.create.foundation.render.backend.instancing.InstanceData; import com.simibubi.create.foundation.render.backend.instancing.InstanceData;
import com.simibubi.create.foundation.render.backend.instancing.InstancedModel; import com.simibubi.create.foundation.render.backend.instancing.InstancedModel;
public class InstanceGroup<D extends InstanceData> extends AbstractCollection<D> { public class InstanceGroup<D extends InstanceData> extends AbstractCollection<D> {
final InstancedModel<D> model; final InstancedModel<D> model;
final List<D> backing; final List<D> backing;
@ -30,7 +30,6 @@ public class InstanceGroup<D extends InstanceData> extends AbstractCollection<D>
} }
/** /**
*
* @param count * @param count
* @return True if the number of elements changed. * @return True if the number of elements changed.
*/ */

View file

@ -2,5 +2,5 @@ package com.simibubi.create.foundation.render.backend.light;
@FunctionalInterface @FunctionalInterface
public interface CoordinateConsumer { public interface CoordinateConsumer {
void consume(int x, int y, int z); void consume(int x, int y, int z);
} }

View file

@ -5,12 +5,12 @@ import com.simibubi.create.content.contraptions.components.structureMovement.Con
// so other contraptions don't crash before they have a lighter // so other contraptions don't crash before they have a lighter
public class EmptyLighter extends ContraptionLighter<Contraption> { public class EmptyLighter extends ContraptionLighter<Contraption> {
public EmptyLighter(Contraption contraption) { public EmptyLighter(Contraption contraption) {
super(contraption); super(contraption);
} }
@Override @Override
public GridAlignedBB getContraptionBounds() { public GridAlignedBB getContraptionBounds() {
return new GridAlignedBB(0, 0, 0, 1, 1, 1); return new GridAlignedBB(0, 0, 0, 1, 1, 1);
} }
} }

View file

@ -11,313 +11,313 @@ import net.minecraft.util.math.SectionPos;
import net.minecraft.util.math.vector.Vector3i; import net.minecraft.util.math.vector.Vector3i;
public class GridAlignedBB { public class GridAlignedBB {
public int minX; public int minX;
public int minY; public int minY;
public int minZ; public int minZ;
public int maxX; public int maxX;
public int maxY; public int maxY;
public int maxZ; public int maxZ;
public GridAlignedBB(int minX, int minY, int minZ, int maxX, int maxY, int maxZ) { public GridAlignedBB(int minX, int minY, int minZ, int maxX, int maxY, int maxZ) {
this.minX = minX; this.minX = minX;
this.minY = minY; this.minY = minY;
this.minZ = minZ; this.minZ = minZ;
this.maxX = maxX; this.maxX = maxX;
this.maxY = maxY; this.maxY = maxY;
this.maxZ = maxZ; this.maxZ = maxZ;
} }
public static GridAlignedBB ofRadius(int radius) { public static GridAlignedBB ofRadius(int radius) {
return new GridAlignedBB(-radius, -radius, -radius, radius + 1, radius + 1, radius + 1); return new GridAlignedBB(-radius, -radius, -radius, radius + 1, radius + 1, radius + 1);
} }
public static GridAlignedBB copy(GridAlignedBB bb) { public static GridAlignedBB copy(GridAlignedBB bb) {
return new GridAlignedBB(bb.minX, bb.minY, bb.minZ, bb.maxX, bb.maxY, bb.maxZ); return new GridAlignedBB(bb.minX, bb.minY, bb.minZ, bb.maxX, bb.maxY, bb.maxZ);
} }
public static GridAlignedBB from(AxisAlignedBB aabb) { public static GridAlignedBB from(AxisAlignedBB aabb) {
int minX = (int) Math.floor(aabb.minX); int minX = (int) Math.floor(aabb.minX);
int minY = (int) Math.floor(aabb.minY); int minY = (int) Math.floor(aabb.minY);
int minZ = (int) Math.floor(aabb.minZ); int minZ = (int) Math.floor(aabb.minZ);
int maxX = (int) Math.ceil(aabb.maxX); int maxX = (int) Math.ceil(aabb.maxX);
int maxY = (int) Math.ceil(aabb.maxY); int maxY = (int) Math.ceil(aabb.maxY);
int maxZ = (int) Math.ceil(aabb.maxZ); int maxZ = (int) Math.ceil(aabb.maxZ);
return new GridAlignedBB(minX, minY, minZ, maxX, maxY, maxZ); return new GridAlignedBB(minX, minY, minZ, maxX, maxY, maxZ);
} }
public static GridAlignedBB from(SectionPos pos) { public static GridAlignedBB from(SectionPos pos) {
return new GridAlignedBB(pos.getWorldStartX(), return new GridAlignedBB(pos.getWorldStartX(),
pos.getWorldStartY(), pos.getWorldStartY(),
pos.getWorldStartZ(), pos.getWorldStartZ(),
pos.getWorldEndX() + 1, pos.getWorldEndX() + 1,
pos.getWorldEndY() + 1, pos.getWorldEndY() + 1,
pos.getWorldEndZ() + 1); pos.getWorldEndZ() + 1);
} }
public static GridAlignedBB from(BlockPos start, BlockPos end) { public static GridAlignedBB from(BlockPos start, BlockPos end) {
return new GridAlignedBB(start.getX(), return new GridAlignedBB(start.getX(),
start.getY(), start.getY(),
start.getZ(), start.getZ(),
end.getX() + 1, end.getX() + 1,
end.getY() + 1, end.getY() + 1,
end.getZ() + 1); end.getZ() + 1);
} }
public static GridAlignedBB from(int sectionX, int sectionZ) { public static GridAlignedBB from(int sectionX, int sectionZ) {
int startX = sectionX << 4; int startX = sectionX << 4;
int startZ = sectionZ << 4; int startZ = sectionZ << 4;
return new GridAlignedBB(startX, return new GridAlignedBB(startX,
0, 0,
startZ, startZ,
startX + 16, startX + 16,
256, 256,
startZ + 16); startZ + 16);
} }
public static AxisAlignedBB toAABB(GridAlignedBB bb) { public static AxisAlignedBB toAABB(GridAlignedBB bb) {
return new AxisAlignedBB(bb.minX, bb.minY, bb.minZ, bb.maxX, bb.maxY, bb.maxZ); return new AxisAlignedBB(bb.minX, bb.minY, bb.minZ, bb.maxX, bb.maxY, bb.maxZ);
} }
public GridAlignedBB copy() { public GridAlignedBB copy() {
return copy(this); return copy(this);
} }
public boolean sameAs(GridAlignedBB other) { public boolean sameAs(GridAlignedBB other) {
return minX == other.minX && return minX == other.minX &&
minY == other.minY && minY == other.minY &&
minZ == other.minZ && minZ == other.minZ &&
maxX == other.maxX && maxX == other.maxX &&
maxY == other.maxY && maxY == other.maxY &&
maxZ == other.maxZ; maxZ == other.maxZ;
} }
public void fixMinMax() { public void fixMinMax() {
int minX = Math.min(this.minX, this.maxX); int minX = Math.min(this.minX, this.maxX);
int minY = Math.min(this.minY, this.maxY); int minY = Math.min(this.minY, this.maxY);
int minZ = Math.min(this.minZ, this.maxZ); int minZ = Math.min(this.minZ, this.maxZ);
int maxX = Math.max(this.minX, this.maxX); int maxX = Math.max(this.minX, this.maxX);
int maxY = Math.max(this.minY, this.maxY); int maxY = Math.max(this.minY, this.maxY);
int maxZ = Math.max(this.minZ, this.maxZ); int maxZ = Math.max(this.minZ, this.maxZ);
this.minX = minX; this.minX = minX;
this.minY = minY; this.minY = minY;
this.minZ = minZ; this.minZ = minZ;
this.maxX = maxX; this.maxX = maxX;
this.maxY = maxY; this.maxY = maxY;
this.maxZ = maxZ; this.maxZ = maxZ;
} }
public int sizeX() { public int sizeX() {
return maxX - minX; return maxX - minX;
} }
public int sizeY() { public int sizeY() {
return maxY - minY; return maxY - minY;
} }
public int sizeZ() { public int sizeZ() {
return maxZ - minZ; return maxZ - minZ;
} }
public int volume() { public int volume() {
return sizeX() * sizeY() * sizeZ(); return sizeX() * sizeY() * sizeZ();
} }
public boolean empty() { public boolean empty() {
// if any dimension has side length 0 this box contains no volume // if any dimension has side length 0 this box contains no volume
return minX == maxX || return minX == maxX ||
minY == maxY || minY == maxY ||
minZ == maxZ; minZ == maxZ;
} }
public void translate(Vector3i by) { public void translate(Vector3i by) {
translate(by.getX(), by.getY(), by.getZ()); translate(by.getX(), by.getY(), by.getZ());
} }
public void translate(int x, int y, int z) { public void translate(int x, int y, int z) {
minX += x; minX += x;
maxX += x; maxX += x;
minY += y; minY += y;
maxY += y; maxY += y;
minZ += z; minZ += z;
maxZ += z; maxZ += z;
} }
public void mirrorAbout(Direction.Axis axis) { public void mirrorAbout(Direction.Axis axis) {
Vector3i axisVec = Direction.getFacingFromAxis(Direction.AxisDirection.POSITIVE, axis).getDirectionVec(); Vector3i axisVec = Direction.getFacingFromAxis(Direction.AxisDirection.POSITIVE, axis).getDirectionVec();
int flipX = axisVec.getX() - 1; int flipX = axisVec.getX() - 1;
int flipY = axisVec.getY() - 1; int flipY = axisVec.getY() - 1;
int flipZ = axisVec.getZ() - 1; int flipZ = axisVec.getZ() - 1;
int maxX = this.maxX * flipX; int maxX = this.maxX * flipX;
int maxY = this.maxY * flipY; int maxY = this.maxY * flipY;
int maxZ = this.maxZ * flipZ; int maxZ = this.maxZ * flipZ;
this.maxX = this.minX * flipX; this.maxX = this.minX * flipX;
this.maxY = this.minY * flipY; this.maxY = this.minY * flipY;
this.maxZ = this.minZ * flipZ; this.maxZ = this.minZ * flipZ;
this.minX = maxX; this.minX = maxX;
this.minY = maxY; this.minY = maxY;
this.minZ = maxZ; this.minZ = maxZ;
} }
/** /**
* Grow this bounding box to have power of 2 side length, scaling from the center. * Grow this bounding box to have power of 2 side length, scaling from the center.
*/ */
public void nextPowerOf2Centered() { public void nextPowerOf2Centered() {
int sizeX = sizeX(); int sizeX = sizeX();
int sizeY = sizeY(); int sizeY = sizeY();
int sizeZ = sizeZ(); int sizeZ = sizeZ();
int newSizeX = RenderUtil.nextPowerOf2(sizeX); int newSizeX = RenderUtil.nextPowerOf2(sizeX);
int newSizeY = RenderUtil.nextPowerOf2(sizeY); int newSizeY = RenderUtil.nextPowerOf2(sizeY);
int newSizeZ = RenderUtil.nextPowerOf2(sizeZ); int newSizeZ = RenderUtil.nextPowerOf2(sizeZ);
int diffX = newSizeX - sizeX; int diffX = newSizeX - sizeX;
int diffY = newSizeY - sizeY; int diffY = newSizeY - sizeY;
int diffZ = newSizeZ - sizeZ; int diffZ = newSizeZ - sizeZ;
minX -= diffX / 2; // floor division for the minimums minX -= diffX / 2; // floor division for the minimums
minY -= diffY / 2; minY -= diffY / 2;
minZ -= diffZ / 2; minZ -= diffZ / 2;
maxX += (diffX + 1) / 2; // ceiling divison for the maximums maxX += (diffX + 1) / 2; // ceiling divison for the maximums
maxY += (diffY + 1) / 2; maxY += (diffY + 1) / 2;
maxZ += (diffZ + 1) / 2; maxZ += (diffZ + 1) / 2;
} }
/** /**
* Grow this bounding box to have power of 2 side lengths, scaling from the minimum coords. * Grow this bounding box to have power of 2 side lengths, scaling from the minimum coords.
*/ */
public void nextPowerOf2() { public void nextPowerOf2() {
int sizeX = RenderUtil.nextPowerOf2(sizeX()); int sizeX = RenderUtil.nextPowerOf2(sizeX());
int sizeY = RenderUtil.nextPowerOf2(sizeY()); int sizeY = RenderUtil.nextPowerOf2(sizeY());
int sizeZ = RenderUtil.nextPowerOf2(sizeZ()); int sizeZ = RenderUtil.nextPowerOf2(sizeZ());
this.maxX = this.minX + sizeX; this.maxX = this.minX + sizeX;
this.maxY = this.minY + sizeY; this.maxY = this.minY + sizeY;
this.maxZ = this.minZ + sizeZ; this.maxZ = this.minZ + sizeZ;
} }
public boolean hasPowerOf2Sides() { public boolean hasPowerOf2Sides() {
// this is only true if all individual side lengths are powers of 2 // this is only true if all individual side lengths are powers of 2
return isPowerOf2(volume()); return isPowerOf2(volume());
} }
public void grow(int s) { public void grow(int s) {
this.grow(s, s, s); this.grow(s, s, s);
} }
public void grow(int x, int y, int z) { public void grow(int x, int y, int z) {
minX -= x; minX -= x;
minY -= y; minY -= y;
minZ -= z; minZ -= z;
maxX += x; maxX += x;
maxY += y; maxY += y;
maxZ += z; maxZ += z;
} }
public GridAlignedBB intersect(GridAlignedBB other) { public GridAlignedBB intersect(GridAlignedBB other) {
int minX = Math.max(this.minX, other.minX); int minX = Math.max(this.minX, other.minX);
int minY = Math.max(this.minY, other.minY); int minY = Math.max(this.minY, other.minY);
int minZ = Math.max(this.minZ, other.minZ); int minZ = Math.max(this.minZ, other.minZ);
int maxX = Math.min(this.maxX, other.maxX); int maxX = Math.min(this.maxX, other.maxX);
int maxY = Math.min(this.maxY, other.maxY); int maxY = Math.min(this.maxY, other.maxY);
int maxZ = Math.min(this.maxZ, other.maxZ); int maxZ = Math.min(this.maxZ, other.maxZ);
return new GridAlignedBB(minX, minY, minZ, maxX, maxY, maxZ); return new GridAlignedBB(minX, minY, minZ, maxX, maxY, maxZ);
} }
public void intersectAssign(GridAlignedBB other) { public void intersectAssign(GridAlignedBB other) {
this.minX = Math.max(this.minX, other.minX); this.minX = Math.max(this.minX, other.minX);
this.minY = Math.max(this.minY, other.minY); this.minY = Math.max(this.minY, other.minY);
this.minZ = Math.max(this.minZ, other.minZ); this.minZ = Math.max(this.minZ, other.minZ);
this.maxX = Math.min(this.maxX, other.maxX); this.maxX = Math.min(this.maxX, other.maxX);
this.maxY = Math.min(this.maxY, other.maxY); this.maxY = Math.min(this.maxY, other.maxY);
this.maxZ = Math.min(this.maxZ, other.maxZ); this.maxZ = Math.min(this.maxZ, other.maxZ);
} }
public GridAlignedBB union(GridAlignedBB other) { public GridAlignedBB union(GridAlignedBB other) {
int minX = Math.min(this.minX, other.minX); int minX = Math.min(this.minX, other.minX);
int minY = Math.min(this.minY, other.minY); int minY = Math.min(this.minY, other.minY);
int minZ = Math.min(this.minZ, other.minZ); int minZ = Math.min(this.minZ, other.minZ);
int maxX = Math.max(this.maxX, other.maxX); int maxX = Math.max(this.maxX, other.maxX);
int maxY = Math.max(this.maxY, other.maxY); int maxY = Math.max(this.maxY, other.maxY);
int maxZ = Math.max(this.maxZ, other.maxZ); int maxZ = Math.max(this.maxZ, other.maxZ);
return new GridAlignedBB(minX, minY, minZ, maxX, maxY, maxZ); return new GridAlignedBB(minX, minY, minZ, maxX, maxY, maxZ);
} }
public void unionAssign(GridAlignedBB other) { public void unionAssign(GridAlignedBB other) {
this.minX = Math.min(this.minX, other.minX); this.minX = Math.min(this.minX, other.minX);
this.minY = Math.min(this.minY, other.minY); this.minY = Math.min(this.minY, other.minY);
this.minZ = Math.min(this.minZ, other.minZ); this.minZ = Math.min(this.minZ, other.minZ);
this.maxX = Math.max(this.maxX, other.maxX); this.maxX = Math.max(this.maxX, other.maxX);
this.maxY = Math.max(this.maxY, other.maxY); this.maxY = Math.max(this.maxY, other.maxY);
this.maxZ = Math.max(this.maxZ, other.maxZ); this.maxZ = Math.max(this.maxZ, other.maxZ);
} }
public void unionAssign(AxisAlignedBB other) { public void unionAssign(AxisAlignedBB other) {
this.minX = Math.min(this.minX, (int) Math.floor(other.minX)); this.minX = Math.min(this.minX, (int) Math.floor(other.minX));
this.minY = Math.min(this.minY, (int) Math.floor(other.minY)); this.minY = Math.min(this.minY, (int) Math.floor(other.minY));
this.minZ = Math.min(this.minZ, (int) Math.floor(other.minZ)); this.minZ = Math.min(this.minZ, (int) Math.floor(other.minZ));
this.maxX = Math.max(this.maxX, (int) Math.ceil(other.maxX)); this.maxX = Math.max(this.maxX, (int) Math.ceil(other.maxX));
this.maxY = Math.max(this.maxY, (int) Math.ceil(other.maxY)); this.maxY = Math.max(this.maxY, (int) Math.ceil(other.maxY));
this.maxZ = Math.max(this.maxZ, (int) Math.ceil(other.maxZ)); this.maxZ = Math.max(this.maxZ, (int) Math.ceil(other.maxZ));
} }
public boolean intersects(GridAlignedBB other) { public boolean intersects(GridAlignedBB other) {
return this.intersects(other.minX, other.minY, other.minZ, other.maxX, other.maxY, other.maxZ); return this.intersects(other.minX, other.minY, other.minZ, other.maxX, other.maxY, other.maxZ);
} }
public boolean contains(GridAlignedBB other) { public boolean contains(GridAlignedBB other) {
return other.minX >= this.minX && return other.minX >= this.minX &&
other.maxX <= this.maxX && other.maxX <= this.maxX &&
other.minY >= this.minY && other.minY >= this.minY &&
other.maxY <= this.maxY && other.maxY <= this.maxY &&
other.minZ >= this.minZ && other.minZ >= this.minZ &&
other.maxZ <= this.maxZ; other.maxZ <= this.maxZ;
} }
public boolean isContainedBy(GridAlignedBB other) { public boolean isContainedBy(GridAlignedBB other) {
return other.contains(this); return other.contains(this);
} }
public boolean intersects(int minX, int minY, int minZ, int maxX, int maxY, int 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; return this.minX < maxX && this.maxX > minX && this.minY < maxY && this.maxY > minY && this.minZ < maxZ && this.maxZ > minZ;
} }
public void forEachContained(CoordinateConsumer func) { public void forEachContained(CoordinateConsumer func) {
if (empty()) return; if (empty()) return;
for (int x = minX; x < maxX; x++) { 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 y = Math.max(minY, 0); y < Math.min(maxY, 255); y++) { // clamp to world height limits
for (int z = minZ; z < maxZ; z++) { for (int z = minZ; z < maxZ; z++) {
func.consume(x, y, z); func.consume(x, y, z);
} }
} }
} }
} }
public AxisAlignedBB toAABB() { public AxisAlignedBB toAABB() {
return toAABB(this); return toAABB(this);
} }
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (this == o) return true; if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false; if (o == null || getClass() != o.getClass()) return false;
GridAlignedBB that = (GridAlignedBB) o; GridAlignedBB that = (GridAlignedBB) o;
return this.sameAs(that); return this.sameAs(that);
} }
@Override @Override
public int hashCode() { public int hashCode() {
int result = minX; int result = minX;
result = 31 * result + minY; result = 31 * result + minY;
result = 31 * result + minZ; result = 31 * result + minZ;
result = 31 * result + maxX; result = 31 * result + maxX;
result = 31 * result + maxY; result = 31 * result + maxY;
result = 31 * result + maxZ; result = 31 * result + maxZ;
return result; return result;
} }
} }

View file

@ -49,11 +49,11 @@ public class LightUpdater {
/** /**
* Add a listener associated with the given {@link BlockPos}. * Add a listener associated with the given {@link BlockPos}.
* * <p>
* When a light update occurs in the chunk the position is contained in, * When a light update occurs in the chunk the position is contained in,
* {@link LightUpdateListener#onLightUpdate} will be called. * {@link LightUpdateListener#onLightUpdate} will be called.
* *
* @param pos The position in the world that the listener cares about. * @param pos The position in the world that the listener cares about.
* @param listener The object that wants to receive light update notifications. * @param listener The object that wants to receive light update notifications.
*/ */
public void startListening(BlockPos pos, LightUpdateListener listener) { public void startListening(BlockPos pos, LightUpdateListener listener) {
@ -71,11 +71,11 @@ public class LightUpdater {
/** /**
* Add a listener associated with the given {@link GridAlignedBB}. * Add a listener associated with the given {@link GridAlignedBB}.
* * <p>
* When a light update occurs in any chunk spanning the given volume, * When a light update occurs in any chunk spanning the given volume,
* {@link LightUpdateListener#onLightUpdate} will be called. * {@link LightUpdateListener#onLightUpdate} will be called.
* *
* @param volume The volume in the world that the listener cares about. * @param volume The volume in the world that the listener cares about.
* @param listener The object that wants to receive light update notifications. * @param listener The object that wants to receive light update notifications.
*/ */
public void startListening(GridAlignedBB volume, LightUpdateListener listener) { public void startListening(GridAlignedBB volume, LightUpdateListener listener) {
@ -106,8 +106,8 @@ public class LightUpdater {
/** /**
* Dispatch light updates to all registered {@link LightUpdateListener}s. * Dispatch light updates to all registered {@link LightUpdateListener}s.
* *
* @param world The world in which light was updated. * @param world The world in which light was updated.
* @param type The type of light that changed. * @param type The type of light that changed.
* @param sectionPos A long representing the section position where light changed. * @param sectionPos A long representing the section position where light changed.
*/ */
public void onLightUpdate(IBlockDisplayReader world, LightType type, long sectionPos) { public void onLightUpdate(IBlockDisplayReader world, LightType type, long sectionPos) {

View file

@ -19,291 +19,294 @@ import net.minecraft.world.LightType;
public class LightVolume { public class LightVolume {
private GridAlignedBB sampleVolume; private GridAlignedBB sampleVolume;
private GridAlignedBB textureVolume; private GridAlignedBB textureVolume;
private ByteBuffer lightData; private ByteBuffer lightData;
private boolean bufferDirty; private boolean bufferDirty;
private boolean removed; private boolean removed;
private final GlTexture glTexture; private final GlTexture glTexture;
private final RGPixelFormat pixelFormat; private final RGPixelFormat pixelFormat;
public LightVolume(GridAlignedBB sampleVolume) { public LightVolume(GridAlignedBB sampleVolume) {
setSampleVolume(sampleVolume); setSampleVolume(sampleVolume);
pixelFormat = Backend.compat.pixelFormat; pixelFormat = Backend.compat.pixelFormat;
this.glTexture = new GlTexture(GL20.GL_TEXTURE_3D); this.glTexture = new GlTexture(GL20.GL_TEXTURE_3D);
this.lightData = MemoryUtil.memAlloc(this.textureVolume.volume() * pixelFormat.byteCount()); this.lightData = MemoryUtil.memAlloc(this.textureVolume.volume() * pixelFormat.byteCount());
// allocate space for the texture // allocate space for the texture
GL20.glActiveTexture(GL20.GL_TEXTURE4); GL20.glActiveTexture(GL20.GL_TEXTURE4);
glTexture.bind(); glTexture.bind();
int sizeX = textureVolume.sizeX(); int sizeX = textureVolume.sizeX();
int sizeY = textureVolume.sizeY(); int sizeY = textureVolume.sizeY();
int sizeZ = textureVolume.sizeZ(); int sizeZ = textureVolume.sizeZ();
GL12.glTexImage3D(GL12.GL_TEXTURE_3D, 0, pixelFormat.internalFormat(), sizeX, sizeY, sizeZ, 0, pixelFormat.format(), GL20.GL_UNSIGNED_BYTE, 0); GL12.glTexImage3D(GL12.GL_TEXTURE_3D, 0, pixelFormat.internalFormat(), sizeX, sizeY, sizeZ, 0, pixelFormat.format(), GL20.GL_UNSIGNED_BYTE, 0);
glTexture.unbind(); glTexture.unbind();
GL20.glActiveTexture(GL20.GL_TEXTURE0); GL20.glActiveTexture(GL20.GL_TEXTURE0);
} }
private void setSampleVolume(GridAlignedBB sampleVolume) { private void setSampleVolume(GridAlignedBB sampleVolume) {
this.sampleVolume = sampleVolume; this.sampleVolume = sampleVolume;
this.textureVolume = sampleVolume.copy(); this.textureVolume = sampleVolume.copy();
this.textureVolume.nextPowerOf2Centered(); this.textureVolume.nextPowerOf2Centered();
} }
public GridAlignedBB getTextureVolume() { public GridAlignedBB getTextureVolume() {
return GridAlignedBB.copy(textureVolume); return GridAlignedBB.copy(textureVolume);
} }
public GridAlignedBB getSampleVolume() { public GridAlignedBB getSampleVolume() {
return GridAlignedBB.copy(sampleVolume); return GridAlignedBB.copy(sampleVolume);
} }
public int getMinX() { public int getMinX() {
return textureVolume.minX; return textureVolume.minX;
} }
public int getMinY() { public int getMinY() {
return textureVolume.minY; return textureVolume.minY;
} }
public int getMinZ() { public int getMinZ() {
return textureVolume.minZ; return textureVolume.minZ;
} }
public int getMaxX() { public int getMaxX() {
return textureVolume.maxX; return textureVolume.maxX;
} }
public int getMaxY() { public int getMaxY() {
return textureVolume.maxY; return textureVolume.maxY;
} }
public int getMaxZ() { public int getMaxZ() {
return textureVolume.maxZ; return textureVolume.maxZ;
} }
public int getSizeX() { public int getSizeX() {
return textureVolume.sizeX(); return textureVolume.sizeX();
} }
public int getSizeY() { public int getSizeY() {
return textureVolume.sizeY(); return textureVolume.sizeY();
} }
public int getSizeZ() { public int getSizeZ() {
return textureVolume.sizeZ(); return textureVolume.sizeZ();
} }
public void move(IBlockDisplayReader world, GridAlignedBB newSampleVolume) { public void move(IBlockDisplayReader world, GridAlignedBB newSampleVolume) {
if (textureVolume.contains(newSampleVolume)) { if (textureVolume.contains(newSampleVolume)) {
if (newSampleVolume.intersects(sampleVolume)) { if (newSampleVolume.intersects(sampleVolume)) {
GridAlignedBB newArea = newSampleVolume.intersect(sampleVolume); GridAlignedBB newArea = newSampleVolume.intersect(sampleVolume);
sampleVolume = newSampleVolume; sampleVolume = newSampleVolume;
copyLight(world, newArea); copyLight(world, newArea);
} else { } else {
sampleVolume = newSampleVolume; sampleVolume = newSampleVolume;
initialize(world); initialize(world);
} }
} else { } else {
setSampleVolume(newSampleVolume); setSampleVolume(newSampleVolume);
int volume = textureVolume.volume(); int volume = textureVolume.volume();
if (volume * 2 > lightData.capacity()) { if (volume * 2 > lightData.capacity()) {
lightData = MemoryUtil.memRealloc(lightData, volume * 2); lightData = MemoryUtil.memRealloc(lightData, volume * 2);
} }
initialize(world); initialize(world);
} }
} }
public void notifyLightUpdate(IBlockDisplayReader world, LightType type, GridAlignedBB changedVolume) { public void notifyLightUpdate(IBlockDisplayReader world, LightType type, GridAlignedBB changedVolume) {
if (removed) if (removed)
return; return;
if (!changedVolume.intersects(sampleVolume)) if (!changedVolume.intersects(sampleVolume))
return; return;
changedVolume = changedVolume.intersect(sampleVolume); // compute the region contained by us that has dirty lighting data. changedVolume = changedVolume.intersect(sampleVolume); // compute the region contained by us that has dirty lighting data.
if (type == LightType.BLOCK) copyBlock(world, changedVolume); if (type == LightType.BLOCK) copyBlock(world, changedVolume);
else if (type == LightType.SKY) copySky(world, changedVolume); else if (type == LightType.SKY) copySky(world, changedVolume);
} }
public void notifyLightPacket(IBlockDisplayReader world, int chunkX, int chunkZ) { public void notifyLightPacket(IBlockDisplayReader world, int chunkX, int chunkZ) {
if (removed) return; if (removed) return;
GridAlignedBB changedVolume = GridAlignedBB.from(chunkX, chunkZ); GridAlignedBB changedVolume = GridAlignedBB.from(chunkX, chunkZ);
if (!changedVolume.intersects(sampleVolume)) if (!changedVolume.intersects(sampleVolume))
return; return;
changedVolume.intersectAssign(sampleVolume); // compute the region contained by us that has dirty lighting data. changedVolume.intersectAssign(sampleVolume); // compute the region contained by us that has dirty lighting data.
copyLight(world, changedVolume); copyLight(world, changedVolume);
} }
/** /**
* Completely (re)populate this volume with block and sky lighting data. * Completely (re)populate this volume with block and sky lighting data.
* This is expensive and should be avoided. * This is expensive and should be avoided.
*/ */
public void initialize(IBlockDisplayReader world) { public void initialize(IBlockDisplayReader world) {
BlockPos.Mutable pos = new BlockPos.Mutable(); BlockPos.Mutable pos = new BlockPos.Mutable();
int shiftX = textureVolume.minX; int shiftX = textureVolume.minX;
int shiftY = textureVolume.minY; int shiftY = textureVolume.minY;
int shiftZ = textureVolume.minZ; int shiftZ = textureVolume.minZ;
sampleVolume.forEachContained((x, y, z) -> { sampleVolume.forEachContained((x, y, z) -> {
pos.setPos(x, y, z); pos.setPos(x, y, z);
int blockLight = world.getLightLevel(LightType.BLOCK, pos); int blockLight = world.getLightLevel(LightType.BLOCK, pos);
int skyLight = world.getLightLevel(LightType.SKY, pos); int skyLight = world.getLightLevel(LightType.SKY, pos);
writeLight(x - shiftX, y - shiftY, z - shiftZ, blockLight, skyLight); writeLight(x - shiftX, y - shiftY, z - shiftZ, blockLight, skyLight);
}); });
bufferDirty = true; bufferDirty = true;
} }
/** /**
* Copy block light from the world into this volume. * Copy block light from the world into this volume.
* @param worldVolume the region in the world to copy data from. *
*/ * @param worldVolume the region in the world to copy data from.
public void copyBlock(IBlockDisplayReader world, GridAlignedBB worldVolume) { */
BlockPos.Mutable pos = new BlockPos.Mutable(); public void copyBlock(IBlockDisplayReader world, GridAlignedBB worldVolume) {
BlockPos.Mutable pos = new BlockPos.Mutable();
int xShift = textureVolume.minX;
int yShift = textureVolume.minY; int xShift = textureVolume.minX;
int zShift = textureVolume.minZ; int yShift = textureVolume.minY;
int zShift = textureVolume.minZ;
worldVolume.forEachContained((x, y, z) -> {
pos.setPos(x, y, z); worldVolume.forEachContained((x, y, z) -> {
pos.setPos(x, y, z);
int light = world.getLightLevel(LightType.BLOCK, pos);
int light = world.getLightLevel(LightType.BLOCK, pos);
writeBlock(x - xShift, y - yShift, z - zShift, light);
}); writeBlock(x - xShift, y - yShift, z - zShift, light);
});
bufferDirty = true;
} bufferDirty = true;
}
/**
* Copy sky light from the world into this volume. /**
* @param worldVolume the region in the world to copy data from. * Copy sky light from the world into this volume.
*/ *
public void copySky(IBlockDisplayReader world, GridAlignedBB worldVolume) { * @param worldVolume the region in the world to copy data from.
BlockPos.Mutable pos = new BlockPos.Mutable(); */
public void copySky(IBlockDisplayReader world, GridAlignedBB worldVolume) {
int xShift = textureVolume.minX; BlockPos.Mutable pos = new BlockPos.Mutable();
int yShift = textureVolume.minY;
int zShift = textureVolume.minZ; int xShift = textureVolume.minX;
int yShift = textureVolume.minY;
worldVolume.forEachContained((x, y, z) -> { int zShift = textureVolume.minZ;
pos.setPos(x, y, z);
worldVolume.forEachContained((x, y, z) -> {
int light = world.getLightLevel(LightType.SKY, pos); pos.setPos(x, y, z);
writeSky(x - xShift, y - yShift, z - zShift, light); int light = world.getLightLevel(LightType.SKY, pos);
});
writeSky(x - xShift, y - yShift, z - zShift, light);
bufferDirty = true; });
}
bufferDirty = true;
/** }
* Copy all light from the world into this volume.
* @param worldVolume the region in the world to copy data from. /**
*/ * Copy all light from the world into this volume.
public void copyLight(IBlockDisplayReader world, GridAlignedBB worldVolume) { *
BlockPos.Mutable pos = new BlockPos.Mutable(); * @param worldVolume the region in the world to copy data from.
*/
int xShift = textureVolume.minX; public void copyLight(IBlockDisplayReader world, GridAlignedBB worldVolume) {
int yShift = textureVolume.minY; BlockPos.Mutable pos = new BlockPos.Mutable();
int zShift = textureVolume.minZ;
int xShift = textureVolume.minX;
worldVolume.forEachContained((x, y, z) -> { int yShift = textureVolume.minY;
pos.setPos(x, y, z); int zShift = textureVolume.minZ;
int block = world.getLightLevel(LightType.BLOCK, pos); worldVolume.forEachContained((x, y, z) -> {
int sky = world.getLightLevel(LightType.SKY, pos); pos.setPos(x, y, z);
writeLight(x - xShift, y - yShift, z - zShift, block, sky); int block = world.getLightLevel(LightType.BLOCK, pos);
}); int sky = world.getLightLevel(LightType.SKY, pos);
bufferDirty = true; writeLight(x - xShift, y - yShift, z - zShift, block, sky);
} });
public void bind() { bufferDirty = true;
// just in case something goes wrong or we accidentally call this before this volume is properly disposed of. }
if (lightData == null || removed) return;
public void bind() {
GL13.glActiveTexture(GL20.GL_TEXTURE4); // just in case something goes wrong or we accidentally call this before this volume is properly disposed of.
glTexture.bind(); if (lightData == null || removed) return;
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); GL13.glActiveTexture(GL20.GL_TEXTURE4);
GL11.glTexParameteri(GL13.GL_TEXTURE_3D, GL13.GL_TEXTURE_WRAP_S, GL20.GL_MIRRORED_REPEAT); glTexture.bind();
GL11.glTexParameteri(GL13.GL_TEXTURE_3D, GL13.GL_TEXTURE_WRAP_R, GL20.GL_MIRRORED_REPEAT); GL11.glTexParameteri(GL13.GL_TEXTURE_3D, GL13.GL_TEXTURE_MIN_FILTER, GL13.GL_LINEAR);
GL11.glTexParameteri(GL13.GL_TEXTURE_3D, GL13.GL_TEXTURE_WRAP_T, GL20.GL_MIRRORED_REPEAT); 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);
uploadTexture(); 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);
private void uploadTexture() { uploadTexture();
if (bufferDirty) { }
GL20.glPixelStorei(GL20.GL_UNPACK_ROW_LENGTH, 0);
GL20.glPixelStorei(GL20.GL_UNPACK_SKIP_PIXELS, 0); private void uploadTexture() {
GL20.glPixelStorei(GL20.GL_UNPACK_SKIP_ROWS, 0); if (bufferDirty) {
GL20.glPixelStorei(GL20.GL_UNPACK_SKIP_IMAGES, 0); GL20.glPixelStorei(GL20.GL_UNPACK_ROW_LENGTH, 0);
GL20.glPixelStorei(GL20.GL_UNPACK_IMAGE_HEIGHT, 0); GL20.glPixelStorei(GL20.GL_UNPACK_SKIP_PIXELS, 0);
GL20.glPixelStorei(GL20.GL_UNPACK_ALIGNMENT, 2); GL20.glPixelStorei(GL20.GL_UNPACK_SKIP_ROWS, 0);
int sizeX = textureVolume.sizeX(); GL20.glPixelStorei(GL20.GL_UNPACK_SKIP_IMAGES, 0);
int sizeY = textureVolume.sizeY(); GL20.glPixelStorei(GL20.GL_UNPACK_IMAGE_HEIGHT, 0);
int sizeZ = textureVolume.sizeZ(); GL20.glPixelStorei(GL20.GL_UNPACK_ALIGNMENT, 2);
int sizeX = textureVolume.sizeX();
GL12.glTexSubImage3D(GL12.GL_TEXTURE_3D, 0, 0, 0, 0, sizeX, sizeY, sizeZ, pixelFormat.format(), GL20.GL_UNSIGNED_BYTE, lightData); int sizeY = textureVolume.sizeY();
int sizeZ = textureVolume.sizeZ();
GL20.glPixelStorei(GL20.GL_UNPACK_ALIGNMENT, 4); // 4 is the default
bufferDirty = false; GL12.glTexSubImage3D(GL12.GL_TEXTURE_3D, 0, 0, 0, 0, sizeX, sizeY, sizeZ, pixelFormat.format(), GL20.GL_UNSIGNED_BYTE, lightData);
}
} GL20.glPixelStorei(GL20.GL_UNPACK_ALIGNMENT, 4); // 4 is the default
bufferDirty = false;
public void unbind() { }
glTexture.unbind(); }
}
public void unbind() {
public void delete() { glTexture.unbind();
removed = true; }
RenderWork.enqueue(() -> {
glTexture.delete(); public void delete() {
MemoryUtil.memFree(lightData); removed = true;
lightData = null; RenderWork.enqueue(() -> {
}); glTexture.delete();
} 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);
private void writeLight(int x, int y, int z, int block, int sky) {
int i = posToIndex(x, y, z); byte b = (byte) ((block & 0xF) << 4);
lightData.put(i, b); byte s = (byte) ((sky & 0xF) << 4);
lightData.put(i + 1, s);
} int i = posToIndex(x, y, z);
lightData.put(i, b);
private void writeBlock(int x, int y, int z, int block) { lightData.put(i + 1, s);
byte b = (byte) ((block & 0xF) << 4); }
lightData.put(posToIndex(x, y, z), b); private void writeBlock(int x, int y, int z, int block) {
} byte b = (byte) ((block & 0xF) << 4);
private void writeSky(int x, int y, int z, int sky) { lightData.put(posToIndex(x, y, z), b);
byte b = (byte) ((sky & 0xF) << 4); }
lightData.put(posToIndex(x, y, z) + 1, b); private void writeSky(int x, int y, int z, int sky) {
} byte b = (byte) ((sky & 0xF) << 4);
private int posToIndex(int x, int y, int z) { lightData.put(posToIndex(x, y, z) + 1, b);
return (x + textureVolume.sizeX() * (y + z * textureVolume.sizeY())) * pixelFormat.byteCount(); }
}
private int posToIndex(int x, int y, int z) {
return (x + textureVolume.sizeX() * (y + z * textureVolume.sizeY())) * pixelFormat.byteCount();
}
} }

View file

@ -10,26 +10,26 @@ import com.simibubi.create.foundation.utility.Pair;
import com.simibubi.create.foundation.utility.outliner.AABBOutline; import com.simibubi.create.foundation.utility.outliner.AABBOutline;
public class LightVolumeDebugger { public class LightVolumeDebugger {
public static void render(MatrixStack ms, SuperRenderTypeBuffer buffer) { public static void render(MatrixStack ms, SuperRenderTypeBuffer buffer) {
ContraptionRenderDispatcher.renderers.values() ContraptionRenderDispatcher.renderers.values()
.stream() .stream()
.flatMap(r -> { .flatMap(r -> {
GridAlignedBB texture = r.getLighter().lightVolume.getTextureVolume(); GridAlignedBB texture = r.getLighter().lightVolume.getTextureVolume();
GridAlignedBB sample = r.getLighter().lightVolume.getSampleVolume(); GridAlignedBB sample = r.getLighter().lightVolume.getSampleVolume();
ArrayList<Pair<GridAlignedBB, Integer>> pairs = new ArrayList<>(2); ArrayList<Pair<GridAlignedBB, Integer>> pairs = new ArrayList<>(2);
pairs.add(Pair.of(texture, 0xFFFFFF)); pairs.add(Pair.of(texture, 0xFFFFFF));
pairs.add(Pair.of(sample, 0xFFFF00)); pairs.add(Pair.of(sample, 0xFFFF00));
return pairs.stream(); return pairs.stream();
}) })
.map(pair -> { .map(pair -> {
AABBOutline outline = new AABBOutline(GridAlignedBB.toAABB(pair.getFirst())); AABBOutline outline = new AABBOutline(GridAlignedBB.toAABB(pair.getFirst()));
outline.getParams().colored(pair.getSecond()); outline.getParams().colored(pair.getSecond());
return outline; return outline;
}) })
.forEach(outline -> outline.render(ms, buffer, AnimationTickHolder.getPartialTicks())); .forEach(outline -> outline.render(ms, buffer, AnimationTickHolder.getPartialTicks()));
} }
} }