mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2024-12-27 15:36:24 +01:00
Separate LightVolume and GPULightVolume
- LightVolumes now can act as a light cache with configurable size - More GridAlignedBB changes - Remove ILightUpdateListeners - Simplify pulley rendering using LightVolume
This commit is contained in:
parent
1f7af0d8b2
commit
7e65eaa00d
11 changed files with 568 additions and 315 deletions
|
@ -6,11 +6,10 @@ import java.util.stream.Stream;
|
|||
import com.jozufozu.flywheel.backend.instancing.tile.TileInstanceManager;
|
||||
import com.jozufozu.flywheel.backend.material.MaterialManager;
|
||||
import com.jozufozu.flywheel.core.materials.IFlatLight;
|
||||
import com.jozufozu.flywheel.light.GridAlignedBB;
|
||||
import com.jozufozu.flywheel.light.ILightUpdateListener;
|
||||
import com.jozufozu.flywheel.light.LightProvider;
|
||||
import com.jozufozu.flywheel.light.ListenerStatus;
|
||||
import com.jozufozu.flywheel.light.ReadOnlyBox;
|
||||
import com.jozufozu.flywheel.light.ImmutableBox;
|
||||
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.LightType;
|
||||
|
@ -74,7 +73,7 @@ public abstract class AbstractInstance implements IInstance, ILightUpdateListene
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onLightUpdate(LightProvider world, LightType type, ReadOnlyBox changed) {
|
||||
public void onLightUpdate(LightProvider world, LightType type, ImmutableBox changed) {
|
||||
updateLight();
|
||||
}
|
||||
|
||||
|
|
|
@ -278,6 +278,8 @@ public abstract class InstanceManager<T> implements MaterialManagerImpl.OriginSh
|
|||
instances.remove(obj);
|
||||
dynamicInstances.remove(obj);
|
||||
tickableInstances.remove(obj);
|
||||
LightUpdater.get(instance.world)
|
||||
.removeListener(instance);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
@ -286,7 +288,8 @@ public abstract class InstanceManager<T> implements MaterialManagerImpl.OriginSh
|
|||
|
||||
if (renderer != null) {
|
||||
renderer.updateLight();
|
||||
LightUpdater.get(renderer.world).addListener(renderer);
|
||||
LightUpdater.get(renderer.world)
|
||||
.addListener(renderer);
|
||||
instances.put(obj, renderer);
|
||||
|
||||
if (renderer instanceof IDynamicInstance) dynamicInstances.put(obj, (IDynamicInstance) renderer);
|
||||
|
|
|
@ -9,6 +9,7 @@ import com.jozufozu.flywheel.core.Materials;
|
|||
import com.jozufozu.flywheel.core.materials.ModelData;
|
||||
import com.jozufozu.flywheel.core.materials.OrientedData;
|
||||
import com.jozufozu.flywheel.light.GridAlignedBB;
|
||||
import com.jozufozu.flywheel.light.ImmutableBox;
|
||||
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
|
@ -84,7 +85,7 @@ public abstract class TileEntityInstance<T extends TileEntity> extends AbstractI
|
|||
}
|
||||
|
||||
@Override
|
||||
public GridAlignedBB getVolume() {
|
||||
public ImmutableBox getVolume() {
|
||||
return GridAlignedBB.from(pos);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@ public class ForgeEvents {
|
|||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void rwle(TickEvent.ClientTickEvent e) {
|
||||
public static void tickLight(TickEvent.ClientTickEvent e) {
|
||||
if (e.phase == TickEvent.Phase.END && Backend.isGameActive())
|
||||
LightUpdater.get(Minecraft.getInstance().level).tick();
|
||||
}
|
||||
|
|
160
src/main/java/com/jozufozu/flywheel/light/GPULightVolume.java
Normal file
160
src/main/java/com/jozufozu/flywheel/light/GPULightVolume.java
Normal file
|
@ -0,0 +1,160 @@
|
|||
package com.jozufozu.flywheel.light;
|
||||
|
||||
import static org.lwjgl.opengl.GL20.GL_LINEAR;
|
||||
import static org.lwjgl.opengl.GL20.GL_MIRRORED_REPEAT;
|
||||
import static org.lwjgl.opengl.GL20.GL_TEXTURE0;
|
||||
import static org.lwjgl.opengl.GL20.GL_TEXTURE4;
|
||||
import static org.lwjgl.opengl.GL20.GL_TEXTURE_3D;
|
||||
import static org.lwjgl.opengl.GL20.GL_TEXTURE_MAG_FILTER;
|
||||
import static org.lwjgl.opengl.GL20.GL_TEXTURE_MIN_FILTER;
|
||||
import static org.lwjgl.opengl.GL20.GL_TEXTURE_WRAP_R;
|
||||
import static org.lwjgl.opengl.GL20.GL_TEXTURE_WRAP_S;
|
||||
import static org.lwjgl.opengl.GL20.GL_TEXTURE_WRAP_T;
|
||||
import static org.lwjgl.opengl.GL20.GL_UNPACK_ALIGNMENT;
|
||||
import static org.lwjgl.opengl.GL20.GL_UNPACK_IMAGE_HEIGHT;
|
||||
import static org.lwjgl.opengl.GL20.GL_UNPACK_ROW_LENGTH;
|
||||
import static org.lwjgl.opengl.GL20.GL_UNPACK_SKIP_IMAGES;
|
||||
import static org.lwjgl.opengl.GL20.GL_UNPACK_SKIP_PIXELS;
|
||||
import static org.lwjgl.opengl.GL20.GL_UNPACK_SKIP_ROWS;
|
||||
import static org.lwjgl.opengl.GL20.GL_UNSIGNED_BYTE;
|
||||
import static org.lwjgl.opengl.GL20.glActiveTexture;
|
||||
import static org.lwjgl.opengl.GL20.glPixelStorei;
|
||||
import static org.lwjgl.opengl.GL20.glTexImage3D;
|
||||
import static org.lwjgl.opengl.GL20.glTexParameteri;
|
||||
import static org.lwjgl.opengl.GL20.glTexSubImage3D;
|
||||
|
||||
|
||||
import com.jozufozu.flywheel.backend.Backend;
|
||||
import com.jozufozu.flywheel.backend.gl.GlTexture;
|
||||
import com.jozufozu.flywheel.backend.gl.GlTextureUnit;
|
||||
import com.jozufozu.flywheel.backend.gl.versioned.RGPixelFormat;
|
||||
|
||||
import net.minecraft.world.LightType;
|
||||
|
||||
public class GPULightVolume extends LightVolume {
|
||||
|
||||
protected final GridAlignedBB sampleVolume = new GridAlignedBB();
|
||||
private final GlTexture glTexture;
|
||||
|
||||
private final RGPixelFormat pixelFormat;
|
||||
protected boolean bufferDirty;
|
||||
|
||||
public GPULightVolume(ImmutableBox sampleVolume) {
|
||||
super(sampleVolume);
|
||||
this.sampleVolume.assign(sampleVolume);
|
||||
|
||||
pixelFormat = Backend.getInstance().compat.pixelFormat;
|
||||
glTexture = new GlTexture(GL_TEXTURE_3D);
|
||||
|
||||
// allocate space for the texture
|
||||
glActiveTexture(GL_TEXTURE4);
|
||||
glTexture.bind();
|
||||
|
||||
int sizeX = box.sizeX();
|
||||
int sizeY = box.sizeY();
|
||||
int sizeZ = box.sizeZ();
|
||||
glTexImage3D(GL_TEXTURE_3D, 0, pixelFormat.internalFormat(), sizeX, sizeY, sizeZ, 0, pixelFormat.format(), GL_UNSIGNED_BYTE, 0);
|
||||
|
||||
glTexture.unbind();
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setBox(ImmutableBox box) {
|
||||
this.box.assign(box);
|
||||
this.box.nextPowerOf2Centered();
|
||||
// called during super ctor
|
||||
if (sampleVolume != null) this.sampleVolume.assign(box);
|
||||
}
|
||||
|
||||
public void bind() {
|
||||
// just in case something goes wrong, or we accidentally call this before this volume is properly disposed of.
|
||||
if (lightData == null) return;
|
||||
|
||||
GlTextureUnit.T4.makeActive();
|
||||
glTexture.bind();
|
||||
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_MIRRORED_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
|
||||
|
||||
uploadTexture();
|
||||
}
|
||||
|
||||
private void uploadTexture() {
|
||||
if (bufferDirty) {
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||
glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
|
||||
glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
|
||||
glPixelStorei(GL_UNPACK_SKIP_IMAGES, 0);
|
||||
glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, 0);
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 2);
|
||||
int sizeX = box.sizeX();
|
||||
int sizeY = box.sizeY();
|
||||
int sizeZ = box.sizeZ();
|
||||
|
||||
glTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, 0, sizeX, sizeY, sizeZ, pixelFormat.format(), GL_UNSIGNED_BYTE, lightData);
|
||||
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 4); // 4 is the default
|
||||
bufferDirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void unbind() {
|
||||
glTexture.unbind();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete() {
|
||||
super.delete();
|
||||
glTexture.delete();
|
||||
}
|
||||
|
||||
public void move(LightProvider world, ImmutableBox newSampleVolume) {
|
||||
if (lightData == null) return;
|
||||
|
||||
if (box.contains(newSampleVolume)) {
|
||||
if (newSampleVolume.intersects(sampleVolume)) {
|
||||
GridAlignedBB newArea = newSampleVolume.intersect(sampleVolume);
|
||||
sampleVolume.assign(newSampleVolume);
|
||||
|
||||
copyLight(world, newArea);
|
||||
} else {
|
||||
sampleVolume.assign(newSampleVolume);
|
||||
initialize(world);
|
||||
}
|
||||
} else {
|
||||
super.move(world, newSampleVolume);
|
||||
}
|
||||
}
|
||||
|
||||
public void onLightUpdate(LightProvider world, LightType type, ImmutableBox changedVolume) {
|
||||
super.onLightUpdate(world, type, changedVolume);
|
||||
bufferDirty = true;
|
||||
}
|
||||
|
||||
public void onLightPacket(LightProvider world, int chunkX, int chunkZ) {
|
||||
super.onLightPacket(world, chunkX, chunkZ);
|
||||
bufferDirty = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Completely (re)populate this volume with block and sky lighting data.
|
||||
* This is expensive and should be avoided.
|
||||
*/
|
||||
public void initialize(LightProvider world) {
|
||||
super.initialize(world);
|
||||
bufferDirty = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getStride() {
|
||||
return Backend.getInstance().compat.pixelFormat.byteCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableBox getVolume() {
|
||||
return sampleVolume;
|
||||
}
|
||||
}
|
|
@ -8,7 +8,7 @@ import net.minecraft.util.math.BlockPos;
|
|||
import net.minecraft.util.math.SectionPos;
|
||||
import net.minecraft.util.math.vector.Vector3i;
|
||||
|
||||
public class GridAlignedBB implements ReadOnlyBox {
|
||||
public class GridAlignedBB implements ImmutableBox {
|
||||
private int minX;
|
||||
private int minY;
|
||||
private int minZ;
|
||||
|
@ -16,6 +16,10 @@ public class GridAlignedBB implements ReadOnlyBox {
|
|||
private int maxY;
|
||||
private int maxZ;
|
||||
|
||||
public GridAlignedBB() {
|
||||
|
||||
}
|
||||
|
||||
public GridAlignedBB(int minX, int minY, int minZ, int maxX, int maxY, int maxZ) {
|
||||
this.minX = minX;
|
||||
this.minY = minY;
|
||||
|
@ -58,19 +62,19 @@ public class GridAlignedBB implements ReadOnlyBox {
|
|||
}
|
||||
|
||||
public void fixMinMax() {
|
||||
int minX = Math.min(this.getMinX(), this.getMaxX());
|
||||
int minY = Math.min(this.getMinY(), this.getMaxY());
|
||||
int minZ = Math.min(this.getMinZ(), this.getMaxZ());
|
||||
int maxX = Math.max(this.getMinX(), this.getMaxX());
|
||||
int maxY = Math.max(this.getMinY(), this.getMaxY());
|
||||
int maxZ = Math.max(this.getMinZ(), this.getMaxZ());
|
||||
int minX = Math.min(this.minX, this.maxX);
|
||||
int minY = Math.min(this.minY, this.maxY);
|
||||
int minZ = Math.min(this.minZ, this.maxZ);
|
||||
int maxX = Math.max(this.minX, this.maxX);
|
||||
int maxY = Math.max(this.minY, this.maxY);
|
||||
int maxZ = Math.max(this.minZ, this.maxZ);
|
||||
|
||||
this.setMinX(minX);
|
||||
this.setMinY(minY);
|
||||
this.setMinZ(minZ);
|
||||
this.setMaxX(maxX);
|
||||
this.setMaxY(maxY);
|
||||
this.setMaxZ(maxZ);
|
||||
this.minX = minX;
|
||||
this.minY = minY;
|
||||
this.minZ = minZ;
|
||||
this.maxX = maxX;
|
||||
this.maxY = maxY;
|
||||
this.maxZ = maxZ;
|
||||
}
|
||||
|
||||
public void translate(Vector3i by) {
|
||||
|
@ -78,12 +82,12 @@ public class GridAlignedBB implements ReadOnlyBox {
|
|||
}
|
||||
|
||||
public void translate(int x, int y, int z) {
|
||||
setMinX(getMinX() + x);
|
||||
setMaxX(getMaxX() + x);
|
||||
setMinY(getMinY() + y);
|
||||
setMaxY(getMaxY() + y);
|
||||
setMinZ(getMinZ() + z);
|
||||
setMaxZ(getMaxZ() + z);
|
||||
minX = minX + x;
|
||||
maxX = maxX + x;
|
||||
minY = minY + y;
|
||||
maxY = maxY + y;
|
||||
minZ = minZ + z;
|
||||
maxZ = maxZ + z;
|
||||
}
|
||||
|
||||
public void mirrorAbout(Direction.Axis axis) {
|
||||
|
@ -93,15 +97,15 @@ public class GridAlignedBB implements ReadOnlyBox {
|
|||
int flipY = axisVec.getY() - 1;
|
||||
int flipZ = axisVec.getZ() - 1;
|
||||
|
||||
int maxX = this.getMaxX() * flipX;
|
||||
int maxY = this.getMaxY() * flipY;
|
||||
int maxZ = this.getMaxZ() * flipZ;
|
||||
this.setMaxX(this.getMinX() * flipX);
|
||||
this.setMaxY(this.getMinY() * flipY);
|
||||
this.setMaxZ(this.getMinZ() * flipZ);
|
||||
this.setMinX(maxX);
|
||||
this.setMinY(maxY);
|
||||
this.setMinZ(maxZ);
|
||||
int maxX = this.maxX * flipX;
|
||||
int maxY = this.maxY * flipY;
|
||||
int maxZ = this.maxZ * flipZ;
|
||||
this.maxX = this.minX * flipX;
|
||||
this.maxY = this.minY * flipY;
|
||||
this.maxZ = this.minZ * flipZ;
|
||||
this.minX = maxX;
|
||||
this.minY = maxY;
|
||||
this.minZ = maxZ;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -120,12 +124,12 @@ public class GridAlignedBB implements ReadOnlyBox {
|
|||
int diffY = newSizeY - sizeY;
|
||||
int diffZ = newSizeZ - sizeZ;
|
||||
|
||||
setMinX(getMinX() - diffX / 2); // floor division for the minimums
|
||||
setMinY(getMinY() - diffY / 2);
|
||||
setMinZ(getMinZ() - diffZ / 2);
|
||||
setMaxX(getMaxX() + (diffX + 1) / 2); // ceiling divison for the maximums
|
||||
setMaxY(getMaxY() + (diffY + 1) / 2);
|
||||
setMaxZ(getMaxZ() + (diffZ + 1) / 2);
|
||||
minX = minX - diffX / 2; // floor division for the minimums
|
||||
minY = minY - diffY / 2;
|
||||
minZ = minZ - diffZ / 2;
|
||||
maxX = maxX + (diffX + 1) / 2; // ceiling divison for the maximums
|
||||
maxY = maxY + (diffY + 1) / 2;
|
||||
maxZ = maxZ + (diffZ + 1) / 2;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -136,9 +140,9 @@ public class GridAlignedBB implements ReadOnlyBox {
|
|||
int sizeY = RenderUtil.nextPowerOf2(sizeY());
|
||||
int sizeZ = RenderUtil.nextPowerOf2(sizeZ());
|
||||
|
||||
this.setMaxX(this.getMinX() + sizeX);
|
||||
this.setMaxY(this.getMinY() + sizeY);
|
||||
this.setMaxZ(this.getMinZ() + sizeZ);
|
||||
maxX = minX + sizeX;
|
||||
maxY = minY + sizeY;
|
||||
maxZ = minZ + sizeZ;
|
||||
}
|
||||
|
||||
public void grow(int s) {
|
||||
|
@ -146,57 +150,57 @@ public class GridAlignedBB implements ReadOnlyBox {
|
|||
}
|
||||
|
||||
public void grow(int x, int y, int z) {
|
||||
setMinX(getMinX() - x);
|
||||
setMinY(getMinY() - y);
|
||||
setMinZ(getMinZ() - z);
|
||||
setMaxX(getMaxX() + x);
|
||||
setMaxY(getMaxY() + y);
|
||||
setMaxZ(getMaxZ() + z);
|
||||
minX = minX - x;
|
||||
minY = minY - y;
|
||||
minZ = minZ - z;
|
||||
maxX = maxX + x;
|
||||
maxY = maxY + y;
|
||||
maxZ = maxZ + z;
|
||||
}
|
||||
|
||||
public void intersectAssign(ReadOnlyBox other) {
|
||||
this.setMinX(Math.max(this.getMinX(), other.getMinX()));
|
||||
this.setMinY(Math.max(this.getMinY(), other.getMinY()));
|
||||
this.setMinZ(Math.max(this.getMinZ(), other.getMinZ()));
|
||||
this.setMaxX(Math.min(this.getMaxX(), other.getMaxX()));
|
||||
this.setMaxY(Math.min(this.getMaxY(), other.getMaxY()));
|
||||
this.setMaxZ(Math.min(this.getMaxZ(), other.getMaxZ()));
|
||||
public void intersectAssign(ImmutableBox other) {
|
||||
minX = Math.max(this.minX, other.getMinX());
|
||||
minY = Math.max(this.minY, other.getMinY());
|
||||
minZ = Math.max(this.minZ, other.getMinZ());
|
||||
maxX = Math.min(this.maxX, other.getMaxX());
|
||||
maxY = Math.min(this.maxY, other.getMaxY());
|
||||
maxZ = Math.min(this.maxZ, other.getMaxZ());
|
||||
}
|
||||
|
||||
public void unionAssign(ReadOnlyBox other) {
|
||||
this.setMinX(Math.min(this.getMinX(), other.getMinX()));
|
||||
this.setMinY(Math.min(this.getMinY(), other.getMinY()));
|
||||
this.setMinZ(Math.min(this.getMinZ(), other.getMinZ()));
|
||||
this.setMaxX(Math.max(this.getMaxX(), other.getMaxX()));
|
||||
this.setMaxY(Math.max(this.getMaxY(), other.getMaxY()));
|
||||
this.setMaxZ(Math.max(this.getMaxZ(), other.getMaxZ()));
|
||||
public void unionAssign(ImmutableBox other) {
|
||||
minX = Math.min(this.minX, other.getMinX());
|
||||
minY = Math.min(this.minY, other.getMinY());
|
||||
minZ = Math.min(this.minZ, other.getMinZ());
|
||||
maxX = Math.max(this.maxX, other.getMaxX());
|
||||
maxY = Math.max(this.maxY, other.getMaxY());
|
||||
maxZ = Math.max(this.maxZ, other.getMaxZ());
|
||||
}
|
||||
|
||||
public void unionAssign(AxisAlignedBB other) {
|
||||
this.setMinX(Math.min(this.getMinX(), (int) Math.floor(other.minX)));
|
||||
this.setMinY(Math.min(this.getMinY(), (int) Math.floor(other.minY)));
|
||||
this.setMinZ(Math.min(this.getMinZ(), (int) Math.floor(other.minZ)));
|
||||
this.setMaxX(Math.max(this.getMaxX(), (int) Math.ceil(other.maxX)));
|
||||
this.setMaxY(Math.max(this.getMaxY(), (int) Math.ceil(other.maxY)));
|
||||
this.setMaxZ(Math.max(this.getMaxZ(), (int) Math.ceil(other.maxZ)));
|
||||
minX = Math.min(this.minX, (int) Math.floor(other.minX));
|
||||
minY = Math.min(this.minY, (int) Math.floor(other.minY));
|
||||
minZ = Math.min(this.minZ, (int) Math.floor(other.minZ));
|
||||
maxX = Math.max(this.maxX, (int) Math.ceil(other.maxX));
|
||||
maxY = Math.max(this.maxY, (int) Math.ceil(other.maxY));
|
||||
maxZ = Math.max(this.maxZ, (int) Math.ceil(other.maxZ));
|
||||
}
|
||||
|
||||
public void assign(AxisAlignedBB other) {
|
||||
this.setMinX((int) Math.floor(other.minX));
|
||||
this.setMinY((int) Math.floor(other.minY));
|
||||
this.setMinZ((int) Math.floor(other.minZ));
|
||||
this.setMaxX((int) Math.ceil(other.maxX));
|
||||
this.setMaxY((int) Math.ceil(other.maxY));
|
||||
this.setMaxZ((int) Math.ceil(other.maxZ));
|
||||
minX = (int) Math.floor(other.minX);
|
||||
minY = (int) Math.floor(other.minY);
|
||||
minZ = (int) Math.floor(other.minZ);
|
||||
maxX = (int) Math.ceil(other.maxX);
|
||||
maxY = (int) Math.ceil(other.maxY);
|
||||
maxZ = (int) Math.ceil(other.maxZ);
|
||||
}
|
||||
|
||||
public void assign(ReadOnlyBox other) {
|
||||
this.setMinX(other.getMinX());
|
||||
this.setMinY(other.getMinY());
|
||||
this.setMinZ(other.getMinZ());
|
||||
this.setMaxX(other.getMaxX());
|
||||
this.setMaxY(other.getMaxY());
|
||||
this.setMaxZ(other.getMaxZ());
|
||||
public void assign(ImmutableBox other) {
|
||||
minX = other.getMinX();
|
||||
minY = other.getMinY();
|
||||
minZ = other.getMinZ();
|
||||
maxX = other.getMaxX();
|
||||
maxY = other.getMaxY();
|
||||
maxZ = other.getMaxZ();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -204,7 +208,7 @@ public class GridAlignedBB implements ReadOnlyBox {
|
|||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
ReadOnlyBox that = (ReadOnlyBox) o;
|
||||
ImmutableBox that = (ImmutableBox) o;
|
||||
|
||||
return this.sameAs(that);
|
||||
}
|
||||
|
@ -279,4 +283,132 @@ public class GridAlignedBB implements ReadOnlyBox {
|
|||
this.maxZ = maxZ;
|
||||
return this;
|
||||
}
|
||||
|
||||
public GridAlignedBB assign(BlockPos start, BlockPos end) {
|
||||
minX = start.getX();
|
||||
minY = start.getY();
|
||||
minZ = start.getZ();
|
||||
maxX = end.getX() + 1;
|
||||
maxY = end.getY() + 1;
|
||||
maxZ = end.getZ() + 1;
|
||||
return this;
|
||||
}
|
||||
|
||||
public GridAlignedBB setMax(Vector3i v) {
|
||||
return setMax(v.getX(), v.getY(), v.getZ());
|
||||
}
|
||||
|
||||
public GridAlignedBB setMin(Vector3i v) {
|
||||
return setMin(v.getX(), v.getY(), v.getZ());
|
||||
}
|
||||
|
||||
public GridAlignedBB setMax(int x, int y, int z) {
|
||||
maxX = x;
|
||||
maxY = y;
|
||||
maxZ = z;
|
||||
return this;
|
||||
}
|
||||
|
||||
public GridAlignedBB setMin(int x, int y, int z) {
|
||||
minX = x;
|
||||
minY = y;
|
||||
minZ = z;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int sizeX() {
|
||||
return maxX - minX;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int sizeY() {
|
||||
return maxY - minY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int sizeZ() {
|
||||
return maxZ - minZ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean empty() {
|
||||
// if any dimension has side length 0 this box contains no volume
|
||||
return minX == maxX || minY == maxY || minZ == maxZ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean sameAs(ImmutableBox other) {
|
||||
return minX == other.getMinX() && minY == other.getMinY() && minZ == other.getMinZ() && maxX == other.getMaxX() && maxY == other.getMaxY() && maxZ == other.getMaxZ();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean sameAs(AxisAlignedBB other) {
|
||||
return minX == Math.floor(other.minX)
|
||||
&& minY == Math.floor(other.minY)
|
||||
&& minZ == Math.floor(other.minZ)
|
||||
&& maxX == Math.ceil(other.maxX)
|
||||
&& maxY == Math.ceil(other.maxY)
|
||||
&& maxZ == Math.ceil(other.maxZ);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GridAlignedBB intersect(ImmutableBox other) {
|
||||
int minX = Math.max(this.minX, other.getMinX());
|
||||
int minY = Math.max(this.minY, other.getMinY());
|
||||
int minZ = Math.max(this.minZ, other.getMinZ());
|
||||
int maxX = Math.min(this.maxX, other.getMaxX());
|
||||
int maxY = Math.min(this.maxY, other.getMaxY());
|
||||
int maxZ = Math.min(this.maxZ, other.getMaxZ());
|
||||
return new GridAlignedBB(minX, minY, minZ, maxX, maxY, maxZ);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableBox union(ImmutableBox other) {
|
||||
int minX = Math.min(this.minX, other.getMinX());
|
||||
int minY = Math.min(this.minY, other.getMinY());
|
||||
int minZ = Math.min(this.minZ, other.getMinZ());
|
||||
int maxX = Math.max(this.maxX, other.getMaxX());
|
||||
int maxY = Math.max(this.maxY, other.getMaxY());
|
||||
int maxZ = Math.max(this.maxZ, other.getMaxZ());
|
||||
return new GridAlignedBB(minX, minY, minZ, maxX, maxY, maxZ);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(ImmutableBox other) {
|
||||
return other.getMinX() >= this.minX && other.getMaxX() <= this.maxX && other.getMinY() >= this.minY && other.getMaxY() <= this.maxY && other.getMinZ() >= this.minZ && other.getMaxZ() <= this.maxZ;
|
||||
}
|
||||
|
||||
@Override
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forEachContained(ICoordinateConsumer func) {
|
||||
if (empty()) return;
|
||||
|
||||
for (int x = minX; x < maxX; x++) {
|
||||
for (int y = Math.max(minY, 0); y < Math.min(maxY, 255); y++) { // clamp to world height limits
|
||||
for (int z = minZ; z < maxZ; z++) {
|
||||
func.consume(x, y, z);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AxisAlignedBB toAABB() {
|
||||
return new AxisAlignedBB(minX, minY, minZ, maxX, maxY, maxZ);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GridAlignedBB copy() {
|
||||
return new GridAlignedBB(minX, minY, minZ, maxX, maxY, maxZ);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "(" + minX + ", " + minY + ", " + minZ + ")->(" + maxX + ", " + maxY + ", " + maxZ + ')';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,14 +4,14 @@ import net.minecraft.world.LightType;
|
|||
|
||||
public interface ILightUpdateListener {
|
||||
|
||||
ReadOnlyBox getVolume();
|
||||
ImmutableBox getVolume();
|
||||
|
||||
ListenerStatus status();
|
||||
|
||||
/**
|
||||
* Called when a light updates in a chunk the implementor cares about.
|
||||
*/
|
||||
void onLightUpdate(LightProvider world, LightType type, ReadOnlyBox changed);
|
||||
void onLightUpdate(LightProvider world, LightType type, ImmutableBox changed);
|
||||
|
||||
/**
|
||||
* Called when the server sends light data to the client.
|
||||
|
|
|
@ -4,7 +4,7 @@ import static com.jozufozu.flywheel.util.RenderUtil.isPowerOf2;
|
|||
|
||||
import net.minecraft.util.math.AxisAlignedBB;
|
||||
|
||||
public interface ReadOnlyBox {
|
||||
public interface ImmutableBox {
|
||||
int getMinX();
|
||||
|
||||
int getMinY();
|
||||
|
@ -38,7 +38,7 @@ public interface ReadOnlyBox {
|
|||
return getMinX() == getMaxX() || getMinY() == getMaxY() || getMinZ() == getMaxZ();
|
||||
}
|
||||
|
||||
default boolean sameAs(ReadOnlyBox other) {
|
||||
default boolean sameAs(ImmutableBox other) {
|
||||
return getMinX() == other.getMinX() && getMinY() == other.getMinY() && getMinZ() == other.getMinZ() && getMaxX() == other.getMaxX() && getMaxY() == other.getMaxY() && getMaxZ() == other.getMaxZ();
|
||||
}
|
||||
|
||||
|
@ -56,7 +56,7 @@ public interface ReadOnlyBox {
|
|||
return isPowerOf2(volume());
|
||||
}
|
||||
|
||||
default GridAlignedBB intersect(ReadOnlyBox other) {
|
||||
default GridAlignedBB intersect(ImmutableBox other) {
|
||||
int minX = Math.max(this.getMinX(), other.getMinX());
|
||||
int minY = Math.max(this.getMinY(), other.getMinY());
|
||||
int minZ = Math.max(this.getMinZ(), other.getMinZ());
|
||||
|
@ -66,7 +66,7 @@ public interface ReadOnlyBox {
|
|||
return new GridAlignedBB(minX, minY, minZ, maxX, maxY, maxZ);
|
||||
}
|
||||
|
||||
default ReadOnlyBox union(ReadOnlyBox other) {
|
||||
default ImmutableBox union(ImmutableBox other) {
|
||||
int minX = Math.min(this.getMinX(), other.getMinX());
|
||||
int minY = Math.min(this.getMinY(), other.getMinY());
|
||||
int minZ = Math.min(this.getMinZ(), other.getMinZ());
|
||||
|
@ -77,12 +77,26 @@ public interface ReadOnlyBox {
|
|||
}
|
||||
|
||||
|
||||
default boolean intersects(ReadOnlyBox other) {
|
||||
default boolean intersects(ImmutableBox other) {
|
||||
return this.intersects(other.getMinX(), other.getMinY(), other.getMinZ(), other.getMaxX(), other.getMaxY(), other.getMaxZ());
|
||||
}
|
||||
|
||||
default boolean contains(ReadOnlyBox other) {
|
||||
return other.getMinX() >= this.getMinX() && other.getMaxX() <= this.getMaxX() && other.getMinY() >= this.getMinY() && other.getMaxY() <= this.getMaxY() && other.getMinZ() >= this.getMinZ() && other.getMaxZ() <= this.getMaxZ();
|
||||
default boolean contains(int x, int y, int z) {
|
||||
return x >= getMinX()
|
||||
&& x <= getMaxX()
|
||||
&& y >= getMinY()
|
||||
&& y <= getMaxY()
|
||||
&& z >= getMinZ()
|
||||
&& z <= getMaxZ();
|
||||
}
|
||||
|
||||
default boolean contains(ImmutableBox other) {
|
||||
return other.getMinX() >= this.getMinX()
|
||||
&& other.getMaxX() <= this.getMaxX()
|
||||
&& other.getMinY() >= this.getMinY()
|
||||
&& other.getMaxY() <= this.getMaxY()
|
||||
&& other.getMinZ() >= this.getMinZ()
|
||||
&& other.getMaxZ() <= this.getMaxZ();
|
||||
}
|
||||
|
||||
default boolean isContainedBy(GridAlignedBB other) {
|
|
@ -14,7 +14,7 @@ import net.minecraft.world.IBlockDisplayReader;
|
|||
import net.minecraft.world.LightType;
|
||||
|
||||
/**
|
||||
* Keeps track of what chunks/sections each listener is in so we can update exactly what needs to be updated.
|
||||
* Keeps track of what chunks/sections each listener is in, so we can update exactly what needs to be updated.
|
||||
*/
|
||||
public class LightUpdater {
|
||||
|
||||
|
@ -33,6 +33,10 @@ public class LightUpdater {
|
|||
provider = BasicProvider.get(world);
|
||||
}
|
||||
|
||||
public LightProvider getProvider() {
|
||||
return provider;
|
||||
}
|
||||
|
||||
public void tick() {
|
||||
for (IMovingListener listener : movingListeners) {
|
||||
if (listener.update(provider)) {
|
||||
|
@ -50,7 +54,7 @@ public class LightUpdater {
|
|||
if (listener instanceof IMovingListener)
|
||||
movingListeners.add(((IMovingListener) listener));
|
||||
|
||||
ReadOnlyBox box = listener.getVolume();
|
||||
ImmutableBox box = listener.getVolume();
|
||||
|
||||
LongSet sections = this.sections.getAndResetContainment(listener);
|
||||
LongSet chunks = this.chunks.getAndResetContainment(listener);
|
||||
|
@ -76,6 +80,11 @@ public class LightUpdater {
|
|||
}
|
||||
}
|
||||
|
||||
public void removeListener(ILightUpdateListener listener) {
|
||||
this.sections.remove(listener);
|
||||
this.chunks.remove(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch light updates to all registered {@link ILightUpdateListener}s.
|
||||
* @param type The type of light that changed.
|
||||
|
@ -88,7 +97,7 @@ public class LightUpdater {
|
|||
|
||||
set.removeIf(l -> l.status().shouldRemove());
|
||||
|
||||
ReadOnlyBox chunkBox = GridAlignedBB.from(SectionPos.of(sectionPos));
|
||||
ImmutableBox chunkBox = GridAlignedBB.from(SectionPos.of(sectionPos));
|
||||
|
||||
for (ILightUpdateListener listener : set) {
|
||||
listener.onLightUpdate(provider, type, chunkBox);
|
||||
|
@ -122,7 +131,7 @@ public class LightUpdater {
|
|||
return sectionPos & 0xFFFFFFFFFFF_00000L;
|
||||
}
|
||||
|
||||
public Stream<ReadOnlyBox> getAllBoxes() {
|
||||
public Stream<ImmutableBox> getAllBoxes() {
|
||||
return chunks.stream().map(ILightUpdateListener::getVolume);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,165 +1,89 @@
|
|||
package com.jozufozu.flywheel.light;
|
||||
|
||||
import static org.lwjgl.opengl.GL20.GL_LINEAR;
|
||||
import static org.lwjgl.opengl.GL20.GL_MIRRORED_REPEAT;
|
||||
import static org.lwjgl.opengl.GL20.GL_TEXTURE0;
|
||||
import static org.lwjgl.opengl.GL20.GL_TEXTURE4;
|
||||
import static org.lwjgl.opengl.GL20.GL_TEXTURE_3D;
|
||||
import static org.lwjgl.opengl.GL20.GL_TEXTURE_MAG_FILTER;
|
||||
import static org.lwjgl.opengl.GL20.GL_TEXTURE_MIN_FILTER;
|
||||
import static org.lwjgl.opengl.GL20.GL_TEXTURE_WRAP_R;
|
||||
import static org.lwjgl.opengl.GL20.GL_TEXTURE_WRAP_S;
|
||||
import static org.lwjgl.opengl.GL20.GL_TEXTURE_WRAP_T;
|
||||
import static org.lwjgl.opengl.GL20.GL_UNPACK_ALIGNMENT;
|
||||
import static org.lwjgl.opengl.GL20.GL_UNPACK_IMAGE_HEIGHT;
|
||||
import static org.lwjgl.opengl.GL20.GL_UNPACK_ROW_LENGTH;
|
||||
import static org.lwjgl.opengl.GL20.GL_UNPACK_SKIP_IMAGES;
|
||||
import static org.lwjgl.opengl.GL20.GL_UNPACK_SKIP_PIXELS;
|
||||
import static org.lwjgl.opengl.GL20.GL_UNPACK_SKIP_ROWS;
|
||||
import static org.lwjgl.opengl.GL20.GL_UNSIGNED_BYTE;
|
||||
import static org.lwjgl.opengl.GL20.glActiveTexture;
|
||||
import static org.lwjgl.opengl.GL20.glPixelStorei;
|
||||
import static org.lwjgl.opengl.GL20.glTexImage3D;
|
||||
import static org.lwjgl.opengl.GL20.glTexParameteri;
|
||||
import static org.lwjgl.opengl.GL20.glTexSubImage3D;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
import com.jozufozu.flywheel.backend.Backend;
|
||||
import com.jozufozu.flywheel.backend.gl.GlTexture;
|
||||
import com.jozufozu.flywheel.backend.gl.GlTextureUnit;
|
||||
import com.jozufozu.flywheel.backend.gl.versioned.RGPixelFormat;
|
||||
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.IBlockDisplayReader;
|
||||
import net.minecraft.world.LightType;
|
||||
|
||||
public class LightVolume {
|
||||
public class LightVolume implements ImmutableBox, ILightUpdateListener {
|
||||
|
||||
private GridAlignedBB sampleVolume;
|
||||
private GridAlignedBB textureVolume;
|
||||
private ByteBuffer lightData;
|
||||
protected final GridAlignedBB box = new GridAlignedBB();
|
||||
protected ByteBuffer lightData;
|
||||
|
||||
private boolean bufferDirty;
|
||||
private boolean removed;
|
||||
public LightVolume(ImmutableBox sampleVolume) {
|
||||
this.setBox(sampleVolume);
|
||||
|
||||
private final GlTexture glTexture;
|
||||
|
||||
private final RGPixelFormat pixelFormat;
|
||||
|
||||
public LightVolume(GridAlignedBB sampleVolume) {
|
||||
setSampleVolume(sampleVolume);
|
||||
|
||||
pixelFormat = Backend.getInstance().compat.pixelFormat;
|
||||
|
||||
this.glTexture = new GlTexture(GL_TEXTURE_3D);
|
||||
this.lightData = MemoryUtil.memAlloc(this.textureVolume.volume() * pixelFormat.byteCount());
|
||||
|
||||
// allocate space for the texture
|
||||
glActiveTexture(GL_TEXTURE4);
|
||||
glTexture.bind();
|
||||
|
||||
int sizeX = textureVolume.sizeX();
|
||||
int sizeY = textureVolume.sizeY();
|
||||
int sizeZ = textureVolume.sizeZ();
|
||||
glTexImage3D(GL_TEXTURE_3D, 0, pixelFormat.internalFormat(), sizeX, sizeY, sizeZ, 0, pixelFormat.format(), GL_UNSIGNED_BYTE, 0);
|
||||
|
||||
glTexture.unbind();
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
this.lightData = MemoryUtil.memAlloc(this.box.volume() * getStride());
|
||||
}
|
||||
|
||||
private void setSampleVolume(GridAlignedBB sampleVolume) {
|
||||
this.sampleVolume = sampleVolume;
|
||||
this.textureVolume = sampleVolume.copy();
|
||||
this.textureVolume.nextPowerOf2Centered();
|
||||
protected void setBox(ImmutableBox box) {
|
||||
this.box.assign(box);
|
||||
}
|
||||
|
||||
public ReadOnlyBox getTextureVolume() {
|
||||
return textureVolume;
|
||||
}
|
||||
|
||||
public ReadOnlyBox getSampleVolume() {
|
||||
return sampleVolume;
|
||||
}
|
||||
|
||||
public int getMinX() {
|
||||
return textureVolume.getMinX();
|
||||
}
|
||||
|
||||
public int getMinY() {
|
||||
return textureVolume.getMinY();
|
||||
}
|
||||
|
||||
public int getMinZ() {
|
||||
return textureVolume.getMinZ();
|
||||
}
|
||||
|
||||
public int getMaxX() {
|
||||
return textureVolume.getMaxX();
|
||||
}
|
||||
|
||||
public int getMaxY() {
|
||||
return textureVolume.getMaxY();
|
||||
}
|
||||
|
||||
public int getMaxZ() {
|
||||
return textureVolume.getMaxZ();
|
||||
}
|
||||
|
||||
public int getSizeX() {
|
||||
return textureVolume.sizeX();
|
||||
}
|
||||
|
||||
public int getSizeY() {
|
||||
return textureVolume.sizeY();
|
||||
}
|
||||
|
||||
public int getSizeZ() {
|
||||
return textureVolume.sizeZ();
|
||||
}
|
||||
|
||||
public void move(IBlockDisplayReader world, GridAlignedBB newSampleVolume) {
|
||||
if (removed) return;
|
||||
|
||||
if (textureVolume.contains(newSampleVolume)) {
|
||||
BasicProvider basicProvider = BasicProvider.get(world);
|
||||
if (newSampleVolume.intersects(sampleVolume)) {
|
||||
GridAlignedBB newArea = newSampleVolume.intersect(sampleVolume);
|
||||
sampleVolume = newSampleVolume;
|
||||
|
||||
copyLight(basicProvider, newArea);
|
||||
} else {
|
||||
sampleVolume = newSampleVolume;
|
||||
initialize(world);
|
||||
}
|
||||
public short getPackedLight(int x, int y, int z) {
|
||||
if (box.contains(x, y, z)) {
|
||||
return lightData.getShort(worldPosToBufferIndex(x, y, z));
|
||||
} else {
|
||||
setSampleVolume(newSampleVolume);
|
||||
int volume = textureVolume.volume();
|
||||
if (volume * 2 > lightData.capacity()) {
|
||||
lightData = MemoryUtil.memRealloc(lightData, volume * 2);
|
||||
}
|
||||
initialize(world);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void notifyLightUpdate(LightProvider world, LightType type, ReadOnlyBox changedVolume) {
|
||||
if (removed) return;
|
||||
|
||||
if (!changedVolume.intersects(sampleVolume)) return;
|
||||
changedVolume = changedVolume.intersect(sampleVolume); // compute the region contained by us that has dirty lighting data.
|
||||
|
||||
if (type == LightType.BLOCK) copyBlock(world, changedVolume);
|
||||
else if (type == LightType.SKY) copySky(world, changedVolume);
|
||||
public int getMinX() {
|
||||
return box.getMinX();
|
||||
}
|
||||
|
||||
public void notifyLightPacket(LightProvider world, int chunkX, int chunkZ) {
|
||||
if (removed) return;
|
||||
public int getMinY() {
|
||||
return box.getMinY();
|
||||
}
|
||||
|
||||
public int getMinZ() {
|
||||
return box.getMinZ();
|
||||
}
|
||||
|
||||
public int getMaxX() {
|
||||
return box.getMaxX();
|
||||
}
|
||||
|
||||
public int getMaxY() {
|
||||
return box.getMaxY();
|
||||
}
|
||||
|
||||
public int getMaxZ() {
|
||||
return box.getMaxZ();
|
||||
}
|
||||
|
||||
public void move(LightProvider world, ImmutableBox newSampleVolume) {
|
||||
if (lightData == null) return;
|
||||
|
||||
setBox(newSampleVolume);
|
||||
int volume = box.volume();
|
||||
if (volume * 2 > lightData.capacity()) {
|
||||
lightData = MemoryUtil.memRealloc(lightData, volume * 2);
|
||||
}
|
||||
initialize(world);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLightUpdate(LightProvider world, LightType type, ImmutableBox changedVolume) {
|
||||
if (lightData == null) return;
|
||||
|
||||
GridAlignedBB vol = changedVolume.copy();
|
||||
if (!vol.intersects(getVolume())) return;
|
||||
vol.intersectAssign(getVolume()); // compute the region contained by us that has dirty lighting data.
|
||||
|
||||
if (type == LightType.BLOCK) copyBlock(world, vol);
|
||||
else if (type == LightType.SKY) copySky(world, vol);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLightPacket(LightProvider world, int chunkX, int chunkZ) {
|
||||
if (lightData == null) return;
|
||||
|
||||
GridAlignedBB changedVolume = GridAlignedBB.from(chunkX, chunkZ);
|
||||
if (!changedVolume.intersects(sampleVolume)) return;
|
||||
changedVolume.intersectAssign(sampleVolume); // compute the region contained by us that has dirty lighting data.
|
||||
if (!changedVolume.intersects(getVolume())) return;
|
||||
changedVolume.intersectAssign(getVolume()); // compute the region contained by us that has dirty lighting data.
|
||||
|
||||
copyLight(world, changedVolume);
|
||||
}
|
||||
|
@ -168,25 +92,20 @@ public class LightVolume {
|
|||
* Completely (re)populate this volume with block and sky lighting data.
|
||||
* This is expensive and should be avoided.
|
||||
*/
|
||||
public void initialize(IBlockDisplayReader world) {
|
||||
if (removed) return;
|
||||
public void initialize(LightProvider world) {
|
||||
if (lightData == null) return;
|
||||
|
||||
BlockPos.Mutable pos = new BlockPos.Mutable();
|
||||
ImmutableBox box = getVolume();
|
||||
int shiftX = box.getMinX();
|
||||
int shiftY = box.getMinY();
|
||||
int shiftZ = box.getMinZ();
|
||||
|
||||
int shiftX = textureVolume.getMinX();
|
||||
int shiftY = textureVolume.getMinY();
|
||||
int shiftZ = textureVolume.getMinZ();
|
||||
|
||||
sampleVolume.forEachContained((x, y, z) -> {
|
||||
pos.set(x, y, z);
|
||||
|
||||
int blockLight = world.getBrightness(LightType.BLOCK, pos);
|
||||
int skyLight = world.getBrightness(LightType.SKY, pos);
|
||||
box.forEachContained((x, y, z) -> {
|
||||
int blockLight = world.getLight(LightType.BLOCK, x, y, z);
|
||||
int skyLight = world.getLight(LightType.SKY, x, y, z);
|
||||
|
||||
writeLight(x - shiftX, y - shiftY, z - shiftZ, blockLight, skyLight);
|
||||
});
|
||||
|
||||
bufferDirty = true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -194,18 +113,16 @@ public class LightVolume {
|
|||
*
|
||||
* @param worldVolume the region in the world to copy data from.
|
||||
*/
|
||||
public void copyBlock(LightProvider world, ReadOnlyBox worldVolume) {
|
||||
int xShift = textureVolume.getMinX();
|
||||
int yShift = textureVolume.getMinY();
|
||||
int zShift = textureVolume.getMinZ();
|
||||
public void copyBlock(LightProvider world, ImmutableBox worldVolume) {
|
||||
int xShift = box.getMinX();
|
||||
int yShift = box.getMinY();
|
||||
int zShift = box.getMinZ();
|
||||
|
||||
worldVolume.forEachContained((x, y, z) -> {
|
||||
int light = world.getLight(LightType.BLOCK, x, y, z);
|
||||
|
||||
writeBlock(x - xShift, y - yShift, z - zShift, light);
|
||||
});
|
||||
|
||||
bufferDirty = true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -213,18 +130,16 @@ public class LightVolume {
|
|||
*
|
||||
* @param worldVolume the region in the world to copy data from.
|
||||
*/
|
||||
public void copySky(LightProvider world, ReadOnlyBox worldVolume) {
|
||||
int xShift = textureVolume.getMinX();
|
||||
int yShift = textureVolume.getMinY();
|
||||
int zShift = textureVolume.getMinZ();
|
||||
public void copySky(LightProvider world, ImmutableBox worldVolume) {
|
||||
int xShift = box.getMinX();
|
||||
int yShift = box.getMinY();
|
||||
int zShift = box.getMinZ();
|
||||
|
||||
worldVolume.forEachContained((x, y, z) -> {
|
||||
int light = world.getLight(LightType.SKY, x, y, z);
|
||||
|
||||
writeSky(x - xShift, y - yShift, z - zShift, light);
|
||||
});
|
||||
|
||||
bufferDirty = true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -232,12 +147,12 @@ public class LightVolume {
|
|||
*
|
||||
* @param worldVolume the region in the world to copy data from.
|
||||
*/
|
||||
public void copyLight(LightProvider world, ReadOnlyBox worldVolume) {
|
||||
public void copyLight(LightProvider world, ImmutableBox worldVolume) {
|
||||
BlockPos.Mutable pos = new BlockPos.Mutable();
|
||||
|
||||
int xShift = textureVolume.getMinX();
|
||||
int yShift = textureVolume.getMinY();
|
||||
int zShift = textureVolume.getMinZ();
|
||||
int xShift = box.getMinX();
|
||||
int yShift = box.getMinY();
|
||||
int zShift = box.getMinZ();
|
||||
|
||||
worldVolume.forEachContained((x, y, z) -> {
|
||||
pos.set(x, y, z);
|
||||
|
@ -247,77 +162,80 @@ public class LightVolume {
|
|||
|
||||
writeLight(x - xShift, y - yShift, z - zShift, block, sky);
|
||||
});
|
||||
|
||||
bufferDirty = true;
|
||||
}
|
||||
|
||||
public void bind() {
|
||||
// just in case something goes wrong or we accidentally call this before this volume is properly disposed of.
|
||||
if (lightData == null || removed) return;
|
||||
|
||||
GlTextureUnit.T4.makeActive();
|
||||
glTexture.bind();
|
||||
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_MIRRORED_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
|
||||
|
||||
uploadTexture();
|
||||
}
|
||||
|
||||
private void uploadTexture() {
|
||||
if (bufferDirty) {
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||
glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
|
||||
glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
|
||||
glPixelStorei(GL_UNPACK_SKIP_IMAGES, 0);
|
||||
glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, 0);
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 2);
|
||||
int sizeX = textureVolume.sizeX();
|
||||
int sizeY = textureVolume.sizeY();
|
||||
int sizeZ = textureVolume.sizeZ();
|
||||
|
||||
glTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, 0, sizeX, sizeY, sizeZ, pixelFormat.format(), GL_UNSIGNED_BYTE, lightData);
|
||||
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 4); // 4 is the default
|
||||
bufferDirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void unbind() {
|
||||
glTexture.unbind();
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
removed = true;
|
||||
glTexture.delete();
|
||||
MemoryUtil.memFree(lightData);
|
||||
lightData = null;
|
||||
}
|
||||
|
||||
private void writeLight(int x, int y, int z, int block, int sky) {
|
||||
protected void writeLight(int x, int y, int z, int block, int sky) {
|
||||
byte b = (byte) ((block & 0xF) << 4);
|
||||
byte s = (byte) ((sky & 0xF) << 4);
|
||||
|
||||
int i = posToIndex(x, y, z);
|
||||
int i = boxPosToBufferIndex(x, y, z);
|
||||
lightData.put(i, b);
|
||||
lightData.put(i + 1, s);
|
||||
}
|
||||
|
||||
private void writeBlock(int x, int y, int z, int block) {
|
||||
protected void writeBlock(int x, int y, int z, int block) {
|
||||
byte b = (byte) ((block & 0xF) << 4);
|
||||
|
||||
lightData.put(posToIndex(x, y, z), b);
|
||||
lightData.put(boxPosToBufferIndex(x, y, z), b);
|
||||
}
|
||||
|
||||
private void writeSky(int x, int y, int z, int sky) {
|
||||
protected void writeSky(int x, int y, int z, int sky) {
|
||||
byte b = (byte) ((sky & 0xF) << 4);
|
||||
|
||||
lightData.put(posToIndex(x, y, z) + 1, b);
|
||||
lightData.put(boxPosToBufferIndex(x, y, z) + 1, b);
|
||||
}
|
||||
|
||||
private int posToIndex(int x, int y, int z) {
|
||||
return (x + textureVolume.sizeX() * (y + z * textureVolume.sizeY())) * pixelFormat.byteCount();
|
||||
protected int worldPosToBufferIndex(int x, int y, int z) {
|
||||
x -= box.getMinX();
|
||||
y -= box.getMinY();
|
||||
z -= box.getMinZ();
|
||||
return boxPosToBufferIndex(x, y, z);
|
||||
}
|
||||
|
||||
protected int boxPosToBufferIndex(int x, int y, int z) {
|
||||
return (x + box.sizeX() * (y + z * box.sizeY())) * getStride();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The stride of the texels, in bytes.
|
||||
*/
|
||||
protected int getStride() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableBox getVolume() {
|
||||
return box;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenerStatus status() {
|
||||
return ListenerStatus.OKAY;
|
||||
}
|
||||
|
||||
public static int unpackBlock(short packed) {
|
||||
return (packed >> 4) & 0xF;
|
||||
}
|
||||
|
||||
public static int unpackSky(short packed) {
|
||||
return (packed >> 12) & 0xF;
|
||||
}
|
||||
|
||||
public static byte packLight(byte block, byte sky) {
|
||||
return (byte) (block | (sky << 4));
|
||||
}
|
||||
|
||||
public static int unpackBlock(byte packed) {
|
||||
return packed & 0xF;
|
||||
}
|
||||
|
||||
public static int unpackSky(byte packed) {
|
||||
return (packed >> 4) & 0xF;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package com.jozufozu.flywheel.light;
|
||||
|
||||
import java.util.AbstractCollection;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
import java.util.WeakHashMap;
|
||||
|
@ -57,6 +56,24 @@ public class WeakContainmentMultiMap<T> extends AbstractCollection<T> {
|
|||
forward.computeIfAbsent(sectionPos, $ -> new WeakHashSet<>()).add(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(Object o) {
|
||||
LongSet containmentSet = reverse.remove(o);
|
||||
|
||||
if (containmentSet != null) {
|
||||
containmentSet.forEach((LongConsumer) l -> {
|
||||
WeakHashSet<T> listeners = forward.get(l);
|
||||
|
||||
if (listeners != null) listeners.remove(o);
|
||||
});
|
||||
|
||||
containmentSet.clear();
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<T> iterator() {
|
||||
return reverse.keySet().iterator();
|
||||
|
|
Loading…
Reference in a new issue