mirror of
https://github.com/Creators-of-Create/Create.git
synced 2025-02-26 20:04:41 +01:00
Reformat backend
This commit is contained in:
parent
d9b04138df
commit
322496f3b4
66 changed files with 2537 additions and 2539 deletions
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<>();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue