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

View file

@ -2,78 +2,71 @@ package com.simibubi.create.foundation.render.backend;
import java.util.concurrent.ConcurrentHashMap;
import com.mojang.blaze3d.matrix.MatrixStack;
import com.simibubi.create.CreateClient;
import com.simibubi.create.content.contraptions.KineticDebugger;
import com.simibubi.create.foundation.render.KineticRenderer;
import com.simibubi.create.foundation.utility.AnimationTickHolder;
import com.simibubi.create.foundation.utility.WorldAttached;
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.world.ClientWorld;
import net.minecraft.entity.Entity;
import net.minecraft.potion.Effects;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.vector.Matrix4f;
import net.minecraft.util.math.vector.Vector3f;
import net.minecraft.world.World;
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) {
queuedUpdates.get(te.getWorld()).add(te);
}
public static void enqueueUpdate(TileEntity te) {
queuedUpdates.get(te.getWorld()).add(te);
}
public static void tick() {
Minecraft mc = Minecraft.getInstance();
ClientWorld world = mc.world;
public static void tick() {
Minecraft mc = Minecraft.getInstance();
ClientWorld world = mc.world;
KineticRenderer kineticRenderer = CreateClient.kineticRenderer.get(world);
KineticRenderer kineticRenderer = CreateClient.kineticRenderer.get(world);
Entity renderViewEntity = mc.renderViewEntity;
kineticRenderer.tick(renderViewEntity.getX(), renderViewEntity.getY(), renderViewEntity.getZ());
Entity renderViewEntity = mc.renderViewEntity;
kineticRenderer.tick(renderViewEntity.getX(), renderViewEntity.getY(), renderViewEntity.getZ());
ConcurrentHashMap.KeySetView<TileEntity, Boolean> map = queuedUpdates.get(world);
map
.forEach(te -> {
map.remove(te);
ConcurrentHashMap.KeySetView<TileEntity, Boolean> map = queuedUpdates.get(world);
map
.forEach(te -> {
map.remove(te);
kineticRenderer.update(te);
});
}
kineticRenderer.update(te);
});
}
public static boolean available() {
return Backend.canUseInstancing();
}
public static boolean available() {
return Backend.canUseInstancing();
}
public static boolean available(World world) {
return Backend.canUseInstancing() && Backend.isFlywheelWorld(world);
}
public static boolean available(World world) {
return Backend.canUseInstancing() && Backend.isFlywheelWorld(world);
}
public static int getDebugMode() {
return KineticDebugger.isActive() ? 1 : 0;
}
public static int getDebugMode() {
return KineticDebugger.isActive() ? 1 : 0;
}
public static void refresh() {
RenderWork.enqueue(Minecraft.getInstance().worldRenderer::loadRenderers);
}
public static void refresh() {
RenderWork.enqueue(Minecraft.getInstance().worldRenderer::loadRenderers);
}
public static void renderLayer(RenderType layer, Matrix4f viewProjection, double cameraX, double cameraY, double cameraZ) {
if (!Backend.canUseInstancing()) return;
public static void renderLayer(RenderType layer, Matrix4f viewProjection, double cameraX, double cameraY, double cameraZ) {
if (!Backend.canUseInstancing()) return;
ClientWorld world = Minecraft.getInstance().world;
KineticRenderer kineticRenderer = CreateClient.kineticRenderer.get(world);
ClientWorld world = Minecraft.getInstance().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;
public class MaterialTypes {
public static final MaterialType<InstancedModel<ModelData>> TRANSFORMED = new MaterialType<>();
public static final MaterialType<InstancedModel<OrientedData>> ORIENTED = new MaterialType<>();
public static final MaterialType<InstancedModel<ModelData>> TRANSFORMED = 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;
public class OptifineHandler {
public static final String OPTIFINE_ROOT_PACKAGE = "net.optifine";
public static final String SHADER_PACKAGE = "net.optifine.shaders";
public static final String OPTIFINE_ROOT_PACKAGE = "net.optifine";
public static final String SHADER_PACKAGE = "net.optifine.shaders";
private static Package optifine;
private static OptifineHandler handler;
private static Package optifine;
private static OptifineHandler handler;
public final boolean usingShaders;
public final boolean usingShaders;
public OptifineHandler(boolean usingShaders) {
this.usingShaders = usingShaders;
}
public OptifineHandler(boolean usingShaders) {
this.usingShaders = usingShaders;
}
/**
* Get information about the current Optifine configuration.
* @return {@link Optional#empty()} if Optifine is not installed.
*/
public static Optional<OptifineHandler> get() {
return Optional.ofNullable(handler);
}
/**
* Get information about the current Optifine configuration.
*
* @return {@link Optional#empty()} if Optifine is not installed.
*/
public static Optional<OptifineHandler> get() {
return Optional.ofNullable(handler);
}
public static boolean optifineInstalled() {
return optifine != null;
}
public static boolean optifineInstalled() {
return optifine != null;
}
public static boolean usingShaders() {
return OptifineHandler.get()
.map(OptifineHandler::isUsingShaders)
.orElse(false);
}
public static boolean usingShaders() {
return OptifineHandler.get()
.map(OptifineHandler::isUsingShaders)
.orElse(false);
}
public static void init() {
optifine = Package.getPackage(OPTIFINE_ROOT_PACKAGE);
public static void init() {
optifine = Package.getPackage(OPTIFINE_ROOT_PACKAGE);
if (optifine == null) {
Backend.log.info("Optifine not detected.");
} else {
Backend.log.info("Optifine detected.");
if (optifine == null) {
Backend.log.info("Optifine not detected.");
} else {
Backend.log.info("Optifine detected.");
refresh();
}
}
refresh();
}
}
public static void refresh() {
if (optifine == null) return;
public static void refresh() {
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;
try {
BufferedReader reader = new BufferedReader(new FileReader(shaderOptions));
boolean shadersOff = true;
try {
BufferedReader reader = new BufferedReader(new FileReader(shaderOptions));
shadersOff = reader.lines()
.anyMatch(it -> {
String line = it.replaceAll("\\s", "");
if (line.startsWith("shaderPack=")) {
String setting = line.substring("shaderPack=".length());
shadersOff = reader.lines()
.anyMatch(it -> {
String line = it.replaceAll("\\s", "");
if (line.startsWith("shaderPack=")) {
String setting = line.substring("shaderPack=".length());
return setting.equals("OFF") || setting.equals("(internal)");
}
return false;
});
} catch (FileNotFoundException e) {
Backend.log.info("No shader config found.");
}
return setting.equals("OFF") || setting.equals("(internal)");
}
return false;
});
} catch (FileNotFoundException e) {
Backend.log.info("No shader config found.");
}
handler = new OptifineHandler(!shadersOff);
}
handler = new OptifineHandler(!shadersOff);
}
public boolean isUsingShaders() {
return usingShaders;
}
public boolean isUsingShaders() {
return usingShaders;
}
}

View file

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

View file

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

View file

@ -28,14 +28,14 @@ import org.lwjgl.system.MemoryUtil;
import com.google.common.collect.Lists;
import com.mojang.blaze3d.systems.RenderSystem;
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.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.ProgramSpec;
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.SingleProgram;
import net.minecraft.resources.IResource;
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 -> {
for (String ext : EXTENSIONS) {
if (s.endsWith(ext)) return true;
@ -185,7 +185,7 @@ public class ShaderLoader {
try {
bytebuffer = readToBuffer(is);
int i = bytebuffer.position();
((Buffer)bytebuffer).rewind();
((Buffer) bytebuffer).rewind();
return MemoryUtil.memASCII(bytebuffer, i);
} catch (IOException e) {
@ -202,11 +202,12 @@ public class ShaderLoader {
public ByteBuffer readToBuffer(InputStream is) throws IOException {
ByteBuffer bytebuffer;
if (is instanceof FileInputStream) {
FileInputStream fileinputstream = (FileInputStream)is;
FileInputStream fileinputstream = (FileInputStream) is;
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 {
bytebuffer = MemoryUtil.memAlloc(8192);
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> {
protected byte blockLight;
protected byte skyLight;
protected byte blockLight;
protected byte skyLight;
protected byte r = (byte) 0xFF;
protected byte g = (byte) 0xFF;
protected byte b = (byte) 0xFF;
protected byte a = (byte) 0xFF;
protected byte r = (byte) 0xFF;
protected byte g = (byte) 0xFF;
protected byte b = (byte) 0xFF;
protected byte a = (byte) 0xFF;
public BasicData(InstancedModel<?> owner) {
super(owner);
}
public BasicData(InstancedModel<?> owner) {
super(owner);
}
@Override
public BasicData setBlockLight(int blockLight) {
this.blockLight = (byte) (blockLight << 4);
markDirty();
return this;
}
@Override
public BasicData setBlockLight(int blockLight) {
this.blockLight = (byte) (blockLight << 4);
markDirty();
return this;
}
@Override
public BasicData setSkyLight(int skyLight) {
this.skyLight = (byte) (skyLight << 4);
markDirty();
return this;
}
@Override
public BasicData setSkyLight(int skyLight) {
this.skyLight = (byte) (skyLight << 4);
markDirty();
return this;
}
public BasicData setColor(int color) {
return setColor(color, false);
}
public BasicData setColor(int color) {
return setColor(color, false);
}
public BasicData setColor(int color, boolean alpha) {
byte r = (byte) ((color >> 16) & 0xFF);
byte g = (byte) ((color >> 8) & 0xFF);
byte b = (byte) (color & 0xFF);
public BasicData setColor(int color, boolean alpha) {
byte r = (byte) ((color >> 16) & 0xFF);
byte g = (byte) ((color >> 8) & 0xFF);
byte b = (byte) (color & 0xFF);
if (alpha) {
byte a = (byte) ((color >> 24) & 0xFF);
return setColor(r, g, b, a);
} else {
return setColor(r, g, b);
}
}
if (alpha) {
byte a = (byte) ((color >> 24) & 0xFF);
return setColor(r, g, b, a);
} else {
return setColor(r, g, b);
}
}
public BasicData setColor(int r, int g, int b) {
return setColor((byte) r, (byte) g, (byte) b);
}
public BasicData setColor(int r, int g, int b) {
return setColor((byte) r, (byte) g, (byte) b);
}
public BasicData setColor(byte r, byte g, byte b) {
this.r = r;
this.g = g;
this.b = b;
markDirty();
return this;
}
public BasicData setColor(byte r, byte g, byte b) {
this.r = r;
this.g = g;
this.b = b;
markDirty();
return this;
}
public BasicData setColor(byte r, byte g, byte b, byte a) {
this.r = r;
this.g = g;
this.b = b;
this.a = a;
markDirty();
return this;
}
public BasicData setColor(byte r, byte g, byte b, byte a) {
this.r = r;
this.g = g;
this.b = b;
this.a = a;
markDirty();
return this;
}
@Override
public void write(ByteBuffer buf) {
buf.put(new byte[] { blockLight, skyLight, r, g, b, a });
}
@Override
public void write(ByteBuffer buf) {
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
* if they wish to make use of Flywheel's provided light update methods.
*
* <p>
* This only covers flat lighting, smooth lighting is still TODO.
*
* @param <D> The name of the class that implements this interface.
*/
public interface IFlatLight<D extends InstanceData & IFlatLight<D>> {
/**
* @param blockLight An integer in the range [0, 15] representing the
* amount of block light this instance should receive.
* @return <code>this</code>
*/
D setBlockLight(int blockLight);
/**
* @param blockLight An integer in the range [0, 15] representing the
* amount of block light this instance should receive.
* @return <code>this</code>
*/
D setBlockLight(int blockLight);
/**
* @param skyLight An integer in the range [0, 15] representing the
* amount of sky light this instance should receive.
* @return <code>this</code>
*/
D setSkyLight(int skyLight);
/**
* @param skyLight An integer in the range [0, 15] representing the
* amount of sky light this instance should receive.
* @return <code>this</code>
*/
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;
public enum ModelAttributes implements IVertexAttrib {
VERTEX_POSITION("aPos", CommonAttributes.VEC3),
NORMAL("aNormal", CommonAttributes.NORMAL),
TEXTURE("aTexCoords", CommonAttributes.UV),
;
VERTEX_POSITION("aPos", CommonAttributes.VEC3),
NORMAL("aNormal", CommonAttributes.NORMAL),
TEXTURE("aTexCoords", CommonAttributes.UV),
;
private final String name;
private final VertexAttribSpec spec;
private final String name;
private final VertexAttribSpec spec;
ModelAttributes(String name, VertexAttribSpec spec) {
this.name = name;
this.spec = spec;
}
ModelAttributes(String name, VertexAttribSpec spec) {
this.name = name;
this.spec = spec;
}
@Override
public String attribName() {
return name;
}
@Override
public String attribName() {
return name;
}
@Override
public IAttribSpec attribSpec() {
return spec;
}
@Override
public IAttribSpec attribSpec() {
return spec;
}
@Override
public int getDivisor() {
return 0;
}
@Override
public int getDivisor() {
return 0;
}
@Override
public int getBufferIndex() {
return 0;
}
@Override
public int getBufferIndex() {
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;
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) {
super(owner);
}
public ModelData(InstancedModel<?> owner) {
super(owner);
}
public ModelData setTransform(MatrixStack stack) {
matrices = RenderUtil.writeMatrixStack(stack);
markDirty();
return this;
}
public ModelData setTransform(MatrixStack stack) {
matrices = RenderUtil.writeMatrixStack(stack);
markDirty();
return this;
}
@Override
public void write(ByteBuffer buf) {
super.write(buf);
buf.asFloatBuffer().put(matrices);
buf.position(buf.position() + matrices.length * 4);
}
@Override
public void write(ByteBuffer buf) {
super.write(buf);
buf.asFloatBuffer().put(matrices);
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;
public enum OrientedAttributes implements IVertexAttrib {
INSTANCE_POS("aInstancePos", CommonAttributes.VEC3),
PIVOT("aPivot", CommonAttributes.VEC3),
ROTATION("aRotation", CommonAttributes.QUATERNION),
;
INSTANCE_POS("aInstancePos", CommonAttributes.VEC3),
PIVOT("aPivot", CommonAttributes.VEC3),
ROTATION("aRotation", CommonAttributes.QUATERNION),
;
private final String name;
private final IAttribSpec spec;
private final String name;
private final IAttribSpec spec;
OrientedAttributes(String name, IAttribSpec spec) {
this.name = name;
this.spec = spec;
}
OrientedAttributes(String name, IAttribSpec spec) {
this.name = name;
this.spec = spec;
}
@Override
public String attribName() {
return name;
}
@Override
public String attribName() {
return name;
}
@Override
public IAttribSpec attribSpec() {
return spec;
}
@Override
public IAttribSpec attribSpec() {
return spec;
}
@Override
public int getDivisor() {
return 0;
}
@Override
public int getDivisor() {
return 0;
}
@Override
public int getBufferIndex() {
return 0;
}
@Override
public int getBufferIndex() {
return 0;
}
}

View file

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

View file

@ -7,22 +7,22 @@ import com.simibubi.create.foundation.render.backend.instancing.InstancedTileRen
import net.minecraft.client.renderer.BufferBuilder;
public class OrientedModel extends InstancedModel<OrientedData> {
public static final VertexFormat INSTANCE_FORMAT = VertexFormat.builder()
.addAttributes(BasicAttributes.class)
.addAttributes(OrientedAttributes.class)
.build();
public static final VertexFormat INSTANCE_FORMAT = VertexFormat.builder()
.addAttributes(BasicAttributes.class)
.addAttributes(OrientedAttributes.class)
.build();
public OrientedModel(InstancedTileRenderer<?> renderer, BufferBuilder buf) {
super(renderer, buf);
}
public OrientedModel(InstancedTileRenderer<?> renderer, BufferBuilder buf) {
super(renderer, buf);
}
@Override
protected OrientedData newInstance() {
return new OrientedData(this);
}
@Override
protected OrientedData newInstance() {
return new OrientedData(this);
}
@Override
protected VertexFormat getInstanceFormat() {
return INSTANCE_FORMAT;
}
@Override
protected VertexFormat getInstanceFormat() {
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;
public enum TransformAttributes implements IVertexAttrib {
TRANSFORM("aTransform", MatrixAttributes.MAT4),
NORMAL_MAT("aNormalMat", MatrixAttributes.MAT3),
;
TRANSFORM("aTransform", MatrixAttributes.MAT4),
NORMAL_MAT("aNormalMat", MatrixAttributes.MAT3),
;
private final String name;
private final IAttribSpec spec;
private final String name;
private final IAttribSpec spec;
TransformAttributes(String name, IAttribSpec spec) {
this.name = name;
this.spec = spec;
}
TransformAttributes(String name, IAttribSpec spec) {
this.name = name;
this.spec = spec;
}
@Override
public String attribName() {
return name;
}
@Override
public String attribName() {
return name;
}
@Override
public IAttribSpec attribSpec() {
return spec;
}
@Override
public IAttribSpec attribSpec() {
return spec;
}
@Override
public int getDivisor() {
return 0;
}
@Override
public int getDivisor() {
return 0;
}
@Override
public int getBufferIndex() {
return 0;
}
@Override
public int getBufferIndex() {
return 0;
}
}

View file

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

View file

@ -11,48 +11,48 @@ import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.vector.Matrix4f;
public class BasicProgram extends GlProgram {
protected final int uTime;
protected final int uViewProjection;
protected final int uDebug;
protected final int uCameraPos;
protected final int uTime;
protected final int uViewProjection;
protected final int uDebug;
protected final int uCameraPos;
protected final ProgramFogMode fogMode;
protected final ProgramFogMode fogMode;
protected int uBlockAtlas;
protected int uLightMap;
protected int uBlockAtlas;
protected int uLightMap;
public BasicProgram(ResourceLocation name, int handle, ProgramFogMode.Factory fogFactory) {
super(name, handle);
uTime = getUniformLocation("uTime");
uViewProjection = getUniformLocation("uViewProjection");
uDebug = getUniformLocation("uDebug");
uCameraPos = getUniformLocation("uCameraPos");
public BasicProgram(ResourceLocation name, int handle, ProgramFogMode.Factory fogFactory) {
super(name, handle);
uTime = getUniformLocation("uTime");
uViewProjection = getUniformLocation("uViewProjection");
uDebug = getUniformLocation("uDebug");
uCameraPos = getUniformLocation("uCameraPos");
fogMode = fogFactory.create(this);
fogMode = fogFactory.create(this);
bind();
registerSamplers();
unbind();
}
bind();
registerSamplers();
unbind();
}
protected void registerSamplers() {
uBlockAtlas = setSamplerBinding("uBlockAtlas", 0);
uLightMap = setSamplerBinding("uLightMap", 2);
}
protected void registerSamplers() {
uBlockAtlas = setSamplerBinding("uBlockAtlas", 0);
uLightMap = setSamplerBinding("uLightMap", 2);
}
public void bind(Matrix4f viewProjection, double camX, double camY, double camZ, int debugMode) {
super.bind();
public void bind(Matrix4f viewProjection, double camX, double camY, double camZ, int debugMode) {
super.bind();
GL20.glUniform1i(uDebug, debugMode);
GL20.glUniform1f(uTime, AnimationTickHolder.getRenderTime());
GL20.glUniform1i(uDebug, debugMode);
GL20.glUniform1f(uTime, AnimationTickHolder.getRenderTime());
uploadMatrixUniform(uViewProjection, viewProjection);
GL20.glUniform3f(uCameraPos, (float) camX, (float) camY, (float) camZ);
uploadMatrixUniform(uViewProjection, viewProjection);
GL20.glUniform3f(uCameraPos, (float) camX, (float) camY, (float) camZ);
fogMode.bind();
}
fogMode.bind();
}
protected static void uploadMatrixUniform(int uniform, Matrix4f mat) {
GL20.glUniformMatrix4fv(uniform, false, RenderUtil.writeMatrix(mat));
}
protected static void uploadMatrixUniform(int uniform, Matrix4f 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 {
protected final int bufferType;
protected final int bufferType;
public GlBuffer(int bufferType) {
setHandle(GL20.glGenBuffers());
this.bufferType = bufferType;
}
public GlBuffer(int bufferType) {
setHandle(GL20.glGenBuffers());
this.bufferType = bufferType;
}
public int getBufferType() {
return bufferType;
}
public int getBufferType() {
return bufferType;
}
public void bind() {
GL20.glBindBuffer(bufferType, handle());
}
public void bind() {
GL20.glBindBuffer(bufferType, handle());
}
public void unbind() {
GL20.glBindBuffer(bufferType, 0);
}
public void unbind() {
GL20.glBindBuffer(bufferType, 0);
}
public void with(Consumer<GlBuffer> action) {
bind();
action.accept(this);
unbind();
}
public void with(Consumer<GlBuffer> action) {
bind();
action.accept(this);
unbind();
}
public void map(int length, Consumer<ByteBuffer> upload) {
Backend.compat.mapBuffer(bufferType, 0, length, upload);
}
public void map(int length, Consumer<ByteBuffer> upload) {
Backend.compat.mapBuffer(bufferType, 0, length, upload);
}
public void map(int offset, int length, Consumer<ByteBuffer> upload) {
Backend.compat.mapBuffer(bufferType, offset, length, upload);
}
public void map(int offset, int length, Consumer<ByteBuffer> upload) {
Backend.compat.mapBuffer(bufferType, offset, length, upload);
}
protected void deleteInternal(int handle) {
GL20.glDeleteBuffers(handle);
}
protected void deleteInternal(int handle) {
GL20.glDeleteBuffers(handle);
}
}

View file

@ -5,43 +5,43 @@ import org.lwjgl.opengl.GL11;
import com.mojang.blaze3d.platform.GlStateManager;
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() {
return GlStateManager.FOG.field_179049_a.field_179201_b;
}
public static boolean fogEnabled() {
return GlStateManager.FOG.field_179049_a.field_179201_b;
}
public static int getFogModeGlEnum() {
return GlStateManager.FOG.field_179047_b;
}
public static int getFogModeGlEnum() {
return GlStateManager.FOG.field_179047_b;
}
public static float getFogDensity() {
return GlStateManager.FOG.field_179048_c;
}
public static float getFogDensity() {
return GlStateManager.FOG.field_179048_c;
}
public static float getFogEnd() {
return GlStateManager.FOG.field_179046_e;
}
public static float getFogEnd() {
return GlStateManager.FOG.field_179046_e;
}
public static float getFogStart() {
return GlStateManager.FOG.field_179045_d;
}
public static float getFogStart() {
return GlStateManager.FOG.field_179045_d;
}
public static GlFogMode getFogMode() {
if (!fogEnabled()) {
return GlFogMode.NONE;
}
public static GlFogMode getFogMode() {
if (!fogEnabled()) {
return GlFogMode.NONE;
}
int mode = getFogModeGlEnum();
int mode = getFogModeGlEnum();
switch (mode) {
case GL11.GL_EXP2:
case GL11.GL_EXP:
return GlFogMode.EXP2;
case GL11.GL_LINEAR:
return GlFogMode.LINEAR;
default:
throw new UnsupportedOperationException("Unknown fog mode: " + mode);
}
}
switch (mode) {
case GL11.GL_EXP2:
case GL11.GL_EXP:
return GlFogMode.EXP2;
case GL11.GL_LINEAR:
return GlFogMode.LINEAR;
default:
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;
public enum GlFogMode {
NONE(ProgramFogMode.None::new),
LINEAR(ProgramFogMode.Linear::new, "USE_FOG_LINEAR"),
EXP2(ProgramFogMode.Exp2::new, "USE_FOG_EXP2"),
;
NONE(ProgramFogMode.None::new),
LINEAR(ProgramFogMode.Linear::new, "USE_FOG_LINEAR"),
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 List<String> defines;
private final ProgramFogMode.Factory fogFactory;
private final List<String> defines;
GlFogMode(ProgramFogMode.Factory fogFactory) {
this.fogFactory = fogFactory;
this.defines = Collections.emptyList();
}
GlFogMode(ProgramFogMode.Factory fogFactory) {
this.fogFactory = fogFactory;
this.defines = Collections.emptyList();
}
GlFogMode(ProgramFogMode.Factory fogFactory, String name) {
this.fogFactory = fogFactory;
this.defines = Lists.newArrayList(USE_FOG, name);
}
GlFogMode(ProgramFogMode.Factory fogFactory, String name) {
this.fogFactory = fogFactory;
this.defines = Lists.newArrayList(USE_FOG, name);
}
public List<String> getDefines() {
return defines;
}
public List<String> getDefines() {
return defines;
}
public ProgramFogMode.Factory getFogFactory() {
return fogFactory;
}
public ProgramFogMode.Factory getFogFactory() {
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.
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) {
this.handle = handle;
}
protected final void setHandle(int handle) {
this.handle = handle;
}
public final int handle() {
this.checkHandle();
public final int handle() {
this.checkHandle();
return this.handle;
}
return this.handle;
}
protected final void checkHandle() {
if (!this.isHandleValid()) {
throw new IllegalStateException("Handle is not valid");
}
}
protected final void checkHandle() {
if (!this.isHandleValid()) {
throw new IllegalStateException("Handle is not valid");
}
}
protected final boolean isHandleValid() {
return this.handle != INVALID_HANDLE;
}
protected final boolean isHandleValid() {
return this.handle != INVALID_HANDLE;
}
protected final void invalidateHandle() {
this.handle = INVALID_HANDLE;
}
protected final void invalidateHandle() {
this.handle = INVALID_HANDLE;
}
public final void delete() {
if (!isHandleValid()) {
throw new IllegalStateException("Handle already deleted.");
}
public final void delete() {
if (!isHandleValid()) {
throw new IllegalStateException("Handle already deleted.");
}
deleteInternal(handle);
invalidateHandle();
}
deleteInternal(handle);
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)
public enum GlPrimitiveType {
FLOAT(4, "float", GL11.GL_FLOAT),
UBYTE(1, "ubyte", GL11.GL_UNSIGNED_BYTE),
BYTE(1, "byte", GL11.GL_BYTE),
USHORT(2, "ushort", GL11.GL_UNSIGNED_SHORT),
SHORT(2, "short", GL11.GL_SHORT),
UINT(4, "uint", GL11.GL_UNSIGNED_INT),
INT(4, "int", GL11.GL_INT);
FLOAT(4, "float", GL11.GL_FLOAT),
UBYTE(1, "ubyte", GL11.GL_UNSIGNED_BYTE),
BYTE(1, "byte", GL11.GL_BYTE),
USHORT(2, "ushort", GL11.GL_UNSIGNED_SHORT),
SHORT(2, "short", GL11.GL_SHORT),
UINT(4, "uint", GL11.GL_UNSIGNED_INT),
INT(4, "int", GL11.GL_INT);
private final int size;
private final String displayName;
private final int glConstant;
private final int size;
private final String displayName;
private final int glConstant;
GlPrimitiveType(int bytes, String name, int glEnum) {
this.size = bytes;
this.displayName = name;
this.glConstant = glEnum;
}
GlPrimitiveType(int bytes, String name, int glEnum) {
this.size = bytes;
this.displayName = name;
this.glConstant = glEnum;
}
public int getSize() {
return this.size;
}
public int getSize() {
return this.size;
}
public String getDisplayName() {
return this.displayName;
}
public String getDisplayName() {
return this.displayName;
}
public int getGlConstant() {
return this.glConstant;
}
}
public int getGlConstant() {
return this.glConstant;
}
}

View file

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

View file

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

View file

@ -4,18 +4,18 @@ import com.simibubi.create.foundation.render.backend.gl.GlPrimitiveType;
public class CommonAttributes {
public static final VertexAttribSpec VEC4 = new VertexAttribSpec(GlPrimitiveType.FLOAT, 4);
public static final VertexAttribSpec VEC3 = new VertexAttribSpec(GlPrimitiveType.FLOAT, 3);
public static final VertexAttribSpec VEC2 = new VertexAttribSpec(GlPrimitiveType.FLOAT, 2);
public static final VertexAttribSpec FLOAT = new VertexAttribSpec(GlPrimitiveType.FLOAT, 1);
public static final VertexAttribSpec VEC4 = new VertexAttribSpec(GlPrimitiveType.FLOAT, 4);
public static final VertexAttribSpec VEC3 = new VertexAttribSpec(GlPrimitiveType.FLOAT, 3);
public static final VertexAttribSpec VEC2 = new VertexAttribSpec(GlPrimitiveType.FLOAT, 2);
public static final VertexAttribSpec FLOAT = new VertexAttribSpec(GlPrimitiveType.FLOAT, 1);
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 UV = new VertexAttribSpec(GlPrimitiveType.FLOAT, 2);
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 UV = new VertexAttribSpec(GlPrimitiveType.FLOAT, 2);
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 LIGHT = new VertexAttribSpec(GlPrimitiveType.UBYTE, 2, 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 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 {
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 {
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;
public enum MatrixAttributes implements IAttribSpec {
MAT3(3, 3),
MAT4(4, 4),
;
MAT3(3, 3),
MAT4(4, 4),
;
private final int rows;
private final int cols;
private final int rows;
private final int cols;
MatrixAttributes(int rows, int cols) {
this.rows = rows;
this.cols = cols;
}
MatrixAttributes(int rows, int cols) {
this.rows = rows;
this.cols = cols;
}
@Override
public void vertexAttribPointer(int stride, int index, int pointer) {
for (int i = 0; i < rows; i++) {
long attribPointer = pointer + (long) i * cols * GlPrimitiveType.FLOAT.getSize();
GL20.glVertexAttribPointer(index + i, cols, GlPrimitiveType.FLOAT.getGlConstant(), false, stride, attribPointer);
}
}
@Override
public void vertexAttribPointer(int stride, int index, int pointer) {
for (int i = 0; i < rows; i++) {
long attribPointer = pointer + (long) i * cols * GlPrimitiveType.FLOAT.getSize();
GL20.glVertexAttribPointer(index + i, cols, GlPrimitiveType.FLOAT.getGlConstant(), false, stride, attribPointer);
}
}
@Override
public int getSize() {
return GlPrimitiveType.FLOAT.getSize() * rows * cols;
}
@Override
public int getSize() {
return GlPrimitiveType.FLOAT.getSize() * rows * cols;
}
@Override
public int getAttributeCount() {
return rows;
}
@Override
public int getAttributeCount() {
return rows;
}
}

View file

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

View file

@ -5,61 +5,61 @@ import java.util.Arrays;
public class VertexFormat {
private final ArrayList<IVertexAttrib> allAttributes;
private final ArrayList<IVertexAttrib> allAttributes;
private final int numAttributes;
private final int stride;
private final int numAttributes;
private final int stride;
public VertexFormat(ArrayList<IVertexAttrib> allAttributes) {
this.allAttributes = allAttributes;
public VertexFormat(ArrayList<IVertexAttrib> allAttributes) {
this.allAttributes = allAttributes;
int numAttributes = 0, stride = 0;
for (IVertexAttrib attrib : allAttributes) {
IAttribSpec spec = attrib.attribSpec();
numAttributes += spec.getAttributeCount();
stride += spec.getSize();
}
this.numAttributes = numAttributes;
this.stride = stride;
}
int numAttributes = 0, stride = 0;
for (IVertexAttrib attrib : allAttributes) {
IAttribSpec spec = attrib.attribSpec();
numAttributes += spec.getAttributeCount();
stride += spec.getSize();
}
this.numAttributes = numAttributes;
this.stride = stride;
}
public int getShaderAttributeCount() {
return numAttributes;
}
public int getShaderAttributeCount() {
return numAttributes;
}
public int getStride() {
return stride;
}
public int getStride() {
return stride;
}
public void vertexAttribPointers(int index) {
int offset = 0;
for (IVertexAttrib attrib : this.allAttributes) {
IAttribSpec spec = attrib.attribSpec();
spec.vertexAttribPointer(stride, index, offset);
index += spec.getAttributeCount();
offset += spec.getSize();
}
}
public void vertexAttribPointers(int index) {
int offset = 0;
for (IVertexAttrib attrib : this.allAttributes) {
IAttribSpec spec = attrib.attribSpec();
spec.vertexAttribPointer(stride, index, offset);
index += spec.getAttributeCount();
offset += spec.getSize();
}
}
public static Builder builder() {
return new Builder();
}
public static Builder builder() {
return new Builder();
}
public static class Builder {
private final ArrayList<IVertexAttrib> allAttributes;
public static class Builder {
private final ArrayList<IVertexAttrib> allAttributes;
public Builder() {
allAttributes = new ArrayList<>();
}
public Builder() {
allAttributes = new ArrayList<>();
}
public <A extends Enum<A> & IVertexAttrib> Builder addAttributes(Class<A> attribEnum) {
allAttributes.addAll(Arrays.asList(attribEnum.getEnumConstants()));
return this;
}
public <A extends Enum<A> & IVertexAttrib> Builder addAttributes(Class<A> attribEnum) {
allAttributes.addAll(Arrays.asList(attribEnum.getEnumConstants()));
return this;
}
public VertexFormat build() {
return new VertexFormat(allAttributes);
}
}
public VertexFormat build() {
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> {
private final Map<GlFogMode, P> programs;
private final Map<GlFogMode, P> programs;
public FogSensitiveProgram(Map<GlFogMode, P> programs) {
this.programs = programs;
}
public FogSensitiveProgram(Map<GlFogMode, P> programs) {
this.programs = programs;
}
@Override
public P get() {
return programs.get(GlFog.getFogMode());
}
@Override
public P get() {
return programs.get(GlFog.getFogMode());
}
@Override
public void delete() {
programs.values().forEach(GlProgram::delete);
}
@Override
public void 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;
public class GLSLType {
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 VEC3 = new GLSLType("vec3", GlPrimitiveType.FLOAT, 3);
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 FLOAT = new GLSLType("mat4", GlPrimitiveType.FLOAT, 16);
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 VEC4 = new GLSLType("vec2", GlPrimitiveType.FLOAT, 2);
public static final GLSLType MAT4 = new GLSLType("float", GlPrimitiveType.FLOAT, 1);
private final String symbol;
private final GlPrimitiveType base;
private final int count;
private final int size;
private final int attributeCount;
private final String symbol;
private final GlPrimitiveType base;
private final int count;
private final int size;
private final int attributeCount;
public GLSLType(String symbol, GlPrimitiveType base, int count) {
this.symbol = symbol;
this.base = base;
this.count = count;
this.size = base.getSize() * count;
this.attributeCount = (this.size + 15) / 16; // ceiling division. GLSL vertex attributes can only be 16 bytes wide
}
public GLSLType(String symbol, GlPrimitiveType base, int count) {
this.symbol = symbol;
this.base = base;
this.count = count;
this.size = base.getSize() * count;
this.attributeCount = (this.size + 15) / 16; // ceiling division. GLSL vertex attributes can only be 16 bytes wide
}
public String getSymbol() {
return symbol;
}
public String getSymbol() {
return symbol;
}
public GlPrimitiveType getBase() {
return base;
}
public GlPrimitiveType getBase() {
return base;
}
public int getCount() {
return count;
}
public int getCount() {
return count;
}
public int getSize() {
return size;
}
public int getSize() {
return size;
}
public int getAttributeCount() {
return attributeCount;
}
public int getAttributeCount() {
return attributeCount;
}
}

View file

@ -11,117 +11,119 @@ import net.minecraft.util.ResourceLocation;
public abstract class GlProgram extends GlObject {
public final ResourceLocation name;
public final ResourceLocation name;
protected GlProgram(ResourceLocation name, int handle) {
setHandle(handle);
this.name = name;
}
protected GlProgram(ResourceLocation name, int handle) {
setHandle(handle);
this.name = name;
}
public static Builder builder(ResourceLocation name, GlFogMode fogMode) {
return new Builder(name, fogMode);
}
public static Builder builder(ResourceLocation name, GlFogMode fogMode) {
return new Builder(name, fogMode);
}
public void bind() {
GL20.glUseProgram(handle());
}
public void bind() {
GL20.glUseProgram(handle());
}
public void unbind() {
GL20.glUseProgram(0);
}
public void unbind() {
GL20.glUseProgram(0);
}
/**
* 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
*/
public int getUniformLocation(String uniform) {
int index = GL20.glGetUniformLocation(this.handle(), uniform);
/**
* 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
*/
public int getUniformLocation(String uniform) {
int index = GL20.glGetUniformLocation(this.handle(), uniform);
if (index < 0) {
Backend.log.debug("No active uniform '{}' exists in program '{}'. Could be unused.", uniform, this.name);
}
if (index < 0) {
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.
* @param name The name of the sampler uniform.
* @param binding The index of the texture unit.
* @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);
/**
* 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.
* @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);
if (samplerUniform >= 0) {
GL20.glUniform1i(samplerUniform, binding);
}
if (samplerUniform >= 0) {
GL20.glUniform1i(samplerUniform, binding);
}
return samplerUniform;
}
return samplerUniform;
}
@Override
protected void deleteInternal(int handle) {
GL20.glDeleteProgram(handle);
}
@Override
protected void deleteInternal(int handle) {
GL20.glDeleteProgram(handle);
}
public static class Builder {
private final ResourceLocation name;
private final int program;
private final GlFogMode fogMode;
public static class Builder {
private final ResourceLocation name;
private final int program;
private final GlFogMode fogMode;
private int attributeIndex;
private int attributeIndex;
public Builder(ResourceLocation name, GlFogMode fogMode) {
this.name = name;
this.program = GL20.glCreateProgram();
this.fogMode = fogMode;
}
public Builder(ResourceLocation name, GlFogMode fogMode) {
this.name = name;
this.program = GL20.glCreateProgram();
this.fogMode = fogMode;
}
public Builder attachShader(GlShader shader) {
GL20.glAttachShader(this.program, shader.handle());
public Builder attachShader(GlShader shader) {
GL20.glAttachShader(this.program, shader.handle());
return this;
}
return this;
}
public <A extends IVertexAttrib> Builder addAttribute(A attrib) {
GL20.glBindAttribLocation(this.program, attributeIndex, attrib.attribName());
attributeIndex += attrib.attribSpec().getAttributeCount();
return this;
}
public <A extends IVertexAttrib> Builder addAttribute(A attrib) {
GL20.glBindAttribLocation(this.program, attributeIndex, attrib.attribName());
attributeIndex += attrib.attribSpec().getAttributeCount();
return this;
}
/**
* 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
* set.
*
* @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
* @return An instantiated shader container as provided by the factory
*/
public <P extends GlProgram> P build(ProgramFactory<P> factory) {
GL20.glLinkProgram(this.program);
/**
* 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
* set.
*
* @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
* @return An instantiated shader container as provided by the factory
*/
public <P extends GlProgram> P build(ProgramFactory<P> factory) {
GL20.glLinkProgram(this.program);
String log = GL20.glGetProgramInfoLog(this.program);
String log = GL20.glGetProgramInfoLog(this.program);
if (!log.isEmpty()) {
Backend.log.debug("Program link log for " + this.name + ": " + log);
}
if (!log.isEmpty()) {
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) {
throw new RuntimeException("Shader program linking failed, see log for details");
}
if (result != GL20.GL_TRUE) {
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
public interface ProgramFactory<P extends GlProgram> {
P create(ResourceLocation name, int handle, ProgramFogMode.Factory fogFactory);
}
@FunctionalInterface
public interface ProgramFactory<P extends GlProgram> {
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 final ResourceLocation name;
public final ShaderType type;
public final ResourceLocation name;
public final ShaderType type;
public GlShader(ShaderType type, ResourceLocation name, String source) {
this.type = type;
this.name = name;
int handle = GL20.glCreateShader(type.glEnum);
public GlShader(ShaderType type, ResourceLocation name, String source) {
this.type = type;
this.name = name;
int handle = GL20.glCreateShader(type.glEnum);
GlCompat.safeShaderSource(handle, source);
GL20.glCompileShader(handle);
GlCompat.safeShaderSource(handle, source);
GL20.glCompileShader(handle);
String log = GL20.glGetShaderInfoLog(handle);
String log = GL20.glGetShaderInfoLog(handle);
if (!log.isEmpty()) {
Backend.log.error("Shader compilation log for " + name + ": " + log);
}
if (!log.isEmpty()) {
Backend.log.error("Shader compilation log for " + name + ": " + log);
}
if (GL20.glGetShaderi(handle, GL20.GL_COMPILE_STATUS) != GL20.GL_TRUE) {
throw new RuntimeException("Could not compile shader. See log for details.");
}
if (GL20.glGetShaderi(handle, GL20.GL_COMPILE_STATUS) != GL20.GL_TRUE) {
throw new RuntimeException("Could not compile shader. See log for details.");
}
setHandle(handle);
}
setHandle(handle);
}
@Override
protected void deleteInternal(int handle) {
GL20.glDeleteShader(handle);
}
@Override
protected void deleteInternal(int handle) {
GL20.glDeleteShader(handle);
}
@FunctionalInterface
public interface PreProcessor {
String process(String source);
@FunctionalInterface
public interface PreProcessor {
String process(String source);
default PreProcessor andThen(PreProcessor that) {
return source -> that.process(this.process(source));
}
}
default PreProcessor andThen(PreProcessor that) {
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.
*
* @return The one true program.
*/
P get();

View file

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

View file

@ -10,75 +10,75 @@ import net.minecraft.util.ResourceLocation;
public class ProgramSpec<P extends GlProgram> {
public final ResourceLocation name;
public final ResourceLocation vert;
public final ResourceLocation frag;
public final ResourceLocation name;
public final ResourceLocation vert;
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) {
return builder(new ResourceLocation(Create.ID, name), factory);
}
public static <P extends GlProgram> Builder<P> builder(String name, GlProgram.ProgramFactory<P> factory) {
return builder(new ResourceLocation(Create.ID, name), factory);
}
public static <P extends GlProgram> Builder<P> builder(ResourceLocation name, GlProgram.ProgramFactory<P> factory) {
return new Builder<>(name, factory);
}
public static <P extends GlProgram> Builder<P> builder(ResourceLocation name, GlProgram.ProgramFactory<P> 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) {
this.name = name;
this.vert = vert;
this.frag = frag;
this.defines = defines;
public ProgramSpec(ResourceLocation name, ResourceLocation vert, ResourceLocation frag, GlProgram.ProgramFactory<P> factory, ShaderConstants defines, ArrayList<IVertexAttrib> attributes, boolean fogSensitive) {
this.name = name;
this.vert = vert;
this.frag = frag;
this.defines = defines;
this.factory = factory;
this.attributes = attributes;
this.factory = factory;
this.attributes = attributes;
this.fogSensitive = fogSensitive;
}
public ResourceLocation getVert() {
return vert;
}
public ResourceLocation getVert() {
return vert;
}
public ResourceLocation getFrag() {
return frag;
}
public ResourceLocation getFrag() {
return frag;
}
public static class Builder<P extends GlProgram> {
private ResourceLocation vert;
private ResourceLocation frag;
private ShaderConstants defines = ShaderConstants.EMPTY;
private boolean fogSensitive = true;
public static class Builder<P extends GlProgram> {
private ResourceLocation vert;
private ResourceLocation frag;
private ShaderConstants defines = ShaderConstants.EMPTY;
private boolean fogSensitive = true;
private final ResourceLocation name;
private final GlProgram.ProgramFactory<P> factory;
private final ArrayList<IVertexAttrib> attributes;
private final ResourceLocation name;
private final GlProgram.ProgramFactory<P> factory;
private final ArrayList<IVertexAttrib> attributes;
public Builder(ResourceLocation name, GlProgram.ProgramFactory<P> factory) {
this.name = name;
this.factory = factory;
attributes = new ArrayList<>();
}
public Builder(ResourceLocation name, GlProgram.ProgramFactory<P> factory) {
this.name = name;
this.factory = factory;
attributes = new ArrayList<>();
}
public Builder<P> setVert(ResourceLocation vert) {
this.vert = vert;
return this;
}
public Builder<P> setVert(ResourceLocation vert) {
this.vert = vert;
return this;
}
public Builder<P> setFrag(ResourceLocation frag) {
this.frag = frag;
return this;
}
public Builder<P> setFrag(ResourceLocation frag) {
this.frag = frag;
return this;
}
public Builder<P> setDefines(ShaderConstants defines) {
this.defines = defines;
return this;
}
public Builder<P> setDefines(ShaderConstants defines) {
this.defines = defines;
return this;
}
public Builder<P> setFogSensitive(boolean 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) {
attributes.addAll(Arrays.asList(attributeEnum.getEnumConstants()));
return this;
}
attributes.addAll(Arrays.asList(attributeEnum.getEnumConstants()));
return this;
}
public ProgramSpec<P> createProgramSpec() {
return new ProgramSpec<>(name, vert, frag, factory, defines, attributes, fogSensitive);
}
}
public ProgramSpec<P> createProgramSpec() {
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
public interface ShaderCallback<P extends GlProgram> {
void call(P program);
void call(P program);
default ShaderCallback<P> andThen(ShaderCallback<P> other) {
return program -> {
call(program);
other.call(program);
};
}
default ShaderCallback<P> andThen(ShaderCallback<P> other) {
return program -> {
call(program);
other.call(program);
};
}
}

View file

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

View file

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

View file

@ -6,52 +6,50 @@ import org.lwjgl.opengl.GL31;
import org.lwjgl.opengl.GLCapabilities;
public enum DrawInstanced implements GlVersioned {
GL31_DRAW_INSTANCED {
@Override
public boolean supported(GLCapabilities caps) {
return caps.OpenGL31;
}
GL31_DRAW_INSTANCED {
@Override
public boolean supported(GLCapabilities caps) {
return caps.OpenGL31;
}
@Override
public void drawArraysInstanced(int mode, int first, int count, int primcount) {
GL31.glDrawArraysInstanced(mode, first, count, primcount);
}
},
ARB_DRAW_INSTANCED {
@Override
public boolean supported(GLCapabilities caps) {
return caps.GL_ARB_draw_instanced;
}
@Override
public void drawArraysInstanced(int mode, int first, int count, int primcount) {
GL31.glDrawArraysInstanced(mode, first, count, primcount);
}
},
ARB_DRAW_INSTANCED {
@Override
public boolean supported(GLCapabilities caps) {
return caps.GL_ARB_draw_instanced;
}
@Override
public void drawArraysInstanced(int mode, int first, int count, int primcount) {
ARBDrawInstanced.glDrawArraysInstancedARB(mode, first, count, primcount);
}
},
EXT_DRAW_INSTANCED {
@Override
public boolean supported(GLCapabilities caps) {
return caps.GL_EXT_draw_instanced;
}
@Override
public void drawArraysInstanced(int mode, int first, int count, int primcount) {
ARBDrawInstanced.glDrawArraysInstancedARB(mode, first, count, primcount);
}
},
EXT_DRAW_INSTANCED {
@Override
public boolean supported(GLCapabilities caps) {
return caps.GL_EXT_draw_instanced;
}
@Override
public void drawArraysInstanced(int mode, int first, int count, int primcount) {
EXTDrawInstanced.glDrawArraysInstancedEXT(mode, first, count, primcount);
}
},
UNSUPPORTED {
@Override
public boolean supported(GLCapabilities caps) {
return true;
}
@Override
public void drawArraysInstanced(int mode, int first, int count, int primcount) {
EXTDrawInstanced.glDrawArraysInstancedEXT(mode, first, count, primcount);
}
},
UNSUPPORTED {
@Override
public boolean supported(GLCapabilities caps) {
return true;
}
@Override
public void drawArraysInstanced(int mode, int first, int count, int primcount) {
throw new UnsupportedOperationException();
}
}
@Override
public void drawArraysInstanced(int mode, int first, int count, int primcount) {
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
* about what OpenGL features are available.
*
* <p>
* Each field stores an enum variant that provides access to the
* most appropriate version of a feature for the current system.
*/
public class GlCompat {
public final MapBuffer mapBuffer;
public final MapBuffer mapBuffer;
public final VertexArrayObject vertexArrayObject;
public final InstancedArrays instancedArrays;
public final DrawInstanced drawInstanced;
public final VertexArrayObject vertexArrayObject;
public final InstancedArrays instancedArrays;
public final DrawInstanced drawInstanced;
public final RGPixelFormat pixelFormat;
public final RGPixelFormat pixelFormat;
public GlCompat(GLCapabilities caps) {
mapBuffer = getLatest(MapBuffer.class, caps);
public GlCompat(GLCapabilities caps) {
mapBuffer = getLatest(MapBuffer.class, caps);
vertexArrayObject = getLatest(VertexArrayObject.class, caps);
instancedArrays = getLatest(InstancedArrays.class, caps);
drawInstanced = getLatest(DrawInstanced.class, caps);
vertexArrayObject = getLatest(VertexArrayObject.class, caps);
instancedArrays = getLatest(InstancedArrays.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) {
mapBuffer.mapBuffer(target, offset, length, upload);
}
public void mapBuffer(int target, int offset, int length, Consumer<ByteBuffer> upload) {
mapBuffer.mapBuffer(target, offset, length, upload);
}
public void vertexAttribDivisor(int index, int divisor) {
instancedArrays.vertexAttribDivisor(index, divisor);
}
public void vertexAttribDivisor(int index, int divisor) {
instancedArrays.vertexAttribDivisor(index, divisor);
}
public void drawArraysInstanced(int mode, int first, int count, int primcount) {
drawInstanced.drawArraysInstanced(mode, first, count, primcount);
}
public void drawArraysInstanced(int mode, int first, int count, int primcount) {
drawInstanced.drawArraysInstanced(mode, first, count, primcount);
}
public int genVertexArrays() {
return vertexArrayObject.genVertexArrays();
}
public int genVertexArrays() {
return vertexArrayObject.genVertexArrays();
}
public void deleteVertexArrays(int array) {
vertexArrayObject.deleteVertexArrays(array);
}
public void deleteVertexArrays(int array) {
vertexArrayObject.deleteVertexArrays(array);
}
public void bindVertexArray(int array) {
vertexArrayObject.bindVertexArray(array);
}
public void bindVertexArray(int array) {
vertexArrayObject.bindVertexArray(array);
}
public boolean vertexArrayObjectsSupported() {
return vertexArrayObject != VertexArrayObject.UNSUPPORTED;
}
public boolean vertexArrayObjectsSupported() {
return vertexArrayObject != VertexArrayObject.UNSUPPORTED;
}
public boolean instancedArraysSupported() {
return instancedArrays != InstancedArrays.UNSUPPORTED;
}
public boolean instancedArraysSupported() {
return instancedArrays != InstancedArrays.UNSUPPORTED;
}
public boolean drawInstancedSupported() {
return drawInstanced != DrawInstanced.UNSUPPORTED;
}
public boolean drawInstancedSupported() {
return drawInstanced != DrawInstanced.UNSUPPORTED;
}
/**
* 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 caps The current system's supported features.
* @param <V> The type of the versioning enum.
* @return The first defined enum variant to return true.
*/
public static <V extends Enum<V> & GlVersioned> V getLatest(Class<V> clazz, GLCapabilities caps) {
V[] constants = clazz.getEnumConstants();
V last = constants[constants.length - 1];
if (!last.supported(caps)) {
throw new IllegalStateException("");
}
/**
* 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 caps The current system's supported features.
* @param <V> The type of the versioning enum.
* @return The first defined enum variant to return true.
*/
public static <V extends Enum<V> & GlVersioned> V getLatest(Class<V> clazz, GLCapabilities caps) {
V[] constants = clazz.getEnumConstants();
V last = constants[constants.length - 1];
if (!last.supported(caps)) {
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:
* <br> https://github.com/grondag/canvas/commit/820bf754092ccaf8d0c169620c2ff575722d7d96
*
* <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
* 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
* an access violation when the driver tries to read past the string memory.
*
* <p>Hat tip to fewizz for the find and the fix.
*/
public static void safeShaderSource(int glId, CharSequence source) {
final MemoryStack stack = MemoryStack.stackGet();
final int stackPointer = stack.getPointer();
/**
* Copied from:
* <br> https://github.com/grondag/canvas/commit/820bf754092ccaf8d0c169620c2ff575722d7d96
*
* <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
* 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
* an access violation when the driver tries to read past the string memory.
*
* <p>Hat tip to fewizz for the find and the fix.
*/
public static void safeShaderSource(int glId, CharSequence source) {
final MemoryStack stack = MemoryStack.stackGet();
final int stackPointer = stack.getPointer();
try {
final ByteBuffer sourceBuffer = MemoryUtil.memUTF8(source, true);
final PointerBuffer pointers = stack.mallocPointer(1);
pointers.put(sourceBuffer);
try {
final ByteBuffer sourceBuffer = MemoryUtil.memUTF8(source, true);
final PointerBuffer pointers = stack.mallocPointer(1);
pointers.put(sourceBuffer);
GL20C.nglShaderSource(glId, 1, pointers.address0(), 0);
org.lwjgl.system.APIUtil.apiArrayFree(pointers.address0(), 1);
} finally {
stack.setPointer(stackPointer);
}
}
GL20C.nglShaderSource(glId, 1, pointers.address0(), 0);
org.lwjgl.system.APIUtil.apiArrayFree(pointers.address0(), 1);
} finally {
stack.setPointer(stackPointer);
}
}
}

View file

@ -7,10 +7,11 @@ import org.lwjgl.opengl.GLCapabilities;
* last defined variant <em>always</em> returns <code>true</code>.
*/
public interface GlVersioned {
/**
* 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.
*/
boolean supported(GLCapabilities caps);
/**
* 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.
*/
boolean supported(GLCapabilities caps);
}

View file

@ -5,41 +5,39 @@ import org.lwjgl.opengl.GL33;
import org.lwjgl.opengl.GLCapabilities;
public enum InstancedArrays implements GlVersioned {
GL33_INSTANCED_ARRAYS {
@Override
public boolean supported(GLCapabilities caps) {
return caps.OpenGL33;
}
GL33_INSTANCED_ARRAYS {
@Override
public boolean supported(GLCapabilities caps) {
return caps.OpenGL33;
}
@Override
public void vertexAttribDivisor(int index, int divisor) {
GL33.glVertexAttribDivisor(index, divisor);
}
},
ARB_INSTANCED_ARRAYS {
@Override
public boolean supported(GLCapabilities caps) {
return caps.GL_ARB_instanced_arrays;
}
@Override
public void vertexAttribDivisor(int index, int divisor) {
GL33.glVertexAttribDivisor(index, divisor);
}
},
ARB_INSTANCED_ARRAYS {
@Override
public boolean supported(GLCapabilities caps) {
return caps.GL_ARB_instanced_arrays;
}
@Override
public void vertexAttribDivisor(int index, int divisor) {
ARBInstancedArrays.glVertexAttribDivisorARB(index, divisor);
}
},
UNSUPPORTED {
@Override
public boolean supported(GLCapabilities caps) {
return true;
}
@Override
public void vertexAttribDivisor(int index, int divisor) {
ARBInstancedArrays.glVertexAttribDivisorARB(index, divisor);
}
},
UNSUPPORTED {
@Override
public boolean supported(GLCapabilities caps) {
return true;
}
@Override
public void vertexAttribDivisor(int index, int divisor) {
throw new UnsupportedOperationException();
}
}
@Override
public void vertexAttribDivisor(int index, int divisor) {
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 {
GL30_RANGE {
@Override
public boolean supported(GLCapabilities caps) {
return caps.OpenGL30;
}
GL30_RANGE {
@Override
public boolean supported(GLCapabilities caps) {
return caps.OpenGL30;
}
@Override
public void mapBuffer(int target, int offset, int length, Consumer<ByteBuffer> upload) {
ByteBuffer buffer = GL30.glMapBufferRange(target, offset, length, GL30.GL_MAP_WRITE_BIT);
@Override
public void mapBuffer(int target, int offset, int length, Consumer<ByteBuffer> upload) {
ByteBuffer buffer = GL30.glMapBufferRange(target, offset, length, GL30.GL_MAP_WRITE_BIT);
upload.accept(buffer);
buffer.rewind();
upload.accept(buffer);
buffer.rewind();
GL30.glUnmapBuffer(target);
}
},
ARB_RANGE {
@Override
public boolean supported(GLCapabilities caps) {
return caps.GL_ARB_map_buffer_range;
}
GL30.glUnmapBuffer(target);
}
},
ARB_RANGE {
@Override
public boolean supported(GLCapabilities caps) {
return caps.GL_ARB_map_buffer_range;
}
@Override
public void mapBuffer(int target, int offset, int length, Consumer<ByteBuffer> upload) {
ByteBuffer buffer = ARBMapBufferRange.glMapBufferRange(target, offset, length, GL30.GL_MAP_WRITE_BIT);
@Override
public void mapBuffer(int target, int offset, int length, Consumer<ByteBuffer> upload) {
ByteBuffer buffer = ARBMapBufferRange.glMapBufferRange(target, offset, length, GL30.GL_MAP_WRITE_BIT);
upload.accept(buffer);
buffer.rewind();
upload.accept(buffer);
buffer.rewind();
GL30.glUnmapBuffer(target);
}
},
GL15_MAP {
@Override
public boolean supported(GLCapabilities caps) {
return caps.OpenGL15;
}
GL30.glUnmapBuffer(target);
}
},
GL15_MAP {
@Override
public boolean supported(GLCapabilities caps) {
return caps.OpenGL15;
}
@Override
public void mapBuffer(int target, int offset, int length, Consumer<ByteBuffer> upload) {
ByteBuffer buffer = GL15.glMapBuffer(target, GL15.GL_WRITE_ONLY);
@Override
public void mapBuffer(int target, int offset, int length, Consumer<ByteBuffer> upload) {
ByteBuffer buffer = GL15.glMapBuffer(target, GL15.GL_WRITE_ONLY);
buffer.position(offset);
upload.accept(buffer);
buffer.rewind();
GL15.glUnmapBuffer(target);
}
},
UNSUPPORTED {
@Override
public boolean supported(GLCapabilities caps) {
return true;
}
buffer.position(offset);
upload.accept(buffer);
buffer.rewind();
GL15.glUnmapBuffer(target);
}
},
UNSUPPORTED {
@Override
public boolean supported(GLCapabilities caps) {
return true;
}
@Override
public void mapBuffer(int target, int offset, int length, Consumer<ByteBuffer> upload) {
throw new UnsupportedOperationException("glMapBuffer not supported");
}
};
@Override
public void mapBuffer(int target, int offset, int length, Consumer<ByteBuffer> upload) {
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;
public enum RGPixelFormat implements GlVersioned {
GL30_RG {
@Override
public boolean supported(GLCapabilities caps) {
return caps.OpenGL30;
}
GL30_RG {
@Override
public boolean supported(GLCapabilities caps) {
return caps.OpenGL30;
}
@Override
public int internalFormat() {
return GL30.GL_RG8;
}
@Override
public int internalFormat() {
return GL30.GL_RG8;
}
@Override
public int format() {
return GL30.GL_RG;
}
@Override
public int format() {
return GL30.GL_RG;
}
@Override
public int byteCount() {
return 2;
}
},
GL11_RGB {
@Override
public boolean supported(GLCapabilities caps) {
return caps.OpenGL11;
}
@Override
public int byteCount() {
return 2;
}
},
GL11_RGB {
@Override
public boolean supported(GLCapabilities caps) {
return caps.OpenGL11;
}
@Override
public int internalFormat() {
return GL11.GL_RGB8;
}
@Override
public int internalFormat() {
return GL11.GL_RGB8;
}
@Override
public int format() {
return GL11.GL_RGB;
}
@Override
public int format() {
return GL11.GL_RGB;
}
@Override
public int byteCount() {
return 3;
}
},
UNSUPPORTED {
@Override
public boolean supported(GLCapabilities caps) {
return true;
}
@Override
public int byteCount() {
return 3;
}
},
UNSUPPORTED {
@Override
public boolean supported(GLCapabilities caps) {
return true;
}
@Override
public int internalFormat() {
throw new UnsupportedOperationException();
}
@Override
public int internalFormat() {
throw new UnsupportedOperationException();
}
@Override
public int format() {
throw new UnsupportedOperationException();
}
@Override
public int format() {
throw new UnsupportedOperationException();
}
@Override
public int byteCount() {
throw new UnsupportedOperationException();
}
}
@Override
public int byteCount() {
throw new UnsupportedOperationException();
}
};
;
public abstract int internalFormat();
public abstract int internalFormat();
public abstract int format();
public abstract int byteCount();
public abstract int format();
public abstract int byteCount();
}

View file

@ -5,75 +5,73 @@ import org.lwjgl.opengl.GL30;
import org.lwjgl.opengl.GLCapabilities;
public enum VertexArrayObject implements GlVersioned {
GL30_VAO {
@Override
public boolean supported(GLCapabilities caps) {
return caps.OpenGL30;
}
GL30_VAO {
@Override
public boolean supported(GLCapabilities caps) {
return caps.OpenGL30;
}
@Override
public int genVertexArrays() {
return GL30.glGenVertexArrays();
}
@Override
public int genVertexArrays() {
return GL30.glGenVertexArrays();
}
@Override
public void bindVertexArray(int array) {
GL30.glBindVertexArray(array);
}
@Override
public void bindVertexArray(int array) {
GL30.glBindVertexArray(array);
}
@Override
public void deleteVertexArrays(int array) {
GL30.glDeleteVertexArrays(array);
}
},
ARB_VAO {
@Override
public boolean supported(GLCapabilities caps) {
return caps.GL_ARB_vertex_array_object;
}
@Override
public void deleteVertexArrays(int array) {
GL30.glDeleteVertexArrays(array);
}
},
ARB_VAO {
@Override
public boolean supported(GLCapabilities caps) {
return caps.GL_ARB_vertex_array_object;
}
@Override
public int genVertexArrays() {
return ARBVertexArrayObject.glGenVertexArrays();
}
@Override
public int genVertexArrays() {
return ARBVertexArrayObject.glGenVertexArrays();
}
@Override
public void bindVertexArray(int array) {
ARBVertexArrayObject.glBindVertexArray(array);
}
@Override
public void bindVertexArray(int array) {
ARBVertexArrayObject.glBindVertexArray(array);
}
@Override
public void deleteVertexArrays(int array) {
ARBVertexArrayObject.glDeleteVertexArrays(array);
}
},
UNSUPPORTED {
@Override
public boolean supported(GLCapabilities caps) {
return true;
}
@Override
public void deleteVertexArrays(int array) {
ARBVertexArrayObject.glDeleteVertexArrays(array);
}
},
UNSUPPORTED {
@Override
public boolean supported(GLCapabilities caps) {
return true;
}
@Override
public int genVertexArrays() {
throw new UnsupportedOperationException();
}
@Override
public int genVertexArrays() {
throw new UnsupportedOperationException();
}
@Override
public void bindVertexArray(int array) {
throw new UnsupportedOperationException();
}
@Override
public void bindVertexArray(int array) {
throw new UnsupportedOperationException();
}
@Override
public void deleteVertexArrays(int array) {
throw new UnsupportedOperationException();
}
}
@Override
public void deleteVertexArrays(int array) {
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.
*/
public interface IDynamicInstance extends IInstance {
/**
* Called every frame.
*/
void beginFrame();
/**
* Called every frame.
*/
void beginFrame();
/**
* As a further optimization, dynamic instances that are far away are ticked less often.
* 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
* 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.
*/
default boolean decreaseFramerateWithDistance() {
return true;
}
/**
* As a further optimization, dynamic instances that are far away are ticked less often.
* 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
* 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.
*/
default boolean decreaseFramerateWithDistance() {
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.
*/
public interface IFlywheelWorld {
default boolean supportsFlywheel() {
return true;
}
default boolean supportsFlywheel() {
return true;
}
}

View file

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

View file

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

View file

@ -10,22 +10,22 @@ import net.minecraft.tileentity.TileEntity;
import net.minecraft.tileentity.TileEntityType;
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) {
this.renderers.put(type, rendererFactory);
}
public <T extends TileEntity> void register(TileEntityType<? extends T> type, IRendererFactory<? super T> rendererFactory) {
this.renderers.put(type, rendererFactory);
}
@SuppressWarnings("unchecked")
@Nullable
public <T extends TileEntity> TileEntityInstance<? super T> create(InstancedTileRenderer<?> manager, T tile) {
TileEntityType<?> type = tile.getType();
IRendererFactory<? super T> factory = (IRendererFactory<? super T>) this.renderers.get(type);
@SuppressWarnings("unchecked")
@Nullable
public <T extends TileEntity> TileEntityInstance<? super T> create(InstancedTileRenderer<?> manager, T tile) {
TileEntityType<?> type = tile.getType();
IRendererFactory<? super T> factory = (IRendererFactory<? super T>) this.renderers.get(type);
if (factory == null) return null;
else return factory.create(manager, tile);
}
if (factory == null) return null;
else return factory.create(manager, tile);
}
}

View file

@ -25,259 +25,259 @@ import net.minecraft.world.IBlockReader;
import net.minecraft.world.World;
public abstract class InstancedTileRenderer<P extends BasicProgram> {
protected ArrayList<TileEntity> queuedAdditions = new ArrayList<>(64);
protected Map<TileEntity, TileEntityInstance<?>> instances = new HashMap<>();
protected Map<TileEntity, ITickableInstance> tickableInstances = new HashMap<>();
protected Map<TileEntity, IDynamicInstance> dynamicInstances = new HashMap<>();
protected ArrayList<TileEntity> queuedAdditions = new ArrayList<>(64);
protected Map<TileEntity, TileEntityInstance<?>> instances = new HashMap<>();
protected Map<TileEntity, ITickableInstance> tickableInstances = new HashMap<>();
protected Map<TileEntity, IDynamicInstance> dynamicInstances = new HashMap<>();
protected Map<MaterialType<?>, RenderMaterial<P, ?>> materials = new HashMap<>();
protected int frame;
protected int tick;
protected InstancedTileRenderer() {
registerMaterials();
}
public abstract BlockPos getOriginCoordinate();
public abstract void registerMaterials();
public void tick(double cameraX, double cameraY, double cameraZ) {
tick++;
// integer camera pos
int cX = (int) cameraX;
int cY = (int) cameraY;
int cZ = (int) cameraZ;
if (tickableInstances.size() > 0) {
for (ITickableInstance instance : tickableInstances.values()) {
if (!instance.decreaseTickRateWithDistance()) {
instance.tick();
continue;
}
BlockPos pos = instance.getWorldPosition();
int dX = pos.getX() - cX;
int dY = pos.getY() - cY;
int dZ = pos.getZ() - cZ;
protected Map<MaterialType<?>, RenderMaterial<P, ?>> materials = new HashMap<>();
protected int frame;
protected int tick;
protected InstancedTileRenderer() {
registerMaterials();
}
public abstract BlockPos getOriginCoordinate();
public abstract void registerMaterials();
public void tick(double cameraX, double cameraY, double cameraZ) {
tick++;
// integer camera pos
int cX = (int) cameraX;
int cY = (int) cameraY;
int cZ = (int) cameraZ;
if (tickableInstances.size() > 0) {
for (ITickableInstance instance : tickableInstances.values()) {
if (!instance.decreaseTickRateWithDistance()) {
instance.tick();
continue;
}
BlockPos pos = instance.getWorldPosition();
int dX = pos.getX() - cX;
int dY = pos.getY() - cY;
int dZ = pos.getZ() - cZ;
if ((tick % getUpdateDivisor(dX, dY, dZ)) == 0)
instance.tick();
}
}
}
if ((tick % getUpdateDivisor(dX, dY, dZ)) == 0)
instance.tick();
}
}
}
public void beginFrame(ActiveRenderInfo info, double cameraX, double cameraY, double cameraZ) {
frame++;
processQueuedAdditions();
Vector3f look = info.getHorizontalPlane();
float lookX = look.getX();
float lookY = look.getY();
float lookZ = look.getZ();
// integer camera pos
int cX = (int) cameraX;
int cY = (int) cameraY;
int cZ = (int) cameraZ;
if (dynamicInstances.size() > 0) {
for (IDynamicInstance dyn : dynamicInstances.values()) {
if (!dyn.decreaseFramerateWithDistance()) {
dyn.beginFrame();
continue;
}
if (shouldTick(dyn.getWorldPosition(), lookX, lookY, lookZ, cX, cY, cZ))
dyn.beginFrame();
}
}
}
public void render(RenderType layer, Matrix4f viewProjection, double camX, double camY, double camZ) {
render(layer, viewProjection, camX, camY, camZ, null);
}
public void render(RenderType layer, Matrix4f viewProjection, double camX, double camY, double camZ, ShaderCallback<P> callback) {
for (RenderMaterial<P, ?> material : materials.values()) {
if (material.canRenderInLayer(layer))
material.render(layer, viewProjection, camX, camY, camZ, callback);
}
}
public void beginFrame(ActiveRenderInfo info, double cameraX, double cameraY, double cameraZ) {
frame++;
processQueuedAdditions();
Vector3f look = info.getHorizontalPlane();
float lookX = look.getX();
float lookY = look.getY();
float lookZ = look.getZ();
// integer camera pos
int cX = (int) cameraX;
int cY = (int) cameraY;
int cZ = (int) cameraZ;
if (dynamicInstances.size() > 0) {
for (IDynamicInstance dyn : dynamicInstances.values()) {
if (!dyn.decreaseFramerateWithDistance()) {
dyn.beginFrame();
continue;
}
if (shouldTick(dyn.getWorldPosition(), lookX, lookY, lookZ, cX, cY, cZ))
dyn.beginFrame();
}
}
}
public void render(RenderType layer, Matrix4f viewProjection, double camX, double camY, double camZ) {
render(layer, viewProjection, camX, camY, camZ, null);
}
public void render(RenderType layer, Matrix4f viewProjection, double camX, double camY, double camZ, ShaderCallback<P> callback) {
for (RenderMaterial<P, ?> material : materials.values()) {
if (material.canRenderInLayer(layer))
material.render(layer, viewProjection, camX, camY, camZ, callback);
}
}
@SuppressWarnings("unchecked")
public <M extends InstancedModel<?>> RenderMaterial<P, M> getMaterial(MaterialType<M> materialType) {
return (RenderMaterial<P, M>) materials.get(materialType);
}
@SuppressWarnings("unchecked")
public <M extends InstancedModel<?>> RenderMaterial<P, M> getMaterial(MaterialType<M> materialType) {
return (RenderMaterial<P, M>) materials.get(materialType);
}
public RenderMaterial<P, InstancedModel<ModelData>> getTransformMaterial() {
return getMaterial(MaterialTypes.TRANSFORMED);
}
public RenderMaterial<P, InstancedModel<ModelData>> getTransformMaterial() {
return getMaterial(MaterialTypes.TRANSFORMED);
}
public RenderMaterial<P, InstancedModel<OrientedData>> getOrientedMaterial() {
return getMaterial(MaterialTypes.ORIENTED);
}
public RenderMaterial<P, InstancedModel<OrientedData>> getOrientedMaterial() {
return getMaterial(MaterialTypes.ORIENTED);
}
@SuppressWarnings("unchecked")
@Nullable
public <T extends TileEntity> TileEntityInstance<? super T> getInstance(T tile, boolean create) {
if (!Backend.canUseInstancing()) return null;
@SuppressWarnings("unchecked")
@Nullable
public <T extends TileEntity> TileEntityInstance<? super T> getInstance(T tile, boolean create) {
if (!Backend.canUseInstancing()) return null;
TileEntityInstance<?> instance = instances.get(tile);
if (instance != null) {
return (TileEntityInstance<? super T>) instance;
} else if (create && canCreateInstance(tile)) {
return createInternal(tile);
} else {
return null;
}
}
public <T extends TileEntity> void onLightUpdate(T tile) {
if (!Backend.canUseInstancing()) return;
if (tile instanceof IInstanceRendered) {
TileEntityInstance<? super T> instance = getInstance(tile, false);
if (instance != null)
instance.updateLight();
}
}
public <T extends TileEntity> void add(T tile) {
if (!Backend.canUseInstancing()) return;
if (tile instanceof IInstanceRendered) {
addInternal(tile);
}
}
public <T extends TileEntity> void update(T tile) {
if (!Backend.canUseInstancing()) return;
if (tile instanceof IInstanceRendered) {
TileEntityInstance<? super T> instance = getInstance(tile, false);
if (instance != null) {
if (instance.shouldReset()) {
removeInternal(tile, instance);
createInternal(tile);
} else {
instance.update();
}
}
}
}
public <T extends TileEntity> void remove(T tile) {
if (!Backend.canUseInstancing()) return;
TileEntityInstance<?> instance = instances.get(tile);
if (instance != null) {
return (TileEntityInstance<? super T>) instance;
} else if (create && canCreateInstance(tile)) {
return createInternal(tile);
} else {
return null;
}
}
public <T extends TileEntity> void onLightUpdate(T tile) {
if (!Backend.canUseInstancing()) return;
if (tile instanceof IInstanceRendered) {
TileEntityInstance<? super T> instance = getInstance(tile, false);
if (instance != null)
instance.updateLight();
}
}
public <T extends TileEntity> void add(T tile) {
if (!Backend.canUseInstancing()) return;
if (tile instanceof IInstanceRendered) {
addInternal(tile);
}
}
public <T extends TileEntity> void update(T tile) {
if (!Backend.canUseInstancing()) return;
if (tile instanceof IInstanceRendered) {
TileEntityInstance<? super T> instance = getInstance(tile, false);
if (instance != null) {
if (instance.shouldReset()) {
removeInternal(tile, instance);
createInternal(tile);
} else {
instance.update();
}
}
}
}
public <T extends TileEntity> void remove(T tile) {
if (!Backend.canUseInstancing()) return;
if (tile instanceof IInstanceRendered) {
removeInternal(tile);
}
}
if (tile instanceof IInstanceRendered) {
removeInternal(tile);
}
}
public synchronized <T extends TileEntity> void queueAdd(T tile) {
if (!Backend.canUseInstancing()) return;
public synchronized <T extends TileEntity> void queueAdd(T tile) {
if (!Backend.canUseInstancing()) return;
queuedAdditions.add(tile);
}
queuedAdditions.add(tile);
}
protected synchronized void processQueuedAdditions() {
if (queuedAdditions.size() > 0) {
queuedAdditions.forEach(this::addInternal);
queuedAdditions.clear();
}
}
protected synchronized void processQueuedAdditions() {
if (queuedAdditions.size() > 0) {
queuedAdditions.forEach(this::addInternal);
queuedAdditions.clear();
}
}
protected boolean shouldTick(BlockPos worldPos, float lookX, float lookY, float lookZ, int cX, int cY, int cZ) {
int dX = worldPos.getX() - cX;
int dY = worldPos.getY() - cY;
int dZ = worldPos.getZ() - cZ;
protected boolean shouldTick(BlockPos worldPos, float lookX, float lookY, float lookZ, int cX, int cY, int cZ) {
int dX = worldPos.getX() - cX;
int dY = worldPos.getY() - cY;
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) {
int dSq = dX * dX + dY * dY + dZ * dZ;
protected int getUpdateDivisor(int dX, int dY, int dZ) {
int dSq = dX * dX + dY * dY + dZ * dZ;
return (dSq / 1024) + 1;
}
return (dSq / 1024) + 1;
}
private void addInternal(TileEntity tile) {
getInstance(tile, true);
}
private void addInternal(TileEntity tile) {
getInstance(tile, true);
}
private <T extends TileEntity> void removeInternal(T tile) {
TileEntityInstance<? super T> instance = getInstance(tile, false);
private <T extends TileEntity> void removeInternal(T tile) {
TileEntityInstance<? super T> instance = getInstance(tile, false);
if (instance != null) {
removeInternal(tile, instance);
}
}
if (instance != null) {
removeInternal(tile, instance);
}
}
private void removeInternal(TileEntity tile, TileEntityInstance<?> instance) {
instance.remove();
instances.remove(tile);
dynamicInstances.remove(tile);
tickableInstances.remove(tile);
}
private void removeInternal(TileEntity tile, TileEntityInstance<?> instance) {
instance.remove();
instances.remove(tile);
dynamicInstances.remove(tile);
tickableInstances.remove(tile);
}
private <T extends TileEntity> TileEntityInstance<? super T> createInternal(T tile) {
TileEntityInstance<? super T> renderer = InstancedTileRenderRegistry.instance.create(this, tile);
private <T extends TileEntity> TileEntityInstance<? super T> createInternal(T tile) {
TileEntityInstance<? super T> renderer = InstancedTileRenderRegistry.instance.create(this, tile);
if (renderer != null) {
renderer.updateLight();
instances.put(tile, renderer);
if (renderer != null) {
renderer.updateLight();
instances.put(tile, renderer);
if (renderer instanceof IDynamicInstance)
dynamicInstances.put(tile, (IDynamicInstance) renderer);
if (renderer instanceof IDynamicInstance)
dynamicInstances.put(tile, (IDynamicInstance) renderer);
if (renderer instanceof ITickableInstance)
tickableInstances.put(tile, ((ITickableInstance) renderer));
}
if (renderer instanceof ITickableInstance)
tickableInstances.put(tile, ((ITickableInstance) renderer));
}
return renderer;
}
return renderer;
}
public void invalidate() {
for (RenderMaterial<?, ?> material : materials.values()) {
material.delete();
}
instances.clear();
dynamicInstances.clear();
tickableInstances.clear();
}
public void invalidate() {
for (RenderMaterial<?, ?> material : materials.values()) {
material.delete();
}
instances.clear();
dynamicInstances.clear();
tickableInstances.clear();
}
public boolean canCreateInstance(TileEntity tile) {
if (tile.isRemoved()) return false;
public boolean canCreateInstance(TileEntity tile) {
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) {
BlockPos pos = tile.getPos();
if (world == Minecraft.getInstance().world) {
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
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<?>> {
protected final InstancedTileRenderer<?> renderer;
protected final Map<Compartment<?>, Cache<Object, MODEL>> models;
protected final ModelFactory<MODEL> factory;
protected final ProgramSpec<P> programSpec;
protected final Predicate<RenderType> layerPredicate;
protected final InstancedTileRenderer<?> renderer;
protected final Map<Compartment<?>, Cache<Object, MODEL>> models;
protected final ModelFactory<MODEL> factory;
protected final ProgramSpec<P> programSpec;
protected final Predicate<RenderType> layerPredicate;
/**
* Creates a material that renders in the default layer (CUTOUT_MIPPED)
*/
public RenderMaterial(InstancedTileRenderer<?> renderer, ProgramSpec<P> programSpec, ModelFactory<MODEL> factory) {
this(renderer, programSpec, factory, type -> type == RenderType.getCutoutMipped());
}
/**
* Creates a material that renders in the default layer (CUTOUT_MIPPED)
*/
public RenderMaterial(InstancedTileRenderer<?> renderer, ProgramSpec<P> programSpec, ModelFactory<MODEL> factory) {
this(renderer, programSpec, factory, type -> type == RenderType.getCutoutMipped());
}
public RenderMaterial(InstancedTileRenderer<?> renderer, ProgramSpec<P> programSpec, ModelFactory<MODEL> factory, Predicate<RenderType> layerPredicate) {
this.renderer = renderer;
this.models = new HashMap<>();
this.factory = factory;
this.programSpec = programSpec;
this.layerPredicate = layerPredicate;
registerCompartment(Compartment.PARTIAL);
registerCompartment(Compartment.DIRECTIONAL_PARTIAL);
registerCompartment(Compartment.GENERIC_TILE);
}
public RenderMaterial(InstancedTileRenderer<?> renderer, ProgramSpec<P> programSpec, ModelFactory<MODEL> factory, Predicate<RenderType> layerPredicate) {
this.renderer = renderer;
this.models = new HashMap<>();
this.factory = factory;
this.programSpec = programSpec;
this.layerPredicate = layerPredicate;
registerCompartment(Compartment.PARTIAL);
registerCompartment(Compartment.DIRECTIONAL_PARTIAL);
registerCompartment(Compartment.GENERIC_TILE);
}
public boolean canRenderInLayer(RenderType layer) {
return layerPredicate.test(layer);
}
public boolean canRenderInLayer(RenderType layer) {
return layerPredicate.test(layer);
}
public void render(RenderType layer, Matrix4f projection, double camX, double camY, double camZ) {
render(layer, projection, camX, camY, camZ, null);
}
public void render(RenderType layer, Matrix4f projection, double camX, double camY, double camZ) {
render(layer, projection, camX, camY, camZ, null);
}
public void render(RenderType layer, Matrix4f viewProjection, double camX, double camY, double camZ, ShaderCallback<P> setup) {
P program = Backend.getProgram(programSpec);
program.bind(viewProjection, camX, camY, camZ, FastRenderDispatcher.getDebugMode());
public void render(RenderType layer, Matrix4f viewProjection, double camX, double camY, double camZ, ShaderCallback<P> setup) {
P program = Backend.getProgram(programSpec);
program.bind(viewProjection, camX, camY, camZ, FastRenderDispatcher.getDebugMode());
if (setup != null) setup.call(program);
if (setup != null) setup.call(program);
makeRenderCalls();
teardown();
}
makeRenderCalls();
teardown();
}
public void teardown() {}
public void teardown() {
}
public void delete() {
runOnAll(InstancedModel::delete);
models.values().forEach(Cache::invalidateAll);
}
public void delete() {
runOnAll(InstancedModel::delete);
models.values().forEach(Cache::invalidateAll);
}
protected void makeRenderCalls() {
runOnAll(InstancedModel::render);
}
protected void makeRenderCalls() {
runOnAll(InstancedModel::render);
}
public void runOnAll(Consumer<MODEL> f) {
for (Cache<Object, MODEL> cache : models.values()) {
for (MODEL model : cache.asMap().values()) {
f.accept(model);
}
}
}
public void runOnAll(Consumer<MODEL> f) {
for (Cache<Object, MODEL> cache : models.values()) {
for (MODEL model : cache.asMap().values()) {
f.accept(model);
}
}
}
public void registerCompartment(Compartment<?> instance) {
models.put(instance, CacheBuilder.newBuilder().build());
}
public void registerCompartment(Compartment<?> instance) {
models.put(instance, CacheBuilder.newBuilder().build());
}
public MODEL getModel(PartialModel partial, BlockState referenceState) {
return get(PARTIAL, partial, () -> buildModel(partial.get(), referenceState));
}
public MODEL getModel(PartialModel partial, BlockState 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));
}
}
public MODEL getModel(PartialModel partial, BlockState referenceState, Direction dir, Supplier<MatrixStack> modelTransform) {
return get(Compartment.DIRECTIONAL_PARTIAL, Pair.of(dir, partial),
() -> buildModel(partial.get(), referenceState, modelTransform.get()));
}
public MODEL getModel(PartialModel partial, BlockState referenceState, Direction dir, Supplier<MatrixStack> modelTransform) {
return get(Compartment.DIRECTIONAL_PARTIAL, Pair.of(dir, partial),
() -> buildModel(partial.get(), referenceState, modelTransform.get()));
}
public MODEL getModel(BlockState toRender) {
return get(Compartment.GENERIC_TILE, toRender, () -> buildModel(toRender));
}
public MODEL getModel(BlockState toRender) {
return get(Compartment.GENERIC_TILE, toRender, () -> buildModel(toRender));
}
public <T> MODEL get(Compartment<T> compartment, T key, Supplier<MODEL> supplier) {
Cache<Object, MODEL> compartmentCache = models.get(compartment);
try {
return compartmentCache.get(key, supplier::get);
} catch (ExecutionException e) {
e.printStackTrace();
return null;
}
}
public <T> MODEL get(Compartment<T> compartment, T key, Supplier<MODEL> supplier) {
Cache<Object, MODEL> compartmentCache = models.get(compartment);
try {
return compartmentCache.get(key, supplier::get);
} catch (ExecutionException e) {
e.printStackTrace();
return null;
}
}
private MODEL buildModel(BlockState renderedState) {
BlockRendererDispatcher dispatcher = Minecraft.getInstance().getBlockRendererDispatcher();
return buildModel(dispatcher.getModelForState(renderedState), renderedState);
}
private MODEL buildModel(BlockState renderedState) {
BlockRendererDispatcher dispatcher = Minecraft.getInstance().getBlockRendererDispatcher();
return buildModel(dispatcher.getModelForState(renderedState), renderedState);
}
private MODEL buildModel(IBakedModel model, BlockState renderedState) {
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 {
protected final InstancedTileRenderer<?> renderer;
protected final T tile;
protected final World world;
protected final BlockPos pos;
protected final BlockPos instancePos;
protected final BlockState blockState;
protected final InstancedTileRenderer<?> renderer;
protected final T tile;
protected final World world;
protected final BlockPos pos;
protected final BlockPos instancePos;
protected final BlockState blockState;
public TileEntityInstance(InstancedTileRenderer<?> renderer, T tile) {
this.renderer = renderer;
this.tile = tile;
this.world = tile.getWorld();
this.pos = tile.getPos();
this.blockState = tile.getBlockState();
this.instancePos = pos.subtract(renderer.getOriginCoordinate());
}
public TileEntityInstance(InstancedTileRenderer<?> renderer, T tile) {
this.renderer = renderer;
this.tile = tile;
this.world = tile.getWorld();
this.pos = tile.getPos();
this.blockState = tile.getBlockState();
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.
* 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}.
*/
protected void update() { }
/**
* 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()}.
*
* <br><br> If your animations are complex or more CPU driven, see {@link IDynamicInstance} or {@link ITickableInstance}.
*/
protected void update() {
}
/**
* Called after construction and when a light update occurs in the world.
*
* <br> If your model needs it, update light here.
*/
public void updateLight() { }
/**
* Called after construction and when a light update occurs in the world.
*
* <br> If your model needs it, update light here.
*/
public void updateLight() {
}
/**
* Free any acquired resources.
*
* <br> eg. call {@link InstanceKey#delete()}.
*/
public abstract void remove();
/**
* Free any acquired resources.
*
* <br> eg. call {@link InstanceKey#delete()}.
*/
public abstract void remove();
/**
* 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,
* 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.
*
* @return <code>true</code> if this instance should be discarded and refreshed.
*/
public boolean shouldReset() {
return tile.getBlockState() != blockState;
}
/**
* 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,
* 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.
*
* @return <code>true</code> if this instance should be discarded and refreshed.
*/
public boolean shouldReset() {
return tile.getBlockState() != blockState;
}
/**
* In order to accommodate for floating point precision errors at high coordinates,
* {@link InstancedTileRenderer}s are allowed to arbitrarily adjust the origin, and
* shift the world matrix provided as a shader uniform accordingly.
*
* @return The {@link BlockPos} at which the {@link TileEntity} this instance
* represents should be rendered at to appear in the correct location.
*/
public BlockPos getInstancePosition() {
return instancePos;
}
/**
* In order to accommodate for floating point precision errors at high coordinates,
* {@link InstancedTileRenderer}s are allowed to arbitrarily adjust the origin, and
* shift the world matrix provided as a shader uniform accordingly.
*
* @return The {@link BlockPos} at which the {@link TileEntity} this instance
* represents should be rendered at to appear in the correct location.
*/
public BlockPos getInstancePosition() {
return instancePos;
}
@Override
public BlockPos getWorldPosition() {
return pos;
}
@Override
public BlockPos getWorldPosition() {
return pos;
}
protected void relight(BlockPos pos, IFlatLight<?>... models) {
relight(world.getLightLevel(LightType.BLOCK, pos), world.getLightLevel(LightType.SKY, pos), models);
}
protected void relight(BlockPos pos, IFlatLight<?>... models) {
relight(world.getLightLevel(LightType.BLOCK, pos), world.getLightLevel(LightType.SKY, pos), models);
}
protected <L extends IFlatLight<?>> void relight(BlockPos pos, Stream<L> models) {
relight(world.getLightLevel(LightType.BLOCK, pos), world.getLightLevel(LightType.SKY, pos), models);
}
protected <L extends IFlatLight<?>> void relight(BlockPos pos, Stream<L> models) {
relight(world.getLightLevel(LightType.BLOCK, pos), world.getLightLevel(LightType.SKY, pos), models);
}
protected void relight(int block, int sky, IFlatLight<?>... models) {
relight(block, sky, Arrays.stream(models));
}
protected void relight(int block, int sky, IFlatLight<?>... models) {
relight(block, sky, Arrays.stream(models));
}
protected <L extends IFlatLight<?>> void relight(int block, int sky, Stream<L> models) {
models.forEach(model -> model.setBlockLight(block).setSkyLight(sky));
}
protected <L extends IFlatLight<?>> void relight(int block, int sky, Stream<L> models) {
models.forEach(model -> model.setBlockLight(block).setSkyLight(sky));
}
protected RenderMaterial<?, InstancedModel<ModelData>> getTransformMaterial() {
return renderer.getTransformMaterial();
}
protected RenderMaterial<?, InstancedModel<ModelData>> getTransformMaterial() {
return renderer.getTransformMaterial();
}
protected RenderMaterial<?, InstancedModel<OrientedData>> getOrientedMaterial() {
return renderer.getOrientedMaterial();
}
protected RenderMaterial<?, InstancedModel<OrientedData>> 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.InstancedModel;
public class InstanceGroup<D extends InstanceData> extends AbstractCollection<D> {
public class InstanceGroup<D extends InstanceData> extends AbstractCollection<D> {
final InstancedModel<D> model;
final List<D> backing;
@ -30,7 +30,6 @@ public class InstanceGroup<D extends InstanceData> extends AbstractCollection<D>
}
/**
*
* @param count
* @return True if the number of elements changed.
*/

View file

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

View file

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

View file

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