Flywheel/src/main/java/com/jozufozu/flywheel/light/GPULightVolume.java

161 lines
5 KiB
Java
Raw Normal View History

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.level.LightLayer;
public class GPULightVolume extends LightVolume {
protected final GridAlignedBB sampleVolume = new GridAlignedBB();
private final GlTexture glTexture;
private final RGPixelFormat pixelFormat;
2021-11-09 23:59:31 +01:00
private final GlTextureUnit textureUnit = GlTextureUnit.T4;
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;
2021-11-09 23:59:31 +01:00
textureUnit.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, LightLayer 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;
}
}