smarter light volumes

start the process of creating sane gl abstractions
stabilized contraption bounds now fit tightly with them
fix global tile entity issues
This commit is contained in:
JozsefA 2021-01-25 01:17:55 -08:00
parent 7bac93a03c
commit 93353b61d6
25 changed files with 328 additions and 158 deletions

View file

@ -39,6 +39,11 @@ public class DeployerRenderer extends SafeTileEntityRenderer<DeployerTileEntity>
super(dispatcher);
}
@Override
public boolean isGlobalRenderer(DeployerTileEntity te) {
return true;
}
@Override
protected void renderSafe(DeployerTileEntity te, float partialTicks, MatrixStack ms, IRenderTypeBuffer buffer,
int light, int overlay) {

View file

@ -376,10 +376,4 @@ public class DeployerTileEntity extends KineticTileEntity {
TooltipHelper.addHint(tooltip, "hint.full_deployer");
return true;
}
@Override
public boolean shouldRenderAsTE() {
return true;
}
}

View file

@ -24,6 +24,11 @@ public class MechanicalMixerRenderer extends KineticTileEntityRenderer {
super(dispatcher);
}
@Override
public boolean isGlobalRenderer(KineticTileEntity te) {
return true;
}
@Override
protected void renderSafe(KineticTileEntity te, float partialTicks, MatrixStack ms, IRenderTypeBuffer buffer,
int light, int overlay) {

View file

@ -9,6 +9,16 @@ public class NonStationaryLighter<C extends Contraption> extends ContraptionLigh
super(contraption);
}
@Override
protected GridAlignedBB contraptionBoundsToVolume(GridAlignedBB bounds) {
bounds = bounds.copy();
bounds.grow(2); // so we have at least enough data on the edges to avoid artifacts and have smooth lighting
bounds.minY = Math.max(bounds.minY, 0);
bounds.maxY = Math.min(bounds.maxY, 255);
return bounds;
}
@Override
public void tick(RenderedContraption owner) {
GridAlignedBB contraptionBounds = getContraptionBounds();

View file

@ -2,6 +2,10 @@ package com.simibubi.create.content.contraptions.components.structureMovement.be
import com.simibubi.create.foundation.render.light.ContraptionLighter;
import com.simibubi.create.foundation.render.light.GridAlignedBB;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import java.util.Set;
public class BearingLighter extends ContraptionLighter<BearingContraption> {
@ -11,9 +15,40 @@ public class BearingLighter extends ContraptionLighter<BearingContraption> {
@Override
public GridAlignedBB getContraptionBounds() {
GridAlignedBB localBounds = GridAlignedBB.fromAABB(contraption.bounds);
localBounds.rotate45(contraption.getFacing().getAxis());
localBounds.translate(contraption.anchor);
return localBounds;
Set<BlockPos> blocks = contraption.getBlocks().keySet();
Direction orientation = contraption.facing;
float maxDistanceSq = -1;
for (BlockPos pos : blocks) {
float x = pos.getX();
float y = pos.getY();
float z = pos.getZ();
float distSq = x * x + y * y + z * z;
if (distSq > maxDistanceSq) maxDistanceSq = distSq;
}
int radius = (int) (Math.ceil(Math.sqrt(maxDistanceSq)));
GridAlignedBB betterBounds = GridAlignedBB.ofRadius(radius);
GridAlignedBB contraptionBounds = GridAlignedBB.fromAABB(contraption.bounds);
Direction.Axis axis = orientation.getAxis();
if (axis == Direction.Axis.X) {
betterBounds.maxX = contraptionBounds.maxX;
betterBounds.minX = contraptionBounds.minX;
} else if (axis == Direction.Axis.Y) {
betterBounds.maxY = contraptionBounds.maxY;
betterBounds.minY = contraptionBounds.minY;
} else if (axis == Direction.Axis.Z) {
betterBounds.maxZ = contraptionBounds.maxZ;
betterBounds.minZ = contraptionBounds.minZ;
}
betterBounds.translate(contraption.anchor);
return betterBounds;
}
}

View file

@ -393,4 +393,8 @@ public class ClockworkBearingTileEntity extends KineticTileEntity implements IBe
return pos;
}
@Override
public boolean shouldRenderAsTE() {
return true;
}
}

View file

@ -282,4 +282,8 @@ public class MechanicalBearingTileEntity extends GeneratingKineticTileEntity imp
return true;
}
@Override
public boolean shouldRenderAsTE() {
return true;
}
}

View file

@ -26,7 +26,6 @@ public class StabilizedContraption extends Contraption {
if (!searchMovedStructure(world, offset, null))
return false;
startMoving(world);
expandBoundsAroundAxis(Axis.Y);
if (blocks.isEmpty())
return false;
return true;

View file

@ -143,8 +143,4 @@ public abstract class BasinOperatingTileEntity extends KineticTileEntity {
protected abstract Object getRecipeCacheKey();
@Override
public boolean shouldRenderAsTE() {
return true;
}
}

View file

@ -50,6 +50,11 @@ public class BeltRenderer extends SafeTileEntityRenderer<BeltTileEntity> impleme
renderItems(te, partialTicks, ms, buffer, light, overlay);
}
@Override
public boolean isGlobalRenderer(BeltTileEntity te) {
return te.isController();
}
@Override
public void addInstanceData(InstanceContext<BeltTileEntity> ctx) {
BeltTileEntity te = ctx.te;

View file

@ -465,11 +465,4 @@ public class BeltTileEntity extends KineticTileEntity {
return new ModelDataMap.Builder().withInitial(CASING_PROPERTY, casing)
.build();
}
@Override
public boolean shouldRenderAsTE() {
// Since only the controller does the item rendering, we potentially
// save a *lot* of time by not processing the other belts.
return isController();
}
}

View file

@ -29,6 +29,11 @@ public class ArmRenderer extends KineticTileEntityRenderer {
super(dispatcher);
}
@Override
public boolean isGlobalRenderer(KineticTileEntity te) {
return true;
}
@Override
protected void renderSafe(KineticTileEntity te, float pt, MatrixStack ms, IRenderTypeBuffer buffer, int light,
int overlay) {

View file

@ -513,8 +513,4 @@ public class ArmTileEntity extends KineticTileEntity {
}
}
@Override
public boolean shouldRenderAsTE() {
return true;
}
}

View file

@ -27,6 +27,7 @@ import com.simibubi.create.foundation.networking.LeftClickPacket;
import com.simibubi.create.foundation.render.FastRenderDispatcher;
import com.simibubi.create.foundation.render.RenderWork;
import com.simibubi.create.foundation.render.contraption.ContraptionRenderDispatcher;
import com.simibubi.create.foundation.render.light.LightVolumeDebugger;
import com.simibubi.create.foundation.renderState.SuperRenderTypeBuffer;
import com.simibubi.create.foundation.tileEntity.behaviour.edgeInteraction.EdgeInteractionRenderer;
import com.simibubi.create.foundation.tileEntity.behaviour.filtering.FilteringRenderer;

View file

@ -1,18 +1,20 @@
package com.simibubi.create.foundation.render;
import com.mojang.blaze3d.platform.GlStateManager;
import com.simibubi.create.foundation.render.gl.GlBuffer;
import com.simibubi.create.foundation.render.gl.GlVertexArray;
import com.simibubi.create.foundation.render.instancing.VertexFormat;
import net.minecraft.client.renderer.BufferBuilder;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL30;
import org.lwjgl.opengl.GL40;
import java.nio.ByteBuffer;
public abstract class GPUBuffer extends TemplateBuffer {
protected int vao, ebo, invariantVBO;
protected GlVertexArray vao;
protected GlBuffer ebo;
protected GlBuffer invariantVBO;
protected boolean removed;
public GPUBuffer(BufferBuilder buf) {
@ -25,12 +27,18 @@ public abstract class GPUBuffer extends TemplateBuffer {
int invariantSize = vertexCount * stride;
vao = GL30.glGenVertexArrays();
ebo = GlStateManager.genBuffers();
invariantVBO = GlStateManager.genBuffers();
vao = new GlVertexArray();
invariantVBO = new GlBuffer();
ebo = createEBO();
GL30.glBindVertexArray(vao);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, invariantVBO);
vao.bind();
int numAttributes = getTotalShaderAttributeCount();
for (int i = 0; i <= numAttributes; i++) {
GL40.glEnableVertexAttribArray(i);
}
invariantVBO.bind(GL15.GL_ARRAY_BUFFER);
// allocate the buffer on the gpu
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, invariantSize, GL15.GL_STATIC_DRAW);
@ -44,14 +52,11 @@ public abstract class GPUBuffer extends TemplateBuffer {
constant.rewind();
GL15.glUnmapBuffer(GL15.GL_ARRAY_BUFFER);
buildEBO(ebo);
getModelFormat().informAttributes(0);
GlStateManager.bindBuffers(GL15.GL_ARRAY_BUFFER, 0);
GlStateManager.bindBuffers(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
invariantVBO.unbind(GL15.GL_ARRAY_BUFFER);
// Deselect (bind to 0) the VAO
GL30.glBindVertexArray(0);
vao.unbind();
}
protected abstract void copyVertex(ByteBuffer to, int index);
@ -69,26 +74,17 @@ public abstract class GPUBuffer extends TemplateBuffer {
}
public void render() {
if (vao == 0 || removed) return;
if (vertexCount == 0 || removed) return;
GL30.glBindVertexArray(vao);
vao.bind();
preDrawTask();
int numAttributes = getTotalShaderAttributeCount();
for (int i = 0; i <= numAttributes; i++) {
GL40.glEnableVertexAttribArray(i);
}
GlStateManager.bindBuffers(GL15.GL_ELEMENT_ARRAY_BUFFER, ebo);
ebo.bind(GL15.GL_ELEMENT_ARRAY_BUFFER);
drawCall();
for (int i = 0; i <= numAttributes; i++) {
GL40.glDisableVertexAttribArray(i);
}
GlStateManager.bindBuffers(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
GL30.glBindVertexArray(0);
ebo.unbind(GL15.GL_ELEMENT_ARRAY_BUFFER);
vao.unbind();
}
public void delete() {
@ -99,11 +95,8 @@ public abstract class GPUBuffer extends TemplateBuffer {
}
protected void deleteInternal() {
GL15.glDeleteBuffers(invariantVBO);
GL15.glDeleteBuffers(ebo);
GL30.glDeleteVertexArrays(vao);
vao = 0;
ebo = 0;
invariantVBO = 0;
invariantVBO.delete();
ebo.delete();
vao.delete();
}
}

View file

@ -1,12 +1,6 @@
package com.simibubi.create.foundation.render;
public class RenderMath {
public static final float SQRT2 = 1.41421356237f;
public static int rotateSideLength(int i) {
return (int) Math.floor((float) i * SQRT2 / 4f);
}
public static int nextPowerOf2(int a) {
int h = Integer.highestOneBit(a);
return (h == a) ? h : (h << 1);

View file

@ -2,6 +2,7 @@ package com.simibubi.create.foundation.render;
import com.mojang.blaze3d.platform.GlStateManager;
import com.mojang.datafixers.util.Pair;
import com.simibubi.create.foundation.render.gl.GlBuffer;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.vertex.VertexFormatElement;
import org.lwjgl.opengl.GL15;
@ -32,10 +33,13 @@ public class TemplateBuffer {
((Buffer)template).rewind();
}
protected void buildEBO(int ebo){
protected final GlBuffer createEBO(){
GlBuffer ebo = new GlBuffer();
int indicesSize = vertexCount * VertexFormatElement.Type.USHORT.getSize();
GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, ebo);
ebo.bind(GL15.GL_ELEMENT_ARRAY_BUFFER);
GL15.glBufferData(GL15.GL_ELEMENT_ARRAY_BUFFER, indicesSize, GL15.GL_STATIC_DRAW);
ByteBuffer indices = GL15.glMapBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, GL15.GL_WRITE_ONLY);
@ -46,6 +50,10 @@ public class TemplateBuffer {
indices.rewind();
GL15.glUnmapBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER);
ebo.unbind(GL15.GL_ELEMENT_ARRAY_BUFFER);
return ebo;
}
public boolean isEmpty() {

View file

@ -0,0 +1,21 @@
package com.simibubi.create.foundation.render.gl;
import org.lwjgl.opengl.GL20;
public class GlBuffer extends GlObject {
public GlBuffer() {
setHandle(GL20.glGenBuffers());
}
public void bind(int target) {
GL20.glBindBuffer(target, handle());
}
public void unbind(int target) {
GL20.glBindBuffer(target, 0);
}
protected void deleteInternal(int handle) {
GL20.glDeleteBuffers(handle);
}
}

View file

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

View file

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

View file

@ -0,0 +1,21 @@
package com.simibubi.create.foundation.render.gl;
import org.lwjgl.opengl.GL30;
public class GlVertexArray extends GlObject {
public GlVertexArray() {
setHandle(GL30.glGenVertexArrays());
}
public void bind() {
GL30.glBindVertexArray(handle());
}
public void unbind() {
GL30.glBindVertexArray(0);
}
protected void deleteInternal(int handle) {
GL30.glDeleteVertexArrays(handle);
}
}

View file

@ -1,11 +1,12 @@
package com.simibubi.create.foundation.render.instancing;
import com.simibubi.create.foundation.render.gl.GlBuffer;
import net.minecraft.client.renderer.BufferBuilder;
import org.lwjgl.opengl.*;
public abstract class DynamicInstanceBuffer<S extends InstanceData, D extends InstanceData> extends InstanceBuffer<S> {
protected int dynamicVBO;
protected GlBuffer dynamicVBO;
protected int dynamicBufferSize = -1;
@ -16,7 +17,7 @@ public abstract class DynamicInstanceBuffer<S extends InstanceData, D extends In
@Override
protected void setup() {
super.setup();
dynamicVBO = GL20.glGenBuffers();
dynamicVBO = new GlBuffer();
}
protected abstract VertexFormat getDynamicFormat();
@ -31,7 +32,11 @@ public abstract class DynamicInstanceBuffer<S extends InstanceData, D extends In
@Override
protected void preDrawTask() {
super.preDrawTask();
}
@Override
protected void deleteInternal() {
super.deleteInternal();
dynamicVBO.delete();
}
}

View file

@ -6,6 +6,7 @@ import com.simibubi.create.foundation.render.GPUBuffer;
import com.simibubi.create.foundation.render.RenderMath;
import com.simibubi.create.foundation.render.RenderWork;
import com.simibubi.create.foundation.render.TemplateBuffer;
import com.simibubi.create.foundation.render.gl.GlBuffer;
import net.minecraft.client.renderer.BufferBuilder;
import org.lwjgl.opengl.*;
@ -18,7 +19,7 @@ import static com.simibubi.create.foundation.render.instancing.VertexAttribute.*
public abstract class InstanceBuffer<D extends InstanceData> extends GPUBuffer {
public static final VertexFormat FORMAT = new VertexFormat(POSITION, NORMAL, UV);
protected int instanceVBO;
protected GlBuffer instanceVBO;
protected int instanceCount;
protected int instanceBufferSize = -1;
@ -34,7 +35,7 @@ public abstract class InstanceBuffer<D extends InstanceData> extends GPUBuffer {
@Override
protected void setup() {
super.setup();
instanceVBO = GlStateManager.genBuffers();
instanceVBO = new GlBuffer();
}
@Override
@ -76,15 +77,8 @@ public abstract class InstanceBuffer<D extends InstanceData> extends GPUBuffer {
}
protected void deleteInternal() {
GL15.glDeleteBuffers(invariantVBO);
GL15.glDeleteBuffers(instanceVBO);
GL15.glDeleteBuffers(ebo);
GL30.glDeleteVertexArrays(vao);
vao = 0;
ebo = 0;
invariantVBO = 0;
instanceVBO = 0;
instanceBufferSize = -1;
super.deleteInternal();
instanceVBO.delete();
}
protected abstract D newInstance();
@ -116,7 +110,7 @@ public abstract class InstanceBuffer<D extends InstanceData> extends GPUBuffer {
int instanceSize = RenderMath.nextPowerOf2(instanceCount * instanceFormat.getStride());
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, instanceVBO);
instanceVBO.bind(GL15.GL_ARRAY_BUFFER);
// this changes enough that it's not worth reallocating the entire buffer every time.
if (instanceSize > instanceBufferSize) {
@ -137,8 +131,7 @@ public abstract class InstanceBuffer<D extends InstanceData> extends GPUBuffer {
GL33.glVertexAttribDivisor(i + staticAttributes, 1);
}
// Deselect (bind to 0) the VBO
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
instanceVBO.unbind(GL15.GL_ARRAY_BUFFER);
shouldBuild = false;
rebuffer = false;

View file

@ -9,7 +9,6 @@ import net.minecraft.util.math.Vec3d;
import net.minecraft.util.math.Vec3i;
import static com.simibubi.create.foundation.render.RenderMath.isPowerOf2;
import static com.simibubi.create.foundation.render.RenderMath.rotateSideLength;
public class GridAlignedBB {
public int minX;
@ -28,6 +27,10 @@ public class GridAlignedBB {
this.maxZ = maxZ;
}
public static GridAlignedBB ofRadius(int radius) {
return new GridAlignedBB(-radius, -radius, -radius, radius + 1, radius + 1, radius + 1);
}
public static GridAlignedBB copy(GridAlignedBB bb) {
return new GridAlignedBB(bb.minX, bb.minY, bb.minZ, bb.maxX, bb.maxY, bb.maxZ);
}
@ -104,16 +107,6 @@ public class GridAlignedBB {
maxZ += z;
}
public void rotate45(Direction.Axis axis) {
if (axis == Direction.Axis.X) {
this.grow(0, rotateSideLength(sizeY()), rotateSideLength(sizeZ()));
} else if (axis == Direction.Axis.Y) {
this.grow(rotateSideLength(sizeX()), 0, rotateSideLength(sizeZ()));
} else if (axis == Direction.Axis.Z) {
this.grow(rotateSideLength(sizeX()), rotateSideLength(sizeY()), 0);
}
}
public void mirrorAbout(Direction.Axis axis) {
Vec3i axisVec = Direction.getFacingFromAxis(Direction.AxisDirection.POSITIVE, axis).getDirectionVec();
int flipX = axisVec.getX() - 1;
@ -131,38 +124,6 @@ public class GridAlignedBB {
this.minZ = maxZ;
}
public void expandAroundAxis(Direction.Axis axis) {
int maxXDiff = Math.max(this.maxX - 1, -this.minX);
int maxYDiff = Math.max(this.maxY - 1, -this.minY);
int maxZDiff = Math.max(this.maxZ - 1, -this.minZ);
int maxDiff;
if (axis == Direction.Axis.X)
maxDiff = Math.max(maxZDiff, maxYDiff);
else if (axis == Direction.Axis.Y)
maxDiff = Math.max(maxZDiff, maxXDiff);
else if (axis == Direction.Axis.Z)
maxDiff = Math.max(maxXDiff, maxYDiff);
else
maxDiff = 0;
Vec3i axisVec = Direction.getFacingFromAxis(Direction.AxisDirection.POSITIVE, axis).getDirectionVec();
int axisX = axisVec.getX();
int axisY = axisVec.getY();
int axisZ = axisVec.getZ();
int planeX = 1 - axisX;
int planeY = 1 - axisY;
int planeZ = 1 - axisZ;
minX = axisX * minX - maxDiff * planeX;
minY = axisY * minY - maxDiff * planeY;
minZ = axisZ * minZ - maxDiff * planeZ;
maxX = axisX * maxX + (maxDiff + 1) * planeX;
maxY = axisY * maxY + (maxDiff + 1) * planeY;
maxZ = axisZ * maxZ + (maxDiff + 1) * planeZ;
}
/**
* Grow this bounding box to have power of 2 side length, scaling from the center.
*/
@ -269,6 +230,19 @@ public class GridAlignedBB {
return this.intersects(other.minX, other.minY, other.minZ, other.maxX, other.maxY, other.maxZ);
}
public boolean contains(GridAlignedBB other) {
return other.minX >= this.minX &&
other.maxX <= this.maxX &&
other.minY >= this.minY &&
other.maxY <= this.maxY &&
other.minZ >= this.minZ &&
other.maxZ <= this.maxZ;
}
public boolean isContainedBy(GridAlignedBB other) {
return other.contains(this);
}
public boolean intersects(int minX, int minY, int minZ, int maxX, int maxY, int maxZ) {
return this.minX < maxX && this.maxX > minX && this.minY < maxY && this.maxY > minY && this.minZ < maxZ && this.maxZ > minZ;
}
@ -292,12 +266,7 @@ public class GridAlignedBB {
GridAlignedBB that = (GridAlignedBB) o;
if (minX != that.minX) return false;
if (minY != that.minY) return false;
if (minZ != that.minZ) return false;
if (maxX != that.maxX) return false;
if (maxY != that.maxY) return false;
return maxZ == that.maxZ;
return this.sameAs(that);
}
@Override

View file

@ -1,6 +1,7 @@
package com.simibubi.create.foundation.render.light;
import com.simibubi.create.foundation.render.RenderWork;
import com.simibubi.create.foundation.render.gl.GlTexture;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.SectionPos;
import net.minecraft.world.ILightReader;
@ -24,12 +25,12 @@ public class LightVolume {
private boolean bufferDirty;
private boolean removed;
private int glTexture;
private final GlTexture glTexture;
public LightVolume(GridAlignedBB sampleVolume) {
setSampleVolume(sampleVolume);
this.glTexture = GL11.glGenTextures();
this.glTexture = new GlTexture(GL20.GL_TEXTURE_3D);
this.lightData = MemoryUtil.memAlloc(this.textureVolume.volume() * 2); // TODO: maybe figure out how to pack light coords into a single byte
}
@ -84,32 +85,48 @@ public class LightVolume {
}
public void move(ILightReader world, GridAlignedBB newSampleVolume) {
setSampleVolume(newSampleVolume);
initialize(world);
if (textureVolume.contains(newSampleVolume)) {
if (newSampleVolume.intersects(sampleVolume)) {
GridAlignedBB newArea = newSampleVolume.intersect(sampleVolume);
sampleVolume = newSampleVolume;
copyLight(world, newArea);
} else {
sampleVolume = newSampleVolume;
initialize(world);
}
} else {
setSampleVolume(newSampleVolume);
int volume = textureVolume.volume();
if (volume * 2 > lightData.capacity()) {
lightData = MemoryUtil.memRealloc(lightData, volume * 2);
}
initialize(world);
}
}
public void notifyLightUpdate(ILightReader world, LightType type, SectionPos location) {
GridAlignedBB changedVolume = GridAlignedBB.fromSection(location);
if (!changedVolume.intersects(sampleVolume))
return;
changedVolume.intersectAssign(sampleVolume); // compute the region contained by us that has dirty lighting data.
if (!changedVolume.empty()) {
if (type == LightType.BLOCK) copyBlock(world, changedVolume);
else if (type == LightType.SKY) copySky(world, changedVolume);
}
if (type == LightType.BLOCK) copyBlock(world, changedVolume);
else if (type == LightType.SKY) copySky(world, changedVolume);
}
/**
* Completely (re)populate this volume with block and sky lighting data.
* This is expensive and should be avoided.
*/
public synchronized void initialize(ILightReader world) {
public void initialize(ILightReader world) {
BlockPos.Mutable pos = new BlockPos.Mutable();
int shiftX = textureVolume.minX;
int shiftY = textureVolume.minY;
int shiftZ = textureVolume.minZ;
textureVolume.forEachContained((x, y, z) -> {
sampleVolume.forEachContained((x, y, z) -> {
pos.setPos(x, y, z);
int blockLight = world.getLightLevel(LightType.BLOCK, pos);
@ -125,7 +142,7 @@ public class LightVolume {
* Copy block light from the world into this volume.
* @param worldVolume the region in the world to copy data from.
*/
public synchronized void copyBlock(ILightReader world, GridAlignedBB worldVolume) {
public void copyBlock(ILightReader world, GridAlignedBB worldVolume) {
BlockPos.Mutable pos = new BlockPos.Mutable();
int xShift = textureVolume.minX;
@ -147,7 +164,7 @@ public class LightVolume {
* Copy sky light from the world into this volume.
* @param worldVolume the region in the world to copy data from.
*/
public synchronized void copySky(ILightReader world, GridAlignedBB worldVolume) {
public void copySky(ILightReader world, GridAlignedBB worldVolume) {
BlockPos.Mutable pos = new BlockPos.Mutable();
int xShift = textureVolume.minX;
@ -165,37 +182,66 @@ public class LightVolume {
bufferDirty = true;
}
/**
* Copy all light from the world into this volume.
* @param worldVolume the region in the world to copy data from.
*/
public void copyLight(ILightReader world, GridAlignedBB worldVolume) {
BlockPos.Mutable pos = new BlockPos.Mutable();
int xShift = textureVolume.minX;
int yShift = textureVolume.minY;
int zShift = textureVolume.minZ;
worldVolume.forEachContained((x, y, z) -> {
pos.setPos(x, y, z);
int block = world.getLightLevel(LightType.BLOCK, pos);
int sky = world.getLightLevel(LightType.SKY, pos);
writeLight(x - xShift, y - yShift, z - zShift, block, sky);
});
bufferDirty = true;
}
public void use() {
// just in case something goes wrong or we accidentally call this before this volume is properly disposed of.
if (glTexture == 0 || lightData == null || removed) return;
if (lightData == null || removed) return;
GL13.glActiveTexture(GL40.GL_TEXTURE4);
GL12.glBindTexture(GL12.GL_TEXTURE_3D, glTexture);
glTexture.bind();
GL11.glTexParameteri(GL13.GL_TEXTURE_3D, GL13.GL_TEXTURE_MIN_FILTER, GL13.GL_LINEAR);
GL11.glTexParameteri(GL13.GL_TEXTURE_3D, GL13.GL_TEXTURE_MAG_FILTER, GL13.GL_LINEAR);
GL11.glTexParameteri(GL13.GL_TEXTURE_3D, GL13.GL_TEXTURE_WRAP_S, GL20.GL_MIRRORED_REPEAT);
GL11.glTexParameteri(GL13.GL_TEXTURE_3D, GL13.GL_TEXTURE_WRAP_R, GL20.GL_MIRRORED_REPEAT);
GL11.glTexParameteri(GL13.GL_TEXTURE_3D, GL13.GL_TEXTURE_WRAP_T, GL20.GL_MIRRORED_REPEAT);
uploadTexture();
}
private void uploadTexture() {
if (bufferDirty) {
uploadTexture();
int sizeX = textureVolume.sizeX();
int sizeY = textureVolume.sizeY();
int sizeZ = textureVolume.sizeZ();
if (sizeX * sizeY * sizeZ * 2 > lightData.capacity())
throw new IllegalStateException("Volume too big for buffer");
lightData.rewind();
GL12.glTexImage3D(GL12.GL_TEXTURE_3D, 0, GL40.GL_RG8, sizeX, sizeY, sizeZ, 0, GL40.GL_RG, GL40.GL_UNSIGNED_BYTE, lightData);
bufferDirty = false;
}
}
private synchronized void uploadTexture() {
lightData.rewind();
GL12.glTexImage3D(GL12.GL_TEXTURE_3D, 0, GL40.GL_RG8, textureVolume.sizeX(), textureVolume.sizeY(), textureVolume.sizeZ(), 0, GL40.GL_RG, GL40.GL_UNSIGNED_BYTE, lightData);
bufferDirty = false;
}
public void release() {
GL12.glBindTexture(GL12.GL_TEXTURE_3D, 0);
glTexture.unbind();
}
public void delete() {
removed = true;
RenderWork.enqueue(() -> {
GL15.glDeleteTextures(glTexture);
glTexture = 0;
glTexture.delete();
MemoryUtil.memFree(lightData);
lightData = null;
});