mirror of
https://github.com/Creators-of-Create/Create.git
synced 2025-02-27 04:14:40 +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 org.lwjgl.opengl.GLCapabilities;
|
||||||
|
|
||||||
import com.simibubi.create.foundation.config.AllConfigs;
|
import com.simibubi.create.foundation.config.AllConfigs;
|
||||||
import com.simibubi.create.foundation.render.backend.gl.GlFog;
|
|
||||||
import com.simibubi.create.foundation.render.backend.gl.shader.GlProgram;
|
import com.simibubi.create.foundation.render.backend.gl.shader.GlProgram;
|
||||||
import com.simibubi.create.foundation.render.backend.gl.shader.IMultiProgram;
|
import com.simibubi.create.foundation.render.backend.gl.shader.IMultiProgram;
|
||||||
import com.simibubi.create.foundation.render.backend.gl.shader.ProgramSpec;
|
import com.simibubi.create.foundation.render.backend.gl.shader.ProgramSpec;
|
||||||
|
|
|
@ -13,75 +13,75 @@ import net.minecraft.client.renderer.BufferBuilder;
|
||||||
|
|
||||||
public abstract class BufferedModel extends TemplateBuffer {
|
public abstract class BufferedModel extends TemplateBuffer {
|
||||||
|
|
||||||
protected GlBuffer modelVBO;
|
protected GlBuffer modelVBO;
|
||||||
protected boolean removed;
|
protected boolean removed;
|
||||||
|
|
||||||
protected BufferedModel(BufferBuilder buf) {
|
protected BufferedModel(BufferBuilder buf) {
|
||||||
super(buf);
|
super(buf);
|
||||||
if (vertexCount > 0) init();
|
if (vertexCount > 0) init();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void init() {
|
protected void init() {
|
||||||
|
|
||||||
modelVBO = new GlBuffer(GL20.GL_ARRAY_BUFFER);
|
modelVBO = new GlBuffer(GL20.GL_ARRAY_BUFFER);
|
||||||
|
|
||||||
modelVBO.with(vbo -> initModel());
|
modelVBO.with(vbo -> initModel());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void initModel() {
|
protected void initModel() {
|
||||||
int stride = getModelFormat().getStride();
|
int stride = getModelFormat().getStride();
|
||||||
int invariantSize = vertexCount * stride;
|
int invariantSize = vertexCount * stride;
|
||||||
|
|
||||||
// allocate the buffer on the gpu
|
// allocate the buffer on the gpu
|
||||||
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, invariantSize, GL15.GL_STATIC_DRAW);
|
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, invariantSize, GL15.GL_STATIC_DRAW);
|
||||||
|
|
||||||
// mirror it in system memory so we can write to it
|
// mirror it in system memory so we can write to it
|
||||||
modelVBO.map(invariantSize, buffer -> {
|
modelVBO.map(invariantSize, buffer -> {
|
||||||
for (int i = 0; i < vertexCount; i++) {
|
for (int i = 0; i < vertexCount; i++) {
|
||||||
copyVertex(buffer, i);
|
copyVertex(buffer, i);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract void copyVertex(ByteBuffer to, int index);
|
protected abstract void copyVertex(ByteBuffer to, int index);
|
||||||
|
|
||||||
protected abstract VertexFormat getModelFormat();
|
protected abstract VertexFormat getModelFormat();
|
||||||
|
|
||||||
protected int getTotalShaderAttributeCount() {
|
protected int getTotalShaderAttributeCount() {
|
||||||
return getModelFormat().getShaderAttributeCount();
|
return getModelFormat().getShaderAttributeCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders this model, checking first if there is anything to render.
|
* Renders this model, checking first if there is anything to render.
|
||||||
*/
|
*/
|
||||||
public final void render() {
|
public final void render() {
|
||||||
if (vertexCount == 0 || removed) return;
|
if (vertexCount == 0 || removed) return;
|
||||||
|
|
||||||
doRender();
|
doRender();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set up any state and make the draw calls.
|
* Set up any state and make the draw calls.
|
||||||
*/
|
*/
|
||||||
protected abstract void doRender();
|
protected abstract void doRender();
|
||||||
|
|
||||||
protected void setupAttributes() {
|
protected void setupAttributes() {
|
||||||
int numAttributes = getTotalShaderAttributeCount();
|
int numAttributes = getTotalShaderAttributeCount();
|
||||||
for (int i = 0; i <= numAttributes; i++) {
|
for (int i = 0; i <= numAttributes; i++) {
|
||||||
GL20.glEnableVertexAttribArray(i);
|
GL20.glEnableVertexAttribArray(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
getModelFormat().vertexAttribPointers(0);
|
getModelFormat().vertexAttribPointers(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void delete() {
|
public final void delete() {
|
||||||
removed = true;
|
removed = true;
|
||||||
if (vertexCount > 0) {
|
if (vertexCount > 0) {
|
||||||
RenderWork.enqueue(this::deleteInternal);
|
RenderWork.enqueue(this::deleteInternal);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void deleteInternal() {
|
protected void deleteInternal() {
|
||||||
modelVBO.delete();
|
modelVBO.delete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,78 +2,71 @@ package com.simibubi.create.foundation.render.backend;
|
||||||
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
import com.mojang.blaze3d.matrix.MatrixStack;
|
|
||||||
import com.simibubi.create.CreateClient;
|
import com.simibubi.create.CreateClient;
|
||||||
import com.simibubi.create.content.contraptions.KineticDebugger;
|
import com.simibubi.create.content.contraptions.KineticDebugger;
|
||||||
import com.simibubi.create.foundation.render.KineticRenderer;
|
import com.simibubi.create.foundation.render.KineticRenderer;
|
||||||
import com.simibubi.create.foundation.utility.AnimationTickHolder;
|
|
||||||
import com.simibubi.create.foundation.utility.WorldAttached;
|
import com.simibubi.create.foundation.utility.WorldAttached;
|
||||||
|
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.client.entity.player.ClientPlayerEntity;
|
|
||||||
import net.minecraft.client.renderer.GameRenderer;
|
|
||||||
import net.minecraft.client.renderer.RenderType;
|
import net.minecraft.client.renderer.RenderType;
|
||||||
import net.minecraft.client.world.ClientWorld;
|
import net.minecraft.client.world.ClientWorld;
|
||||||
import net.minecraft.entity.Entity;
|
import net.minecraft.entity.Entity;
|
||||||
import net.minecraft.potion.Effects;
|
|
||||||
import net.minecraft.tileentity.TileEntity;
|
import net.minecraft.tileentity.TileEntity;
|
||||||
import net.minecraft.util.math.MathHelper;
|
|
||||||
import net.minecraft.util.math.vector.Matrix4f;
|
import net.minecraft.util.math.vector.Matrix4f;
|
||||||
import net.minecraft.util.math.vector.Vector3f;
|
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
public class FastRenderDispatcher {
|
public class FastRenderDispatcher {
|
||||||
|
|
||||||
public static WorldAttached<ConcurrentHashMap.KeySetView<TileEntity, Boolean>> queuedUpdates = new WorldAttached<>(ConcurrentHashMap::newKeySet);
|
public static WorldAttached<ConcurrentHashMap.KeySetView<TileEntity, Boolean>> queuedUpdates = new WorldAttached<>(ConcurrentHashMap::newKeySet);
|
||||||
|
|
||||||
public static void enqueueUpdate(TileEntity te) {
|
public static void enqueueUpdate(TileEntity te) {
|
||||||
queuedUpdates.get(te.getWorld()).add(te);
|
queuedUpdates.get(te.getWorld()).add(te);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void tick() {
|
public static void tick() {
|
||||||
Minecraft mc = Minecraft.getInstance();
|
Minecraft mc = Minecraft.getInstance();
|
||||||
ClientWorld world = mc.world;
|
ClientWorld world = mc.world;
|
||||||
|
|
||||||
KineticRenderer kineticRenderer = CreateClient.kineticRenderer.get(world);
|
KineticRenderer kineticRenderer = CreateClient.kineticRenderer.get(world);
|
||||||
|
|
||||||
Entity renderViewEntity = mc.renderViewEntity;
|
Entity renderViewEntity = mc.renderViewEntity;
|
||||||
kineticRenderer.tick(renderViewEntity.getX(), renderViewEntity.getY(), renderViewEntity.getZ());
|
kineticRenderer.tick(renderViewEntity.getX(), renderViewEntity.getY(), renderViewEntity.getZ());
|
||||||
|
|
||||||
ConcurrentHashMap.KeySetView<TileEntity, Boolean> map = queuedUpdates.get(world);
|
ConcurrentHashMap.KeySetView<TileEntity, Boolean> map = queuedUpdates.get(world);
|
||||||
map
|
map
|
||||||
.forEach(te -> {
|
.forEach(te -> {
|
||||||
map.remove(te);
|
map.remove(te);
|
||||||
|
|
||||||
kineticRenderer.update(te);
|
kineticRenderer.update(te);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean available() {
|
public static boolean available() {
|
||||||
return Backend.canUseInstancing();
|
return Backend.canUseInstancing();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean available(World world) {
|
public static boolean available(World world) {
|
||||||
return Backend.canUseInstancing() && Backend.isFlywheelWorld(world);
|
return Backend.canUseInstancing() && Backend.isFlywheelWorld(world);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int getDebugMode() {
|
public static int getDebugMode() {
|
||||||
return KineticDebugger.isActive() ? 1 : 0;
|
return KineticDebugger.isActive() ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void refresh() {
|
public static void refresh() {
|
||||||
RenderWork.enqueue(Minecraft.getInstance().worldRenderer::loadRenderers);
|
RenderWork.enqueue(Minecraft.getInstance().worldRenderer::loadRenderers);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void renderLayer(RenderType layer, Matrix4f viewProjection, double cameraX, double cameraY, double cameraZ) {
|
public static void renderLayer(RenderType layer, Matrix4f viewProjection, double cameraX, double cameraY, double cameraZ) {
|
||||||
if (!Backend.canUseInstancing()) return;
|
if (!Backend.canUseInstancing()) return;
|
||||||
|
|
||||||
ClientWorld world = Minecraft.getInstance().world;
|
ClientWorld world = Minecraft.getInstance().world;
|
||||||
KineticRenderer kineticRenderer = CreateClient.kineticRenderer.get(world);
|
KineticRenderer kineticRenderer = CreateClient.kineticRenderer.get(world);
|
||||||
|
|
||||||
layer.startDrawing();
|
layer.startDrawing();
|
||||||
|
|
||||||
kineticRenderer.render(layer, viewProjection, cameraX, cameraY, cameraZ);
|
kineticRenderer.render(layer, viewProjection, cameraX, cameraY, cameraZ);
|
||||||
|
|
||||||
layer.endDrawing();
|
layer.endDrawing();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,6 @@ import com.simibubi.create.foundation.render.backend.core.OrientedData;
|
||||||
import com.simibubi.create.foundation.render.backend.instancing.InstancedModel;
|
import com.simibubi.create.foundation.render.backend.instancing.InstancedModel;
|
||||||
|
|
||||||
public class MaterialTypes {
|
public class MaterialTypes {
|
||||||
public static final MaterialType<InstancedModel<ModelData>> TRANSFORMED = new MaterialType<>();
|
public static final MaterialType<InstancedModel<ModelData>> TRANSFORMED = new MaterialType<>();
|
||||||
public static final MaterialType<InstancedModel<OrientedData>> ORIENTED = new MaterialType<>();
|
public static final MaterialType<InstancedModel<OrientedData>> ORIENTED = new MaterialType<>();
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,77 +9,78 @@ import java.util.Optional;
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
|
|
||||||
public class OptifineHandler {
|
public class OptifineHandler {
|
||||||
public static final String OPTIFINE_ROOT_PACKAGE = "net.optifine";
|
public static final String OPTIFINE_ROOT_PACKAGE = "net.optifine";
|
||||||
public static final String SHADER_PACKAGE = "net.optifine.shaders";
|
public static final String SHADER_PACKAGE = "net.optifine.shaders";
|
||||||
|
|
||||||
private static Package optifine;
|
private static Package optifine;
|
||||||
private static OptifineHandler handler;
|
private static OptifineHandler handler;
|
||||||
|
|
||||||
public final boolean usingShaders;
|
public final boolean usingShaders;
|
||||||
|
|
||||||
public OptifineHandler(boolean usingShaders) {
|
public OptifineHandler(boolean usingShaders) {
|
||||||
this.usingShaders = usingShaders;
|
this.usingShaders = usingShaders;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get information about the current Optifine configuration.
|
* Get information about the current Optifine configuration.
|
||||||
* @return {@link Optional#empty()} if Optifine is not installed.
|
*
|
||||||
*/
|
* @return {@link Optional#empty()} if Optifine is not installed.
|
||||||
public static Optional<OptifineHandler> get() {
|
*/
|
||||||
return Optional.ofNullable(handler);
|
public static Optional<OptifineHandler> get() {
|
||||||
}
|
return Optional.ofNullable(handler);
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean optifineInstalled() {
|
public static boolean optifineInstalled() {
|
||||||
return optifine != null;
|
return optifine != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean usingShaders() {
|
public static boolean usingShaders() {
|
||||||
return OptifineHandler.get()
|
return OptifineHandler.get()
|
||||||
.map(OptifineHandler::isUsingShaders)
|
.map(OptifineHandler::isUsingShaders)
|
||||||
.orElse(false);
|
.orElse(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void init() {
|
public static void init() {
|
||||||
optifine = Package.getPackage(OPTIFINE_ROOT_PACKAGE);
|
optifine = Package.getPackage(OPTIFINE_ROOT_PACKAGE);
|
||||||
|
|
||||||
if (optifine == null) {
|
if (optifine == null) {
|
||||||
Backend.log.info("Optifine not detected.");
|
Backend.log.info("Optifine not detected.");
|
||||||
} else {
|
} else {
|
||||||
Backend.log.info("Optifine detected.");
|
Backend.log.info("Optifine detected.");
|
||||||
|
|
||||||
refresh();
|
refresh();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void refresh() {
|
public static void refresh() {
|
||||||
if (optifine == null) return;
|
if (optifine == null) return;
|
||||||
|
|
||||||
File dir = Minecraft.getInstance().gameDir;
|
File dir = Minecraft.getInstance().gameDir;
|
||||||
|
|
||||||
File shaderOptions = new File(dir, "optionsshaders.txt");
|
File shaderOptions = new File(dir, "optionsshaders.txt");
|
||||||
|
|
||||||
boolean shadersOff = true;
|
boolean shadersOff = true;
|
||||||
try {
|
try {
|
||||||
BufferedReader reader = new BufferedReader(new FileReader(shaderOptions));
|
BufferedReader reader = new BufferedReader(new FileReader(shaderOptions));
|
||||||
|
|
||||||
shadersOff = reader.lines()
|
shadersOff = reader.lines()
|
||||||
.anyMatch(it -> {
|
.anyMatch(it -> {
|
||||||
String line = it.replaceAll("\\s", "");
|
String line = it.replaceAll("\\s", "");
|
||||||
if (line.startsWith("shaderPack=")) {
|
if (line.startsWith("shaderPack=")) {
|
||||||
String setting = line.substring("shaderPack=".length());
|
String setting = line.substring("shaderPack=".length());
|
||||||
|
|
||||||
return setting.equals("OFF") || setting.equals("(internal)");
|
return setting.equals("OFF") || setting.equals("(internal)");
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
} catch (FileNotFoundException e) {
|
} catch (FileNotFoundException e) {
|
||||||
Backend.log.info("No shader config found.");
|
Backend.log.info("No shader config found.");
|
||||||
}
|
}
|
||||||
|
|
||||||
handler = new OptifineHandler(!shadersOff);
|
handler = new OptifineHandler(!shadersOff);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isUsingShaders() {
|
public boolean isUsingShaders() {
|
||||||
return usingShaders;
|
return usingShaders;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ import net.minecraft.util.math.vector.Matrix3f;
|
||||||
import net.minecraft.util.math.vector.Matrix4f;
|
import net.minecraft.util.math.vector.Matrix4f;
|
||||||
|
|
||||||
public class RenderUtil {
|
public class RenderUtil {
|
||||||
public static int nextPowerOf2(int a) {
|
public static int nextPowerOf2(int a) {
|
||||||
int h = Integer.highestOneBit(a);
|
int h = Integer.highestOneBit(a);
|
||||||
return (h == a) ? h : (h << 1);
|
return (h == a) ? h : (h << 1);
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ public class RenderUtil {
|
||||||
|
|
||||||
// GPUs want matrices in column major order.
|
// GPUs want matrices in column major order.
|
||||||
public static float[] writeMatrixStack(Matrix4f model, Matrix3f normal) {
|
public static float[] writeMatrixStack(Matrix4f model, Matrix3f normal) {
|
||||||
return new float[] {
|
return new float[]{
|
||||||
model.a00,
|
model.a00,
|
||||||
model.a10,
|
model.a10,
|
||||||
model.a20,
|
model.a20,
|
||||||
|
|
|
@ -4,18 +4,18 @@ import java.util.Queue;
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
|
|
||||||
public class RenderWork {
|
public class RenderWork {
|
||||||
private static final Queue<Runnable> runs = new ConcurrentLinkedQueue<>();
|
private static final Queue<Runnable> runs = new ConcurrentLinkedQueue<>();
|
||||||
|
|
||||||
public static void runAll() {
|
public static void runAll() {
|
||||||
while (!runs.isEmpty()) {
|
while (!runs.isEmpty()) {
|
||||||
runs.remove().run();
|
runs.remove().run();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Queue work to be executed at the end of a frame
|
* Queue work to be executed at the end of a frame
|
||||||
*/
|
*/
|
||||||
public static void enqueue(Runnable run) {
|
public static void enqueue(Runnable run) {
|
||||||
runs.add(run);
|
runs.add(run);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,14 +28,14 @@ import org.lwjgl.system.MemoryUtil;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import com.mojang.blaze3d.systems.RenderSystem;
|
import com.mojang.blaze3d.systems.RenderSystem;
|
||||||
import com.simibubi.create.foundation.render.backend.gl.GlFogMode;
|
import com.simibubi.create.foundation.render.backend.gl.GlFogMode;
|
||||||
import com.simibubi.create.foundation.render.backend.gl.shader.SingleProgram;
|
import com.simibubi.create.foundation.render.backend.gl.shader.FogSensitiveProgram;
|
||||||
import com.simibubi.create.foundation.render.backend.gl.shader.GlProgram;
|
import com.simibubi.create.foundation.render.backend.gl.shader.GlProgram;
|
||||||
import com.simibubi.create.foundation.render.backend.gl.shader.GlShader;
|
import com.simibubi.create.foundation.render.backend.gl.shader.GlShader;
|
||||||
import com.simibubi.create.foundation.render.backend.gl.shader.FogSensitiveProgram;
|
|
||||||
import com.simibubi.create.foundation.render.backend.gl.shader.IMultiProgram;
|
import com.simibubi.create.foundation.render.backend.gl.shader.IMultiProgram;
|
||||||
import com.simibubi.create.foundation.render.backend.gl.shader.ProgramSpec;
|
import com.simibubi.create.foundation.render.backend.gl.shader.ProgramSpec;
|
||||||
import com.simibubi.create.foundation.render.backend.gl.shader.ShaderConstants;
|
import com.simibubi.create.foundation.render.backend.gl.shader.ShaderConstants;
|
||||||
import com.simibubi.create.foundation.render.backend.gl.shader.ShaderType;
|
import com.simibubi.create.foundation.render.backend.gl.shader.ShaderType;
|
||||||
|
import com.simibubi.create.foundation.render.backend.gl.shader.SingleProgram;
|
||||||
|
|
||||||
import net.minecraft.resources.IResource;
|
import net.minecraft.resources.IResource;
|
||||||
import net.minecraft.resources.IResourceManager;
|
import net.minecraft.resources.IResourceManager;
|
||||||
|
@ -70,7 +70,7 @@ public class ShaderLoader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadShaderSources(IResourceManager manager){
|
private void loadShaderSources(IResourceManager manager) {
|
||||||
Collection<ResourceLocation> allShaders = manager.getAllResourceLocations(SHADER_DIR, s -> {
|
Collection<ResourceLocation> allShaders = manager.getAllResourceLocations(SHADER_DIR, s -> {
|
||||||
for (String ext : EXTENSIONS) {
|
for (String ext : EXTENSIONS) {
|
||||||
if (s.endsWith(ext)) return true;
|
if (s.endsWith(ext)) return true;
|
||||||
|
@ -185,7 +185,7 @@ public class ShaderLoader {
|
||||||
try {
|
try {
|
||||||
bytebuffer = readToBuffer(is);
|
bytebuffer = readToBuffer(is);
|
||||||
int i = bytebuffer.position();
|
int i = bytebuffer.position();
|
||||||
((Buffer)bytebuffer).rewind();
|
((Buffer) bytebuffer).rewind();
|
||||||
return MemoryUtil.memASCII(bytebuffer, i);
|
return MemoryUtil.memASCII(bytebuffer, i);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
|
||||||
|
@ -202,11 +202,12 @@ public class ShaderLoader {
|
||||||
public ByteBuffer readToBuffer(InputStream is) throws IOException {
|
public ByteBuffer readToBuffer(InputStream is) throws IOException {
|
||||||
ByteBuffer bytebuffer;
|
ByteBuffer bytebuffer;
|
||||||
if (is instanceof FileInputStream) {
|
if (is instanceof FileInputStream) {
|
||||||
FileInputStream fileinputstream = (FileInputStream)is;
|
FileInputStream fileinputstream = (FileInputStream) is;
|
||||||
FileChannel filechannel = fileinputstream.getChannel();
|
FileChannel filechannel = fileinputstream.getChannel();
|
||||||
bytebuffer = MemoryUtil.memAlloc((int)filechannel.size() + 1);
|
bytebuffer = MemoryUtil.memAlloc((int) filechannel.size() + 1);
|
||||||
|
|
||||||
while (filechannel.read(bytebuffer) != -1) { }
|
while (filechannel.read(bytebuffer) != -1) {
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
bytebuffer = MemoryUtil.memAlloc(8192);
|
bytebuffer = MemoryUtil.memAlloc(8192);
|
||||||
ReadableByteChannel readablebytechannel = Channels.newChannel(is);
|
ReadableByteChannel readablebytechannel = Channels.newChannel(is);
|
||||||
|
|
|
@ -7,72 +7,72 @@ import com.simibubi.create.foundation.render.backend.instancing.InstancedModel;
|
||||||
|
|
||||||
public class BasicData extends InstanceData implements IFlatLight<BasicData> {
|
public class BasicData extends InstanceData implements IFlatLight<BasicData> {
|
||||||
|
|
||||||
protected byte blockLight;
|
protected byte blockLight;
|
||||||
protected byte skyLight;
|
protected byte skyLight;
|
||||||
|
|
||||||
protected byte r = (byte) 0xFF;
|
protected byte r = (byte) 0xFF;
|
||||||
protected byte g = (byte) 0xFF;
|
protected byte g = (byte) 0xFF;
|
||||||
protected byte b = (byte) 0xFF;
|
protected byte b = (byte) 0xFF;
|
||||||
protected byte a = (byte) 0xFF;
|
protected byte a = (byte) 0xFF;
|
||||||
|
|
||||||
public BasicData(InstancedModel<?> owner) {
|
public BasicData(InstancedModel<?> owner) {
|
||||||
super(owner);
|
super(owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BasicData setBlockLight(int blockLight) {
|
public BasicData setBlockLight(int blockLight) {
|
||||||
this.blockLight = (byte) (blockLight << 4);
|
this.blockLight = (byte) (blockLight << 4);
|
||||||
markDirty();
|
markDirty();
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BasicData setSkyLight(int skyLight) {
|
public BasicData setSkyLight(int skyLight) {
|
||||||
this.skyLight = (byte) (skyLight << 4);
|
this.skyLight = (byte) (skyLight << 4);
|
||||||
markDirty();
|
markDirty();
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BasicData setColor(int color) {
|
public BasicData setColor(int color) {
|
||||||
return setColor(color, false);
|
return setColor(color, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public BasicData setColor(int color, boolean alpha) {
|
public BasicData setColor(int color, boolean alpha) {
|
||||||
byte r = (byte) ((color >> 16) & 0xFF);
|
byte r = (byte) ((color >> 16) & 0xFF);
|
||||||
byte g = (byte) ((color >> 8) & 0xFF);
|
byte g = (byte) ((color >> 8) & 0xFF);
|
||||||
byte b = (byte) (color & 0xFF);
|
byte b = (byte) (color & 0xFF);
|
||||||
|
|
||||||
if (alpha) {
|
if (alpha) {
|
||||||
byte a = (byte) ((color >> 24) & 0xFF);
|
byte a = (byte) ((color >> 24) & 0xFF);
|
||||||
return setColor(r, g, b, a);
|
return setColor(r, g, b, a);
|
||||||
} else {
|
} else {
|
||||||
return setColor(r, g, b);
|
return setColor(r, g, b);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public BasicData setColor(int r, int g, int b) {
|
public BasicData setColor(int r, int g, int b) {
|
||||||
return setColor((byte) r, (byte) g, (byte) b);
|
return setColor((byte) r, (byte) g, (byte) b);
|
||||||
}
|
}
|
||||||
|
|
||||||
public BasicData setColor(byte r, byte g, byte b) {
|
public BasicData setColor(byte r, byte g, byte b) {
|
||||||
this.r = r;
|
this.r = r;
|
||||||
this.g = g;
|
this.g = g;
|
||||||
this.b = b;
|
this.b = b;
|
||||||
markDirty();
|
markDirty();
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BasicData setColor(byte r, byte g, byte b, byte a) {
|
public BasicData setColor(byte r, byte g, byte b, byte a) {
|
||||||
this.r = r;
|
this.r = r;
|
||||||
this.g = g;
|
this.g = g;
|
||||||
this.b = b;
|
this.b = b;
|
||||||
this.a = a;
|
this.a = a;
|
||||||
markDirty();
|
markDirty();
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(ByteBuffer buf) {
|
public void write(ByteBuffer buf) {
|
||||||
buf.put(new byte[] { blockLight, skyLight, r, g, b, a });
|
buf.put(new byte[]{blockLight, skyLight, r, g, b, a});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,22 +5,23 @@ import com.simibubi.create.foundation.render.backend.instancing.InstanceData;
|
||||||
/**
|
/**
|
||||||
* An interface that implementors of {@link InstanceData} should also implement
|
* An interface that implementors of {@link InstanceData} should also implement
|
||||||
* if they wish to make use of Flywheel's provided light update methods.
|
* if they wish to make use of Flywheel's provided light update methods.
|
||||||
*
|
* <p>
|
||||||
* This only covers flat lighting, smooth lighting is still TODO.
|
* This only covers flat lighting, smooth lighting is still TODO.
|
||||||
|
*
|
||||||
* @param <D> The name of the class that implements this interface.
|
* @param <D> The name of the class that implements this interface.
|
||||||
*/
|
*/
|
||||||
public interface IFlatLight<D extends InstanceData & IFlatLight<D>> {
|
public interface IFlatLight<D extends InstanceData & IFlatLight<D>> {
|
||||||
/**
|
/**
|
||||||
* @param blockLight An integer in the range [0, 15] representing the
|
* @param blockLight An integer in the range [0, 15] representing the
|
||||||
* amount of block light this instance should receive.
|
* amount of block light this instance should receive.
|
||||||
* @return <code>this</code>
|
* @return <code>this</code>
|
||||||
*/
|
*/
|
||||||
D setBlockLight(int blockLight);
|
D setBlockLight(int blockLight);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param skyLight An integer in the range [0, 15] representing the
|
* @param skyLight An integer in the range [0, 15] representing the
|
||||||
* amount of sky light this instance should receive.
|
* amount of sky light this instance should receive.
|
||||||
* @return <code>this</code>
|
* @return <code>this</code>
|
||||||
*/
|
*/
|
||||||
D setSkyLight(int skyLight);
|
D setSkyLight(int skyLight);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,36 +6,36 @@ import com.simibubi.create.foundation.render.backend.gl.attrib.IVertexAttrib;
|
||||||
import com.simibubi.create.foundation.render.backend.gl.attrib.VertexAttribSpec;
|
import com.simibubi.create.foundation.render.backend.gl.attrib.VertexAttribSpec;
|
||||||
|
|
||||||
public enum ModelAttributes implements IVertexAttrib {
|
public enum ModelAttributes implements IVertexAttrib {
|
||||||
VERTEX_POSITION("aPos", CommonAttributes.VEC3),
|
VERTEX_POSITION("aPos", CommonAttributes.VEC3),
|
||||||
NORMAL("aNormal", CommonAttributes.NORMAL),
|
NORMAL("aNormal", CommonAttributes.NORMAL),
|
||||||
TEXTURE("aTexCoords", CommonAttributes.UV),
|
TEXTURE("aTexCoords", CommonAttributes.UV),
|
||||||
;
|
;
|
||||||
|
|
||||||
private final String name;
|
private final String name;
|
||||||
private final VertexAttribSpec spec;
|
private final VertexAttribSpec spec;
|
||||||
|
|
||||||
ModelAttributes(String name, VertexAttribSpec spec) {
|
ModelAttributes(String name, VertexAttribSpec spec) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.spec = spec;
|
this.spec = spec;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String attribName() {
|
public String attribName() {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IAttribSpec attribSpec() {
|
public IAttribSpec attribSpec() {
|
||||||
return spec;
|
return spec;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getDivisor() {
|
public int getDivisor() {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getBufferIndex() {
|
public int getBufferIndex() {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,24 +7,24 @@ import com.simibubi.create.foundation.render.backend.RenderUtil;
|
||||||
import com.simibubi.create.foundation.render.backend.instancing.InstancedModel;
|
import com.simibubi.create.foundation.render.backend.instancing.InstancedModel;
|
||||||
|
|
||||||
public class ModelData extends BasicData {
|
public class ModelData extends BasicData {
|
||||||
private static final float[] empty = new float[25];
|
private static final float[] empty = new float[25];
|
||||||
|
|
||||||
private float[] matrices = empty;
|
private float[] matrices = empty;
|
||||||
|
|
||||||
public ModelData(InstancedModel<?> owner) {
|
public ModelData(InstancedModel<?> owner) {
|
||||||
super(owner);
|
super(owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ModelData setTransform(MatrixStack stack) {
|
public ModelData setTransform(MatrixStack stack) {
|
||||||
matrices = RenderUtil.writeMatrixStack(stack);
|
matrices = RenderUtil.writeMatrixStack(stack);
|
||||||
markDirty();
|
markDirty();
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(ByteBuffer buf) {
|
public void write(ByteBuffer buf) {
|
||||||
super.write(buf);
|
super.write(buf);
|
||||||
buf.asFloatBuffer().put(matrices);
|
buf.asFloatBuffer().put(matrices);
|
||||||
buf.position(buf.position() + matrices.length * 4);
|
buf.position(buf.position() + matrices.length * 4);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,36 +5,36 @@ import com.simibubi.create.foundation.render.backend.gl.attrib.IAttribSpec;
|
||||||
import com.simibubi.create.foundation.render.backend.gl.attrib.IVertexAttrib;
|
import com.simibubi.create.foundation.render.backend.gl.attrib.IVertexAttrib;
|
||||||
|
|
||||||
public enum OrientedAttributes implements IVertexAttrib {
|
public enum OrientedAttributes implements IVertexAttrib {
|
||||||
INSTANCE_POS("aInstancePos", CommonAttributes.VEC3),
|
INSTANCE_POS("aInstancePos", CommonAttributes.VEC3),
|
||||||
PIVOT("aPivot", CommonAttributes.VEC3),
|
PIVOT("aPivot", CommonAttributes.VEC3),
|
||||||
ROTATION("aRotation", CommonAttributes.QUATERNION),
|
ROTATION("aRotation", CommonAttributes.QUATERNION),
|
||||||
;
|
;
|
||||||
|
|
||||||
private final String name;
|
private final String name;
|
||||||
private final IAttribSpec spec;
|
private final IAttribSpec spec;
|
||||||
|
|
||||||
OrientedAttributes(String name, IAttribSpec spec) {
|
OrientedAttributes(String name, IAttribSpec spec) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.spec = spec;
|
this.spec = spec;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String attribName() {
|
public String attribName() {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IAttribSpec attribSpec() {
|
public IAttribSpec attribSpec() {
|
||||||
return spec;
|
return spec;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getDivisor() {
|
public int getDivisor() {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getBufferIndex() {
|
public int getBufferIndex() {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,7 +85,7 @@ public class OrientedData extends BasicData {
|
||||||
public void write(ByteBuffer buf) {
|
public void write(ByteBuffer buf) {
|
||||||
super.write(buf);
|
super.write(buf);
|
||||||
|
|
||||||
buf.asFloatBuffer().put(new float[] {
|
buf.asFloatBuffer().put(new float[]{
|
||||||
posX,
|
posX,
|
||||||
posY,
|
posY,
|
||||||
posZ,
|
posZ,
|
||||||
|
|
|
@ -7,22 +7,22 @@ import com.simibubi.create.foundation.render.backend.instancing.InstancedTileRen
|
||||||
import net.minecraft.client.renderer.BufferBuilder;
|
import net.minecraft.client.renderer.BufferBuilder;
|
||||||
|
|
||||||
public class OrientedModel extends InstancedModel<OrientedData> {
|
public class OrientedModel extends InstancedModel<OrientedData> {
|
||||||
public static final VertexFormat INSTANCE_FORMAT = VertexFormat.builder()
|
public static final VertexFormat INSTANCE_FORMAT = VertexFormat.builder()
|
||||||
.addAttributes(BasicAttributes.class)
|
.addAttributes(BasicAttributes.class)
|
||||||
.addAttributes(OrientedAttributes.class)
|
.addAttributes(OrientedAttributes.class)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
public OrientedModel(InstancedTileRenderer<?> renderer, BufferBuilder buf) {
|
public OrientedModel(InstancedTileRenderer<?> renderer, BufferBuilder buf) {
|
||||||
super(renderer, buf);
|
super(renderer, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected OrientedData newInstance() {
|
protected OrientedData newInstance() {
|
||||||
return new OrientedData(this);
|
return new OrientedData(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected VertexFormat getInstanceFormat() {
|
protected VertexFormat getInstanceFormat() {
|
||||||
return INSTANCE_FORMAT;
|
return INSTANCE_FORMAT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,35 +5,35 @@ import com.simibubi.create.foundation.render.backend.gl.attrib.IVertexAttrib;
|
||||||
import com.simibubi.create.foundation.render.backend.gl.attrib.MatrixAttributes;
|
import com.simibubi.create.foundation.render.backend.gl.attrib.MatrixAttributes;
|
||||||
|
|
||||||
public enum TransformAttributes implements IVertexAttrib {
|
public enum TransformAttributes implements IVertexAttrib {
|
||||||
TRANSFORM("aTransform", MatrixAttributes.MAT4),
|
TRANSFORM("aTransform", MatrixAttributes.MAT4),
|
||||||
NORMAL_MAT("aNormalMat", MatrixAttributes.MAT3),
|
NORMAL_MAT("aNormalMat", MatrixAttributes.MAT3),
|
||||||
;
|
;
|
||||||
|
|
||||||
private final String name;
|
private final String name;
|
||||||
private final IAttribSpec spec;
|
private final IAttribSpec spec;
|
||||||
|
|
||||||
TransformAttributes(String name, IAttribSpec spec) {
|
TransformAttributes(String name, IAttribSpec spec) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.spec = spec;
|
this.spec = spec;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String attribName() {
|
public String attribName() {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IAttribSpec attribSpec() {
|
public IAttribSpec attribSpec() {
|
||||||
return spec;
|
return spec;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getDivisor() {
|
public int getDivisor() {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getBufferIndex() {
|
public int getBufferIndex() {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,22 +7,22 @@ import com.simibubi.create.foundation.render.backend.instancing.InstancedTileRen
|
||||||
import net.minecraft.client.renderer.BufferBuilder;
|
import net.minecraft.client.renderer.BufferBuilder;
|
||||||
|
|
||||||
public class TransformedModel extends InstancedModel<ModelData> {
|
public class TransformedModel extends InstancedModel<ModelData> {
|
||||||
public static final VertexFormat INSTANCE_FORMAT = VertexFormat.builder()
|
public static final VertexFormat INSTANCE_FORMAT = VertexFormat.builder()
|
||||||
.addAttributes(BasicAttributes.class)
|
.addAttributes(BasicAttributes.class)
|
||||||
.addAttributes(TransformAttributes.class)
|
.addAttributes(TransformAttributes.class)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
public TransformedModel(InstancedTileRenderer<?> renderer, BufferBuilder buf) {
|
public TransformedModel(InstancedTileRenderer<?> renderer, BufferBuilder buf) {
|
||||||
super(renderer, buf);
|
super(renderer, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected ModelData newInstance() {
|
protected ModelData newInstance() {
|
||||||
return new ModelData(this);
|
return new ModelData(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected VertexFormat getInstanceFormat() {
|
protected VertexFormat getInstanceFormat() {
|
||||||
return INSTANCE_FORMAT;
|
return INSTANCE_FORMAT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,48 +11,48 @@ import net.minecraft.util.ResourceLocation;
|
||||||
import net.minecraft.util.math.vector.Matrix4f;
|
import net.minecraft.util.math.vector.Matrix4f;
|
||||||
|
|
||||||
public class BasicProgram extends GlProgram {
|
public class BasicProgram extends GlProgram {
|
||||||
protected final int uTime;
|
protected final int uTime;
|
||||||
protected final int uViewProjection;
|
protected final int uViewProjection;
|
||||||
protected final int uDebug;
|
protected final int uDebug;
|
||||||
protected final int uCameraPos;
|
protected final int uCameraPos;
|
||||||
|
|
||||||
protected final ProgramFogMode fogMode;
|
protected final ProgramFogMode fogMode;
|
||||||
|
|
||||||
protected int uBlockAtlas;
|
protected int uBlockAtlas;
|
||||||
protected int uLightMap;
|
protected int uLightMap;
|
||||||
|
|
||||||
public BasicProgram(ResourceLocation name, int handle, ProgramFogMode.Factory fogFactory) {
|
public BasicProgram(ResourceLocation name, int handle, ProgramFogMode.Factory fogFactory) {
|
||||||
super(name, handle);
|
super(name, handle);
|
||||||
uTime = getUniformLocation("uTime");
|
uTime = getUniformLocation("uTime");
|
||||||
uViewProjection = getUniformLocation("uViewProjection");
|
uViewProjection = getUniformLocation("uViewProjection");
|
||||||
uDebug = getUniformLocation("uDebug");
|
uDebug = getUniformLocation("uDebug");
|
||||||
uCameraPos = getUniformLocation("uCameraPos");
|
uCameraPos = getUniformLocation("uCameraPos");
|
||||||
|
|
||||||
fogMode = fogFactory.create(this);
|
fogMode = fogFactory.create(this);
|
||||||
|
|
||||||
bind();
|
bind();
|
||||||
registerSamplers();
|
registerSamplers();
|
||||||
unbind();
|
unbind();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void registerSamplers() {
|
protected void registerSamplers() {
|
||||||
uBlockAtlas = setSamplerBinding("uBlockAtlas", 0);
|
uBlockAtlas = setSamplerBinding("uBlockAtlas", 0);
|
||||||
uLightMap = setSamplerBinding("uLightMap", 2);
|
uLightMap = setSamplerBinding("uLightMap", 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void bind(Matrix4f viewProjection, double camX, double camY, double camZ, int debugMode) {
|
public void bind(Matrix4f viewProjection, double camX, double camY, double camZ, int debugMode) {
|
||||||
super.bind();
|
super.bind();
|
||||||
|
|
||||||
GL20.glUniform1i(uDebug, debugMode);
|
GL20.glUniform1i(uDebug, debugMode);
|
||||||
GL20.glUniform1f(uTime, AnimationTickHolder.getRenderTime());
|
GL20.glUniform1f(uTime, AnimationTickHolder.getRenderTime());
|
||||||
|
|
||||||
uploadMatrixUniform(uViewProjection, viewProjection);
|
uploadMatrixUniform(uViewProjection, viewProjection);
|
||||||
GL20.glUniform3f(uCameraPos, (float) camX, (float) camY, (float) camZ);
|
GL20.glUniform3f(uCameraPos, (float) camX, (float) camY, (float) camZ);
|
||||||
|
|
||||||
fogMode.bind();
|
fogMode.bind();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static void uploadMatrixUniform(int uniform, Matrix4f mat) {
|
protected static void uploadMatrixUniform(int uniform, Matrix4f mat) {
|
||||||
GL20.glUniformMatrix4fv(uniform, false, RenderUtil.writeMatrix(mat));
|
GL20.glUniformMatrix4fv(uniform, false, RenderUtil.writeMatrix(mat));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,40 +9,40 @@ import com.simibubi.create.foundation.render.backend.Backend;
|
||||||
|
|
||||||
public class GlBuffer extends GlObject {
|
public class GlBuffer extends GlObject {
|
||||||
|
|
||||||
protected final int bufferType;
|
protected final int bufferType;
|
||||||
|
|
||||||
public GlBuffer(int bufferType) {
|
public GlBuffer(int bufferType) {
|
||||||
setHandle(GL20.glGenBuffers());
|
setHandle(GL20.glGenBuffers());
|
||||||
this.bufferType = bufferType;
|
this.bufferType = bufferType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getBufferType() {
|
public int getBufferType() {
|
||||||
return bufferType;
|
return bufferType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void bind() {
|
public void bind() {
|
||||||
GL20.glBindBuffer(bufferType, handle());
|
GL20.glBindBuffer(bufferType, handle());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void unbind() {
|
public void unbind() {
|
||||||
GL20.glBindBuffer(bufferType, 0);
|
GL20.glBindBuffer(bufferType, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void with(Consumer<GlBuffer> action) {
|
public void with(Consumer<GlBuffer> action) {
|
||||||
bind();
|
bind();
|
||||||
action.accept(this);
|
action.accept(this);
|
||||||
unbind();
|
unbind();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void map(int length, Consumer<ByteBuffer> upload) {
|
public void map(int length, Consumer<ByteBuffer> upload) {
|
||||||
Backend.compat.mapBuffer(bufferType, 0, length, upload);
|
Backend.compat.mapBuffer(bufferType, 0, length, upload);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void map(int offset, int length, Consumer<ByteBuffer> upload) {
|
public void map(int offset, int length, Consumer<ByteBuffer> upload) {
|
||||||
Backend.compat.mapBuffer(bufferType, offset, length, upload);
|
Backend.compat.mapBuffer(bufferType, offset, length, upload);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void deleteInternal(int handle) {
|
protected void deleteInternal(int handle) {
|
||||||
GL20.glDeleteBuffers(handle);
|
GL20.glDeleteBuffers(handle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,43 +5,43 @@ import org.lwjgl.opengl.GL11;
|
||||||
import com.mojang.blaze3d.platform.GlStateManager;
|
import com.mojang.blaze3d.platform.GlStateManager;
|
||||||
|
|
||||||
public class GlFog {
|
public class GlFog {
|
||||||
public static float[] FOG_COLOR = new float[] {0, 0, 0, 0};
|
public static float[] FOG_COLOR = new float[]{0, 0, 0, 0};
|
||||||
|
|
||||||
public static boolean fogEnabled() {
|
public static boolean fogEnabled() {
|
||||||
return GlStateManager.FOG.field_179049_a.field_179201_b;
|
return GlStateManager.FOG.field_179049_a.field_179201_b;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int getFogModeGlEnum() {
|
public static int getFogModeGlEnum() {
|
||||||
return GlStateManager.FOG.field_179047_b;
|
return GlStateManager.FOG.field_179047_b;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static float getFogDensity() {
|
public static float getFogDensity() {
|
||||||
return GlStateManager.FOG.field_179048_c;
|
return GlStateManager.FOG.field_179048_c;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static float getFogEnd() {
|
public static float getFogEnd() {
|
||||||
return GlStateManager.FOG.field_179046_e;
|
return GlStateManager.FOG.field_179046_e;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static float getFogStart() {
|
public static float getFogStart() {
|
||||||
return GlStateManager.FOG.field_179045_d;
|
return GlStateManager.FOG.field_179045_d;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static GlFogMode getFogMode() {
|
public static GlFogMode getFogMode() {
|
||||||
if (!fogEnabled()) {
|
if (!fogEnabled()) {
|
||||||
return GlFogMode.NONE;
|
return GlFogMode.NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
int mode = getFogModeGlEnum();
|
int mode = getFogModeGlEnum();
|
||||||
|
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case GL11.GL_EXP2:
|
case GL11.GL_EXP2:
|
||||||
case GL11.GL_EXP:
|
case GL11.GL_EXP:
|
||||||
return GlFogMode.EXP2;
|
return GlFogMode.EXP2;
|
||||||
case GL11.GL_LINEAR:
|
case GL11.GL_LINEAR:
|
||||||
return GlFogMode.LINEAR;
|
return GlFogMode.LINEAR;
|
||||||
default:
|
default:
|
||||||
throw new UnsupportedOperationException("Unknown fog mode: " + mode);
|
throw new UnsupportedOperationException("Unknown fog mode: " + mode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,31 +7,31 @@ import com.google.common.collect.Lists;
|
||||||
import com.simibubi.create.foundation.render.backend.gl.shader.ProgramFogMode;
|
import com.simibubi.create.foundation.render.backend.gl.shader.ProgramFogMode;
|
||||||
|
|
||||||
public enum GlFogMode {
|
public enum GlFogMode {
|
||||||
NONE(ProgramFogMode.None::new),
|
NONE(ProgramFogMode.None::new),
|
||||||
LINEAR(ProgramFogMode.Linear::new, "USE_FOG_LINEAR"),
|
LINEAR(ProgramFogMode.Linear::new, "USE_FOG_LINEAR"),
|
||||||
EXP2(ProgramFogMode.Exp2::new, "USE_FOG_EXP2"),
|
EXP2(ProgramFogMode.Exp2::new, "USE_FOG_EXP2"),
|
||||||
;
|
;
|
||||||
|
|
||||||
public static final String USE_FOG = "USE_FOG";
|
public static final String USE_FOG = "USE_FOG";
|
||||||
|
|
||||||
private final ProgramFogMode.Factory fogFactory;
|
private final ProgramFogMode.Factory fogFactory;
|
||||||
private final List<String> defines;
|
private final List<String> defines;
|
||||||
|
|
||||||
GlFogMode(ProgramFogMode.Factory fogFactory) {
|
GlFogMode(ProgramFogMode.Factory fogFactory) {
|
||||||
this.fogFactory = fogFactory;
|
this.fogFactory = fogFactory;
|
||||||
this.defines = Collections.emptyList();
|
this.defines = Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
GlFogMode(ProgramFogMode.Factory fogFactory, String name) {
|
GlFogMode(ProgramFogMode.Factory fogFactory, String name) {
|
||||||
this.fogFactory = fogFactory;
|
this.fogFactory = fogFactory;
|
||||||
this.defines = Lists.newArrayList(USE_FOG, name);
|
this.defines = Lists.newArrayList(USE_FOG, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<String> getDefines() {
|
public List<String> getDefines() {
|
||||||
return defines;
|
return defines;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ProgramFogMode.Factory getFogFactory() {
|
public ProgramFogMode.Factory getFogFactory() {
|
||||||
return fogFactory;
|
return fogFactory;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,42 +2,42 @@ package com.simibubi.create.foundation.render.backend.gl;
|
||||||
|
|
||||||
// Utility class for safely dealing with gl object handles.
|
// Utility class for safely dealing with gl object handles.
|
||||||
public abstract class GlObject {
|
public abstract class GlObject {
|
||||||
private static final int INVALID_HANDLE = Integer.MIN_VALUE;
|
private static final int INVALID_HANDLE = Integer.MIN_VALUE;
|
||||||
|
|
||||||
private int handle = INVALID_HANDLE;
|
private int handle = INVALID_HANDLE;
|
||||||
|
|
||||||
protected final void setHandle(int handle) {
|
protected final void setHandle(int handle) {
|
||||||
this.handle = handle;
|
this.handle = handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final int handle() {
|
public final int handle() {
|
||||||
this.checkHandle();
|
this.checkHandle();
|
||||||
|
|
||||||
return this.handle;
|
return this.handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected final void checkHandle() {
|
protected final void checkHandle() {
|
||||||
if (!this.isHandleValid()) {
|
if (!this.isHandleValid()) {
|
||||||
throw new IllegalStateException("Handle is not valid");
|
throw new IllegalStateException("Handle is not valid");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected final boolean isHandleValid() {
|
protected final boolean isHandleValid() {
|
||||||
return this.handle != INVALID_HANDLE;
|
return this.handle != INVALID_HANDLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected final void invalidateHandle() {
|
protected final void invalidateHandle() {
|
||||||
this.handle = INVALID_HANDLE;
|
this.handle = INVALID_HANDLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void delete() {
|
public final void delete() {
|
||||||
if (!isHandleValid()) {
|
if (!isHandleValid()) {
|
||||||
throw new IllegalStateException("Handle already deleted.");
|
throw new IllegalStateException("Handle already deleted.");
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteInternal(handle);
|
deleteInternal(handle);
|
||||||
invalidateHandle();
|
invalidateHandle();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract void deleteInternal(int handle);
|
protected abstract void deleteInternal(int handle);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,33 +7,33 @@ import net.minecraftforge.api.distmarker.OnlyIn;
|
||||||
|
|
||||||
@OnlyIn(Dist.CLIENT)
|
@OnlyIn(Dist.CLIENT)
|
||||||
public enum GlPrimitiveType {
|
public enum GlPrimitiveType {
|
||||||
FLOAT(4, "float", GL11.GL_FLOAT),
|
FLOAT(4, "float", GL11.GL_FLOAT),
|
||||||
UBYTE(1, "ubyte", GL11.GL_UNSIGNED_BYTE),
|
UBYTE(1, "ubyte", GL11.GL_UNSIGNED_BYTE),
|
||||||
BYTE(1, "byte", GL11.GL_BYTE),
|
BYTE(1, "byte", GL11.GL_BYTE),
|
||||||
USHORT(2, "ushort", GL11.GL_UNSIGNED_SHORT),
|
USHORT(2, "ushort", GL11.GL_UNSIGNED_SHORT),
|
||||||
SHORT(2, "short", GL11.GL_SHORT),
|
SHORT(2, "short", GL11.GL_SHORT),
|
||||||
UINT(4, "uint", GL11.GL_UNSIGNED_INT),
|
UINT(4, "uint", GL11.GL_UNSIGNED_INT),
|
||||||
INT(4, "int", GL11.GL_INT);
|
INT(4, "int", GL11.GL_INT);
|
||||||
|
|
||||||
private final int size;
|
private final int size;
|
||||||
private final String displayName;
|
private final String displayName;
|
||||||
private final int glConstant;
|
private final int glConstant;
|
||||||
|
|
||||||
GlPrimitiveType(int bytes, String name, int glEnum) {
|
GlPrimitiveType(int bytes, String name, int glEnum) {
|
||||||
this.size = bytes;
|
this.size = bytes;
|
||||||
this.displayName = name;
|
this.displayName = name;
|
||||||
this.glConstant = glEnum;
|
this.glConstant = glEnum;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getSize() {
|
public int getSize() {
|
||||||
return this.size;
|
return this.size;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getDisplayName() {
|
public String getDisplayName() {
|
||||||
return this.displayName;
|
return this.displayName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getGlConstant() {
|
public int getGlConstant() {
|
||||||
return this.glConstant;
|
return this.glConstant;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -3,23 +3,23 @@ package com.simibubi.create.foundation.render.backend.gl;
|
||||||
import org.lwjgl.opengl.GL20;
|
import org.lwjgl.opengl.GL20;
|
||||||
|
|
||||||
public class GlTexture extends GlObject {
|
public class GlTexture extends GlObject {
|
||||||
private final int textureType;
|
private final int textureType;
|
||||||
|
|
||||||
public GlTexture(int textureType) {
|
public GlTexture(int textureType) {
|
||||||
this.textureType = textureType;
|
this.textureType = textureType;
|
||||||
setHandle(GL20.glGenTextures());
|
setHandle(GL20.glGenTextures());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void deleteInternal(int handle) {
|
protected void deleteInternal(int handle) {
|
||||||
GL20.glDeleteTextures(handle);
|
GL20.glDeleteTextures(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void bind() {
|
public void bind() {
|
||||||
GL20.glBindTexture(textureType, handle());
|
GL20.glBindTexture(textureType, handle());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void unbind() {
|
public void unbind() {
|
||||||
GL20.glBindTexture(textureType, 0);
|
GL20.glBindTexture(textureType, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,25 +5,25 @@ import java.util.function.Consumer;
|
||||||
import com.simibubi.create.foundation.render.backend.Backend;
|
import com.simibubi.create.foundation.render.backend.Backend;
|
||||||
|
|
||||||
public class GlVertexArray extends GlObject {
|
public class GlVertexArray extends GlObject {
|
||||||
public GlVertexArray() {
|
public GlVertexArray() {
|
||||||
setHandle(Backend.compat.genVertexArrays());
|
setHandle(Backend.compat.genVertexArrays());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void bind() {
|
public void bind() {
|
||||||
Backend.compat.bindVertexArray(handle());
|
Backend.compat.bindVertexArray(handle());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void unbind() {
|
public void unbind() {
|
||||||
Backend.compat.bindVertexArray(0);
|
Backend.compat.bindVertexArray(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void with(Consumer<GlVertexArray> action) {
|
public void with(Consumer<GlVertexArray> action) {
|
||||||
bind();
|
bind();
|
||||||
action.accept(this);
|
action.accept(this);
|
||||||
unbind();
|
unbind();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void deleteInternal(int handle) {
|
protected void deleteInternal(int handle) {
|
||||||
Backend.compat.deleteVertexArrays(handle);
|
Backend.compat.deleteVertexArrays(handle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,18 +4,18 @@ import com.simibubi.create.foundation.render.backend.gl.GlPrimitiveType;
|
||||||
|
|
||||||
public class CommonAttributes {
|
public class CommonAttributes {
|
||||||
|
|
||||||
public static final VertexAttribSpec VEC4 = new VertexAttribSpec(GlPrimitiveType.FLOAT, 4);
|
public static final VertexAttribSpec VEC4 = new VertexAttribSpec(GlPrimitiveType.FLOAT, 4);
|
||||||
public static final VertexAttribSpec VEC3 = new VertexAttribSpec(GlPrimitiveType.FLOAT, 3);
|
public static final VertexAttribSpec VEC3 = new VertexAttribSpec(GlPrimitiveType.FLOAT, 3);
|
||||||
public static final VertexAttribSpec VEC2 = new VertexAttribSpec(GlPrimitiveType.FLOAT, 2);
|
public static final VertexAttribSpec VEC2 = new VertexAttribSpec(GlPrimitiveType.FLOAT, 2);
|
||||||
public static final VertexAttribSpec FLOAT = new VertexAttribSpec(GlPrimitiveType.FLOAT, 1);
|
public static final VertexAttribSpec FLOAT = new VertexAttribSpec(GlPrimitiveType.FLOAT, 1);
|
||||||
|
|
||||||
public static final VertexAttribSpec QUATERNION = new VertexAttribSpec(GlPrimitiveType.FLOAT, 4);
|
public static final VertexAttribSpec QUATERNION = new VertexAttribSpec(GlPrimitiveType.FLOAT, 4);
|
||||||
public static final VertexAttribSpec NORMAL = new VertexAttribSpec(GlPrimitiveType.BYTE, 3, true);
|
public static final VertexAttribSpec NORMAL = new VertexAttribSpec(GlPrimitiveType.BYTE, 3, true);
|
||||||
public static final VertexAttribSpec UV = new VertexAttribSpec(GlPrimitiveType.FLOAT, 2);
|
public static final VertexAttribSpec UV = new VertexAttribSpec(GlPrimitiveType.FLOAT, 2);
|
||||||
|
|
||||||
public static final VertexAttribSpec RGBA = new VertexAttribSpec(GlPrimitiveType.UBYTE, 4, true);
|
public static final VertexAttribSpec RGBA = new VertexAttribSpec(GlPrimitiveType.UBYTE, 4, true);
|
||||||
public static final VertexAttribSpec RGB = new VertexAttribSpec(GlPrimitiveType.UBYTE, 3, true);
|
public static final VertexAttribSpec RGB = new VertexAttribSpec(GlPrimitiveType.UBYTE, 3, true);
|
||||||
public static final VertexAttribSpec LIGHT = new VertexAttribSpec(GlPrimitiveType.UBYTE, 2, true);
|
public static final VertexAttribSpec LIGHT = new VertexAttribSpec(GlPrimitiveType.UBYTE, 2, true);
|
||||||
|
|
||||||
public static final VertexAttribSpec NORMALIZED_BYTE = new VertexAttribSpec(GlPrimitiveType.BYTE, 1, true);
|
public static final VertexAttribSpec NORMALIZED_BYTE = new VertexAttribSpec(GlPrimitiveType.BYTE, 1, true);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,9 @@ package com.simibubi.create.foundation.render.backend.gl.attrib;
|
||||||
|
|
||||||
public interface IAttribSpec {
|
public interface IAttribSpec {
|
||||||
|
|
||||||
void vertexAttribPointer(int stride, int index, int pointer);
|
void vertexAttribPointer(int stride, int index, int pointer);
|
||||||
|
|
||||||
int getSize();
|
int getSize();
|
||||||
|
|
||||||
int getAttributeCount();
|
int getAttributeCount();
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,11 +2,11 @@ package com.simibubi.create.foundation.render.backend.gl.attrib;
|
||||||
|
|
||||||
public interface IVertexAttrib {
|
public interface IVertexAttrib {
|
||||||
|
|
||||||
String attribName();
|
String attribName();
|
||||||
|
|
||||||
IAttribSpec attribSpec();
|
IAttribSpec attribSpec();
|
||||||
|
|
||||||
int getDivisor();
|
int getDivisor();
|
||||||
|
|
||||||
int getBufferIndex();
|
int getBufferIndex();
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,33 +5,33 @@ import org.lwjgl.opengl.GL20;
|
||||||
import com.simibubi.create.foundation.render.backend.gl.GlPrimitiveType;
|
import com.simibubi.create.foundation.render.backend.gl.GlPrimitiveType;
|
||||||
|
|
||||||
public enum MatrixAttributes implements IAttribSpec {
|
public enum MatrixAttributes implements IAttribSpec {
|
||||||
MAT3(3, 3),
|
MAT3(3, 3),
|
||||||
MAT4(4, 4),
|
MAT4(4, 4),
|
||||||
;
|
;
|
||||||
|
|
||||||
private final int rows;
|
private final int rows;
|
||||||
private final int cols;
|
private final int cols;
|
||||||
|
|
||||||
MatrixAttributes(int rows, int cols) {
|
MatrixAttributes(int rows, int cols) {
|
||||||
this.rows = rows;
|
this.rows = rows;
|
||||||
this.cols = cols;
|
this.cols = cols;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void vertexAttribPointer(int stride, int index, int pointer) {
|
public void vertexAttribPointer(int stride, int index, int pointer) {
|
||||||
for (int i = 0; i < rows; i++) {
|
for (int i = 0; i < rows; i++) {
|
||||||
long attribPointer = pointer + (long) i * cols * GlPrimitiveType.FLOAT.getSize();
|
long attribPointer = pointer + (long) i * cols * GlPrimitiveType.FLOAT.getSize();
|
||||||
GL20.glVertexAttribPointer(index + i, cols, GlPrimitiveType.FLOAT.getGlConstant(), false, stride, attribPointer);
|
GL20.glVertexAttribPointer(index + i, cols, GlPrimitiveType.FLOAT.getGlConstant(), false, stride, attribPointer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getSize() {
|
public int getSize() {
|
||||||
return GlPrimitiveType.FLOAT.getSize() * rows * cols;
|
return GlPrimitiveType.FLOAT.getSize() * rows * cols;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getAttributeCount() {
|
public int getAttributeCount() {
|
||||||
return rows;
|
return rows;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,36 +6,36 @@ import com.simibubi.create.foundation.render.backend.gl.GlPrimitiveType;
|
||||||
|
|
||||||
public class VertexAttribSpec implements IAttribSpec {
|
public class VertexAttribSpec implements IAttribSpec {
|
||||||
|
|
||||||
private final GlPrimitiveType type;
|
private final GlPrimitiveType type;
|
||||||
private final int count;
|
private final int count;
|
||||||
private final int size;
|
private final int size;
|
||||||
private final int attributeCount;
|
private final int attributeCount;
|
||||||
private final boolean normalized;
|
private final boolean normalized;
|
||||||
|
|
||||||
public VertexAttribSpec(GlPrimitiveType type, int count) {
|
public VertexAttribSpec(GlPrimitiveType type, int count) {
|
||||||
this(type, count, false);
|
this(type, count, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public VertexAttribSpec(GlPrimitiveType type, int count, boolean normalized) {
|
public VertexAttribSpec(GlPrimitiveType type, int count, boolean normalized) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.count = count;
|
this.count = count;
|
||||||
this.size = type.getSize() * count;
|
this.size = type.getSize() * count;
|
||||||
this.attributeCount = (this.size + 15) / 16; // ceiling division. GLSL vertex attributes can only be 16 bytes wide
|
this.attributeCount = (this.size + 15) / 16; // ceiling division. GLSL vertex attributes can only be 16 bytes wide
|
||||||
this.normalized = normalized;
|
this.normalized = normalized;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void vertexAttribPointer(int stride, int index, int pointer) {
|
public void vertexAttribPointer(int stride, int index, int pointer) {
|
||||||
GL20.glVertexAttribPointer(index, count, type.getGlConstant(), normalized, stride, pointer);
|
GL20.glVertexAttribPointer(index, count, type.getGlConstant(), normalized, stride, pointer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getSize() {
|
public int getSize() {
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getAttributeCount() {
|
public int getAttributeCount() {
|
||||||
return attributeCount;
|
return attributeCount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,61 +5,61 @@ import java.util.Arrays;
|
||||||
|
|
||||||
public class VertexFormat {
|
public class VertexFormat {
|
||||||
|
|
||||||
private final ArrayList<IVertexAttrib> allAttributes;
|
private final ArrayList<IVertexAttrib> allAttributes;
|
||||||
|
|
||||||
private final int numAttributes;
|
private final int numAttributes;
|
||||||
private final int stride;
|
private final int stride;
|
||||||
|
|
||||||
public VertexFormat(ArrayList<IVertexAttrib> allAttributes) {
|
public VertexFormat(ArrayList<IVertexAttrib> allAttributes) {
|
||||||
this.allAttributes = allAttributes;
|
this.allAttributes = allAttributes;
|
||||||
|
|
||||||
int numAttributes = 0, stride = 0;
|
int numAttributes = 0, stride = 0;
|
||||||
for (IVertexAttrib attrib : allAttributes) {
|
for (IVertexAttrib attrib : allAttributes) {
|
||||||
IAttribSpec spec = attrib.attribSpec();
|
IAttribSpec spec = attrib.attribSpec();
|
||||||
numAttributes += spec.getAttributeCount();
|
numAttributes += spec.getAttributeCount();
|
||||||
stride += spec.getSize();
|
stride += spec.getSize();
|
||||||
}
|
}
|
||||||
this.numAttributes = numAttributes;
|
this.numAttributes = numAttributes;
|
||||||
this.stride = stride;
|
this.stride = stride;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getShaderAttributeCount() {
|
public int getShaderAttributeCount() {
|
||||||
return numAttributes;
|
return numAttributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getStride() {
|
public int getStride() {
|
||||||
return stride;
|
return stride;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void vertexAttribPointers(int index) {
|
public void vertexAttribPointers(int index) {
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
for (IVertexAttrib attrib : this.allAttributes) {
|
for (IVertexAttrib attrib : this.allAttributes) {
|
||||||
IAttribSpec spec = attrib.attribSpec();
|
IAttribSpec spec = attrib.attribSpec();
|
||||||
spec.vertexAttribPointer(stride, index, offset);
|
spec.vertexAttribPointer(stride, index, offset);
|
||||||
index += spec.getAttributeCount();
|
index += spec.getAttributeCount();
|
||||||
offset += spec.getSize();
|
offset += spec.getSize();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Builder builder() {
|
public static Builder builder() {
|
||||||
return new Builder();
|
return new Builder();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static class Builder {
|
public static class Builder {
|
||||||
private final ArrayList<IVertexAttrib> allAttributes;
|
private final ArrayList<IVertexAttrib> allAttributes;
|
||||||
|
|
||||||
public Builder() {
|
public Builder() {
|
||||||
allAttributes = new ArrayList<>();
|
allAttributes = new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public <A extends Enum<A> & IVertexAttrib> Builder addAttributes(Class<A> attribEnum) {
|
public <A extends Enum<A> & IVertexAttrib> Builder addAttributes(Class<A> attribEnum) {
|
||||||
allAttributes.addAll(Arrays.asList(attribEnum.getEnumConstants()));
|
allAttributes.addAll(Arrays.asList(attribEnum.getEnumConstants()));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public VertexFormat build() {
|
public VertexFormat build() {
|
||||||
return new VertexFormat(allAttributes);
|
return new VertexFormat(allAttributes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,20 +7,20 @@ import com.simibubi.create.foundation.render.backend.gl.GlFogMode;
|
||||||
|
|
||||||
public class FogSensitiveProgram<P extends GlProgram> implements IMultiProgram<P> {
|
public class FogSensitiveProgram<P extends GlProgram> implements IMultiProgram<P> {
|
||||||
|
|
||||||
private final Map<GlFogMode, P> programs;
|
private final Map<GlFogMode, P> programs;
|
||||||
|
|
||||||
public FogSensitiveProgram(Map<GlFogMode, P> programs) {
|
public FogSensitiveProgram(Map<GlFogMode, P> programs) {
|
||||||
this.programs = programs;
|
this.programs = programs;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public P get() {
|
public P get() {
|
||||||
return programs.get(GlFog.getFogMode());
|
return programs.get(GlFog.getFogMode());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void delete() {
|
public void delete() {
|
||||||
programs.values().forEach(GlProgram::delete);
|
programs.values().forEach(GlProgram::delete);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,43 +3,43 @@ package com.simibubi.create.foundation.render.backend.gl.shader;
|
||||||
import com.simibubi.create.foundation.render.backend.gl.GlPrimitiveType;
|
import com.simibubi.create.foundation.render.backend.gl.GlPrimitiveType;
|
||||||
|
|
||||||
public class GLSLType {
|
public class GLSLType {
|
||||||
public static final GLSLType FLOAT = new GLSLType("mat4", GlPrimitiveType.FLOAT, 16);
|
public static final GLSLType FLOAT = new GLSLType("mat4", GlPrimitiveType.FLOAT, 16);
|
||||||
public static final GLSLType VEC2 = new GLSLType("vec4", GlPrimitiveType.FLOAT, 4);
|
public static final GLSLType VEC2 = new GLSLType("vec4", GlPrimitiveType.FLOAT, 4);
|
||||||
public static final GLSLType VEC3 = new GLSLType("vec3", GlPrimitiveType.FLOAT, 3);
|
public static final GLSLType VEC3 = new GLSLType("vec3", GlPrimitiveType.FLOAT, 3);
|
||||||
public static final GLSLType VEC4 = new GLSLType("vec2", GlPrimitiveType.FLOAT, 2);
|
public static final GLSLType VEC4 = new GLSLType("vec2", GlPrimitiveType.FLOAT, 2);
|
||||||
public static final GLSLType MAT4 = new GLSLType("float", GlPrimitiveType.FLOAT, 1);
|
public static final GLSLType MAT4 = new GLSLType("float", GlPrimitiveType.FLOAT, 1);
|
||||||
|
|
||||||
private final String symbol;
|
private final String symbol;
|
||||||
private final GlPrimitiveType base;
|
private final GlPrimitiveType base;
|
||||||
private final int count;
|
private final int count;
|
||||||
private final int size;
|
private final int size;
|
||||||
private final int attributeCount;
|
private final int attributeCount;
|
||||||
|
|
||||||
public GLSLType(String symbol, GlPrimitiveType base, int count) {
|
public GLSLType(String symbol, GlPrimitiveType base, int count) {
|
||||||
this.symbol = symbol;
|
this.symbol = symbol;
|
||||||
this.base = base;
|
this.base = base;
|
||||||
this.count = count;
|
this.count = count;
|
||||||
this.size = base.getSize() * count;
|
this.size = base.getSize() * count;
|
||||||
this.attributeCount = (this.size + 15) / 16; // ceiling division. GLSL vertex attributes can only be 16 bytes wide
|
this.attributeCount = (this.size + 15) / 16; // ceiling division. GLSL vertex attributes can only be 16 bytes wide
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getSymbol() {
|
public String getSymbol() {
|
||||||
return symbol;
|
return symbol;
|
||||||
}
|
}
|
||||||
|
|
||||||
public GlPrimitiveType getBase() {
|
public GlPrimitiveType getBase() {
|
||||||
return base;
|
return base;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getCount() {
|
public int getCount() {
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getSize() {
|
public int getSize() {
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getAttributeCount() {
|
public int getAttributeCount() {
|
||||||
return attributeCount;
|
return attributeCount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,117 +11,119 @@ import net.minecraft.util.ResourceLocation;
|
||||||
|
|
||||||
public abstract class GlProgram extends GlObject {
|
public abstract class GlProgram extends GlObject {
|
||||||
|
|
||||||
public final ResourceLocation name;
|
public final ResourceLocation name;
|
||||||
|
|
||||||
protected GlProgram(ResourceLocation name, int handle) {
|
protected GlProgram(ResourceLocation name, int handle) {
|
||||||
setHandle(handle);
|
setHandle(handle);
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Builder builder(ResourceLocation name, GlFogMode fogMode) {
|
public static Builder builder(ResourceLocation name, GlFogMode fogMode) {
|
||||||
return new Builder(name, fogMode);
|
return new Builder(name, fogMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void bind() {
|
public void bind() {
|
||||||
GL20.glUseProgram(handle());
|
GL20.glUseProgram(handle());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void unbind() {
|
public void unbind() {
|
||||||
GL20.glUseProgram(0);
|
GL20.glUseProgram(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the index of the uniform with the given name.
|
* Retrieves the index of the uniform with the given name.
|
||||||
* @param uniform The name of the uniform to find the index of
|
*
|
||||||
* @return The uniform's index
|
* @param uniform The name of the uniform to find the index of
|
||||||
*/
|
* @return The uniform's index
|
||||||
public int getUniformLocation(String uniform) {
|
*/
|
||||||
int index = GL20.glGetUniformLocation(this.handle(), uniform);
|
public int getUniformLocation(String uniform) {
|
||||||
|
int index = GL20.glGetUniformLocation(this.handle(), uniform);
|
||||||
|
|
||||||
if (index < 0) {
|
if (index < 0) {
|
||||||
Backend.log.debug("No active uniform '{}' exists in program '{}'. Could be unused.", uniform, this.name);
|
Backend.log.debug("No active uniform '{}' exists in program '{}'. Could be unused.", uniform, this.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Binds a sampler uniform to the given texture unit.
|
* Binds a sampler uniform to the given texture unit.
|
||||||
* @param name The name of the sampler uniform.
|
*
|
||||||
* @param binding The index of the texture unit.
|
* @param name The name of the sampler uniform.
|
||||||
* @return The sampler uniform's index.
|
* @param binding The index of the texture unit.
|
||||||
* @throws NullPointerException If no uniform exists with the given name.
|
* @return The sampler uniform's index.
|
||||||
*/
|
* @throws NullPointerException If no uniform exists with the given name.
|
||||||
public int setSamplerBinding(String name, int binding) {
|
*/
|
||||||
int samplerUniform = getUniformLocation(name);
|
public int setSamplerBinding(String name, int binding) {
|
||||||
|
int samplerUniform = getUniformLocation(name);
|
||||||
|
|
||||||
if (samplerUniform >= 0) {
|
if (samplerUniform >= 0) {
|
||||||
GL20.glUniform1i(samplerUniform, binding);
|
GL20.glUniform1i(samplerUniform, binding);
|
||||||
}
|
}
|
||||||
|
|
||||||
return samplerUniform;
|
return samplerUniform;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void deleteInternal(int handle) {
|
protected void deleteInternal(int handle) {
|
||||||
GL20.glDeleteProgram(handle);
|
GL20.glDeleteProgram(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Builder {
|
public static class Builder {
|
||||||
private final ResourceLocation name;
|
private final ResourceLocation name;
|
||||||
private final int program;
|
private final int program;
|
||||||
private final GlFogMode fogMode;
|
private final GlFogMode fogMode;
|
||||||
|
|
||||||
private int attributeIndex;
|
private int attributeIndex;
|
||||||
|
|
||||||
public Builder(ResourceLocation name, GlFogMode fogMode) {
|
public Builder(ResourceLocation name, GlFogMode fogMode) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.program = GL20.glCreateProgram();
|
this.program = GL20.glCreateProgram();
|
||||||
this.fogMode = fogMode;
|
this.fogMode = fogMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder attachShader(GlShader shader) {
|
public Builder attachShader(GlShader shader) {
|
||||||
GL20.glAttachShader(this.program, shader.handle());
|
GL20.glAttachShader(this.program, shader.handle());
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public <A extends IVertexAttrib> Builder addAttribute(A attrib) {
|
public <A extends IVertexAttrib> Builder addAttribute(A attrib) {
|
||||||
GL20.glBindAttribLocation(this.program, attributeIndex, attrib.attribName());
|
GL20.glBindAttribLocation(this.program, attributeIndex, attrib.attribName());
|
||||||
attributeIndex += attrib.attribSpec().getAttributeCount();
|
attributeIndex += attrib.attribSpec().getAttributeCount();
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Links the attached shaders to this program and returns a user-defined container which wraps the shader
|
* Links the attached shaders to this program and returns a user-defined container which wraps the shader
|
||||||
* program. This container can, for example, provide methods for updating the specific uniforms of that shader
|
* program. This container can, for example, provide methods for updating the specific uniforms of that shader
|
||||||
* set.
|
* set.
|
||||||
*
|
*
|
||||||
* @param factory The factory which will create the shader program's container
|
* @param factory The factory which will create the shader program's container
|
||||||
* @param <P> The type which should be instantiated with the new program's handle
|
* @param <P> The type which should be instantiated with the new program's handle
|
||||||
* @return An instantiated shader container as provided by the factory
|
* @return An instantiated shader container as provided by the factory
|
||||||
*/
|
*/
|
||||||
public <P extends GlProgram> P build(ProgramFactory<P> factory) {
|
public <P extends GlProgram> P build(ProgramFactory<P> factory) {
|
||||||
GL20.glLinkProgram(this.program);
|
GL20.glLinkProgram(this.program);
|
||||||
|
|
||||||
String log = GL20.glGetProgramInfoLog(this.program);
|
String log = GL20.glGetProgramInfoLog(this.program);
|
||||||
|
|
||||||
if (!log.isEmpty()) {
|
if (!log.isEmpty()) {
|
||||||
Backend.log.debug("Program link log for " + this.name + ": " + log);
|
Backend.log.debug("Program link log for " + this.name + ": " + log);
|
||||||
}
|
}
|
||||||
|
|
||||||
int result = GL20.glGetProgrami(this.program, GL20.GL_LINK_STATUS);
|
int result = GL20.glGetProgrami(this.program, GL20.GL_LINK_STATUS);
|
||||||
|
|
||||||
if (result != GL20.GL_TRUE) {
|
if (result != GL20.GL_TRUE) {
|
||||||
throw new RuntimeException("Shader program linking failed, see log for details");
|
throw new RuntimeException("Shader program linking failed, see log for details");
|
||||||
}
|
}
|
||||||
|
|
||||||
return factory.create(this.name, this.program, this.fogMode.getFogFactory());
|
return factory.create(this.name, this.program, this.fogMode.getFogFactory());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
public interface ProgramFactory<P extends GlProgram> {
|
public interface ProgramFactory<P extends GlProgram> {
|
||||||
P create(ResourceLocation name, int handle, ProgramFogMode.Factory fogFactory);
|
P create(ResourceLocation name, int handle, ProgramFogMode.Factory fogFactory);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,41 +10,41 @@ import net.minecraft.util.ResourceLocation;
|
||||||
|
|
||||||
public class GlShader extends GlObject {
|
public class GlShader extends GlObject {
|
||||||
|
|
||||||
public final ResourceLocation name;
|
public final ResourceLocation name;
|
||||||
public final ShaderType type;
|
public final ShaderType type;
|
||||||
|
|
||||||
public GlShader(ShaderType type, ResourceLocation name, String source) {
|
public GlShader(ShaderType type, ResourceLocation name, String source) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
int handle = GL20.glCreateShader(type.glEnum);
|
int handle = GL20.glCreateShader(type.glEnum);
|
||||||
|
|
||||||
GlCompat.safeShaderSource(handle, source);
|
GlCompat.safeShaderSource(handle, source);
|
||||||
GL20.glCompileShader(handle);
|
GL20.glCompileShader(handle);
|
||||||
|
|
||||||
String log = GL20.glGetShaderInfoLog(handle);
|
String log = GL20.glGetShaderInfoLog(handle);
|
||||||
|
|
||||||
if (!log.isEmpty()) {
|
if (!log.isEmpty()) {
|
||||||
Backend.log.error("Shader compilation log for " + name + ": " + log);
|
Backend.log.error("Shader compilation log for " + name + ": " + log);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GL20.glGetShaderi(handle, GL20.GL_COMPILE_STATUS) != GL20.GL_TRUE) {
|
if (GL20.glGetShaderi(handle, GL20.GL_COMPILE_STATUS) != GL20.GL_TRUE) {
|
||||||
throw new RuntimeException("Could not compile shader. See log for details.");
|
throw new RuntimeException("Could not compile shader. See log for details.");
|
||||||
}
|
}
|
||||||
|
|
||||||
setHandle(handle);
|
setHandle(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void deleteInternal(int handle) {
|
protected void deleteInternal(int handle) {
|
||||||
GL20.glDeleteShader(handle);
|
GL20.glDeleteShader(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
public interface PreProcessor {
|
public interface PreProcessor {
|
||||||
String process(String source);
|
String process(String source);
|
||||||
|
|
||||||
default PreProcessor andThen(PreProcessor that) {
|
default PreProcessor andThen(PreProcessor that) {
|
||||||
return source -> that.process(this.process(source));
|
return source -> that.process(this.process(source));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ public interface IMultiProgram<P extends GlProgram> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the shader program most suited for the current game state.
|
* Get the shader program most suited for the current game state.
|
||||||
|
*
|
||||||
* @return The one true program.
|
* @return The one true program.
|
||||||
*/
|
*/
|
||||||
P get();
|
P get();
|
||||||
|
|
|
@ -6,53 +6,53 @@ import com.simibubi.create.foundation.render.backend.gl.GlFog;
|
||||||
|
|
||||||
public abstract class ProgramFogMode {
|
public abstract class ProgramFogMode {
|
||||||
|
|
||||||
public abstract void bind();
|
public abstract void bind();
|
||||||
|
|
||||||
public static class None extends ProgramFogMode {
|
public static class None extends ProgramFogMode {
|
||||||
|
|
||||||
public None(GlProgram program) {
|
public None(GlProgram program) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void bind() {
|
public void bind() {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Linear extends ProgramFogMode {
|
public static class Linear extends ProgramFogMode {
|
||||||
private final int uFogColor;
|
private final int uFogColor;
|
||||||
private final int uFogRange;
|
private final int uFogRange;
|
||||||
|
|
||||||
public Linear(GlProgram program) {
|
public Linear(GlProgram program) {
|
||||||
this.uFogColor = program.getUniformLocation("uFogColor");
|
this.uFogColor = program.getUniformLocation("uFogColor");
|
||||||
this.uFogRange = program.getUniformLocation("uFogRange");
|
this.uFogRange = program.getUniformLocation("uFogRange");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void bind() {
|
public void bind() {
|
||||||
GL20.glUniform2f(uFogRange, GlFog.getFogStart(), GlFog.getFogEnd());
|
GL20.glUniform2f(uFogRange, GlFog.getFogStart(), GlFog.getFogEnd());
|
||||||
GL20.glUniform4fv(uFogColor, GlFog.FOG_COLOR);
|
GL20.glUniform4fv(uFogColor, GlFog.FOG_COLOR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Exp2 extends ProgramFogMode {
|
public static class Exp2 extends ProgramFogMode {
|
||||||
private final int uFogColor;
|
private final int uFogColor;
|
||||||
private final int uFogDensity;
|
private final int uFogDensity;
|
||||||
|
|
||||||
public Exp2(GlProgram program) {
|
public Exp2(GlProgram program) {
|
||||||
this.uFogColor = program.getUniformLocation("uFogColor");
|
this.uFogColor = program.getUniformLocation("uFogColor");
|
||||||
this.uFogDensity = program.getUniformLocation("uFogDensity");
|
this.uFogDensity = program.getUniformLocation("uFogDensity");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void bind() {
|
public void bind() {
|
||||||
GL20.glUniform1f(uFogDensity, GlFog.getFogDensity());
|
GL20.glUniform1f(uFogDensity, GlFog.getFogDensity());
|
||||||
GL20.glUniform4fv(uFogColor, GlFog.FOG_COLOR);
|
GL20.glUniform4fv(uFogColor, GlFog.FOG_COLOR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface Factory {
|
public interface Factory {
|
||||||
ProgramFogMode create(GlProgram program);
|
ProgramFogMode create(GlProgram program);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,75 +10,75 @@ import net.minecraft.util.ResourceLocation;
|
||||||
|
|
||||||
public class ProgramSpec<P extends GlProgram> {
|
public class ProgramSpec<P extends GlProgram> {
|
||||||
|
|
||||||
public final ResourceLocation name;
|
public final ResourceLocation name;
|
||||||
public final ResourceLocation vert;
|
public final ResourceLocation vert;
|
||||||
public final ResourceLocation frag;
|
public final ResourceLocation frag;
|
||||||
|
|
||||||
public final ShaderConstants defines;
|
public final ShaderConstants defines;
|
||||||
|
|
||||||
public final GlProgram.ProgramFactory<P> factory;
|
public final GlProgram.ProgramFactory<P> factory;
|
||||||
|
|
||||||
public final ArrayList<IVertexAttrib> attributes;
|
public final ArrayList<IVertexAttrib> attributes;
|
||||||
|
|
||||||
public final boolean fogSensitive;
|
public final boolean fogSensitive;
|
||||||
|
|
||||||
public static <P extends GlProgram> Builder<P> builder(String name, GlProgram.ProgramFactory<P> factory) {
|
public static <P extends GlProgram> Builder<P> builder(String name, GlProgram.ProgramFactory<P> factory) {
|
||||||
return builder(new ResourceLocation(Create.ID, name), factory);
|
return builder(new ResourceLocation(Create.ID, name), factory);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <P extends GlProgram> Builder<P> builder(ResourceLocation name, GlProgram.ProgramFactory<P> factory) {
|
public static <P extends GlProgram> Builder<P> builder(ResourceLocation name, GlProgram.ProgramFactory<P> factory) {
|
||||||
return new Builder<>(name, factory);
|
return new Builder<>(name, factory);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ProgramSpec(ResourceLocation name, ResourceLocation vert, ResourceLocation frag, GlProgram.ProgramFactory<P> factory, ShaderConstants defines, ArrayList<IVertexAttrib> attributes, boolean fogSensitive) {
|
public ProgramSpec(ResourceLocation name, ResourceLocation vert, ResourceLocation frag, GlProgram.ProgramFactory<P> factory, ShaderConstants defines, ArrayList<IVertexAttrib> attributes, boolean fogSensitive) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.vert = vert;
|
this.vert = vert;
|
||||||
this.frag = frag;
|
this.frag = frag;
|
||||||
this.defines = defines;
|
this.defines = defines;
|
||||||
|
|
||||||
this.factory = factory;
|
this.factory = factory;
|
||||||
this.attributes = attributes;
|
this.attributes = attributes;
|
||||||
this.fogSensitive = fogSensitive;
|
this.fogSensitive = fogSensitive;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResourceLocation getVert() {
|
public ResourceLocation getVert() {
|
||||||
return vert;
|
return vert;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResourceLocation getFrag() {
|
public ResourceLocation getFrag() {
|
||||||
return frag;
|
return frag;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Builder<P extends GlProgram> {
|
public static class Builder<P extends GlProgram> {
|
||||||
private ResourceLocation vert;
|
private ResourceLocation vert;
|
||||||
private ResourceLocation frag;
|
private ResourceLocation frag;
|
||||||
private ShaderConstants defines = ShaderConstants.EMPTY;
|
private ShaderConstants defines = ShaderConstants.EMPTY;
|
||||||
private boolean fogSensitive = true;
|
private boolean fogSensitive = true;
|
||||||
|
|
||||||
private final ResourceLocation name;
|
private final ResourceLocation name;
|
||||||
private final GlProgram.ProgramFactory<P> factory;
|
private final GlProgram.ProgramFactory<P> factory;
|
||||||
private final ArrayList<IVertexAttrib> attributes;
|
private final ArrayList<IVertexAttrib> attributes;
|
||||||
|
|
||||||
public Builder(ResourceLocation name, GlProgram.ProgramFactory<P> factory) {
|
public Builder(ResourceLocation name, GlProgram.ProgramFactory<P> factory) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.factory = factory;
|
this.factory = factory;
|
||||||
attributes = new ArrayList<>();
|
attributes = new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder<P> setVert(ResourceLocation vert) {
|
public Builder<P> setVert(ResourceLocation vert) {
|
||||||
this.vert = vert;
|
this.vert = vert;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder<P> setFrag(ResourceLocation frag) {
|
public Builder<P> setFrag(ResourceLocation frag) {
|
||||||
this.frag = frag;
|
this.frag = frag;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder<P> setDefines(ShaderConstants defines) {
|
public Builder<P> setDefines(ShaderConstants defines) {
|
||||||
this.defines = defines;
|
this.defines = defines;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder<P> setFogSensitive(boolean fogSensitive) {
|
public Builder<P> setFogSensitive(boolean fogSensitive) {
|
||||||
this.fogSensitive = fogSensitive;
|
this.fogSensitive = fogSensitive;
|
||||||
|
@ -86,12 +86,12 @@ public class ProgramSpec<P extends GlProgram> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public <A extends Enum<A> & IVertexAttrib> Builder<P> addAttributes(Class<A> attributeEnum) {
|
public <A extends Enum<A> & IVertexAttrib> Builder<P> addAttributes(Class<A> attributeEnum) {
|
||||||
attributes.addAll(Arrays.asList(attributeEnum.getEnumConstants()));
|
attributes.addAll(Arrays.asList(attributeEnum.getEnumConstants()));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ProgramSpec<P> createProgramSpec() {
|
public ProgramSpec<P> createProgramSpec() {
|
||||||
return new ProgramSpec<>(name, vert, frag, factory, defines, attributes, fogSensitive);
|
return new ProgramSpec<>(name, vert, frag, factory, defines, attributes, fogSensitive);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,12 +6,12 @@ package com.simibubi.create.foundation.render.backend.gl.shader;
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
public interface ShaderCallback<P extends GlProgram> {
|
public interface ShaderCallback<P extends GlProgram> {
|
||||||
|
|
||||||
void call(P program);
|
void call(P program);
|
||||||
|
|
||||||
default ShaderCallback<P> andThen(ShaderCallback<P> other) {
|
default ShaderCallback<P> andThen(ShaderCallback<P> other) {
|
||||||
return program -> {
|
return program -> {
|
||||||
call(program);
|
call(program);
|
||||||
other.call(program);
|
other.call(program);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,50 +10,50 @@ import java.util.stream.Stream;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
public class ShaderConstants implements GlShader.PreProcessor {
|
public class ShaderConstants implements GlShader.PreProcessor {
|
||||||
public static final ShaderConstants EMPTY = new ShaderConstants();
|
public static final ShaderConstants EMPTY = new ShaderConstants();
|
||||||
|
|
||||||
private final ArrayList<String> defines;
|
private final ArrayList<String> defines;
|
||||||
|
|
||||||
public ShaderConstants() {
|
public ShaderConstants() {
|
||||||
defines = new ArrayList<>();
|
defines = new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ShaderConstants(ShaderConstants other) {
|
public ShaderConstants(ShaderConstants other) {
|
||||||
this.defines = Lists.newArrayList(other.defines);
|
this.defines = Lists.newArrayList(other.defines);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ShaderConstants define(String def) {
|
public static ShaderConstants define(String def) {
|
||||||
return new ShaderConstants().def(def);
|
return new ShaderConstants().def(def);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ShaderConstants def(String def) {
|
public ShaderConstants def(String def) {
|
||||||
defines.add(def);
|
defines.add(def);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ShaderConstants defineAll(Collection<String> defines) {
|
public ShaderConstants defineAll(Collection<String> defines) {
|
||||||
this.defines.addAll(defines);
|
this.defines.addAll(defines);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ArrayList<String> getDefines() {
|
public ArrayList<String> getDefines() {
|
||||||
return defines;
|
return defines;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Stream<String> directives() {
|
public Stream<String> directives() {
|
||||||
return defines.stream().map(it -> "#define " + it);
|
return defines.stream().map(it -> "#define " + it);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String process(String source) {
|
public String process(String source) {
|
||||||
return new BufferedReader(new StringReader(source)).lines().flatMap(line -> {
|
return new BufferedReader(new StringReader(source)).lines().flatMap(line -> {
|
||||||
Stream<String> map = Stream.of(line);
|
Stream<String> map = Stream.of(line);
|
||||||
|
|
||||||
if (line.startsWith("#version")) {
|
if (line.startsWith("#version")) {
|
||||||
map = Stream.concat(map, directives());
|
map = Stream.concat(map, directives());
|
||||||
}
|
}
|
||||||
|
|
||||||
return map;
|
return map;
|
||||||
}).collect(Collectors.joining("\n"));
|
}).collect(Collectors.joining("\n"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,13 +3,13 @@ package com.simibubi.create.foundation.render.backend.gl.shader;
|
||||||
import org.lwjgl.opengl.GL20;
|
import org.lwjgl.opengl.GL20;
|
||||||
|
|
||||||
public enum ShaderType {
|
public enum ShaderType {
|
||||||
VERTEX(GL20.GL_VERTEX_SHADER),
|
VERTEX(GL20.GL_VERTEX_SHADER),
|
||||||
FRAGMENT(GL20.GL_FRAGMENT_SHADER),
|
FRAGMENT(GL20.GL_FRAGMENT_SHADER),
|
||||||
;
|
;
|
||||||
|
|
||||||
public final int glEnum;
|
public final int glEnum;
|
||||||
|
|
||||||
ShaderType(int glEnum) {
|
ShaderType(int glEnum) {
|
||||||
this.glEnum = glEnum;
|
this.glEnum = glEnum;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,52 +6,50 @@ import org.lwjgl.opengl.GL31;
|
||||||
import org.lwjgl.opengl.GLCapabilities;
|
import org.lwjgl.opengl.GLCapabilities;
|
||||||
|
|
||||||
public enum DrawInstanced implements GlVersioned {
|
public enum DrawInstanced implements GlVersioned {
|
||||||
GL31_DRAW_INSTANCED {
|
GL31_DRAW_INSTANCED {
|
||||||
@Override
|
@Override
|
||||||
public boolean supported(GLCapabilities caps) {
|
public boolean supported(GLCapabilities caps) {
|
||||||
return caps.OpenGL31;
|
return caps.OpenGL31;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void drawArraysInstanced(int mode, int first, int count, int primcount) {
|
public void drawArraysInstanced(int mode, int first, int count, int primcount) {
|
||||||
GL31.glDrawArraysInstanced(mode, first, count, primcount);
|
GL31.glDrawArraysInstanced(mode, first, count, primcount);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ARB_DRAW_INSTANCED {
|
ARB_DRAW_INSTANCED {
|
||||||
@Override
|
@Override
|
||||||
public boolean supported(GLCapabilities caps) {
|
public boolean supported(GLCapabilities caps) {
|
||||||
return caps.GL_ARB_draw_instanced;
|
return caps.GL_ARB_draw_instanced;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void drawArraysInstanced(int mode, int first, int count, int primcount) {
|
public void drawArraysInstanced(int mode, int first, int count, int primcount) {
|
||||||
ARBDrawInstanced.glDrawArraysInstancedARB(mode, first, count, primcount);
|
ARBDrawInstanced.glDrawArraysInstancedARB(mode, first, count, primcount);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
EXT_DRAW_INSTANCED {
|
EXT_DRAW_INSTANCED {
|
||||||
@Override
|
@Override
|
||||||
public boolean supported(GLCapabilities caps) {
|
public boolean supported(GLCapabilities caps) {
|
||||||
return caps.GL_EXT_draw_instanced;
|
return caps.GL_EXT_draw_instanced;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void drawArraysInstanced(int mode, int first, int count, int primcount) {
|
public void drawArraysInstanced(int mode, int first, int count, int primcount) {
|
||||||
EXTDrawInstanced.glDrawArraysInstancedEXT(mode, first, count, primcount);
|
EXTDrawInstanced.glDrawArraysInstancedEXT(mode, first, count, primcount);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
UNSUPPORTED {
|
UNSUPPORTED {
|
||||||
@Override
|
@Override
|
||||||
public boolean supported(GLCapabilities caps) {
|
public boolean supported(GLCapabilities caps) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void drawArraysInstanced(int mode, int first, int count, int primcount) {
|
public void drawArraysInstanced(int mode, int first, int count, int primcount) {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
;
|
public abstract void drawArraysInstanced(int mode, int first, int count, int primcount);
|
||||||
|
|
||||||
public abstract void drawArraysInstanced(int mode, int first, int count, int primcount);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,109 +13,109 @@ import org.lwjgl.system.MemoryUtil;
|
||||||
/**
|
/**
|
||||||
* An instance of this class stores information
|
* An instance of this class stores information
|
||||||
* about what OpenGL features are available.
|
* about what OpenGL features are available.
|
||||||
*
|
* <p>
|
||||||
* Each field stores an enum variant that provides access to the
|
* Each field stores an enum variant that provides access to the
|
||||||
* most appropriate version of a feature for the current system.
|
* most appropriate version of a feature for the current system.
|
||||||
*/
|
*/
|
||||||
public class GlCompat {
|
public class GlCompat {
|
||||||
public final MapBuffer mapBuffer;
|
public final MapBuffer mapBuffer;
|
||||||
|
|
||||||
public final VertexArrayObject vertexArrayObject;
|
public final VertexArrayObject vertexArrayObject;
|
||||||
public final InstancedArrays instancedArrays;
|
public final InstancedArrays instancedArrays;
|
||||||
public final DrawInstanced drawInstanced;
|
public final DrawInstanced drawInstanced;
|
||||||
|
|
||||||
public final RGPixelFormat pixelFormat;
|
public final RGPixelFormat pixelFormat;
|
||||||
|
|
||||||
public GlCompat(GLCapabilities caps) {
|
public GlCompat(GLCapabilities caps) {
|
||||||
mapBuffer = getLatest(MapBuffer.class, caps);
|
mapBuffer = getLatest(MapBuffer.class, caps);
|
||||||
|
|
||||||
vertexArrayObject = getLatest(VertexArrayObject.class, caps);
|
vertexArrayObject = getLatest(VertexArrayObject.class, caps);
|
||||||
instancedArrays = getLatest(InstancedArrays.class, caps);
|
instancedArrays = getLatest(InstancedArrays.class, caps);
|
||||||
drawInstanced = getLatest(DrawInstanced.class, caps);
|
drawInstanced = getLatest(DrawInstanced.class, caps);
|
||||||
|
|
||||||
pixelFormat = getLatest(RGPixelFormat.class, caps);
|
pixelFormat = getLatest(RGPixelFormat.class, caps);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void mapBuffer(int target, int offset, int length, Consumer<ByteBuffer> upload) {
|
public void mapBuffer(int target, int offset, int length, Consumer<ByteBuffer> upload) {
|
||||||
mapBuffer.mapBuffer(target, offset, length, upload);
|
mapBuffer.mapBuffer(target, offset, length, upload);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void vertexAttribDivisor(int index, int divisor) {
|
public void vertexAttribDivisor(int index, int divisor) {
|
||||||
instancedArrays.vertexAttribDivisor(index, divisor);
|
instancedArrays.vertexAttribDivisor(index, divisor);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void drawArraysInstanced(int mode, int first, int count, int primcount) {
|
public void drawArraysInstanced(int mode, int first, int count, int primcount) {
|
||||||
drawInstanced.drawArraysInstanced(mode, first, count, primcount);
|
drawInstanced.drawArraysInstanced(mode, first, count, primcount);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int genVertexArrays() {
|
public int genVertexArrays() {
|
||||||
return vertexArrayObject.genVertexArrays();
|
return vertexArrayObject.genVertexArrays();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void deleteVertexArrays(int array) {
|
public void deleteVertexArrays(int array) {
|
||||||
vertexArrayObject.deleteVertexArrays(array);
|
vertexArrayObject.deleteVertexArrays(array);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void bindVertexArray(int array) {
|
public void bindVertexArray(int array) {
|
||||||
vertexArrayObject.bindVertexArray(array);
|
vertexArrayObject.bindVertexArray(array);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean vertexArrayObjectsSupported() {
|
public boolean vertexArrayObjectsSupported() {
|
||||||
return vertexArrayObject != VertexArrayObject.UNSUPPORTED;
|
return vertexArrayObject != VertexArrayObject.UNSUPPORTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean instancedArraysSupported() {
|
public boolean instancedArraysSupported() {
|
||||||
return instancedArrays != InstancedArrays.UNSUPPORTED;
|
return instancedArrays != InstancedArrays.UNSUPPORTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean drawInstancedSupported() {
|
public boolean drawInstancedSupported() {
|
||||||
return drawInstanced != DrawInstanced.UNSUPPORTED;
|
return drawInstanced != DrawInstanced.UNSUPPORTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the most compatible version of a specific OpenGL feature by iterating over enum constants in order.
|
* Get the most compatible version of a specific OpenGL feature by iterating over enum constants in order.
|
||||||
*
|
*
|
||||||
* @param clazz The class of the versioning enum.
|
* @param clazz The class of the versioning enum.
|
||||||
* @param caps The current system's supported features.
|
* @param caps The current system's supported features.
|
||||||
* @param <V> The type of the versioning enum.
|
* @param <V> The type of the versioning enum.
|
||||||
* @return The first defined enum variant to return true.
|
* @return The first defined enum variant to return true.
|
||||||
*/
|
*/
|
||||||
public static <V extends Enum<V> & GlVersioned> V getLatest(Class<V> clazz, GLCapabilities caps) {
|
public static <V extends Enum<V> & GlVersioned> V getLatest(Class<V> clazz, GLCapabilities caps) {
|
||||||
V[] constants = clazz.getEnumConstants();
|
V[] constants = clazz.getEnumConstants();
|
||||||
V last = constants[constants.length - 1];
|
V last = constants[constants.length - 1];
|
||||||
if (!last.supported(caps)) {
|
if (!last.supported(caps)) {
|
||||||
throw new IllegalStateException("");
|
throw new IllegalStateException("");
|
||||||
}
|
}
|
||||||
|
|
||||||
return Arrays.stream(constants).filter(it -> it.supported(caps)).findFirst().get();
|
return Arrays.stream(constants).filter(it -> it.supported(caps)).findFirst().get();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copied from:
|
* Copied from:
|
||||||
* <br> https://github.com/grondag/canvas/commit/820bf754092ccaf8d0c169620c2ff575722d7d96
|
* <br> https://github.com/grondag/canvas/commit/820bf754092ccaf8d0c169620c2ff575722d7d96
|
||||||
*
|
*
|
||||||
* <p>Identical in function to {@link GL20C#glShaderSource(int, CharSequence)} but
|
* <p>Identical in function to {@link GL20C#glShaderSource(int, CharSequence)} but
|
||||||
* passes a null pointer for string length to force the driver to rely on the null
|
* passes a null pointer for string length to force the driver to rely on the null
|
||||||
* terminator for string length. This is a workaround for an apparent flaw with some
|
* terminator for string length. This is a workaround for an apparent flaw with some
|
||||||
* AMD drivers that don't receive or interpret the length correctly, resulting in
|
* AMD drivers that don't receive or interpret the length correctly, resulting in
|
||||||
* an access violation when the driver tries to read past the string memory.
|
* an access violation when the driver tries to read past the string memory.
|
||||||
*
|
*
|
||||||
* <p>Hat tip to fewizz for the find and the fix.
|
* <p>Hat tip to fewizz for the find and the fix.
|
||||||
*/
|
*/
|
||||||
public static void safeShaderSource(int glId, CharSequence source) {
|
public static void safeShaderSource(int glId, CharSequence source) {
|
||||||
final MemoryStack stack = MemoryStack.stackGet();
|
final MemoryStack stack = MemoryStack.stackGet();
|
||||||
final int stackPointer = stack.getPointer();
|
final int stackPointer = stack.getPointer();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final ByteBuffer sourceBuffer = MemoryUtil.memUTF8(source, true);
|
final ByteBuffer sourceBuffer = MemoryUtil.memUTF8(source, true);
|
||||||
final PointerBuffer pointers = stack.mallocPointer(1);
|
final PointerBuffer pointers = stack.mallocPointer(1);
|
||||||
pointers.put(sourceBuffer);
|
pointers.put(sourceBuffer);
|
||||||
|
|
||||||
GL20C.nglShaderSource(glId, 1, pointers.address0(), 0);
|
GL20C.nglShaderSource(glId, 1, pointers.address0(), 0);
|
||||||
org.lwjgl.system.APIUtil.apiArrayFree(pointers.address0(), 1);
|
org.lwjgl.system.APIUtil.apiArrayFree(pointers.address0(), 1);
|
||||||
} finally {
|
} finally {
|
||||||
stack.setPointer(stackPointer);
|
stack.setPointer(stackPointer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,10 +7,11 @@ import org.lwjgl.opengl.GLCapabilities;
|
||||||
* last defined variant <em>always</em> returns <code>true</code>.
|
* last defined variant <em>always</em> returns <code>true</code>.
|
||||||
*/
|
*/
|
||||||
public interface GlVersioned {
|
public interface GlVersioned {
|
||||||
/**
|
/**
|
||||||
* Queries whether this variant is supported by the current system.
|
* Queries whether this variant is supported by the current system.
|
||||||
* @param caps The {@link GLCapabilities} reported by the current system.
|
*
|
||||||
* @return <code>true</code> if this variant is supported, or if this is the last defined variant.
|
* @param caps The {@link GLCapabilities} reported by the current system.
|
||||||
*/
|
* @return <code>true</code> if this variant is supported, or if this is the last defined variant.
|
||||||
boolean supported(GLCapabilities caps);
|
*/
|
||||||
|
boolean supported(GLCapabilities caps);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,41 +5,39 @@ import org.lwjgl.opengl.GL33;
|
||||||
import org.lwjgl.opengl.GLCapabilities;
|
import org.lwjgl.opengl.GLCapabilities;
|
||||||
|
|
||||||
public enum InstancedArrays implements GlVersioned {
|
public enum InstancedArrays implements GlVersioned {
|
||||||
GL33_INSTANCED_ARRAYS {
|
GL33_INSTANCED_ARRAYS {
|
||||||
@Override
|
@Override
|
||||||
public boolean supported(GLCapabilities caps) {
|
public boolean supported(GLCapabilities caps) {
|
||||||
return caps.OpenGL33;
|
return caps.OpenGL33;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void vertexAttribDivisor(int index, int divisor) {
|
public void vertexAttribDivisor(int index, int divisor) {
|
||||||
GL33.glVertexAttribDivisor(index, divisor);
|
GL33.glVertexAttribDivisor(index, divisor);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ARB_INSTANCED_ARRAYS {
|
ARB_INSTANCED_ARRAYS {
|
||||||
@Override
|
@Override
|
||||||
public boolean supported(GLCapabilities caps) {
|
public boolean supported(GLCapabilities caps) {
|
||||||
return caps.GL_ARB_instanced_arrays;
|
return caps.GL_ARB_instanced_arrays;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void vertexAttribDivisor(int index, int divisor) {
|
public void vertexAttribDivisor(int index, int divisor) {
|
||||||
ARBInstancedArrays.glVertexAttribDivisorARB(index, divisor);
|
ARBInstancedArrays.glVertexAttribDivisorARB(index, divisor);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
UNSUPPORTED {
|
UNSUPPORTED {
|
||||||
@Override
|
@Override
|
||||||
public boolean supported(GLCapabilities caps) {
|
public boolean supported(GLCapabilities caps) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void vertexAttribDivisor(int index, int divisor) {
|
public void vertexAttribDivisor(int index, int divisor) {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
;
|
public abstract void vertexAttribDivisor(int index, int divisor);
|
||||||
|
|
||||||
public abstract void vertexAttribDivisor(int index, int divisor);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,66 +10,66 @@ import org.lwjgl.opengl.GLCapabilities;
|
||||||
|
|
||||||
public enum MapBuffer implements GlVersioned {
|
public enum MapBuffer implements GlVersioned {
|
||||||
|
|
||||||
GL30_RANGE {
|
GL30_RANGE {
|
||||||
@Override
|
@Override
|
||||||
public boolean supported(GLCapabilities caps) {
|
public boolean supported(GLCapabilities caps) {
|
||||||
return caps.OpenGL30;
|
return caps.OpenGL30;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void mapBuffer(int target, int offset, int length, Consumer<ByteBuffer> upload) {
|
public void mapBuffer(int target, int offset, int length, Consumer<ByteBuffer> upload) {
|
||||||
ByteBuffer buffer = GL30.glMapBufferRange(target, offset, length, GL30.GL_MAP_WRITE_BIT);
|
ByteBuffer buffer = GL30.glMapBufferRange(target, offset, length, GL30.GL_MAP_WRITE_BIT);
|
||||||
|
|
||||||
upload.accept(buffer);
|
upload.accept(buffer);
|
||||||
buffer.rewind();
|
buffer.rewind();
|
||||||
|
|
||||||
GL30.glUnmapBuffer(target);
|
GL30.glUnmapBuffer(target);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ARB_RANGE {
|
ARB_RANGE {
|
||||||
@Override
|
@Override
|
||||||
public boolean supported(GLCapabilities caps) {
|
public boolean supported(GLCapabilities caps) {
|
||||||
return caps.GL_ARB_map_buffer_range;
|
return caps.GL_ARB_map_buffer_range;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void mapBuffer(int target, int offset, int length, Consumer<ByteBuffer> upload) {
|
public void mapBuffer(int target, int offset, int length, Consumer<ByteBuffer> upload) {
|
||||||
ByteBuffer buffer = ARBMapBufferRange.glMapBufferRange(target, offset, length, GL30.GL_MAP_WRITE_BIT);
|
ByteBuffer buffer = ARBMapBufferRange.glMapBufferRange(target, offset, length, GL30.GL_MAP_WRITE_BIT);
|
||||||
|
|
||||||
upload.accept(buffer);
|
upload.accept(buffer);
|
||||||
buffer.rewind();
|
buffer.rewind();
|
||||||
|
|
||||||
GL30.glUnmapBuffer(target);
|
GL30.glUnmapBuffer(target);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
GL15_MAP {
|
GL15_MAP {
|
||||||
@Override
|
@Override
|
||||||
public boolean supported(GLCapabilities caps) {
|
public boolean supported(GLCapabilities caps) {
|
||||||
return caps.OpenGL15;
|
return caps.OpenGL15;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void mapBuffer(int target, int offset, int length, Consumer<ByteBuffer> upload) {
|
public void mapBuffer(int target, int offset, int length, Consumer<ByteBuffer> upload) {
|
||||||
ByteBuffer buffer = GL15.glMapBuffer(target, GL15.GL_WRITE_ONLY);
|
ByteBuffer buffer = GL15.glMapBuffer(target, GL15.GL_WRITE_ONLY);
|
||||||
|
|
||||||
buffer.position(offset);
|
buffer.position(offset);
|
||||||
upload.accept(buffer);
|
upload.accept(buffer);
|
||||||
buffer.rewind();
|
buffer.rewind();
|
||||||
GL15.glUnmapBuffer(target);
|
GL15.glUnmapBuffer(target);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
UNSUPPORTED {
|
UNSUPPORTED {
|
||||||
@Override
|
@Override
|
||||||
public boolean supported(GLCapabilities caps) {
|
public boolean supported(GLCapabilities caps) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void mapBuffer(int target, int offset, int length, Consumer<ByteBuffer> upload) {
|
public void mapBuffer(int target, int offset, int length, Consumer<ByteBuffer> upload) {
|
||||||
throw new UnsupportedOperationException("glMapBuffer not supported");
|
throw new UnsupportedOperationException("glMapBuffer not supported");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
public abstract void mapBuffer(int target, int offset, int length, Consumer<ByteBuffer> upload);
|
public abstract void mapBuffer(int target, int offset, int length, Consumer<ByteBuffer> upload);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,73 +5,73 @@ import org.lwjgl.opengl.GL30;
|
||||||
import org.lwjgl.opengl.GLCapabilities;
|
import org.lwjgl.opengl.GLCapabilities;
|
||||||
|
|
||||||
public enum RGPixelFormat implements GlVersioned {
|
public enum RGPixelFormat implements GlVersioned {
|
||||||
GL30_RG {
|
GL30_RG {
|
||||||
@Override
|
@Override
|
||||||
public boolean supported(GLCapabilities caps) {
|
public boolean supported(GLCapabilities caps) {
|
||||||
return caps.OpenGL30;
|
return caps.OpenGL30;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int internalFormat() {
|
public int internalFormat() {
|
||||||
return GL30.GL_RG8;
|
return GL30.GL_RG8;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int format() {
|
public int format() {
|
||||||
return GL30.GL_RG;
|
return GL30.GL_RG;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int byteCount() {
|
public int byteCount() {
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
GL11_RGB {
|
GL11_RGB {
|
||||||
@Override
|
@Override
|
||||||
public boolean supported(GLCapabilities caps) {
|
public boolean supported(GLCapabilities caps) {
|
||||||
return caps.OpenGL11;
|
return caps.OpenGL11;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int internalFormat() {
|
public int internalFormat() {
|
||||||
return GL11.GL_RGB8;
|
return GL11.GL_RGB8;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int format() {
|
public int format() {
|
||||||
return GL11.GL_RGB;
|
return GL11.GL_RGB;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int byteCount() {
|
public int byteCount() {
|
||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
UNSUPPORTED {
|
UNSUPPORTED {
|
||||||
@Override
|
@Override
|
||||||
public boolean supported(GLCapabilities caps) {
|
public boolean supported(GLCapabilities caps) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int internalFormat() {
|
public int internalFormat() {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int format() {
|
public int format() {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int byteCount() {
|
public int byteCount() {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
;
|
public abstract int internalFormat();
|
||||||
|
|
||||||
public abstract int internalFormat();
|
public abstract int format();
|
||||||
public abstract int format();
|
|
||||||
public abstract int byteCount();
|
public abstract int byteCount();
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,75 +5,73 @@ import org.lwjgl.opengl.GL30;
|
||||||
import org.lwjgl.opengl.GLCapabilities;
|
import org.lwjgl.opengl.GLCapabilities;
|
||||||
|
|
||||||
public enum VertexArrayObject implements GlVersioned {
|
public enum VertexArrayObject implements GlVersioned {
|
||||||
GL30_VAO {
|
GL30_VAO {
|
||||||
@Override
|
@Override
|
||||||
public boolean supported(GLCapabilities caps) {
|
public boolean supported(GLCapabilities caps) {
|
||||||
return caps.OpenGL30;
|
return caps.OpenGL30;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int genVertexArrays() {
|
public int genVertexArrays() {
|
||||||
return GL30.glGenVertexArrays();
|
return GL30.glGenVertexArrays();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void bindVertexArray(int array) {
|
public void bindVertexArray(int array) {
|
||||||
GL30.glBindVertexArray(array);
|
GL30.glBindVertexArray(array);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void deleteVertexArrays(int array) {
|
public void deleteVertexArrays(int array) {
|
||||||
GL30.glDeleteVertexArrays(array);
|
GL30.glDeleteVertexArrays(array);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ARB_VAO {
|
ARB_VAO {
|
||||||
@Override
|
@Override
|
||||||
public boolean supported(GLCapabilities caps) {
|
public boolean supported(GLCapabilities caps) {
|
||||||
return caps.GL_ARB_vertex_array_object;
|
return caps.GL_ARB_vertex_array_object;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int genVertexArrays() {
|
public int genVertexArrays() {
|
||||||
return ARBVertexArrayObject.glGenVertexArrays();
|
return ARBVertexArrayObject.glGenVertexArrays();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void bindVertexArray(int array) {
|
public void bindVertexArray(int array) {
|
||||||
ARBVertexArrayObject.glBindVertexArray(array);
|
ARBVertexArrayObject.glBindVertexArray(array);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void deleteVertexArrays(int array) {
|
public void deleteVertexArrays(int array) {
|
||||||
ARBVertexArrayObject.glDeleteVertexArrays(array);
|
ARBVertexArrayObject.glDeleteVertexArrays(array);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
UNSUPPORTED {
|
UNSUPPORTED {
|
||||||
@Override
|
@Override
|
||||||
public boolean supported(GLCapabilities caps) {
|
public boolean supported(GLCapabilities caps) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int genVertexArrays() {
|
public int genVertexArrays() {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void bindVertexArray(int array) {
|
public void bindVertexArray(int array) {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void deleteVertexArrays(int array) {
|
public void deleteVertexArrays(int array) {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
;
|
public abstract int genVertexArrays();
|
||||||
|
|
||||||
public abstract int genVertexArrays();
|
public abstract void bindVertexArray(int array);
|
||||||
|
|
||||||
public abstract void bindVertexArray(int array);
|
public abstract void deleteVertexArrays(int array);
|
||||||
|
|
||||||
public abstract void deleteVertexArrays(int array);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,21 +10,21 @@ package com.simibubi.create.foundation.render.backend.instancing;
|
||||||
* to parameterize the instances, you're encouraged to implement this for prototyping.
|
* to parameterize the instances, you're encouraged to implement this for prototyping.
|
||||||
*/
|
*/
|
||||||
public interface IDynamicInstance extends IInstance {
|
public interface IDynamicInstance extends IInstance {
|
||||||
/**
|
/**
|
||||||
* Called every frame.
|
* Called every frame.
|
||||||
*/
|
*/
|
||||||
void beginFrame();
|
void beginFrame();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* As a further optimization, dynamic instances that are far away are ticked less often.
|
* As a further optimization, dynamic instances that are far away are ticked less often.
|
||||||
* This behavior can be disabled by returning false.
|
* This behavior can be disabled by returning false.
|
||||||
*
|
*
|
||||||
* <br> You might want to opt out of this if you want your animations to remain smooth
|
* <br> You might want to opt out of this if you want your animations to remain smooth
|
||||||
* even when far away from the camera. It is recommended to keep this as is, however.
|
* even when far away from the camera. It is recommended to keep this as is, however.
|
||||||
*
|
*
|
||||||
* @return <code>true</code> if your instance should be slow ticked.
|
* @return <code>true</code> if your instance should be slow ticked.
|
||||||
*/
|
*/
|
||||||
default boolean decreaseFramerateWithDistance() {
|
default boolean decreaseFramerateWithDistance() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ package com.simibubi.create.foundation.render.backend.instancing;
|
||||||
* <code>Minecraft.getInstance().world</code> will always support Flywheel.
|
* <code>Minecraft.getInstance().world</code> will always support Flywheel.
|
||||||
*/
|
*/
|
||||||
public interface IFlywheelWorld {
|
public interface IFlywheelWorld {
|
||||||
default boolean supportsFlywheel() {
|
default boolean supportsFlywheel() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package com.simibubi.create.foundation.render.backend.instancing;
|
package com.simibubi.create.foundation.render.backend.instancing;
|
||||||
|
|
||||||
public interface IInstanceRendered {
|
public interface IInstanceRendered {
|
||||||
default boolean shouldRenderAsTE() {
|
default boolean shouldRenderAsTE() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,5 +4,5 @@ import net.minecraft.tileentity.TileEntity;
|
||||||
|
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
public interface IRendererFactory<T extends TileEntity> {
|
public interface IRendererFactory<T extends TileEntity> {
|
||||||
TileEntityInstance<? super T> create(InstancedTileRenderer<?> manager, T te);
|
TileEntityInstance<? super T> create(InstancedTileRenderer<?> manager, T te);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,61 +4,61 @@ import java.nio.ByteBuffer;
|
||||||
|
|
||||||
public abstract class InstanceData {
|
public abstract class InstanceData {
|
||||||
|
|
||||||
protected final InstancedModel<?> owner;
|
protected final InstancedModel<?> owner;
|
||||||
|
|
||||||
boolean dirty;
|
boolean dirty;
|
||||||
boolean removed;
|
boolean removed;
|
||||||
|
|
||||||
protected InstanceData(InstancedModel<?> owner) {
|
protected InstanceData(InstancedModel<?> owner) {
|
||||||
this.owner = owner;
|
this.owner = owner;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract void write(ByteBuffer buf);
|
public abstract void write(ByteBuffer buf);
|
||||||
|
|
||||||
public void markDirty() {
|
public void markDirty() {
|
||||||
owner.anyToUpdate = true;
|
owner.anyToUpdate = true;
|
||||||
dirty = true;
|
dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void delete() {
|
public void delete() {
|
||||||
owner.anyToRemove = true;
|
owner.anyToRemove = true;
|
||||||
removed = true;
|
removed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void putVec4(ByteBuffer buf, float x, float y, float z, float w) {
|
public void putVec4(ByteBuffer buf, float x, float y, float z, float w) {
|
||||||
put(buf, x);
|
put(buf, x);
|
||||||
put(buf, y);
|
put(buf, y);
|
||||||
put(buf, z);
|
put(buf, z);
|
||||||
put(buf, w);
|
put(buf, w);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void putVec3(ByteBuffer buf, float x, float y, float z) {
|
public void putVec3(ByteBuffer buf, float x, float y, float z) {
|
||||||
put(buf, x);
|
put(buf, x);
|
||||||
put(buf, y);
|
put(buf, y);
|
||||||
put(buf, z);
|
put(buf, z);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void putVec2(ByteBuffer buf, float x, float y) {
|
public void putVec2(ByteBuffer buf, float x, float y) {
|
||||||
put(buf, x);
|
put(buf, x);
|
||||||
put(buf, y);
|
put(buf, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void putVec3(ByteBuffer buf, byte x, byte y, byte z) {
|
public void putVec3(ByteBuffer buf, byte x, byte y, byte z) {
|
||||||
put(buf, x);
|
put(buf, x);
|
||||||
put(buf, y);
|
put(buf, y);
|
||||||
put(buf, z);
|
put(buf, z);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void putVec2(ByteBuffer buf, byte x, byte y) {
|
public void putVec2(ByteBuffer buf, byte x, byte y) {
|
||||||
put(buf, x);
|
put(buf, x);
|
||||||
put(buf, y);
|
put(buf, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void put(ByteBuffer buf, byte b) {
|
public void put(ByteBuffer buf, byte b) {
|
||||||
buf.put(b);
|
buf.put(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void put(ByteBuffer buf, float f) {
|
public void put(ByteBuffer buf, float f) {
|
||||||
buf.putFloat(f);
|
buf.putFloat(f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,239 +19,239 @@ import com.simibubi.create.foundation.render.backend.gl.attrib.VertexFormat;
|
||||||
import net.minecraft.client.renderer.BufferBuilder;
|
import net.minecraft.client.renderer.BufferBuilder;
|
||||||
|
|
||||||
public abstract class InstancedModel<D extends InstanceData> extends BufferedModel {
|
public abstract class InstancedModel<D extends InstanceData> extends BufferedModel {
|
||||||
public static final VertexFormat FORMAT = VertexFormat.builder().addAttributes(ModelAttributes.class).build();
|
public static final VertexFormat FORMAT = VertexFormat.builder().addAttributes(ModelAttributes.class).build();
|
||||||
|
|
||||||
public final InstancedTileRenderer<?> renderer;
|
public final InstancedTileRenderer<?> renderer;
|
||||||
|
|
||||||
protected GlVertexArray vao;
|
protected GlVertexArray vao;
|
||||||
protected GlBuffer instanceVBO;
|
protected GlBuffer instanceVBO;
|
||||||
protected int glBufferSize = -1;
|
protected int glBufferSize = -1;
|
||||||
protected int glInstanceCount = 0;
|
protected int glInstanceCount = 0;
|
||||||
|
|
||||||
protected final ArrayList<D> data = new ArrayList<>();
|
protected final ArrayList<D> data = new ArrayList<>();
|
||||||
|
|
||||||
boolean anyToRemove;
|
boolean anyToRemove;
|
||||||
boolean anyToUpdate;
|
boolean anyToUpdate;
|
||||||
|
|
||||||
public InstancedModel(InstancedTileRenderer<?> renderer, BufferBuilder buf) {
|
public InstancedModel(InstancedTileRenderer<?> renderer, BufferBuilder buf) {
|
||||||
super(buf);
|
super(buf);
|
||||||
this.renderer = renderer;
|
this.renderer = renderer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void init() {
|
protected void init() {
|
||||||
vao = new GlVertexArray();
|
vao = new GlVertexArray();
|
||||||
instanceVBO = new GlBuffer(GL20.GL_ARRAY_BUFFER);
|
instanceVBO = new GlBuffer(GL20.GL_ARRAY_BUFFER);
|
||||||
|
|
||||||
vao.with(vao -> super.init());
|
vao.with(vao -> super.init());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void initModel() {
|
protected void initModel() {
|
||||||
super.initModel();
|
super.initModel();
|
||||||
setupAttributes();
|
setupAttributes();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int instanceCount() {
|
public int instanceCount() {
|
||||||
return data.size();
|
return data.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isEmpty() {
|
public boolean isEmpty() {
|
||||||
return instanceCount() == 0;
|
return instanceCount() == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void deleteInternal() {
|
protected void deleteInternal() {
|
||||||
super.deleteInternal();
|
super.deleteInternal();
|
||||||
|
|
||||||
instanceVBO.delete();
|
instanceVBO.delete();
|
||||||
vao.delete();
|
vao.delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized D createInstance() {
|
public synchronized D createInstance() {
|
||||||
D instanceData = newInstance();
|
D instanceData = newInstance();
|
||||||
instanceData.dirty = true;
|
instanceData.dirty = true;
|
||||||
anyToUpdate = true;
|
anyToUpdate = true;
|
||||||
data.add(instanceData);
|
data.add(instanceData);
|
||||||
|
|
||||||
return instanceData;
|
return instanceData;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract D newInstance();
|
protected abstract D newInstance();
|
||||||
|
|
||||||
protected void doRender() {
|
protected void doRender() {
|
||||||
vao.with(vao -> {
|
vao.with(vao -> {
|
||||||
renderSetup();
|
renderSetup();
|
||||||
|
|
||||||
if (glInstanceCount > 0)
|
if (glInstanceCount > 0)
|
||||||
Backend.compat.drawArraysInstanced(GL11.GL_QUADS, 0, vertexCount, glInstanceCount);
|
Backend.compat.drawArraysInstanced(GL11.GL_QUADS, 0, vertexCount, glInstanceCount);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void renderSetup() {
|
protected void renderSetup() {
|
||||||
if (anyToRemove) {
|
if (anyToRemove) {
|
||||||
removeDeletedInstances();
|
removeDeletedInstances();
|
||||||
}
|
}
|
||||||
|
|
||||||
instanceVBO.bind();
|
instanceVBO.bind();
|
||||||
if (!realloc()) {
|
if (!realloc()) {
|
||||||
|
|
||||||
if (anyToRemove) {
|
if (anyToRemove) {
|
||||||
clearBufferTail();
|
clearBufferTail();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (anyToUpdate) {
|
if (anyToUpdate) {
|
||||||
updateBuffer();
|
updateBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
glInstanceCount = data.size();
|
glInstanceCount = data.size();
|
||||||
informAttribDivisors();
|
informAttribDivisors();
|
||||||
instanceVBO.unbind();
|
instanceVBO.unbind();
|
||||||
|
|
||||||
this.anyToRemove = false;
|
this.anyToRemove = false;
|
||||||
this.anyToUpdate = false;
|
this.anyToUpdate = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void informAttribDivisors() {
|
private void informAttribDivisors() {
|
||||||
int staticAttributes = getModelFormat().getShaderAttributeCount();
|
int staticAttributes = getModelFormat().getShaderAttributeCount();
|
||||||
getInstanceFormat().vertexAttribPointers(staticAttributes);
|
getInstanceFormat().vertexAttribPointers(staticAttributes);
|
||||||
|
|
||||||
for (int i = 0; i < getInstanceFormat().getShaderAttributeCount(); i++) {
|
for (int i = 0; i < getInstanceFormat().getShaderAttributeCount(); i++) {
|
||||||
Backend.compat.vertexAttribDivisor(i + staticAttributes, 1);
|
Backend.compat.vertexAttribDivisor(i + staticAttributes, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void clearBufferTail() {
|
private void clearBufferTail() {
|
||||||
int size = data.size();
|
int size = data.size();
|
||||||
final int offset = size * getInstanceFormat().getStride();
|
final int offset = size * getInstanceFormat().getStride();
|
||||||
final int length = glBufferSize - offset;
|
final int length = glBufferSize - offset;
|
||||||
if (length > 0) {
|
if (length > 0) {
|
||||||
instanceVBO.map(offset, length, buffer -> {
|
instanceVBO.map(offset, length, buffer -> {
|
||||||
buffer.put(new byte[length]);
|
buffer.put(new byte[length]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateBuffer() {
|
private void updateBuffer() {
|
||||||
final int size = data.size();
|
final int size = data.size();
|
||||||
|
|
||||||
if (size <= 0) return;
|
if (size <= 0) return;
|
||||||
|
|
||||||
final int stride = getInstanceFormat().getStride();
|
final int stride = getInstanceFormat().getStride();
|
||||||
final BitSet dirtySet = getDirtyBitSet();
|
final BitSet dirtySet = getDirtyBitSet();
|
||||||
|
|
||||||
if (dirtySet.isEmpty()) return;
|
if (dirtySet.isEmpty()) return;
|
||||||
|
|
||||||
final int firstDirty = dirtySet.nextSetBit(0);
|
final int firstDirty = dirtySet.nextSetBit(0);
|
||||||
final int lastDirty = dirtySet.previousSetBit(size);
|
final int lastDirty = dirtySet.previousSetBit(size);
|
||||||
|
|
||||||
final int offset = firstDirty * stride;
|
final int offset = firstDirty * stride;
|
||||||
final int length = (1 + lastDirty - firstDirty) * stride;
|
final int length = (1 + lastDirty - firstDirty) * stride;
|
||||||
|
|
||||||
if (length > 0) {
|
if (length > 0) {
|
||||||
instanceVBO.map(offset, length, buffer -> {
|
instanceVBO.map(offset, length, buffer -> {
|
||||||
dirtySet.stream().forEach(i -> {
|
dirtySet.stream().forEach(i -> {
|
||||||
final D d = data.get(i);
|
final D d = data.get(i);
|
||||||
|
|
||||||
buffer.position(i * stride - offset);
|
buffer.position(i * stride - offset);
|
||||||
d.write(buffer);
|
d.write(buffer);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private BitSet getDirtyBitSet() {
|
private BitSet getDirtyBitSet() {
|
||||||
final int size = data.size();
|
final int size = data.size();
|
||||||
final BitSet dirtySet = new BitSet(size);
|
final BitSet dirtySet = new BitSet(size);
|
||||||
|
|
||||||
for (int i = 0; i < size; i++) {
|
for (int i = 0; i < size; i++) {
|
||||||
D element = data.get(i);
|
D element = data.get(i);
|
||||||
if (element.dirty) {
|
if (element.dirty) {
|
||||||
dirtySet.set(i);
|
dirtySet.set(i);
|
||||||
|
|
||||||
element.dirty = false;
|
element.dirty = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return dirtySet;
|
return dirtySet;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean realloc() {
|
private boolean realloc() {
|
||||||
int size = this.data.size();
|
int size = this.data.size();
|
||||||
int stride = getInstanceFormat().getStride();
|
int stride = getInstanceFormat().getStride();
|
||||||
int requiredSize = size * stride;
|
int requiredSize = size * stride;
|
||||||
if (requiredSize > glBufferSize) {
|
if (requiredSize > glBufferSize) {
|
||||||
glBufferSize = requiredSize + stride * 16;
|
glBufferSize = requiredSize + stride * 16;
|
||||||
GL15.glBufferData(instanceVBO.getBufferType(), glBufferSize, GL15.GL_STATIC_DRAW);
|
GL15.glBufferData(instanceVBO.getBufferType(), glBufferSize, GL15.GL_STATIC_DRAW);
|
||||||
|
|
||||||
instanceVBO.map(glBufferSize, buffer -> {
|
instanceVBO.map(glBufferSize, buffer -> {
|
||||||
for (D datum : data) {
|
for (D datum : data) {
|
||||||
datum.write(buffer);
|
datum.write(buffer);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
glInstanceCount = size;
|
glInstanceCount = size;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void removeDeletedInstances() {
|
private void removeDeletedInstances() {
|
||||||
// figure out which elements are to be removed
|
// figure out which elements are to be removed
|
||||||
// any exception thrown from the filter predicate at this stage
|
// any exception thrown from the filter predicate at this stage
|
||||||
// will leave the collection unmodified
|
// will leave the collection unmodified
|
||||||
final int oldSize = this.data.size();
|
final int oldSize = this.data.size();
|
||||||
int removeCount = 0;
|
int removeCount = 0;
|
||||||
final BitSet removeSet = new BitSet(oldSize);
|
final BitSet removeSet = new BitSet(oldSize);
|
||||||
for (int i = 0; i < oldSize; i++) {
|
for (int i = 0; i < oldSize; i++) {
|
||||||
final D element = this.data.get(i);
|
final D element = this.data.get(i);
|
||||||
if (element.removed) {
|
if (element.removed) {
|
||||||
removeSet.set(i);
|
removeSet.set(i);
|
||||||
removeCount++;
|
removeCount++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final int newSize = oldSize - removeCount;
|
final int newSize = oldSize - removeCount;
|
||||||
|
|
||||||
// shift surviving elements left over the spaces left by removed elements
|
// shift surviving elements left over the spaces left by removed elements
|
||||||
for (int i = 0, j = 0; (i < oldSize) && (j < newSize); i++, j++) {
|
for (int i = 0, j = 0; (i < oldSize) && (j < newSize); i++, j++) {
|
||||||
i = removeSet.nextClearBit(i);
|
i = removeSet.nextClearBit(i);
|
||||||
|
|
||||||
if (i != j) {
|
if (i != j) {
|
||||||
D element = data.get(i);
|
D element = data.get(i);
|
||||||
data.set(j, element);
|
data.set(j, element);
|
||||||
element.dirty = true;
|
element.dirty = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
anyToUpdate = true;
|
anyToUpdate = true;
|
||||||
|
|
||||||
data.subList(newSize, oldSize).clear();
|
data.subList(newSize, oldSize).clear();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void copyVertex(ByteBuffer constant, int i) {
|
protected void copyVertex(ByteBuffer constant, int i) {
|
||||||
constant.putFloat(getX(template, i));
|
constant.putFloat(getX(template, i));
|
||||||
constant.putFloat(getY(template, i));
|
constant.putFloat(getY(template, i));
|
||||||
constant.putFloat(getZ(template, i));
|
constant.putFloat(getZ(template, i));
|
||||||
|
|
||||||
constant.put(getNX(template, i));
|
constant.put(getNX(template, i));
|
||||||
constant.put(getNY(template, i));
|
constant.put(getNY(template, i));
|
||||||
constant.put(getNZ(template, i));
|
constant.put(getNZ(template, i));
|
||||||
|
|
||||||
constant.putFloat(getU(template, i));
|
constant.putFloat(getU(template, i));
|
||||||
constant.putFloat(getV(template, i));
|
constant.putFloat(getV(template, i));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected VertexFormat getModelFormat() {
|
protected VertexFormat getModelFormat() {
|
||||||
return FORMAT;
|
return FORMAT;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract VertexFormat getInstanceFormat();
|
protected abstract VertexFormat getInstanceFormat();
|
||||||
|
|
||||||
protected int getTotalShaderAttributeCount() {
|
protected int getTotalShaderAttributeCount() {
|
||||||
return getInstanceFormat().getShaderAttributeCount() + super.getTotalShaderAttributeCount();
|
return getInstanceFormat().getShaderAttributeCount() + super.getTotalShaderAttributeCount();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,22 +10,22 @@ import net.minecraft.tileentity.TileEntity;
|
||||||
import net.minecraft.tileentity.TileEntityType;
|
import net.minecraft.tileentity.TileEntityType;
|
||||||
|
|
||||||
public class InstancedTileRenderRegistry {
|
public class InstancedTileRenderRegistry {
|
||||||
public static final InstancedTileRenderRegistry instance = new InstancedTileRenderRegistry();
|
public static final InstancedTileRenderRegistry instance = new InstancedTileRenderRegistry();
|
||||||
|
|
||||||
private final Map<TileEntityType<?>, IRendererFactory<?>> renderers = Maps.newHashMap();
|
private final Map<TileEntityType<?>, IRendererFactory<?>> renderers = Maps.newHashMap();
|
||||||
|
|
||||||
public <T extends TileEntity> void register(TileEntityType<? extends T> type, IRendererFactory<? super T> rendererFactory) {
|
public <T extends TileEntity> void register(TileEntityType<? extends T> type, IRendererFactory<? super T> rendererFactory) {
|
||||||
this.renderers.put(type, rendererFactory);
|
this.renderers.put(type, rendererFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@Nullable
|
@Nullable
|
||||||
public <T extends TileEntity> TileEntityInstance<? super T> create(InstancedTileRenderer<?> manager, T tile) {
|
public <T extends TileEntity> TileEntityInstance<? super T> create(InstancedTileRenderer<?> manager, T tile) {
|
||||||
TileEntityType<?> type = tile.getType();
|
TileEntityType<?> type = tile.getType();
|
||||||
IRendererFactory<? super T> factory = (IRendererFactory<? super T>) this.renderers.get(type);
|
IRendererFactory<? super T> factory = (IRendererFactory<? super T>) this.renderers.get(type);
|
||||||
|
|
||||||
if (factory == null) return null;
|
if (factory == null) return null;
|
||||||
else return factory.create(manager, tile);
|
else return factory.create(manager, tile);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,259 +25,259 @@ import net.minecraft.world.IBlockReader;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
public abstract class InstancedTileRenderer<P extends BasicProgram> {
|
public abstract class InstancedTileRenderer<P extends BasicProgram> {
|
||||||
protected ArrayList<TileEntity> queuedAdditions = new ArrayList<>(64);
|
protected ArrayList<TileEntity> queuedAdditions = new ArrayList<>(64);
|
||||||
|
|
||||||
protected Map<TileEntity, TileEntityInstance<?>> instances = new HashMap<>();
|
protected Map<TileEntity, TileEntityInstance<?>> instances = new HashMap<>();
|
||||||
|
|
||||||
protected Map<TileEntity, ITickableInstance> tickableInstances = new HashMap<>();
|
protected Map<TileEntity, ITickableInstance> tickableInstances = new HashMap<>();
|
||||||
protected Map<TileEntity, IDynamicInstance> dynamicInstances = new HashMap<>();
|
protected Map<TileEntity, IDynamicInstance> dynamicInstances = new HashMap<>();
|
||||||
|
|
||||||
protected Map<MaterialType<?>, RenderMaterial<P, ?>> materials = new HashMap<>();
|
protected Map<MaterialType<?>, RenderMaterial<P, ?>> materials = new HashMap<>();
|
||||||
|
|
||||||
protected int frame;
|
protected int frame;
|
||||||
protected int tick;
|
protected int tick;
|
||||||
|
|
||||||
protected InstancedTileRenderer() {
|
protected InstancedTileRenderer() {
|
||||||
registerMaterials();
|
registerMaterials();
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract BlockPos getOriginCoordinate();
|
public abstract BlockPos getOriginCoordinate();
|
||||||
|
|
||||||
public abstract void registerMaterials();
|
public abstract void registerMaterials();
|
||||||
|
|
||||||
public void tick(double cameraX, double cameraY, double cameraZ) {
|
public void tick(double cameraX, double cameraY, double cameraZ) {
|
||||||
tick++;
|
tick++;
|
||||||
|
|
||||||
// integer camera pos
|
// integer camera pos
|
||||||
int cX = (int) cameraX;
|
int cX = (int) cameraX;
|
||||||
int cY = (int) cameraY;
|
int cY = (int) cameraY;
|
||||||
int cZ = (int) cameraZ;
|
int cZ = (int) cameraZ;
|
||||||
|
|
||||||
if (tickableInstances.size() > 0) {
|
if (tickableInstances.size() > 0) {
|
||||||
for (ITickableInstance instance : tickableInstances.values()) {
|
for (ITickableInstance instance : tickableInstances.values()) {
|
||||||
if (!instance.decreaseTickRateWithDistance()) {
|
if (!instance.decreaseTickRateWithDistance()) {
|
||||||
instance.tick();
|
instance.tick();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
BlockPos pos = instance.getWorldPosition();
|
BlockPos pos = instance.getWorldPosition();
|
||||||
|
|
||||||
int dX = pos.getX() - cX;
|
int dX = pos.getX() - cX;
|
||||||
int dY = pos.getY() - cY;
|
int dY = pos.getY() - cY;
|
||||||
int dZ = pos.getZ() - cZ;
|
int dZ = pos.getZ() - cZ;
|
||||||
|
|
||||||
if ((tick % getUpdateDivisor(dX, dY, dZ)) == 0)
|
if ((tick % getUpdateDivisor(dX, dY, dZ)) == 0)
|
||||||
instance.tick();
|
instance.tick();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void beginFrame(ActiveRenderInfo info, double cameraX, double cameraY, double cameraZ) {
|
public void beginFrame(ActiveRenderInfo info, double cameraX, double cameraY, double cameraZ) {
|
||||||
frame++;
|
frame++;
|
||||||
processQueuedAdditions();
|
processQueuedAdditions();
|
||||||
|
|
||||||
Vector3f look = info.getHorizontalPlane();
|
Vector3f look = info.getHorizontalPlane();
|
||||||
float lookX = look.getX();
|
float lookX = look.getX();
|
||||||
float lookY = look.getY();
|
float lookY = look.getY();
|
||||||
float lookZ = look.getZ();
|
float lookZ = look.getZ();
|
||||||
|
|
||||||
// integer camera pos
|
// integer camera pos
|
||||||
int cX = (int) cameraX;
|
int cX = (int) cameraX;
|
||||||
int cY = (int) cameraY;
|
int cY = (int) cameraY;
|
||||||
int cZ = (int) cameraZ;
|
int cZ = (int) cameraZ;
|
||||||
|
|
||||||
if (dynamicInstances.size() > 0) {
|
if (dynamicInstances.size() > 0) {
|
||||||
for (IDynamicInstance dyn : dynamicInstances.values()) {
|
for (IDynamicInstance dyn : dynamicInstances.values()) {
|
||||||
if (!dyn.decreaseFramerateWithDistance()) {
|
if (!dyn.decreaseFramerateWithDistance()) {
|
||||||
dyn.beginFrame();
|
dyn.beginFrame();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (shouldTick(dyn.getWorldPosition(), lookX, lookY, lookZ, cX, cY, cZ))
|
if (shouldTick(dyn.getWorldPosition(), lookX, lookY, lookZ, cX, cY, cZ))
|
||||||
dyn.beginFrame();
|
dyn.beginFrame();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void render(RenderType layer, Matrix4f viewProjection, double camX, double camY, double camZ) {
|
public void render(RenderType layer, Matrix4f viewProjection, double camX, double camY, double camZ) {
|
||||||
render(layer, viewProjection, camX, camY, camZ, null);
|
render(layer, viewProjection, camX, camY, camZ, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void render(RenderType layer, Matrix4f viewProjection, double camX, double camY, double camZ, ShaderCallback<P> callback) {
|
public void render(RenderType layer, Matrix4f viewProjection, double camX, double camY, double camZ, ShaderCallback<P> callback) {
|
||||||
for (RenderMaterial<P, ?> material : materials.values()) {
|
for (RenderMaterial<P, ?> material : materials.values()) {
|
||||||
if (material.canRenderInLayer(layer))
|
if (material.canRenderInLayer(layer))
|
||||||
material.render(layer, viewProjection, camX, camY, camZ, callback);
|
material.render(layer, viewProjection, camX, camY, camZ, callback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public <M extends InstancedModel<?>> RenderMaterial<P, M> getMaterial(MaterialType<M> materialType) {
|
public <M extends InstancedModel<?>> RenderMaterial<P, M> getMaterial(MaterialType<M> materialType) {
|
||||||
return (RenderMaterial<P, M>) materials.get(materialType);
|
return (RenderMaterial<P, M>) materials.get(materialType);
|
||||||
}
|
}
|
||||||
|
|
||||||
public RenderMaterial<P, InstancedModel<ModelData>> getTransformMaterial() {
|
public RenderMaterial<P, InstancedModel<ModelData>> getTransformMaterial() {
|
||||||
return getMaterial(MaterialTypes.TRANSFORMED);
|
return getMaterial(MaterialTypes.TRANSFORMED);
|
||||||
}
|
}
|
||||||
|
|
||||||
public RenderMaterial<P, InstancedModel<OrientedData>> getOrientedMaterial() {
|
public RenderMaterial<P, InstancedModel<OrientedData>> getOrientedMaterial() {
|
||||||
return getMaterial(MaterialTypes.ORIENTED);
|
return getMaterial(MaterialTypes.ORIENTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@Nullable
|
@Nullable
|
||||||
public <T extends TileEntity> TileEntityInstance<? super T> getInstance(T tile, boolean create) {
|
public <T extends TileEntity> TileEntityInstance<? super T> getInstance(T tile, boolean create) {
|
||||||
if (!Backend.canUseInstancing()) return null;
|
if (!Backend.canUseInstancing()) return null;
|
||||||
|
|
||||||
TileEntityInstance<?> instance = instances.get(tile);
|
TileEntityInstance<?> instance = instances.get(tile);
|
||||||
|
|
||||||
if (instance != null) {
|
if (instance != null) {
|
||||||
return (TileEntityInstance<? super T>) instance;
|
return (TileEntityInstance<? super T>) instance;
|
||||||
} else if (create && canCreateInstance(tile)) {
|
} else if (create && canCreateInstance(tile)) {
|
||||||
return createInternal(tile);
|
return createInternal(tile);
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T extends TileEntity> void onLightUpdate(T tile) {
|
public <T extends TileEntity> void onLightUpdate(T tile) {
|
||||||
if (!Backend.canUseInstancing()) return;
|
if (!Backend.canUseInstancing()) return;
|
||||||
|
|
||||||
if (tile instanceof IInstanceRendered) {
|
if (tile instanceof IInstanceRendered) {
|
||||||
TileEntityInstance<? super T> instance = getInstance(tile, false);
|
TileEntityInstance<? super T> instance = getInstance(tile, false);
|
||||||
|
|
||||||
if (instance != null)
|
if (instance != null)
|
||||||
instance.updateLight();
|
instance.updateLight();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T extends TileEntity> void add(T tile) {
|
public <T extends TileEntity> void add(T tile) {
|
||||||
if (!Backend.canUseInstancing()) return;
|
if (!Backend.canUseInstancing()) return;
|
||||||
|
|
||||||
if (tile instanceof IInstanceRendered) {
|
if (tile instanceof IInstanceRendered) {
|
||||||
addInternal(tile);
|
addInternal(tile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T extends TileEntity> void update(T tile) {
|
public <T extends TileEntity> void update(T tile) {
|
||||||
if (!Backend.canUseInstancing()) return;
|
if (!Backend.canUseInstancing()) return;
|
||||||
|
|
||||||
if (tile instanceof IInstanceRendered) {
|
if (tile instanceof IInstanceRendered) {
|
||||||
TileEntityInstance<? super T> instance = getInstance(tile, false);
|
TileEntityInstance<? super T> instance = getInstance(tile, false);
|
||||||
|
|
||||||
if (instance != null) {
|
if (instance != null) {
|
||||||
|
|
||||||
if (instance.shouldReset()) {
|
if (instance.shouldReset()) {
|
||||||
removeInternal(tile, instance);
|
removeInternal(tile, instance);
|
||||||
|
|
||||||
createInternal(tile);
|
createInternal(tile);
|
||||||
} else {
|
} else {
|
||||||
instance.update();
|
instance.update();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T extends TileEntity> void remove(T tile) {
|
public <T extends TileEntity> void remove(T tile) {
|
||||||
if (!Backend.canUseInstancing()) return;
|
if (!Backend.canUseInstancing()) return;
|
||||||
|
|
||||||
if (tile instanceof IInstanceRendered) {
|
if (tile instanceof IInstanceRendered) {
|
||||||
removeInternal(tile);
|
removeInternal(tile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized <T extends TileEntity> void queueAdd(T tile) {
|
public synchronized <T extends TileEntity> void queueAdd(T tile) {
|
||||||
if (!Backend.canUseInstancing()) return;
|
if (!Backend.canUseInstancing()) return;
|
||||||
|
|
||||||
queuedAdditions.add(tile);
|
queuedAdditions.add(tile);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected synchronized void processQueuedAdditions() {
|
protected synchronized void processQueuedAdditions() {
|
||||||
if (queuedAdditions.size() > 0) {
|
if (queuedAdditions.size() > 0) {
|
||||||
queuedAdditions.forEach(this::addInternal);
|
queuedAdditions.forEach(this::addInternal);
|
||||||
queuedAdditions.clear();
|
queuedAdditions.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean shouldTick(BlockPos worldPos, float lookX, float lookY, float lookZ, int cX, int cY, int cZ) {
|
protected boolean shouldTick(BlockPos worldPos, float lookX, float lookY, float lookZ, int cX, int cY, int cZ) {
|
||||||
int dX = worldPos.getX() - cX;
|
int dX = worldPos.getX() - cX;
|
||||||
int dY = worldPos.getY() - cY;
|
int dY = worldPos.getY() - cY;
|
||||||
int dZ = worldPos.getZ() - cZ;
|
int dZ = worldPos.getZ() - cZ;
|
||||||
|
|
||||||
float dot = (dX + lookX * 2) * lookX + (dY + lookY * 2) * lookY + (dZ + lookZ * 2) * lookZ;
|
float dot = (dX + lookX * 2) * lookX + (dY + lookY * 2) * lookY + (dZ + lookZ * 2) * lookZ;
|
||||||
|
|
||||||
if (dot < 0) return false; // is it more than 2 blocks behind the camera?
|
if (dot < 0) return false; // is it more than 2 blocks behind the camera?
|
||||||
|
|
||||||
return (frame % getUpdateDivisor(dX, dY, dZ)) == 0;
|
return (frame % getUpdateDivisor(dX, dY, dZ)) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected int getUpdateDivisor(int dX, int dY, int dZ) {
|
protected int getUpdateDivisor(int dX, int dY, int dZ) {
|
||||||
int dSq = dX * dX + dY * dY + dZ * dZ;
|
int dSq = dX * dX + dY * dY + dZ * dZ;
|
||||||
|
|
||||||
return (dSq / 1024) + 1;
|
return (dSq / 1024) + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addInternal(TileEntity tile) {
|
private void addInternal(TileEntity tile) {
|
||||||
getInstance(tile, true);
|
getInstance(tile, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T extends TileEntity> void removeInternal(T tile) {
|
private <T extends TileEntity> void removeInternal(T tile) {
|
||||||
TileEntityInstance<? super T> instance = getInstance(tile, false);
|
TileEntityInstance<? super T> instance = getInstance(tile, false);
|
||||||
|
|
||||||
if (instance != null) {
|
if (instance != null) {
|
||||||
removeInternal(tile, instance);
|
removeInternal(tile, instance);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void removeInternal(TileEntity tile, TileEntityInstance<?> instance) {
|
private void removeInternal(TileEntity tile, TileEntityInstance<?> instance) {
|
||||||
instance.remove();
|
instance.remove();
|
||||||
instances.remove(tile);
|
instances.remove(tile);
|
||||||
dynamicInstances.remove(tile);
|
dynamicInstances.remove(tile);
|
||||||
tickableInstances.remove(tile);
|
tickableInstances.remove(tile);
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T extends TileEntity> TileEntityInstance<? super T> createInternal(T tile) {
|
private <T extends TileEntity> TileEntityInstance<? super T> createInternal(T tile) {
|
||||||
TileEntityInstance<? super T> renderer = InstancedTileRenderRegistry.instance.create(this, tile);
|
TileEntityInstance<? super T> renderer = InstancedTileRenderRegistry.instance.create(this, tile);
|
||||||
|
|
||||||
if (renderer != null) {
|
if (renderer != null) {
|
||||||
renderer.updateLight();
|
renderer.updateLight();
|
||||||
instances.put(tile, renderer);
|
instances.put(tile, renderer);
|
||||||
|
|
||||||
if (renderer instanceof IDynamicInstance)
|
if (renderer instanceof IDynamicInstance)
|
||||||
dynamicInstances.put(tile, (IDynamicInstance) renderer);
|
dynamicInstances.put(tile, (IDynamicInstance) renderer);
|
||||||
|
|
||||||
if (renderer instanceof ITickableInstance)
|
if (renderer instanceof ITickableInstance)
|
||||||
tickableInstances.put(tile, ((ITickableInstance) renderer));
|
tickableInstances.put(tile, ((ITickableInstance) renderer));
|
||||||
}
|
}
|
||||||
|
|
||||||
return renderer;
|
return renderer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void invalidate() {
|
public void invalidate() {
|
||||||
for (RenderMaterial<?, ?> material : materials.values()) {
|
for (RenderMaterial<?, ?> material : materials.values()) {
|
||||||
material.delete();
|
material.delete();
|
||||||
}
|
}
|
||||||
instances.clear();
|
instances.clear();
|
||||||
dynamicInstances.clear();
|
dynamicInstances.clear();
|
||||||
tickableInstances.clear();
|
tickableInstances.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean canCreateInstance(TileEntity tile) {
|
public boolean canCreateInstance(TileEntity tile) {
|
||||||
if (tile.isRemoved()) return false;
|
if (tile.isRemoved()) return false;
|
||||||
|
|
||||||
World world = tile.getWorld();
|
World world = tile.getWorld();
|
||||||
|
|
||||||
if (world == null) return false;
|
if (world == null) return false;
|
||||||
|
|
||||||
if (world.isAirBlock(tile.getPos())) return false;
|
if (world.isAirBlock(tile.getPos())) return false;
|
||||||
|
|
||||||
if (world == Minecraft.getInstance().world) {
|
if (world == Minecraft.getInstance().world) {
|
||||||
BlockPos pos = tile.getPos();
|
BlockPos pos = tile.getPos();
|
||||||
|
|
||||||
IBlockReader existingChunk = world.getExistingChunk(pos.getX() >> 4, pos.getZ() >> 4);
|
IBlockReader existingChunk = world.getExistingChunk(pos.getX() >> 4, pos.getZ() >> 4);
|
||||||
|
|
||||||
return existingChunk != null;
|
return existingChunk != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return world instanceof IFlywheelWorld && ((IFlywheelWorld) world).supportsFlywheel();
|
return world instanceof IFlywheelWorld && ((IFlywheelWorld) world).supportsFlywheel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,5 +4,5 @@ import net.minecraft.client.renderer.BufferBuilder;
|
||||||
|
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
public interface ModelFactory<B extends InstancedModel<?>> {
|
public interface ModelFactory<B extends InstancedModel<?>> {
|
||||||
B makeModel(InstancedTileRenderer<?> renderer, BufferBuilder buf);
|
B makeModel(InstancedTileRenderer<?> renderer, BufferBuilder buf);
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,102 +36,103 @@ import net.minecraft.util.math.vector.Matrix4f;
|
||||||
|
|
||||||
public class RenderMaterial<P extends BasicProgram, MODEL extends InstancedModel<?>> {
|
public class RenderMaterial<P extends BasicProgram, MODEL extends InstancedModel<?>> {
|
||||||
|
|
||||||
protected final InstancedTileRenderer<?> renderer;
|
protected final InstancedTileRenderer<?> renderer;
|
||||||
protected final Map<Compartment<?>, Cache<Object, MODEL>> models;
|
protected final Map<Compartment<?>, Cache<Object, MODEL>> models;
|
||||||
protected final ModelFactory<MODEL> factory;
|
protected final ModelFactory<MODEL> factory;
|
||||||
protected final ProgramSpec<P> programSpec;
|
protected final ProgramSpec<P> programSpec;
|
||||||
protected final Predicate<RenderType> layerPredicate;
|
protected final Predicate<RenderType> layerPredicate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a material that renders in the default layer (CUTOUT_MIPPED)
|
* Creates a material that renders in the default layer (CUTOUT_MIPPED)
|
||||||
*/
|
*/
|
||||||
public RenderMaterial(InstancedTileRenderer<?> renderer, ProgramSpec<P> programSpec, ModelFactory<MODEL> factory) {
|
public RenderMaterial(InstancedTileRenderer<?> renderer, ProgramSpec<P> programSpec, ModelFactory<MODEL> factory) {
|
||||||
this(renderer, programSpec, factory, type -> type == RenderType.getCutoutMipped());
|
this(renderer, programSpec, factory, type -> type == RenderType.getCutoutMipped());
|
||||||
}
|
}
|
||||||
|
|
||||||
public RenderMaterial(InstancedTileRenderer<?> renderer, ProgramSpec<P> programSpec, ModelFactory<MODEL> factory, Predicate<RenderType> layerPredicate) {
|
public RenderMaterial(InstancedTileRenderer<?> renderer, ProgramSpec<P> programSpec, ModelFactory<MODEL> factory, Predicate<RenderType> layerPredicate) {
|
||||||
this.renderer = renderer;
|
this.renderer = renderer;
|
||||||
this.models = new HashMap<>();
|
this.models = new HashMap<>();
|
||||||
this.factory = factory;
|
this.factory = factory;
|
||||||
this.programSpec = programSpec;
|
this.programSpec = programSpec;
|
||||||
this.layerPredicate = layerPredicate;
|
this.layerPredicate = layerPredicate;
|
||||||
registerCompartment(Compartment.PARTIAL);
|
registerCompartment(Compartment.PARTIAL);
|
||||||
registerCompartment(Compartment.DIRECTIONAL_PARTIAL);
|
registerCompartment(Compartment.DIRECTIONAL_PARTIAL);
|
||||||
registerCompartment(Compartment.GENERIC_TILE);
|
registerCompartment(Compartment.GENERIC_TILE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean canRenderInLayer(RenderType layer) {
|
public boolean canRenderInLayer(RenderType layer) {
|
||||||
return layerPredicate.test(layer);
|
return layerPredicate.test(layer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void render(RenderType layer, Matrix4f projection, double camX, double camY, double camZ) {
|
public void render(RenderType layer, Matrix4f projection, double camX, double camY, double camZ) {
|
||||||
render(layer, projection, camX, camY, camZ, null);
|
render(layer, projection, camX, camY, camZ, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void render(RenderType layer, Matrix4f viewProjection, double camX, double camY, double camZ, ShaderCallback<P> setup) {
|
public void render(RenderType layer, Matrix4f viewProjection, double camX, double camY, double camZ, ShaderCallback<P> setup) {
|
||||||
P program = Backend.getProgram(programSpec);
|
P program = Backend.getProgram(programSpec);
|
||||||
program.bind(viewProjection, camX, camY, camZ, FastRenderDispatcher.getDebugMode());
|
program.bind(viewProjection, camX, camY, camZ, FastRenderDispatcher.getDebugMode());
|
||||||
|
|
||||||
if (setup != null) setup.call(program);
|
if (setup != null) setup.call(program);
|
||||||
|
|
||||||
makeRenderCalls();
|
makeRenderCalls();
|
||||||
teardown();
|
teardown();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void teardown() {}
|
public void teardown() {
|
||||||
|
}
|
||||||
|
|
||||||
public void delete() {
|
public void delete() {
|
||||||
runOnAll(InstancedModel::delete);
|
runOnAll(InstancedModel::delete);
|
||||||
models.values().forEach(Cache::invalidateAll);
|
models.values().forEach(Cache::invalidateAll);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void makeRenderCalls() {
|
protected void makeRenderCalls() {
|
||||||
runOnAll(InstancedModel::render);
|
runOnAll(InstancedModel::render);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void runOnAll(Consumer<MODEL> f) {
|
public void runOnAll(Consumer<MODEL> f) {
|
||||||
for (Cache<Object, MODEL> cache : models.values()) {
|
for (Cache<Object, MODEL> cache : models.values()) {
|
||||||
for (MODEL model : cache.asMap().values()) {
|
for (MODEL model : cache.asMap().values()) {
|
||||||
f.accept(model);
|
f.accept(model);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void registerCompartment(Compartment<?> instance) {
|
public void registerCompartment(Compartment<?> instance) {
|
||||||
models.put(instance, CacheBuilder.newBuilder().build());
|
models.put(instance, CacheBuilder.newBuilder().build());
|
||||||
}
|
}
|
||||||
|
|
||||||
public MODEL getModel(PartialModel partial, BlockState referenceState) {
|
public MODEL getModel(PartialModel partial, BlockState referenceState) {
|
||||||
return get(PARTIAL, partial, () -> buildModel(partial.get(), referenceState));
|
return get(PARTIAL, partial, () -> buildModel(partial.get(), referenceState));
|
||||||
}
|
}
|
||||||
|
|
||||||
public MODEL getModel(PartialModel partial, BlockState referenceState, Direction dir) {
|
public MODEL getModel(PartialModel partial, BlockState referenceState, Direction dir) {
|
||||||
return getModel(partial, referenceState, dir, rotateToFace(dir));
|
return getModel(partial, referenceState, dir, rotateToFace(dir));
|
||||||
}
|
}
|
||||||
|
|
||||||
public MODEL getModel(PartialModel partial, BlockState referenceState, Direction dir, Supplier<MatrixStack> modelTransform) {
|
public MODEL getModel(PartialModel partial, BlockState referenceState, Direction dir, Supplier<MatrixStack> modelTransform) {
|
||||||
return get(Compartment.DIRECTIONAL_PARTIAL, Pair.of(dir, partial),
|
return get(Compartment.DIRECTIONAL_PARTIAL, Pair.of(dir, partial),
|
||||||
() -> buildModel(partial.get(), referenceState, modelTransform.get()));
|
() -> buildModel(partial.get(), referenceState, modelTransform.get()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public MODEL getModel(BlockState toRender) {
|
public MODEL getModel(BlockState toRender) {
|
||||||
return get(Compartment.GENERIC_TILE, toRender, () -> buildModel(toRender));
|
return get(Compartment.GENERIC_TILE, toRender, () -> buildModel(toRender));
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T> MODEL get(Compartment<T> compartment, T key, Supplier<MODEL> supplier) {
|
public <T> MODEL get(Compartment<T> compartment, T key, Supplier<MODEL> supplier) {
|
||||||
Cache<Object, MODEL> compartmentCache = models.get(compartment);
|
Cache<Object, MODEL> compartmentCache = models.get(compartment);
|
||||||
try {
|
try {
|
||||||
return compartmentCache.get(key, supplier::get);
|
return compartmentCache.get(key, supplier::get);
|
||||||
} catch (ExecutionException e) {
|
} catch (ExecutionException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private MODEL buildModel(BlockState renderedState) {
|
private MODEL buildModel(BlockState renderedState) {
|
||||||
BlockRendererDispatcher dispatcher = Minecraft.getInstance().getBlockRendererDispatcher();
|
BlockRendererDispatcher dispatcher = Minecraft.getInstance().getBlockRendererDispatcher();
|
||||||
return buildModel(dispatcher.getModelForState(renderedState), renderedState);
|
return buildModel(dispatcher.getModelForState(renderedState), renderedState);
|
||||||
}
|
}
|
||||||
|
|
||||||
private MODEL buildModel(IBakedModel model, BlockState renderedState) {
|
private MODEL buildModel(IBakedModel model, BlockState renderedState) {
|
||||||
return buildModel(model, renderedState, new MatrixStack());
|
return buildModel(model, renderedState, new MatrixStack());
|
||||||
|
|
|
@ -32,94 +32,96 @@ import net.minecraft.world.World;
|
||||||
*/
|
*/
|
||||||
public abstract class TileEntityInstance<T extends TileEntity> implements IInstance {
|
public abstract class TileEntityInstance<T extends TileEntity> implements IInstance {
|
||||||
|
|
||||||
protected final InstancedTileRenderer<?> renderer;
|
protected final InstancedTileRenderer<?> renderer;
|
||||||
protected final T tile;
|
protected final T tile;
|
||||||
protected final World world;
|
protected final World world;
|
||||||
protected final BlockPos pos;
|
protected final BlockPos pos;
|
||||||
protected final BlockPos instancePos;
|
protected final BlockPos instancePos;
|
||||||
protected final BlockState blockState;
|
protected final BlockState blockState;
|
||||||
|
|
||||||
public TileEntityInstance(InstancedTileRenderer<?> renderer, T tile) {
|
public TileEntityInstance(InstancedTileRenderer<?> renderer, T tile) {
|
||||||
this.renderer = renderer;
|
this.renderer = renderer;
|
||||||
this.tile = tile;
|
this.tile = tile;
|
||||||
this.world = tile.getWorld();
|
this.world = tile.getWorld();
|
||||||
this.pos = tile.getPos();
|
this.pos = tile.getPos();
|
||||||
this.blockState = tile.getBlockState();
|
this.blockState = tile.getBlockState();
|
||||||
this.instancePos = pos.subtract(renderer.getOriginCoordinate());
|
this.instancePos = pos.subtract(renderer.getOriginCoordinate());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update instance data here. Good for when data doesn't change very often and when animations are GPU based.
|
* Update instance data here. Good for when data doesn't change very often and when animations are GPU based.
|
||||||
* Don't query lighting data here, that's handled separately in {@link #updateLight()}.
|
* Don't query lighting data here, that's handled separately in {@link #updateLight()}.
|
||||||
*
|
*
|
||||||
* <br><br> If your animations are complex or more CPU driven, see {@link IDynamicInstance} or {@link ITickableInstance}.
|
* <br><br> If your animations are complex or more CPU driven, see {@link IDynamicInstance} or {@link ITickableInstance}.
|
||||||
*/
|
*/
|
||||||
protected void update() { }
|
protected void update() {
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called after construction and when a light update occurs in the world.
|
* Called after construction and when a light update occurs in the world.
|
||||||
*
|
*
|
||||||
* <br> If your model needs it, update light here.
|
* <br> If your model needs it, update light here.
|
||||||
*/
|
*/
|
||||||
public void updateLight() { }
|
public void updateLight() {
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Free any acquired resources.
|
* Free any acquired resources.
|
||||||
*
|
*
|
||||||
* <br> eg. call {@link InstanceKey#delete()}.
|
* <br> eg. call {@link InstanceKey#delete()}.
|
||||||
*/
|
*/
|
||||||
public abstract void remove();
|
public abstract void remove();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Just before {@link #update()} would be called, <code>shouldReset()</code> is checked.
|
* Just before {@link #update()} would be called, <code>shouldReset()</code> is checked.
|
||||||
* If this function returns <code>true</code>, then this instance will be {@link #remove}d,
|
* If this function returns <code>true</code>, then this instance will be {@link #remove}d,
|
||||||
* and another instance will be constructed to replace it. This allows for more sane resource
|
* and another instance will be constructed to replace it. This allows for more sane resource
|
||||||
* acquisition compared to trying to update everything within the lifetime of an instance.
|
* acquisition compared to trying to update everything within the lifetime of an instance.
|
||||||
*
|
*
|
||||||
* @return <code>true</code> if this instance should be discarded and refreshed.
|
* @return <code>true</code> if this instance should be discarded and refreshed.
|
||||||
*/
|
*/
|
||||||
public boolean shouldReset() {
|
public boolean shouldReset() {
|
||||||
return tile.getBlockState() != blockState;
|
return tile.getBlockState() != blockState;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* In order to accommodate for floating point precision errors at high coordinates,
|
* In order to accommodate for floating point precision errors at high coordinates,
|
||||||
* {@link InstancedTileRenderer}s are allowed to arbitrarily adjust the origin, and
|
* {@link InstancedTileRenderer}s are allowed to arbitrarily adjust the origin, and
|
||||||
* shift the world matrix provided as a shader uniform accordingly.
|
* shift the world matrix provided as a shader uniform accordingly.
|
||||||
*
|
*
|
||||||
* @return The {@link BlockPos} at which the {@link TileEntity} this instance
|
* @return The {@link BlockPos} at which the {@link TileEntity} this instance
|
||||||
* represents should be rendered at to appear in the correct location.
|
* represents should be rendered at to appear in the correct location.
|
||||||
*/
|
*/
|
||||||
public BlockPos getInstancePosition() {
|
public BlockPos getInstancePosition() {
|
||||||
return instancePos;
|
return instancePos;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BlockPos getWorldPosition() {
|
public BlockPos getWorldPosition() {
|
||||||
return pos;
|
return pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void relight(BlockPos pos, IFlatLight<?>... models) {
|
protected void relight(BlockPos pos, IFlatLight<?>... models) {
|
||||||
relight(world.getLightLevel(LightType.BLOCK, pos), world.getLightLevel(LightType.SKY, pos), models);
|
relight(world.getLightLevel(LightType.BLOCK, pos), world.getLightLevel(LightType.SKY, pos), models);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected <L extends IFlatLight<?>> void relight(BlockPos pos, Stream<L> models) {
|
protected <L extends IFlatLight<?>> void relight(BlockPos pos, Stream<L> models) {
|
||||||
relight(world.getLightLevel(LightType.BLOCK, pos), world.getLightLevel(LightType.SKY, pos), models);
|
relight(world.getLightLevel(LightType.BLOCK, pos), world.getLightLevel(LightType.SKY, pos), models);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void relight(int block, int sky, IFlatLight<?>... models) {
|
protected void relight(int block, int sky, IFlatLight<?>... models) {
|
||||||
relight(block, sky, Arrays.stream(models));
|
relight(block, sky, Arrays.stream(models));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected <L extends IFlatLight<?>> void relight(int block, int sky, Stream<L> models) {
|
protected <L extends IFlatLight<?>> void relight(int block, int sky, Stream<L> models) {
|
||||||
models.forEach(model -> model.setBlockLight(block).setSkyLight(sky));
|
models.forEach(model -> model.setBlockLight(block).setSkyLight(sky));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected RenderMaterial<?, InstancedModel<ModelData>> getTransformMaterial() {
|
protected RenderMaterial<?, InstancedModel<ModelData>> getTransformMaterial() {
|
||||||
return renderer.getTransformMaterial();
|
return renderer.getTransformMaterial();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected RenderMaterial<?, InstancedModel<OrientedData>> getOrientedMaterial() {
|
protected RenderMaterial<?, InstancedModel<OrientedData>> getOrientedMaterial() {
|
||||||
return renderer.getOrientedMaterial();
|
return renderer.getOrientedMaterial();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ import java.util.List;
|
||||||
import com.simibubi.create.foundation.render.backend.instancing.InstanceData;
|
import com.simibubi.create.foundation.render.backend.instancing.InstanceData;
|
||||||
import com.simibubi.create.foundation.render.backend.instancing.InstancedModel;
|
import com.simibubi.create.foundation.render.backend.instancing.InstancedModel;
|
||||||
|
|
||||||
public class InstanceGroup<D extends InstanceData> extends AbstractCollection<D> {
|
public class InstanceGroup<D extends InstanceData> extends AbstractCollection<D> {
|
||||||
|
|
||||||
final InstancedModel<D> model;
|
final InstancedModel<D> model;
|
||||||
final List<D> backing;
|
final List<D> backing;
|
||||||
|
@ -30,7 +30,6 @@ public class InstanceGroup<D extends InstanceData> extends AbstractCollection<D>
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @param count
|
* @param count
|
||||||
* @return True if the number of elements changed.
|
* @return True if the number of elements changed.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -2,5 +2,5 @@ package com.simibubi.create.foundation.render.backend.light;
|
||||||
|
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
public interface CoordinateConsumer {
|
public interface CoordinateConsumer {
|
||||||
void consume(int x, int y, int z);
|
void consume(int x, int y, int z);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,12 +5,12 @@ import com.simibubi.create.content.contraptions.components.structureMovement.Con
|
||||||
|
|
||||||
// so other contraptions don't crash before they have a lighter
|
// so other contraptions don't crash before they have a lighter
|
||||||
public class EmptyLighter extends ContraptionLighter<Contraption> {
|
public class EmptyLighter extends ContraptionLighter<Contraption> {
|
||||||
public EmptyLighter(Contraption contraption) {
|
public EmptyLighter(Contraption contraption) {
|
||||||
super(contraption);
|
super(contraption);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GridAlignedBB getContraptionBounds() {
|
public GridAlignedBB getContraptionBounds() {
|
||||||
return new GridAlignedBB(0, 0, 0, 1, 1, 1);
|
return new GridAlignedBB(0, 0, 0, 1, 1, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,313 +11,313 @@ import net.minecraft.util.math.SectionPos;
|
||||||
import net.minecraft.util.math.vector.Vector3i;
|
import net.minecraft.util.math.vector.Vector3i;
|
||||||
|
|
||||||
public class GridAlignedBB {
|
public class GridAlignedBB {
|
||||||
public int minX;
|
public int minX;
|
||||||
public int minY;
|
public int minY;
|
||||||
public int minZ;
|
public int minZ;
|
||||||
public int maxX;
|
public int maxX;
|
||||||
public int maxY;
|
public int maxY;
|
||||||
public int maxZ;
|
public int maxZ;
|
||||||
|
|
||||||
public GridAlignedBB(int minX, int minY, int minZ, int maxX, int maxY, int maxZ) {
|
public GridAlignedBB(int minX, int minY, int minZ, int maxX, int maxY, int maxZ) {
|
||||||
this.minX = minX;
|
this.minX = minX;
|
||||||
this.minY = minY;
|
this.minY = minY;
|
||||||
this.minZ = minZ;
|
this.minZ = minZ;
|
||||||
this.maxX = maxX;
|
this.maxX = maxX;
|
||||||
this.maxY = maxY;
|
this.maxY = maxY;
|
||||||
this.maxZ = maxZ;
|
this.maxZ = maxZ;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static GridAlignedBB ofRadius(int radius) {
|
public static GridAlignedBB ofRadius(int radius) {
|
||||||
return new GridAlignedBB(-radius, -radius, -radius, radius + 1, radius + 1, radius + 1);
|
return new GridAlignedBB(-radius, -radius, -radius, radius + 1, radius + 1, radius + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static GridAlignedBB copy(GridAlignedBB bb) {
|
public static GridAlignedBB copy(GridAlignedBB bb) {
|
||||||
return new GridAlignedBB(bb.minX, bb.minY, bb.minZ, bb.maxX, bb.maxY, bb.maxZ);
|
return new GridAlignedBB(bb.minX, bb.minY, bb.minZ, bb.maxX, bb.maxY, bb.maxZ);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static GridAlignedBB from(AxisAlignedBB aabb) {
|
public static GridAlignedBB from(AxisAlignedBB aabb) {
|
||||||
int minX = (int) Math.floor(aabb.minX);
|
int minX = (int) Math.floor(aabb.minX);
|
||||||
int minY = (int) Math.floor(aabb.minY);
|
int minY = (int) Math.floor(aabb.minY);
|
||||||
int minZ = (int) Math.floor(aabb.minZ);
|
int minZ = (int) Math.floor(aabb.minZ);
|
||||||
int maxX = (int) Math.ceil(aabb.maxX);
|
int maxX = (int) Math.ceil(aabb.maxX);
|
||||||
int maxY = (int) Math.ceil(aabb.maxY);
|
int maxY = (int) Math.ceil(aabb.maxY);
|
||||||
int maxZ = (int) Math.ceil(aabb.maxZ);
|
int maxZ = (int) Math.ceil(aabb.maxZ);
|
||||||
return new GridAlignedBB(minX, minY, minZ, maxX, maxY, maxZ);
|
return new GridAlignedBB(minX, minY, minZ, maxX, maxY, maxZ);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static GridAlignedBB from(SectionPos pos) {
|
public static GridAlignedBB from(SectionPos pos) {
|
||||||
return new GridAlignedBB(pos.getWorldStartX(),
|
return new GridAlignedBB(pos.getWorldStartX(),
|
||||||
pos.getWorldStartY(),
|
pos.getWorldStartY(),
|
||||||
pos.getWorldStartZ(),
|
pos.getWorldStartZ(),
|
||||||
pos.getWorldEndX() + 1,
|
pos.getWorldEndX() + 1,
|
||||||
pos.getWorldEndY() + 1,
|
pos.getWorldEndY() + 1,
|
||||||
pos.getWorldEndZ() + 1);
|
pos.getWorldEndZ() + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static GridAlignedBB from(BlockPos start, BlockPos end) {
|
public static GridAlignedBB from(BlockPos start, BlockPos end) {
|
||||||
return new GridAlignedBB(start.getX(),
|
return new GridAlignedBB(start.getX(),
|
||||||
start.getY(),
|
start.getY(),
|
||||||
start.getZ(),
|
start.getZ(),
|
||||||
end.getX() + 1,
|
end.getX() + 1,
|
||||||
end.getY() + 1,
|
end.getY() + 1,
|
||||||
end.getZ() + 1);
|
end.getZ() + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static GridAlignedBB from(int sectionX, int sectionZ) {
|
public static GridAlignedBB from(int sectionX, int sectionZ) {
|
||||||
int startX = sectionX << 4;
|
int startX = sectionX << 4;
|
||||||
int startZ = sectionZ << 4;
|
int startZ = sectionZ << 4;
|
||||||
return new GridAlignedBB(startX,
|
return new GridAlignedBB(startX,
|
||||||
0,
|
0,
|
||||||
startZ,
|
startZ,
|
||||||
startX + 16,
|
startX + 16,
|
||||||
256,
|
256,
|
||||||
startZ + 16);
|
startZ + 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static AxisAlignedBB toAABB(GridAlignedBB bb) {
|
public static AxisAlignedBB toAABB(GridAlignedBB bb) {
|
||||||
return new AxisAlignedBB(bb.minX, bb.minY, bb.minZ, bb.maxX, bb.maxY, bb.maxZ);
|
return new AxisAlignedBB(bb.minX, bb.minY, bb.minZ, bb.maxX, bb.maxY, bb.maxZ);
|
||||||
}
|
}
|
||||||
|
|
||||||
public GridAlignedBB copy() {
|
public GridAlignedBB copy() {
|
||||||
return copy(this);
|
return copy(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean sameAs(GridAlignedBB other) {
|
public boolean sameAs(GridAlignedBB other) {
|
||||||
return minX == other.minX &&
|
return minX == other.minX &&
|
||||||
minY == other.minY &&
|
minY == other.minY &&
|
||||||
minZ == other.minZ &&
|
minZ == other.minZ &&
|
||||||
maxX == other.maxX &&
|
maxX == other.maxX &&
|
||||||
maxY == other.maxY &&
|
maxY == other.maxY &&
|
||||||
maxZ == other.maxZ;
|
maxZ == other.maxZ;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void fixMinMax() {
|
public void fixMinMax() {
|
||||||
int minX = Math.min(this.minX, this.maxX);
|
int minX = Math.min(this.minX, this.maxX);
|
||||||
int minY = Math.min(this.minY, this.maxY);
|
int minY = Math.min(this.minY, this.maxY);
|
||||||
int minZ = Math.min(this.minZ, this.maxZ);
|
int minZ = Math.min(this.minZ, this.maxZ);
|
||||||
int maxX = Math.max(this.minX, this.maxX);
|
int maxX = Math.max(this.minX, this.maxX);
|
||||||
int maxY = Math.max(this.minY, this.maxY);
|
int maxY = Math.max(this.minY, this.maxY);
|
||||||
int maxZ = Math.max(this.minZ, this.maxZ);
|
int maxZ = Math.max(this.minZ, this.maxZ);
|
||||||
|
|
||||||
this.minX = minX;
|
this.minX = minX;
|
||||||
this.minY = minY;
|
this.minY = minY;
|
||||||
this.minZ = minZ;
|
this.minZ = minZ;
|
||||||
this.maxX = maxX;
|
this.maxX = maxX;
|
||||||
this.maxY = maxY;
|
this.maxY = maxY;
|
||||||
this.maxZ = maxZ;
|
this.maxZ = maxZ;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int sizeX() {
|
public int sizeX() {
|
||||||
return maxX - minX;
|
return maxX - minX;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int sizeY() {
|
public int sizeY() {
|
||||||
return maxY - minY;
|
return maxY - minY;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int sizeZ() {
|
public int sizeZ() {
|
||||||
return maxZ - minZ;
|
return maxZ - minZ;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int volume() {
|
public int volume() {
|
||||||
return sizeX() * sizeY() * sizeZ();
|
return sizeX() * sizeY() * sizeZ();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean empty() {
|
public boolean empty() {
|
||||||
// if any dimension has side length 0 this box contains no volume
|
// if any dimension has side length 0 this box contains no volume
|
||||||
return minX == maxX ||
|
return minX == maxX ||
|
||||||
minY == maxY ||
|
minY == maxY ||
|
||||||
minZ == maxZ;
|
minZ == maxZ;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void translate(Vector3i by) {
|
public void translate(Vector3i by) {
|
||||||
translate(by.getX(), by.getY(), by.getZ());
|
translate(by.getX(), by.getY(), by.getZ());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void translate(int x, int y, int z) {
|
public void translate(int x, int y, int z) {
|
||||||
minX += x;
|
minX += x;
|
||||||
maxX += x;
|
maxX += x;
|
||||||
minY += y;
|
minY += y;
|
||||||
maxY += y;
|
maxY += y;
|
||||||
minZ += z;
|
minZ += z;
|
||||||
maxZ += z;
|
maxZ += z;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void mirrorAbout(Direction.Axis axis) {
|
public void mirrorAbout(Direction.Axis axis) {
|
||||||
Vector3i axisVec = Direction.getFacingFromAxis(Direction.AxisDirection.POSITIVE, axis).getDirectionVec();
|
Vector3i axisVec = Direction.getFacingFromAxis(Direction.AxisDirection.POSITIVE, axis).getDirectionVec();
|
||||||
int flipX = axisVec.getX() - 1;
|
int flipX = axisVec.getX() - 1;
|
||||||
int flipY = axisVec.getY() - 1;
|
int flipY = axisVec.getY() - 1;
|
||||||
int flipZ = axisVec.getZ() - 1;
|
int flipZ = axisVec.getZ() - 1;
|
||||||
|
|
||||||
int maxX = this.maxX * flipX;
|
int maxX = this.maxX * flipX;
|
||||||
int maxY = this.maxY * flipY;
|
int maxY = this.maxY * flipY;
|
||||||
int maxZ = this.maxZ * flipZ;
|
int maxZ = this.maxZ * flipZ;
|
||||||
this.maxX = this.minX * flipX;
|
this.maxX = this.minX * flipX;
|
||||||
this.maxY = this.minY * flipY;
|
this.maxY = this.minY * flipY;
|
||||||
this.maxZ = this.minZ * flipZ;
|
this.maxZ = this.minZ * flipZ;
|
||||||
this.minX = maxX;
|
this.minX = maxX;
|
||||||
this.minY = maxY;
|
this.minY = maxY;
|
||||||
this.minZ = maxZ;
|
this.minZ = maxZ;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Grow this bounding box to have power of 2 side length, scaling from the center.
|
* Grow this bounding box to have power of 2 side length, scaling from the center.
|
||||||
*/
|
*/
|
||||||
public void nextPowerOf2Centered() {
|
public void nextPowerOf2Centered() {
|
||||||
int sizeX = sizeX();
|
int sizeX = sizeX();
|
||||||
int sizeY = sizeY();
|
int sizeY = sizeY();
|
||||||
int sizeZ = sizeZ();
|
int sizeZ = sizeZ();
|
||||||
|
|
||||||
int newSizeX = RenderUtil.nextPowerOf2(sizeX);
|
int newSizeX = RenderUtil.nextPowerOf2(sizeX);
|
||||||
int newSizeY = RenderUtil.nextPowerOf2(sizeY);
|
int newSizeY = RenderUtil.nextPowerOf2(sizeY);
|
||||||
int newSizeZ = RenderUtil.nextPowerOf2(sizeZ);
|
int newSizeZ = RenderUtil.nextPowerOf2(sizeZ);
|
||||||
|
|
||||||
int diffX = newSizeX - sizeX;
|
int diffX = newSizeX - sizeX;
|
||||||
int diffY = newSizeY - sizeY;
|
int diffY = newSizeY - sizeY;
|
||||||
int diffZ = newSizeZ - sizeZ;
|
int diffZ = newSizeZ - sizeZ;
|
||||||
|
|
||||||
minX -= diffX / 2; // floor division for the minimums
|
minX -= diffX / 2; // floor division for the minimums
|
||||||
minY -= diffY / 2;
|
minY -= diffY / 2;
|
||||||
minZ -= diffZ / 2;
|
minZ -= diffZ / 2;
|
||||||
maxX += (diffX + 1) / 2; // ceiling divison for the maximums
|
maxX += (diffX + 1) / 2; // ceiling divison for the maximums
|
||||||
maxY += (diffY + 1) / 2;
|
maxY += (diffY + 1) / 2;
|
||||||
maxZ += (diffZ + 1) / 2;
|
maxZ += (diffZ + 1) / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Grow this bounding box to have power of 2 side lengths, scaling from the minimum coords.
|
* Grow this bounding box to have power of 2 side lengths, scaling from the minimum coords.
|
||||||
*/
|
*/
|
||||||
public void nextPowerOf2() {
|
public void nextPowerOf2() {
|
||||||
int sizeX = RenderUtil.nextPowerOf2(sizeX());
|
int sizeX = RenderUtil.nextPowerOf2(sizeX());
|
||||||
int sizeY = RenderUtil.nextPowerOf2(sizeY());
|
int sizeY = RenderUtil.nextPowerOf2(sizeY());
|
||||||
int sizeZ = RenderUtil.nextPowerOf2(sizeZ());
|
int sizeZ = RenderUtil.nextPowerOf2(sizeZ());
|
||||||
|
|
||||||
this.maxX = this.minX + sizeX;
|
this.maxX = this.minX + sizeX;
|
||||||
this.maxY = this.minY + sizeY;
|
this.maxY = this.minY + sizeY;
|
||||||
this.maxZ = this.minZ + sizeZ;
|
this.maxZ = this.minZ + sizeZ;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasPowerOf2Sides() {
|
public boolean hasPowerOf2Sides() {
|
||||||
// this is only true if all individual side lengths are powers of 2
|
// this is only true if all individual side lengths are powers of 2
|
||||||
return isPowerOf2(volume());
|
return isPowerOf2(volume());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void grow(int s) {
|
public void grow(int s) {
|
||||||
this.grow(s, s, s);
|
this.grow(s, s, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void grow(int x, int y, int z) {
|
public void grow(int x, int y, int z) {
|
||||||
minX -= x;
|
minX -= x;
|
||||||
minY -= y;
|
minY -= y;
|
||||||
minZ -= z;
|
minZ -= z;
|
||||||
maxX += x;
|
maxX += x;
|
||||||
maxY += y;
|
maxY += y;
|
||||||
maxZ += z;
|
maxZ += z;
|
||||||
}
|
}
|
||||||
|
|
||||||
public GridAlignedBB intersect(GridAlignedBB other) {
|
public GridAlignedBB intersect(GridAlignedBB other) {
|
||||||
int minX = Math.max(this.minX, other.minX);
|
int minX = Math.max(this.minX, other.minX);
|
||||||
int minY = Math.max(this.minY, other.minY);
|
int minY = Math.max(this.minY, other.minY);
|
||||||
int minZ = Math.max(this.minZ, other.minZ);
|
int minZ = Math.max(this.minZ, other.minZ);
|
||||||
int maxX = Math.min(this.maxX, other.maxX);
|
int maxX = Math.min(this.maxX, other.maxX);
|
||||||
int maxY = Math.min(this.maxY, other.maxY);
|
int maxY = Math.min(this.maxY, other.maxY);
|
||||||
int maxZ = Math.min(this.maxZ, other.maxZ);
|
int maxZ = Math.min(this.maxZ, other.maxZ);
|
||||||
return new GridAlignedBB(minX, minY, minZ, maxX, maxY, maxZ);
|
return new GridAlignedBB(minX, minY, minZ, maxX, maxY, maxZ);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void intersectAssign(GridAlignedBB other) {
|
public void intersectAssign(GridAlignedBB other) {
|
||||||
this.minX = Math.max(this.minX, other.minX);
|
this.minX = Math.max(this.minX, other.minX);
|
||||||
this.minY = Math.max(this.minY, other.minY);
|
this.minY = Math.max(this.minY, other.minY);
|
||||||
this.minZ = Math.max(this.minZ, other.minZ);
|
this.minZ = Math.max(this.minZ, other.minZ);
|
||||||
this.maxX = Math.min(this.maxX, other.maxX);
|
this.maxX = Math.min(this.maxX, other.maxX);
|
||||||
this.maxY = Math.min(this.maxY, other.maxY);
|
this.maxY = Math.min(this.maxY, other.maxY);
|
||||||
this.maxZ = Math.min(this.maxZ, other.maxZ);
|
this.maxZ = Math.min(this.maxZ, other.maxZ);
|
||||||
}
|
}
|
||||||
|
|
||||||
public GridAlignedBB union(GridAlignedBB other) {
|
public GridAlignedBB union(GridAlignedBB other) {
|
||||||
int minX = Math.min(this.minX, other.minX);
|
int minX = Math.min(this.minX, other.minX);
|
||||||
int minY = Math.min(this.minY, other.minY);
|
int minY = Math.min(this.minY, other.minY);
|
||||||
int minZ = Math.min(this.minZ, other.minZ);
|
int minZ = Math.min(this.minZ, other.minZ);
|
||||||
int maxX = Math.max(this.maxX, other.maxX);
|
int maxX = Math.max(this.maxX, other.maxX);
|
||||||
int maxY = Math.max(this.maxY, other.maxY);
|
int maxY = Math.max(this.maxY, other.maxY);
|
||||||
int maxZ = Math.max(this.maxZ, other.maxZ);
|
int maxZ = Math.max(this.maxZ, other.maxZ);
|
||||||
return new GridAlignedBB(minX, minY, minZ, maxX, maxY, maxZ);
|
return new GridAlignedBB(minX, minY, minZ, maxX, maxY, maxZ);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void unionAssign(GridAlignedBB other) {
|
public void unionAssign(GridAlignedBB other) {
|
||||||
this.minX = Math.min(this.minX, other.minX);
|
this.minX = Math.min(this.minX, other.minX);
|
||||||
this.minY = Math.min(this.minY, other.minY);
|
this.minY = Math.min(this.minY, other.minY);
|
||||||
this.minZ = Math.min(this.minZ, other.minZ);
|
this.minZ = Math.min(this.minZ, other.minZ);
|
||||||
this.maxX = Math.max(this.maxX, other.maxX);
|
this.maxX = Math.max(this.maxX, other.maxX);
|
||||||
this.maxY = Math.max(this.maxY, other.maxY);
|
this.maxY = Math.max(this.maxY, other.maxY);
|
||||||
this.maxZ = Math.max(this.maxZ, other.maxZ);
|
this.maxZ = Math.max(this.maxZ, other.maxZ);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void unionAssign(AxisAlignedBB other) {
|
public void unionAssign(AxisAlignedBB other) {
|
||||||
this.minX = Math.min(this.minX, (int) Math.floor(other.minX));
|
this.minX = Math.min(this.minX, (int) Math.floor(other.minX));
|
||||||
this.minY = Math.min(this.minY, (int) Math.floor(other.minY));
|
this.minY = Math.min(this.minY, (int) Math.floor(other.minY));
|
||||||
this.minZ = Math.min(this.minZ, (int) Math.floor(other.minZ));
|
this.minZ = Math.min(this.minZ, (int) Math.floor(other.minZ));
|
||||||
this.maxX = Math.max(this.maxX, (int) Math.ceil(other.maxX));
|
this.maxX = Math.max(this.maxX, (int) Math.ceil(other.maxX));
|
||||||
this.maxY = Math.max(this.maxY, (int) Math.ceil(other.maxY));
|
this.maxY = Math.max(this.maxY, (int) Math.ceil(other.maxY));
|
||||||
this.maxZ = Math.max(this.maxZ, (int) Math.ceil(other.maxZ));
|
this.maxZ = Math.max(this.maxZ, (int) Math.ceil(other.maxZ));
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean intersects(GridAlignedBB other) {
|
public boolean intersects(GridAlignedBB other) {
|
||||||
return this.intersects(other.minX, other.minY, other.minZ, other.maxX, other.maxY, other.maxZ);
|
return this.intersects(other.minX, other.minY, other.minZ, other.maxX, other.maxY, other.maxZ);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean contains(GridAlignedBB other) {
|
public boolean contains(GridAlignedBB other) {
|
||||||
return other.minX >= this.minX &&
|
return other.minX >= this.minX &&
|
||||||
other.maxX <= this.maxX &&
|
other.maxX <= this.maxX &&
|
||||||
other.minY >= this.minY &&
|
other.minY >= this.minY &&
|
||||||
other.maxY <= this.maxY &&
|
other.maxY <= this.maxY &&
|
||||||
other.minZ >= this.minZ &&
|
other.minZ >= this.minZ &&
|
||||||
other.maxZ <= this.maxZ;
|
other.maxZ <= this.maxZ;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isContainedBy(GridAlignedBB other) {
|
public boolean isContainedBy(GridAlignedBB other) {
|
||||||
return other.contains(this);
|
return other.contains(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean intersects(int minX, int minY, int minZ, int maxX, int maxY, int maxZ) {
|
public boolean intersects(int minX, int minY, int minZ, int maxX, int maxY, int maxZ) {
|
||||||
return this.minX < maxX && this.maxX > minX && this.minY < maxY && this.maxY > minY && this.minZ < maxZ && this.maxZ > minZ;
|
return this.minX < maxX && this.maxX > minX && this.minY < maxY && this.maxY > minY && this.minZ < maxZ && this.maxZ > minZ;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void forEachContained(CoordinateConsumer func) {
|
public void forEachContained(CoordinateConsumer func) {
|
||||||
if (empty()) return;
|
if (empty()) return;
|
||||||
|
|
||||||
for (int x = minX; x < maxX; x++) {
|
for (int x = minX; x < maxX; x++) {
|
||||||
for (int y = Math.max(minY, 0); y < Math.min(maxY, 255); y++) { // clamp to world height limits
|
for (int y = Math.max(minY, 0); y < Math.min(maxY, 255); y++) { // clamp to world height limits
|
||||||
for (int z = minZ; z < maxZ; z++) {
|
for (int z = minZ; z < maxZ; z++) {
|
||||||
func.consume(x, y, z);
|
func.consume(x, y, z);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public AxisAlignedBB toAABB() {
|
public AxisAlignedBB toAABB() {
|
||||||
return toAABB(this);
|
return toAABB(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (this == o) return true;
|
if (this == o) return true;
|
||||||
if (o == null || getClass() != o.getClass()) return false;
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
|
||||||
GridAlignedBB that = (GridAlignedBB) o;
|
GridAlignedBB that = (GridAlignedBB) o;
|
||||||
|
|
||||||
return this.sameAs(that);
|
return this.sameAs(that);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
int result = minX;
|
int result = minX;
|
||||||
result = 31 * result + minY;
|
result = 31 * result + minY;
|
||||||
result = 31 * result + minZ;
|
result = 31 * result + minZ;
|
||||||
result = 31 * result + maxX;
|
result = 31 * result + maxX;
|
||||||
result = 31 * result + maxY;
|
result = 31 * result + maxY;
|
||||||
result = 31 * result + maxZ;
|
result = 31 * result + maxZ;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,11 +49,11 @@ public class LightUpdater {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a listener associated with the given {@link BlockPos}.
|
* Add a listener associated with the given {@link BlockPos}.
|
||||||
*
|
* <p>
|
||||||
* When a light update occurs in the chunk the position is contained in,
|
* When a light update occurs in the chunk the position is contained in,
|
||||||
* {@link LightUpdateListener#onLightUpdate} will be called.
|
* {@link LightUpdateListener#onLightUpdate} will be called.
|
||||||
*
|
*
|
||||||
* @param pos The position in the world that the listener cares about.
|
* @param pos The position in the world that the listener cares about.
|
||||||
* @param listener The object that wants to receive light update notifications.
|
* @param listener The object that wants to receive light update notifications.
|
||||||
*/
|
*/
|
||||||
public void startListening(BlockPos pos, LightUpdateListener listener) {
|
public void startListening(BlockPos pos, LightUpdateListener listener) {
|
||||||
|
@ -71,11 +71,11 @@ public class LightUpdater {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a listener associated with the given {@link GridAlignedBB}.
|
* Add a listener associated with the given {@link GridAlignedBB}.
|
||||||
*
|
* <p>
|
||||||
* When a light update occurs in any chunk spanning the given volume,
|
* When a light update occurs in any chunk spanning the given volume,
|
||||||
* {@link LightUpdateListener#onLightUpdate} will be called.
|
* {@link LightUpdateListener#onLightUpdate} will be called.
|
||||||
*
|
*
|
||||||
* @param volume The volume in the world that the listener cares about.
|
* @param volume The volume in the world that the listener cares about.
|
||||||
* @param listener The object that wants to receive light update notifications.
|
* @param listener The object that wants to receive light update notifications.
|
||||||
*/
|
*/
|
||||||
public void startListening(GridAlignedBB volume, LightUpdateListener listener) {
|
public void startListening(GridAlignedBB volume, LightUpdateListener listener) {
|
||||||
|
@ -106,8 +106,8 @@ public class LightUpdater {
|
||||||
/**
|
/**
|
||||||
* Dispatch light updates to all registered {@link LightUpdateListener}s.
|
* Dispatch light updates to all registered {@link LightUpdateListener}s.
|
||||||
*
|
*
|
||||||
* @param world The world in which light was updated.
|
* @param world The world in which light was updated.
|
||||||
* @param type The type of light that changed.
|
* @param type The type of light that changed.
|
||||||
* @param sectionPos A long representing the section position where light changed.
|
* @param sectionPos A long representing the section position where light changed.
|
||||||
*/
|
*/
|
||||||
public void onLightUpdate(IBlockDisplayReader world, LightType type, long sectionPos) {
|
public void onLightUpdate(IBlockDisplayReader world, LightType type, long sectionPos) {
|
||||||
|
|
|
@ -19,291 +19,294 @@ import net.minecraft.world.LightType;
|
||||||
|
|
||||||
public class LightVolume {
|
public class LightVolume {
|
||||||
|
|
||||||
private GridAlignedBB sampleVolume;
|
private GridAlignedBB sampleVolume;
|
||||||
private GridAlignedBB textureVolume;
|
private GridAlignedBB textureVolume;
|
||||||
private ByteBuffer lightData;
|
private ByteBuffer lightData;
|
||||||
|
|
||||||
private boolean bufferDirty;
|
private boolean bufferDirty;
|
||||||
private boolean removed;
|
private boolean removed;
|
||||||
|
|
||||||
private final GlTexture glTexture;
|
private final GlTexture glTexture;
|
||||||
|
|
||||||
private final RGPixelFormat pixelFormat;
|
private final RGPixelFormat pixelFormat;
|
||||||
|
|
||||||
public LightVolume(GridAlignedBB sampleVolume) {
|
public LightVolume(GridAlignedBB sampleVolume) {
|
||||||
setSampleVolume(sampleVolume);
|
setSampleVolume(sampleVolume);
|
||||||
|
|
||||||
pixelFormat = Backend.compat.pixelFormat;
|
pixelFormat = Backend.compat.pixelFormat;
|
||||||
|
|
||||||
this.glTexture = new GlTexture(GL20.GL_TEXTURE_3D);
|
this.glTexture = new GlTexture(GL20.GL_TEXTURE_3D);
|
||||||
this.lightData = MemoryUtil.memAlloc(this.textureVolume.volume() * pixelFormat.byteCount());
|
this.lightData = MemoryUtil.memAlloc(this.textureVolume.volume() * pixelFormat.byteCount());
|
||||||
|
|
||||||
// allocate space for the texture
|
// allocate space for the texture
|
||||||
GL20.glActiveTexture(GL20.GL_TEXTURE4);
|
GL20.glActiveTexture(GL20.GL_TEXTURE4);
|
||||||
glTexture.bind();
|
glTexture.bind();
|
||||||
|
|
||||||
int sizeX = textureVolume.sizeX();
|
int sizeX = textureVolume.sizeX();
|
||||||
int sizeY = textureVolume.sizeY();
|
int sizeY = textureVolume.sizeY();
|
||||||
int sizeZ = textureVolume.sizeZ();
|
int sizeZ = textureVolume.sizeZ();
|
||||||
GL12.glTexImage3D(GL12.GL_TEXTURE_3D, 0, pixelFormat.internalFormat(), sizeX, sizeY, sizeZ, 0, pixelFormat.format(), GL20.GL_UNSIGNED_BYTE, 0);
|
GL12.glTexImage3D(GL12.GL_TEXTURE_3D, 0, pixelFormat.internalFormat(), sizeX, sizeY, sizeZ, 0, pixelFormat.format(), GL20.GL_UNSIGNED_BYTE, 0);
|
||||||
|
|
||||||
glTexture.unbind();
|
glTexture.unbind();
|
||||||
GL20.glActiveTexture(GL20.GL_TEXTURE0);
|
GL20.glActiveTexture(GL20.GL_TEXTURE0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setSampleVolume(GridAlignedBB sampleVolume) {
|
private void setSampleVolume(GridAlignedBB sampleVolume) {
|
||||||
this.sampleVolume = sampleVolume;
|
this.sampleVolume = sampleVolume;
|
||||||
this.textureVolume = sampleVolume.copy();
|
this.textureVolume = sampleVolume.copy();
|
||||||
this.textureVolume.nextPowerOf2Centered();
|
this.textureVolume.nextPowerOf2Centered();
|
||||||
}
|
}
|
||||||
|
|
||||||
public GridAlignedBB getTextureVolume() {
|
public GridAlignedBB getTextureVolume() {
|
||||||
return GridAlignedBB.copy(textureVolume);
|
return GridAlignedBB.copy(textureVolume);
|
||||||
}
|
}
|
||||||
|
|
||||||
public GridAlignedBB getSampleVolume() {
|
public GridAlignedBB getSampleVolume() {
|
||||||
return GridAlignedBB.copy(sampleVolume);
|
return GridAlignedBB.copy(sampleVolume);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getMinX() {
|
public int getMinX() {
|
||||||
return textureVolume.minX;
|
return textureVolume.minX;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getMinY() {
|
public int getMinY() {
|
||||||
return textureVolume.minY;
|
return textureVolume.minY;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getMinZ() {
|
public int getMinZ() {
|
||||||
return textureVolume.minZ;
|
return textureVolume.minZ;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getMaxX() {
|
public int getMaxX() {
|
||||||
return textureVolume.maxX;
|
return textureVolume.maxX;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getMaxY() {
|
public int getMaxY() {
|
||||||
return textureVolume.maxY;
|
return textureVolume.maxY;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getMaxZ() {
|
public int getMaxZ() {
|
||||||
return textureVolume.maxZ;
|
return textureVolume.maxZ;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getSizeX() {
|
public int getSizeX() {
|
||||||
return textureVolume.sizeX();
|
return textureVolume.sizeX();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getSizeY() {
|
public int getSizeY() {
|
||||||
return textureVolume.sizeY();
|
return textureVolume.sizeY();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getSizeZ() {
|
public int getSizeZ() {
|
||||||
return textureVolume.sizeZ();
|
return textureVolume.sizeZ();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void move(IBlockDisplayReader world, GridAlignedBB newSampleVolume) {
|
public void move(IBlockDisplayReader world, GridAlignedBB newSampleVolume) {
|
||||||
if (textureVolume.contains(newSampleVolume)) {
|
if (textureVolume.contains(newSampleVolume)) {
|
||||||
if (newSampleVolume.intersects(sampleVolume)) {
|
if (newSampleVolume.intersects(sampleVolume)) {
|
||||||
GridAlignedBB newArea = newSampleVolume.intersect(sampleVolume);
|
GridAlignedBB newArea = newSampleVolume.intersect(sampleVolume);
|
||||||
sampleVolume = newSampleVolume;
|
sampleVolume = newSampleVolume;
|
||||||
|
|
||||||
copyLight(world, newArea);
|
copyLight(world, newArea);
|
||||||
} else {
|
} else {
|
||||||
sampleVolume = newSampleVolume;
|
sampleVolume = newSampleVolume;
|
||||||
initialize(world);
|
initialize(world);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
setSampleVolume(newSampleVolume);
|
setSampleVolume(newSampleVolume);
|
||||||
int volume = textureVolume.volume();
|
int volume = textureVolume.volume();
|
||||||
if (volume * 2 > lightData.capacity()) {
|
if (volume * 2 > lightData.capacity()) {
|
||||||
lightData = MemoryUtil.memRealloc(lightData, volume * 2);
|
lightData = MemoryUtil.memRealloc(lightData, volume * 2);
|
||||||
}
|
}
|
||||||
initialize(world);
|
initialize(world);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void notifyLightUpdate(IBlockDisplayReader world, LightType type, GridAlignedBB changedVolume) {
|
public void notifyLightUpdate(IBlockDisplayReader world, LightType type, GridAlignedBB changedVolume) {
|
||||||
if (removed)
|
if (removed)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!changedVolume.intersects(sampleVolume))
|
if (!changedVolume.intersects(sampleVolume))
|
||||||
return;
|
return;
|
||||||
changedVolume = changedVolume.intersect(sampleVolume); // compute the region contained by us that has dirty lighting data.
|
changedVolume = changedVolume.intersect(sampleVolume); // compute the region contained by us that has dirty lighting data.
|
||||||
|
|
||||||
if (type == LightType.BLOCK) copyBlock(world, changedVolume);
|
if (type == LightType.BLOCK) copyBlock(world, changedVolume);
|
||||||
else if (type == LightType.SKY) copySky(world, changedVolume);
|
else if (type == LightType.SKY) copySky(world, changedVolume);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void notifyLightPacket(IBlockDisplayReader world, int chunkX, int chunkZ) {
|
public void notifyLightPacket(IBlockDisplayReader world, int chunkX, int chunkZ) {
|
||||||
if (removed) return;
|
if (removed) return;
|
||||||
|
|
||||||
GridAlignedBB changedVolume = GridAlignedBB.from(chunkX, chunkZ);
|
GridAlignedBB changedVolume = GridAlignedBB.from(chunkX, chunkZ);
|
||||||
if (!changedVolume.intersects(sampleVolume))
|
if (!changedVolume.intersects(sampleVolume))
|
||||||
return;
|
return;
|
||||||
changedVolume.intersectAssign(sampleVolume); // compute the region contained by us that has dirty lighting data.
|
changedVolume.intersectAssign(sampleVolume); // compute the region contained by us that has dirty lighting data.
|
||||||
|
|
||||||
copyLight(world, changedVolume);
|
copyLight(world, changedVolume);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Completely (re)populate this volume with block and sky lighting data.
|
* Completely (re)populate this volume with block and sky lighting data.
|
||||||
* This is expensive and should be avoided.
|
* This is expensive and should be avoided.
|
||||||
*/
|
*/
|
||||||
public void initialize(IBlockDisplayReader world) {
|
public void initialize(IBlockDisplayReader world) {
|
||||||
BlockPos.Mutable pos = new BlockPos.Mutable();
|
BlockPos.Mutable pos = new BlockPos.Mutable();
|
||||||
|
|
||||||
int shiftX = textureVolume.minX;
|
int shiftX = textureVolume.minX;
|
||||||
int shiftY = textureVolume.minY;
|
int shiftY = textureVolume.minY;
|
||||||
int shiftZ = textureVolume.minZ;
|
int shiftZ = textureVolume.minZ;
|
||||||
|
|
||||||
sampleVolume.forEachContained((x, y, z) -> {
|
sampleVolume.forEachContained((x, y, z) -> {
|
||||||
pos.setPos(x, y, z);
|
pos.setPos(x, y, z);
|
||||||
|
|
||||||
int blockLight = world.getLightLevel(LightType.BLOCK, pos);
|
int blockLight = world.getLightLevel(LightType.BLOCK, pos);
|
||||||
int skyLight = world.getLightLevel(LightType.SKY, pos);
|
int skyLight = world.getLightLevel(LightType.SKY, pos);
|
||||||
|
|
||||||
writeLight(x - shiftX, y - shiftY, z - shiftZ, blockLight, skyLight);
|
writeLight(x - shiftX, y - shiftY, z - shiftZ, blockLight, skyLight);
|
||||||
});
|
});
|
||||||
|
|
||||||
bufferDirty = true;
|
bufferDirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copy block light from the world into this volume.
|
* Copy block light from the world into this volume.
|
||||||
* @param worldVolume the region in the world to copy data from.
|
*
|
||||||
*/
|
* @param worldVolume the region in the world to copy data from.
|
||||||
public void copyBlock(IBlockDisplayReader world, GridAlignedBB worldVolume) {
|
*/
|
||||||
BlockPos.Mutable pos = new BlockPos.Mutable();
|
public void copyBlock(IBlockDisplayReader world, GridAlignedBB worldVolume) {
|
||||||
|
BlockPos.Mutable pos = new BlockPos.Mutable();
|
||||||
int xShift = textureVolume.minX;
|
|
||||||
int yShift = textureVolume.minY;
|
int xShift = textureVolume.minX;
|
||||||
int zShift = textureVolume.minZ;
|
int yShift = textureVolume.minY;
|
||||||
|
int zShift = textureVolume.minZ;
|
||||||
worldVolume.forEachContained((x, y, z) -> {
|
|
||||||
pos.setPos(x, y, z);
|
worldVolume.forEachContained((x, y, z) -> {
|
||||||
|
pos.setPos(x, y, z);
|
||||||
int light = world.getLightLevel(LightType.BLOCK, pos);
|
|
||||||
|
int light = world.getLightLevel(LightType.BLOCK, pos);
|
||||||
writeBlock(x - xShift, y - yShift, z - zShift, light);
|
|
||||||
});
|
writeBlock(x - xShift, y - yShift, z - zShift, light);
|
||||||
|
});
|
||||||
bufferDirty = true;
|
|
||||||
}
|
bufferDirty = true;
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Copy sky light from the world into this volume.
|
/**
|
||||||
* @param worldVolume the region in the world to copy data from.
|
* Copy sky light from the world into this volume.
|
||||||
*/
|
*
|
||||||
public void copySky(IBlockDisplayReader world, GridAlignedBB worldVolume) {
|
* @param worldVolume the region in the world to copy data from.
|
||||||
BlockPos.Mutable pos = new BlockPos.Mutable();
|
*/
|
||||||
|
public void copySky(IBlockDisplayReader world, GridAlignedBB worldVolume) {
|
||||||
int xShift = textureVolume.minX;
|
BlockPos.Mutable pos = new BlockPos.Mutable();
|
||||||
int yShift = textureVolume.minY;
|
|
||||||
int zShift = textureVolume.minZ;
|
int xShift = textureVolume.minX;
|
||||||
|
int yShift = textureVolume.minY;
|
||||||
worldVolume.forEachContained((x, y, z) -> {
|
int zShift = textureVolume.minZ;
|
||||||
pos.setPos(x, y, z);
|
|
||||||
|
worldVolume.forEachContained((x, y, z) -> {
|
||||||
int light = world.getLightLevel(LightType.SKY, pos);
|
pos.setPos(x, y, z);
|
||||||
|
|
||||||
writeSky(x - xShift, y - yShift, z - zShift, light);
|
int light = world.getLightLevel(LightType.SKY, pos);
|
||||||
});
|
|
||||||
|
writeSky(x - xShift, y - yShift, z - zShift, light);
|
||||||
bufferDirty = true;
|
});
|
||||||
}
|
|
||||||
|
bufferDirty = true;
|
||||||
/**
|
}
|
||||||
* Copy all light from the world into this volume.
|
|
||||||
* @param worldVolume the region in the world to copy data from.
|
/**
|
||||||
*/
|
* Copy all light from the world into this volume.
|
||||||
public void copyLight(IBlockDisplayReader world, GridAlignedBB worldVolume) {
|
*
|
||||||
BlockPos.Mutable pos = new BlockPos.Mutable();
|
* @param worldVolume the region in the world to copy data from.
|
||||||
|
*/
|
||||||
int xShift = textureVolume.minX;
|
public void copyLight(IBlockDisplayReader world, GridAlignedBB worldVolume) {
|
||||||
int yShift = textureVolume.minY;
|
BlockPos.Mutable pos = new BlockPos.Mutable();
|
||||||
int zShift = textureVolume.minZ;
|
|
||||||
|
int xShift = textureVolume.minX;
|
||||||
worldVolume.forEachContained((x, y, z) -> {
|
int yShift = textureVolume.minY;
|
||||||
pos.setPos(x, y, z);
|
int zShift = textureVolume.minZ;
|
||||||
|
|
||||||
int block = world.getLightLevel(LightType.BLOCK, pos);
|
worldVolume.forEachContained((x, y, z) -> {
|
||||||
int sky = world.getLightLevel(LightType.SKY, pos);
|
pos.setPos(x, y, z);
|
||||||
|
|
||||||
writeLight(x - xShift, y - yShift, z - zShift, block, sky);
|
int block = world.getLightLevel(LightType.BLOCK, pos);
|
||||||
});
|
int sky = world.getLightLevel(LightType.SKY, pos);
|
||||||
|
|
||||||
bufferDirty = true;
|
writeLight(x - xShift, y - yShift, z - zShift, block, sky);
|
||||||
}
|
});
|
||||||
|
|
||||||
public void bind() {
|
bufferDirty = true;
|
||||||
// just in case something goes wrong or we accidentally call this before this volume is properly disposed of.
|
}
|
||||||
if (lightData == null || removed) return;
|
|
||||||
|
public void bind() {
|
||||||
GL13.glActiveTexture(GL20.GL_TEXTURE4);
|
// just in case something goes wrong or we accidentally call this before this volume is properly disposed of.
|
||||||
glTexture.bind();
|
if (lightData == null || removed) return;
|
||||||
GL11.glTexParameteri(GL13.GL_TEXTURE_3D, GL13.GL_TEXTURE_MIN_FILTER, GL13.GL_LINEAR);
|
|
||||||
GL11.glTexParameteri(GL13.GL_TEXTURE_3D, GL13.GL_TEXTURE_MAG_FILTER, GL13.GL_LINEAR);
|
GL13.glActiveTexture(GL20.GL_TEXTURE4);
|
||||||
GL11.glTexParameteri(GL13.GL_TEXTURE_3D, GL13.GL_TEXTURE_WRAP_S, GL20.GL_MIRRORED_REPEAT);
|
glTexture.bind();
|
||||||
GL11.glTexParameteri(GL13.GL_TEXTURE_3D, GL13.GL_TEXTURE_WRAP_R, GL20.GL_MIRRORED_REPEAT);
|
GL11.glTexParameteri(GL13.GL_TEXTURE_3D, GL13.GL_TEXTURE_MIN_FILTER, GL13.GL_LINEAR);
|
||||||
GL11.glTexParameteri(GL13.GL_TEXTURE_3D, GL13.GL_TEXTURE_WRAP_T, GL20.GL_MIRRORED_REPEAT);
|
GL11.glTexParameteri(GL13.GL_TEXTURE_3D, GL13.GL_TEXTURE_MAG_FILTER, GL13.GL_LINEAR);
|
||||||
|
GL11.glTexParameteri(GL13.GL_TEXTURE_3D, GL13.GL_TEXTURE_WRAP_S, GL20.GL_MIRRORED_REPEAT);
|
||||||
uploadTexture();
|
GL11.glTexParameteri(GL13.GL_TEXTURE_3D, GL13.GL_TEXTURE_WRAP_R, GL20.GL_MIRRORED_REPEAT);
|
||||||
}
|
GL11.glTexParameteri(GL13.GL_TEXTURE_3D, GL13.GL_TEXTURE_WRAP_T, GL20.GL_MIRRORED_REPEAT);
|
||||||
|
|
||||||
private void uploadTexture() {
|
uploadTexture();
|
||||||
if (bufferDirty) {
|
}
|
||||||
GL20.glPixelStorei(GL20.GL_UNPACK_ROW_LENGTH, 0);
|
|
||||||
GL20.glPixelStorei(GL20.GL_UNPACK_SKIP_PIXELS, 0);
|
private void uploadTexture() {
|
||||||
GL20.glPixelStorei(GL20.GL_UNPACK_SKIP_ROWS, 0);
|
if (bufferDirty) {
|
||||||
GL20.glPixelStorei(GL20.GL_UNPACK_SKIP_IMAGES, 0);
|
GL20.glPixelStorei(GL20.GL_UNPACK_ROW_LENGTH, 0);
|
||||||
GL20.glPixelStorei(GL20.GL_UNPACK_IMAGE_HEIGHT, 0);
|
GL20.glPixelStorei(GL20.GL_UNPACK_SKIP_PIXELS, 0);
|
||||||
GL20.glPixelStorei(GL20.GL_UNPACK_ALIGNMENT, 2);
|
GL20.glPixelStorei(GL20.GL_UNPACK_SKIP_ROWS, 0);
|
||||||
int sizeX = textureVolume.sizeX();
|
GL20.glPixelStorei(GL20.GL_UNPACK_SKIP_IMAGES, 0);
|
||||||
int sizeY = textureVolume.sizeY();
|
GL20.glPixelStorei(GL20.GL_UNPACK_IMAGE_HEIGHT, 0);
|
||||||
int sizeZ = textureVolume.sizeZ();
|
GL20.glPixelStorei(GL20.GL_UNPACK_ALIGNMENT, 2);
|
||||||
|
int sizeX = textureVolume.sizeX();
|
||||||
GL12.glTexSubImage3D(GL12.GL_TEXTURE_3D, 0, 0, 0, 0, sizeX, sizeY, sizeZ, pixelFormat.format(), GL20.GL_UNSIGNED_BYTE, lightData);
|
int sizeY = textureVolume.sizeY();
|
||||||
|
int sizeZ = textureVolume.sizeZ();
|
||||||
GL20.glPixelStorei(GL20.GL_UNPACK_ALIGNMENT, 4); // 4 is the default
|
|
||||||
bufferDirty = false;
|
GL12.glTexSubImage3D(GL12.GL_TEXTURE_3D, 0, 0, 0, 0, sizeX, sizeY, sizeZ, pixelFormat.format(), GL20.GL_UNSIGNED_BYTE, lightData);
|
||||||
}
|
|
||||||
}
|
GL20.glPixelStorei(GL20.GL_UNPACK_ALIGNMENT, 4); // 4 is the default
|
||||||
|
bufferDirty = false;
|
||||||
public void unbind() {
|
}
|
||||||
glTexture.unbind();
|
}
|
||||||
}
|
|
||||||
|
public void unbind() {
|
||||||
public void delete() {
|
glTexture.unbind();
|
||||||
removed = true;
|
}
|
||||||
RenderWork.enqueue(() -> {
|
|
||||||
glTexture.delete();
|
public void delete() {
|
||||||
MemoryUtil.memFree(lightData);
|
removed = true;
|
||||||
lightData = null;
|
RenderWork.enqueue(() -> {
|
||||||
});
|
glTexture.delete();
|
||||||
}
|
MemoryUtil.memFree(lightData);
|
||||||
|
lightData = null;
|
||||||
private void writeLight(int x, int y, int z, int block, int sky) {
|
});
|
||||||
byte b = (byte) ((block & 0xF) << 4);
|
}
|
||||||
byte s = (byte) ((sky & 0xF) << 4);
|
|
||||||
|
private void writeLight(int x, int y, int z, int block, int sky) {
|
||||||
int i = posToIndex(x, y, z);
|
byte b = (byte) ((block & 0xF) << 4);
|
||||||
lightData.put(i, b);
|
byte s = (byte) ((sky & 0xF) << 4);
|
||||||
lightData.put(i + 1, s);
|
|
||||||
}
|
int i = posToIndex(x, y, z);
|
||||||
|
lightData.put(i, b);
|
||||||
private void writeBlock(int x, int y, int z, int block) {
|
lightData.put(i + 1, s);
|
||||||
byte b = (byte) ((block & 0xF) << 4);
|
}
|
||||||
|
|
||||||
lightData.put(posToIndex(x, y, z), b);
|
private void writeBlock(int x, int y, int z, int block) {
|
||||||
}
|
byte b = (byte) ((block & 0xF) << 4);
|
||||||
|
|
||||||
private void writeSky(int x, int y, int z, int sky) {
|
lightData.put(posToIndex(x, y, z), b);
|
||||||
byte b = (byte) ((sky & 0xF) << 4);
|
}
|
||||||
|
|
||||||
lightData.put(posToIndex(x, y, z) + 1, b);
|
private void writeSky(int x, int y, int z, int sky) {
|
||||||
}
|
byte b = (byte) ((sky & 0xF) << 4);
|
||||||
|
|
||||||
private int posToIndex(int x, int y, int z) {
|
lightData.put(posToIndex(x, y, z) + 1, b);
|
||||||
return (x + textureVolume.sizeX() * (y + z * textureVolume.sizeY())) * pixelFormat.byteCount();
|
}
|
||||||
}
|
|
||||||
|
private int posToIndex(int x, int y, int z) {
|
||||||
|
return (x + textureVolume.sizeX() * (y + z * textureVolume.sizeY())) * pixelFormat.byteCount();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,26 +10,26 @@ import com.simibubi.create.foundation.utility.Pair;
|
||||||
import com.simibubi.create.foundation.utility.outliner.AABBOutline;
|
import com.simibubi.create.foundation.utility.outliner.AABBOutline;
|
||||||
|
|
||||||
public class LightVolumeDebugger {
|
public class LightVolumeDebugger {
|
||||||
public static void render(MatrixStack ms, SuperRenderTypeBuffer buffer) {
|
public static void render(MatrixStack ms, SuperRenderTypeBuffer buffer) {
|
||||||
ContraptionRenderDispatcher.renderers.values()
|
ContraptionRenderDispatcher.renderers.values()
|
||||||
.stream()
|
.stream()
|
||||||
.flatMap(r -> {
|
.flatMap(r -> {
|
||||||
GridAlignedBB texture = r.getLighter().lightVolume.getTextureVolume();
|
GridAlignedBB texture = r.getLighter().lightVolume.getTextureVolume();
|
||||||
GridAlignedBB sample = r.getLighter().lightVolume.getSampleVolume();
|
GridAlignedBB sample = r.getLighter().lightVolume.getSampleVolume();
|
||||||
|
|
||||||
ArrayList<Pair<GridAlignedBB, Integer>> pairs = new ArrayList<>(2);
|
ArrayList<Pair<GridAlignedBB, Integer>> pairs = new ArrayList<>(2);
|
||||||
|
|
||||||
pairs.add(Pair.of(texture, 0xFFFFFF));
|
pairs.add(Pair.of(texture, 0xFFFFFF));
|
||||||
pairs.add(Pair.of(sample, 0xFFFF00));
|
pairs.add(Pair.of(sample, 0xFFFF00));
|
||||||
|
|
||||||
return pairs.stream();
|
return pairs.stream();
|
||||||
})
|
})
|
||||||
.map(pair -> {
|
.map(pair -> {
|
||||||
AABBOutline outline = new AABBOutline(GridAlignedBB.toAABB(pair.getFirst()));
|
AABBOutline outline = new AABBOutline(GridAlignedBB.toAABB(pair.getFirst()));
|
||||||
|
|
||||||
outline.getParams().colored(pair.getSecond());
|
outline.getParams().colored(pair.getSecond());
|
||||||
return outline;
|
return outline;
|
||||||
})
|
})
|
||||||
.forEach(outline -> outline.render(ms, buffer, AnimationTickHolder.getPartialTicks()));
|
.forEach(outline -> outline.render(ms, buffer, AnimationTickHolder.getPartialTicks()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue