SimpleJavaEngine/src/main/java/speiger/src/coreengine/rendering/textures/normal/DynamicTexture.java

239 lines
6.0 KiB
Java

package speiger.src.coreengine.rendering.textures.normal;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Iterator;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.lwjgl.opengl.GL11;
import org.lwjgl.system.MemoryStack;
import org.lwjgl.system.MemoryUtil;
import speiger.src.collections.ints.collections.IntIterator;
import speiger.src.collections.ints.maps.impl.hash.Int2ObjectLinkedOpenHashMap;
import speiger.src.collections.ints.maps.interfaces.Int2ObjectMap;
import speiger.src.collections.ints.maps.interfaces.Int2ObjectMap.Entry;
import speiger.src.collections.ints.sets.IntOpenHashSet;
import speiger.src.collections.ints.sets.IntSet;
import speiger.src.collections.ints.utils.maps.Int2ObjectMaps;
import speiger.src.collections.utils.ITrimmable;
import speiger.src.coreengine.math.BitUtil;
import speiger.src.coreengine.rendering.textures.base.AbstractTexture;
import speiger.src.coreengine.rendering.textures.base.TextureManager;
import speiger.src.coreengine.rendering.textures.custom.IDynamicTexture;
import speiger.src.coreengine.rendering.utils.AllocationTracker;
public class DynamicTexture extends AbstractTexture implements IDynamicTexture
{
public static final int R = 0xFF << 16;
public static final int G = 0xFF << 8;
public static final int B = 0xFF;
public static final int A = 0xFF << 24;
Int2ObjectMap<IntSet> dirtyChunks = new Int2ObjectLinkedOpenHashMap<IntSet>();
Lock lock = new ReentrantLock();
int[] data;
int width;
int height;
public DynamicTexture(int width, int height)
{
this.width = width;
this.height = height;
setTextureID(GL11.glGenTextures());
data = new int[width * height];
}
public DynamicTexture(int width, int height, int defaultValue)
{
this(width, height);
Arrays.fill(data, defaultValue);
processChanges(true);
}
public int[] getTextureData()
{
return data;
}
@Override
public void markDirty(int x, int z)
{
lock.lock();
int chunkIndex = BitUtil.toInt(x >> 4, z >> 4);
IntSet set = dirtyChunks.get(chunkIndex);
if(set == null)
{
set = new IntOpenHashSet();
dirtyChunks.put(chunkIndex, set);
}
set.add((x & 15) << 4 | (z & 15));
lock.unlock();
}
@Override
public void setData(int index, int value)
{
data[index] = value;
markDirty(index);
}
@Override
public void setRed(int index, int red)
{
if(getRed(index) == red) return;
data[index] = (data[index] & ~R) | (red & 0xFF) << 16;
markDirty(index);
}
@Override
public void setGreen(int index, int green)
{
if(getGreen(index) == green) return;
data[index] = (data[index] & ~G) | (green & 0xFF) << 8;
markDirty(index);
}
@Override
public void setBlue(int index, int blue)
{
if(getBlue(index) == blue) return;
data[index] = (data[index] & ~B) | (blue & 0xFF);
markDirty(index);
}
@Override
public void setAlpha(int index, int alpha)
{
if(getAlpha(index) == alpha) return;
data[index] = (data[index] & ~A) | (alpha & 0xFF) << 24;
markDirty(index);
}
@Override
public int getRGB(int index)
{
return data[index];
}
@Override
public int getRed(int index)
{
return (data[index] >> 16) & 0xFF;
}
@Override
public int getGreen(int index)
{
return (data[index] >> 8) & 0xFF;
}
@Override
public int getBlue(int index)
{
return data[index] & 0xFF;
}
@Override
public int getAlpha(int index)
{
return (data[index] >> 24) & 0xFF;
}
@Override
public boolean isDirty()
{
return dirtyChunks.size() > 0;
}
@Override
public void processChanges(boolean full)
{
lock.lock();
if(full)
{
ByteBuffer buffer = MemoryUtil.memAlloc(data.length * 4);
for(int i = 0;i<data.length;i++)
{
int pixel = data[i];
buffer.put((byte)((pixel >> 16) & 0xFF));
buffer.put((byte)((pixel >> 8) & 0xFF));
buffer.put((byte)(pixel & 0xFF));
buffer.put((byte)((pixel >> 24) & 0xFF));
}
buffer.flip();
bindTexture();
GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST);
GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
AllocationTracker.INSTANCE.addGPUBytes(buffer.remaining());
GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA, width, height, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, buffer);
MemoryUtil.memFree(buffer);
dirtyChunks.clear();
}
else
{
bindTexture();
Thread thread = Thread.currentThread();
for(Iterator<Entry<IntSet>> iter = Int2ObjectMaps.fastIterator(dirtyChunks);iter.hasNext() && !thread.isInterrupted();iter.remove())
{
Entry<IntSet> values = iter.next();
int key = values.getIntKey();
int chunkX = BitUtil.toFirstShort(key) * 16;
int chunkZ = BitUtil.toSecondShort(key) * 16;
int width = 0;
int height = 0;
for(IntIterator subIt = values.getValue().iterator();subIt.hasNext();)
{
int index = subIt.nextInt();
width = Math.max(width, ((index >> 4) & 0xF) + 1);
height = Math.max(height, (index & 0xF) + 1);
}
try(MemoryStack stack = MemoryStack.stackPush())
{
ByteBuffer buffer = stack.malloc(width * height * 4);
for(int y = 0;y<height;y++)
{
for(int x = 0;x<width;x++)
{
int pixel = data[((chunkZ + y) * this.width) + chunkX + x];
buffer.put((byte)((pixel >> 16) & 0xFF));
buffer.put((byte)((pixel >> 8) & 0xFF));
buffer.put((byte)(pixel & 0xFF));
buffer.put((byte)((pixel >> 24) & 0xFF));
}
}
buffer.flip();
AllocationTracker.INSTANCE.addGPUBytes(buffer.remaining());
GL11.glTexSubImage2D(GL11.GL_TEXTURE_2D, 0, chunkX, chunkZ, width, height, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, buffer);
}
}
}
if(dirtyChunks.isEmpty())
{
((ITrimmable)dirtyChunks).trim();
}
lock.unlock();
}
@Override
public void reload()
{
int old = getTextureId();
setTextureID(GL11.glGenTextures());
TextureManager.INSTANCE.removeTexture(old);
processChanges(true);
}
@Override
public int getWidth()
{
return width;
}
@Override
public int getHeight()
{
return height;
}
}