Next Milestone reached.

This commit is contained in:
2026-07-24 18:20:36 +02:00
parent bc17e7886d
commit d8cfb79c9a
56 changed files with 2526 additions and 1000 deletions
+1 -1
View File
@@ -5,7 +5,7 @@ connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER)
connection.project.dir=
eclipse.preferences.version=1
gradle.user.home=
java.home=C\:/Program Files/Eclipse Adoptium/jdk25
java.home=C\:/Program Files/Java/jdk25
jvm.arguments=
offline.mode=false
override.workspace.settings=true
+1 -1
View File
@@ -1,4 +1,4 @@
org.gradle.jvmargs=-Xmx2G
lwjglVersion = 3.3.4
lwjglVersion = 3.4.2
lwjglNatives = natives-windows
@@ -1,6 +1,8 @@
package speiger.src.coreengine.assets.api;
import java.io.IOException;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributes;
@@ -12,7 +14,9 @@ import java.util.function.Consumer;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
import speiger.src.coreengine.assets.impl.FolderAssetPackage;
import speiger.src.coreengine.assets.impl.FolderWatcher.ChangeType;
import speiger.src.coreengine.assets.impl.ZipAssetPackage;
@NullMarked
public interface IAssetPackage extends AutoCloseable {
@@ -23,6 +27,23 @@ public interface IAssetPackage extends AutoCloseable {
IAsset get(ID location);
void getAll(ID folder, BiConsumer<ID, IAsset> assets);
public static IAssetPackage asset(Path path) {
return create(path, "assets");
}
public static IAssetPackage create(Path path, String baseFolder) {
if(Files.exists(path)) {
if(Files.isDirectory(path)) return new FolderAssetPackage(path.resolve(baseFolder));
if(isZip(path)) return new ZipAssetPackage(path, baseFolder);
}
return null;
}
private static boolean isZip(Path path) {
try(FileSystem system = FileSystems.newFileSystem(path)) { return "jar".equals(system.provider().getScheme()); }
catch(Exception _) { return false; }
}
public record AssetIdentity(long size, FileTime lastModified) {
public AssetIdentity(Path path) throws IOException {
@@ -43,8 +43,8 @@ public record ID(String domain, String path) implements Comparable<ID> {
public static final ID of(String location) {
Objects.requireNonNull(location);
int index = location.indexOf(":");
String domain = location.substring(0, index);
String path = location.substring(index + 1);
String domain = index == -1 ? "base" : location.substring(0, index);
String path = index == -1 ? location : location.substring(index + 1);
if(!isValidDomain(domain)) throw new IllegalArgumentException("Non [a-zA-Z0-9_.-] Character found in domain of location ["+domain+":"+path+"]");
if(!isValidPath(path)) throw new IllegalArgumentException("Non [a-zA-Z0-9/_.-] Character found in path of location ["+domain+":"+path+"]");
return new ID(domain, path);
@@ -18,7 +18,7 @@ public class Checksums {
public static class CRC implements IAssetParser<String> {
@Override
public String parse(Path path) throws IOException {
Map<String, Object> files = Files.readAttributes(path, "crc");
Map<String, Object> files = Files.readAttributes(path, "*");
if(files.containsKey("crc")) return Long.toHexString(((Long)files.get("crc")) & 0xFFFFFFFFL);
CRC32C crc = new CRC32C();
crc.update(Files.readAllBytes(path));
@@ -4,6 +4,7 @@ import java.nio.file.Path;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import java.util.function.Predicate;
@@ -27,6 +28,7 @@ public class AssetManager {
List<IAssetPackage> packages;
ReloadTracker tracker = new ReloadTracker();
ReloadManager manager;
AtomicInteger InUse = new AtomicInteger();
public AssetManager(BaseProvider provider) {
setPackages(provider);
@@ -89,21 +91,29 @@ public class AssetManager {
}
public IAssetProvider get() {
return new WrappedProvider(provider);
InUse.incrementAndGet();
return new WrappedProvider(provider, this::manageClose);
}
private void manageClose() throws Exception {
if(InUse.decrementAndGet() > 0) return;
provider.close();
}
public final class WrappedProvider implements IAssetProvider {
BaseProvider provider;
AutoCloseable closer;
public WrappedProvider(BaseProvider provider) {
public WrappedProvider(BaseProvider provider, AutoCloseable closer) {
this.provider = provider;
this.closer = closer;
}
@Override
public void close() throws Exception {
BaseProvider provider = this.provider;
if(provider == null) return;
this.provider = null;
provider.close();
closer.close();
}
@Override
@@ -5,6 +5,6 @@ import java.util.List;
import speiger.src.coreengine.assets.api.IAssetPackage;
import speiger.src.coreengine.assets.api.IAssetProvider;
public interface BaseProvider extends IAssetProvider, AutoCloseable {
public interface BaseProvider extends IAssetProvider {
List<IAssetPackage> packages();
}
@@ -38,13 +38,21 @@ public abstract class VertexBuffer implements GraphicsResource {
public abstract VertexBuffer allocate(int totalBytes);
public abstract VertexBuffer set(long pointer, int totalBytes);
public VertexBuffer set(ByteBuffer buffer) {
return set(MemoryUtil.memAddress(buffer), buffer.remaining());
public VertexBuffer set(ByteBuffer buffer) { return set(MemoryUtil.memAddress(buffer), buffer.remaining()); }
public VertexBuffer set(byte[] data) {
ByteBuffer buffer = MemoryUtil.memAlloc(data.length).put(data).flip();
set(buffer);
MemoryUtil.memFree(buffer);
return this;
}
public abstract VertexBuffer fill(long pointer, int totalBytes, int offset);
public VertexBuffer fill(int offset, ByteBuffer buffer) {
return fill(MemoryUtil.memAddress(buffer), buffer.remaining(), offset);
public VertexBuffer fill(int offset, ByteBuffer buffer) { return fill(MemoryUtil.memAddress(buffer), buffer.remaining(), offset); }
public VertexBuffer fill(int offset, byte[] data) {
ByteBuffer buffer = MemoryUtil.memAlloc(data.length).put(data).flip();
fill(offset, buffer);
MemoryUtil.memFree(buffer);
return this;
}
public abstract VertexBuffer read(long pointer, int totalBytes, int offset);
@@ -13,10 +13,13 @@ public interface GraphicsCommandBuffer extends GraphicsResource {
GraphicsCommandBuffer pipeline(ShaderPipeline pipeline);
GraphicsCommandBuffer mesh(Mesh mesh);
GraphicsCommandBuffer texture(int set, int binding, Texture texture, Sampler sampler);
GraphicsCommandBuffer uniform(int set, int binding, VertexBuffer buffer);
GraphicsCommandBuffer texture(int binding, Texture texture, Sampler sampler);
GraphicsCommandBuffer drawIndexed(int start, int count);
GraphicsCommandBuffer uniform(int binding, VertexBuffer buffer);
GraphicsCommandBuffer uniform(int binding, VertexBuffer buffer, long offset, long size);
GraphicsCommandBuffer drawArrays(int offset, int count);
GraphicsCommandBuffer drawElements(int offset, int count);
GraphicsCommandBuffer pushScissors(int x, int y, int width, int height);
GraphicsCommandBuffer popScissors();
@@ -1,34 +1,31 @@
package speiger.src.coreengine.graphics.api.core;
import speiger.src.coreengine.graphics.api.shader.states.GraphicsDataType;
import speiger.src.coreengine.graphics.api.target.RenderTarget;
import speiger.src.coreengine.graphics.api.texture.Texture;
import speiger.src.coreengine.graphics.api.texture.states.TextureFormat;
import speiger.src.coreengine.graphics.api.utils.PushableResource;
import speiger.src.coreengine.math.vector.floats.Vec4f;
public interface GraphicsCommandQueue {
public static final int CLEAR_COLOR = 1;
public static final int CLEAR_DEPTH = 2;
public static final int CLEAR_BOTH = 3;
public PushableResource putClearColor(Vec4f color);
public PushableResource putClearDepth(double value);
public PushableResource putRenderTarget(RenderTarget target);
public PushableResource pushViewPort(int x, int y, int width, int height);
public void clearTexture(int clearParam);
public void clearTexture(int x, int y, int width, int height, int clearParam);
public void clearTexture(Texture texture, int clearParam);
public void clearTexture(Texture texture, int x, int y, int width, int height, int clearParam);
public void writeToTexture(Texture target, int x, int y, int width, int height, long source);
public void readFromTexture(Texture source, int x, int y, int width, int height, long target);
public void writeToTexture(Texture target, TextureFormat format, GraphicsDataType dataType, long source);
public default void writeToTexture(Texture target, int x, int y, int width, int height, TextureFormat format, GraphicsDataType dataType, long source) {
writeToTexture(target, 0, 0, x, y, width, height, format, dataType, source);
}
public void writeToTexture(Texture target, int sourceX, int sourceY, int targetX, int targetY, int width, int height, TextureFormat format, GraphicsDataType dataType, long source);
public void submitCommands(GraphicsCommandBuffer buffer);
public record DrawArea(int x, int y, int width, int height) {
public boolean fits(int x, int y, int width, int height) {
return x >= x() && y >= y() && x + width < width() && y + height < height();
}
public boolean fills(int width, int height) {
return x == 0 && y == 0 && width == width() && height == height();
}
}
}
@@ -1,24 +1,28 @@
package speiger.src.coreengine.graphics.api.core;
import java.util.List;
import speiger.src.coreengine.graphics.api.buffer.VertexBuffer;
import speiger.src.coreengine.graphics.api.buffer.states.BufferState;
import speiger.src.coreengine.graphics.api.buffer.states.BufferType;
import speiger.src.coreengine.graphics.api.mesh.Mesh;
import speiger.src.coreengine.graphics.api.sampler.Sampler;
import speiger.src.coreengine.graphics.api.sampler.SamplerSettings;
import speiger.src.coreengine.graphics.api.shader.ShaderPipeline;
import speiger.src.coreengine.graphics.api.texture.Texture;
import speiger.src.coreengine.graphics.api.texture.TextureSettings;
import speiger.src.coreengine.graphics.api.utils.ExecutionType;
import speiger.src.coreengine.rendering.input.window.Window;
public interface GraphicsDevice {
public Window getWindow();
public GraphicsSurface createSurface();
public GraphicsCommandBuffer createCommandBuffer(ExecutionType type);
public GraphicsCommandQueue getQueue();
public VertexBuffer createBuffer(BufferType type, BufferState state);
public VertexBuffer createBuffer(BufferType type, BufferState state, int allocatedBytes);
public Texture createTexture(TextureSettings data, int width, int height);
public Sampler createSampler(SamplerSettings settings);
public Mesh createMesh(Mesh.Builder builder);
Window window();
GraphicsSurface createSurface();
GraphicsCommandBuffer createCommandBuffer(ExecutionType type);
GraphicsCommandQueue queue();
VertexBuffer createBuffer(BufferType type, BufferState state);
VertexBuffer createBuffer(BufferType type, BufferState state, int allocatedBytes);
Texture createTexture(TextureSettings data, int width, int height);
Sampler createSampler(SamplerSettings settings);
Mesh createMesh(Mesh.Builder builder);
void preloadPipelines(List<ShaderPipeline> pipelines);
}
@@ -16,6 +16,10 @@ public record SamplerSettings(BorderMode wrapS, BorderMode wrapT, Optional<Borde
Objects.requireNonNull(magSample);
}
public static Builder builder() {
return new Builder();
}
public static class Builder {
BorderMode wrapS;
BorderMode wrapT;
@@ -23,6 +27,19 @@ public record SamplerSettings(BorderMode wrapS, BorderMode wrapT, Optional<Borde
SampleMode minSample;
SampleMode magSample;
public Builder wrap(BorderMode mode) {
return wrap(mode, false);
}
public Builder wrap(BorderMode mode, boolean includeR) {
wrapS = Objects.requireNonNull(mode);
wrapT = Objects.requireNonNull(mode);
if(includeR) {
wrapR = Objects.requireNonNull(mode);
}
return this;
}
public Builder wrapS(BorderMode mode) {
wrapS = Objects.requireNonNull(mode);
return this;
@@ -38,6 +55,13 @@ public record SamplerSettings(BorderMode wrapS, BorderMode wrapT, Optional<Borde
return this;
}
public Builder sampler(SampleMode mode) {
minSample = Objects.requireNonNull(mode);
if(mode != SampleMode.LINEAR && mode != SampleMode.NEAREST) throw new IllegalArgumentException("["+mode+"] is not supported in mag filter");
magSample = Objects.requireNonNull(mode);
return this;
}
public Builder minSample(SampleMode mode) {
minSample = Objects.requireNonNull(mode);
return this;
@@ -4,8 +4,8 @@ import java.util.Objects;
import speiger.src.coreengine.graphics.api.utils.AlphaFunction;
public record DepthTarget(AlphaFunction function, boolean write) {
public static final DepthTarget DEFAULT = new DepthTarget(AlphaFunction.GEQUAL, true);
public record DepthTarget(boolean enabled, AlphaFunction function, boolean write) {
public static final DepthTarget DEFAULT = new DepthTarget(true, AlphaFunction.GEQUAL, true);
public DepthTarget {
Objects.requireNonNull(function);
@@ -1,51 +0,0 @@
package speiger.src.coreengine.graphics.api.shader;
import speiger.src.coreengine.graphics.api.buffer.VertexBuffer;
import speiger.src.coreengine.graphics.api.buffer.states.IndeciesType;
import speiger.src.coreengine.graphics.api.core.GraphicsCommandQueue.DrawArea;
import speiger.src.coreengine.graphics.api.sampler.Sampler;
import speiger.src.coreengine.graphics.api.texture.Texture;
import speiger.src.coreengine.graphics.api.utils.ScissorsManager;
public abstract class RenderPass implements AutoCloseable {
protected final DrawArea area;
protected final boolean hasColor;
protected final boolean hasDepth;
protected final ScissorsManager scissors = new ScissorsManager(16);
public RenderPass(DrawArea area, boolean hasColor, boolean hasDepth) {
this.area = area;
this.hasColor = hasColor;
this.hasDepth = hasDepth;
}
public void pushScissors(int x, int y, int width, int height) {
if(area != null && area.fits(x, y, width, height)) {
//TODO implement logging
return;
}
scissors.push(x, y, width, height);
}
public void popScissors() {
scissors.pop();
}
public abstract void setShader(ShaderPipeline pipeline);
public abstract void setTexture(String name, Texture texture, Sampler sampler);
public abstract void clearTextures();
public abstract void removeTexture(String name);
public abstract void setUniform(String name, VertexBuffer buffer);
public abstract void setIndecies(VertexBuffer buffer, IndeciesType type);
public abstract void clearIndecies();
public abstract void setVertexBuffer(int index, VertexBuffer buffer);
public DrawArea area() { return area; }
public boolean hasDepth() { return hasDepth; }
public boolean hasColor() { return hasColor; }
@Override
public abstract void close();
}
@@ -69,13 +69,13 @@ public record ShaderPipeline(ID id, Map<ShaderType, ID> shaders, List<VertexBind
return this;
}
public Builder withUniform(String name, int binding, int set, int bytes) {
uniforms.add(new UniformBinding(name, binding, set, bytes));
public Builder withUniform(String name, int binding, int slot, int bytes) {
uniforms.add(new UniformBinding(name, binding, slot, bytes));
return this;
}
public Builder withTexture(String name, int binding, int set, TextureType type) {
textures.add(new TextureBinding(name, binding, set, type));
public Builder withTexture(String name, int binding, int slot, TextureType type) {
textures.add(new TextureBinding(name, binding, slot, type));
return this;
}
@@ -5,13 +5,13 @@ import java.util.Objects;
import speiger.src.coreengine.graphics.api.texture.states.TextureType;
public class Bindings {
public record UniformBinding(String name, int binding, int set, int bytes) {
public record UniformBinding(String name, int binding, int slot, int bytes) {
public UniformBinding {
Objects.requireNonNull(name, "A name has to be provided");
}
}
public record TextureBinding(String name, int binding, int set, TextureType type) {
public record TextureBinding(String name, int binding, int slot, TextureType type) {
public TextureBinding {
Objects.requireNonNull(name, "A name has to be provided");
Objects.requireNonNull(type, "A TextureType has to be provided");
@@ -1,6 +1,7 @@
package speiger.src.coreengine.graphics.api.shader.states;
public record BlendFunction(BlendFactor colorSource, BlendFactor alphaSource, BlendFactor colorTarget, BlendFactor alphaTarget) {
public static final BlendFunction DEFAULT = new BlendFunction(BlendFactor.ONE, BlendFactor.ZERO);
public BlendFunction(BlendFactor source, BlendFactor target) {
this(source, BlendFactor.ONE, target, BlendFactor.ZERO);
}
@@ -1,10 +1,10 @@
package speiger.src.coreengine.graphics.api.target;
import speiger.src.coreengine.assets.AssetLocation;
import speiger.src.coreengine.assets.api.ID;
import speiger.src.coreengine.graphics.api.texture.Texture;
import speiger.src.coreengine.graphics.api.texture.states.TextureType;
public record TextureTarget(AssetLocation id, Texture color, Texture depth) implements RenderTarget {
public record TextureTarget(ID id, Texture color, Texture depth) implements RenderTarget {
public TextureTarget {
if(color == null && depth == null) throw new IllegalArgumentException("At least either texture has to be nonNull");
if(color != null && depth != null) {
@@ -10,12 +10,11 @@ import speiger.src.coreengine.graphics.api.texture.states.SwizzleMask;
import speiger.src.coreengine.graphics.api.texture.states.TextureFormat;
import speiger.src.coreengine.graphics.api.texture.states.TextureType;
public record TextureSettings(TextureType type, TextureFormat internal, TextureFormat external, boolean generateMipmapping, OptionalInt baseLevel, OptionalInt maxLevel, ImmutableObjectList<Optional<SwizzleMask>> swizzle, Optional<StencilType> stencilType) {
public record TextureSettings(TextureType type, TextureFormat internal, boolean generateMipmapping, OptionalInt baseLevel, OptionalInt maxLevel, ImmutableObjectList<Optional<SwizzleMask>> swizzle, Optional<StencilType> stencilType) {
public TextureSettings {
Objects.requireNonNull(type, "Texture Type shouldn't be null");
Objects.requireNonNull(internal, "Internal Format shouldn't be null");
Objects.requireNonNull(external, "External Format shouldn't be null");
Objects.requireNonNull(baseLevel);
Objects.requireNonNull(maxLevel);
Objects.requireNonNull(swizzle);
@@ -33,9 +32,8 @@ public record TextureSettings(TextureType type, TextureFormat internal, TextureF
}
public static class Builder {
TextureFormat internalFormat;
TextureFormat externalFormat;
TextureType type;
TextureFormat internalFormat = TextureFormat.RGBA;
TextureType type = TextureType.TEXTURE_2D;
boolean generateMipmapping = false;
OptionalInt baseLevel = OptionalInt.empty();
OptionalInt maxLevel = OptionalInt.empty();
@@ -47,7 +45,6 @@ public record TextureSettings(TextureType type, TextureFormat internal, TextureF
private Builder(TextureSettings settings) {
type = settings.type;
internalFormat = settings.internal;
externalFormat = settings.external;
generateMipmapping = settings.generateMipmapping;
baseLevel = settings.baseLevel;
maxLevel = settings.maxLevel;
@@ -58,8 +55,7 @@ public record TextureSettings(TextureType type, TextureFormat internal, TextureF
public TextureSettings build() {
return new TextureSettings(
Objects.requireNonNull(type, "Texture Type shouldn't be null"),
Objects.requireNonNull(internalFormat, "Internal Format shouldn't be null"),
Objects.requireNonNull(externalFormat, "External Format shouldn't be null"),
Objects.requireNonNull(internalFormat, "Internal Format shouldn't be null"),
generateMipmapping, baseLevel, maxLevel, new ImmutableObjectList<>(swizzle), stencilType);
}
@@ -70,7 +66,6 @@ public record TextureSettings(TextureType type, TextureFormat internal, TextureF
public Builder format(TextureFormat format) {
this.internalFormat = format;
this.externalFormat = format;
return this;
}
@@ -79,11 +74,6 @@ public record TextureSettings(TextureType type, TextureFormat internal, TextureF
return this;
}
public Builder external(TextureFormat format) {
this.externalFormat = format;
return this;
}
public Builder swizzle(SwizzleMask red, SwizzleMask green, SwizzleMask blue, SwizzleMask alpha) {
swizzle[0] = Optional.ofNullable(red);
swizzle[1] = Optional.ofNullable(green);
@@ -1,11 +1,22 @@
package speiger.src.coreengine.graphics.api.texture.states;
public enum TextureFormat {
R,
RGB,
RGBA,
DEPTH,
DEPTH_STENCIL,
LUMINANCE,
LUMINANCE_ALPHA
R(1),
RG(2),
RGB(3),
RGBA(4),
DEPTH(1),
DEPTH_STENCIL(2),
LUMINANCE(1),
LUMINANCE_ALPHA(2);
int components;
private TextureFormat(int components) {
this.components = components;
}
public int components() {
return components;
}
}
@@ -1,18 +1,29 @@
package speiger.src.coreengine.graphics.api.utils;
import org.lwjgl.opengl.GL11;
import java.util.Objects;
import speiger.src.collections.objects.lists.ObjectArrayList;
import speiger.src.collections.utils.Stack;
import speiger.src.coreengine.math.vector.ints.Vec4i;
public class ScissorsManager {
IScissorsHandler handler;
Stack<Vec4i> stack = new ObjectArrayList<>();
boolean withRoot = false;
int limit;
public ScissorsManager(int limit) {
public ScissorsManager(int limit, IScissorsHandler handler) {
if(limit <= 0) throw new IllegalStateException("Limit is negative");
this.limit = limit;
this.handler = Objects.requireNonNull(handler, "Handler is required");
}
public void setRoot(int x, int y, int width, int height) {
if(stack.size() > 0) throw new IllegalStateException("A Root can't be set if the Stack is already in use or the root is already set");
withRoot = true;
stack.push(Vec4i.of(x, y, x + width, y + height));
handler.enable();
handler.apply(x, y, width, height);
}
public void push(int x, int y, int width, int height) {
@@ -22,12 +33,18 @@ public class ScissorsManager {
}
if(stack.isEmpty()) {
stack.push(Vec4i.of(x, y, x + width, y + height));
GL11.glEnable(GL11.GL_SCISSOR_TEST);
GL11.glScissor(x, y, width, height);
handler.enable();
handler.apply(x, y, width, height);
return;
}
Vec4i top = stack.top();
stack.push(Vec4i.of(Math.max(x, top.x()), Math.max(y, top.y()), Math.min(x + width, top.z()), Math.min(y + height, top.w())));
Vec4i result = Vec4i.of(Math.max(x, top.x()), Math.max(y, top.y()), Math.min(x + width, top.z()), Math.min(y + height, top.w()));
stack.push(result);
handler.apply(result.x(), result.y(), result.z() - result.x(), result.w() - result.y());
}
public Vec4i top() {
return stack.isEmpty() ? Vec4i.MINUS_ONE : stack.top();
}
public boolean contains(int x, int y, int width, int height) {
@@ -39,14 +56,39 @@ public class ScissorsManager {
}
public void pop() {
if(stack.isEmpty()) {
if(stack.size() <= (withRoot ? 1 : 0)) {
//TODO implement logging
return;
}
stack.pop();
if(stack.isEmpty()) {
handler.disable();
return;
}
Vec4i result = stack.top();
handler.apply(result.x(), result.y(), result.z() - result.x(), result.w() - result.y());
}
public void clear() {
stack.clear();
withRoot = false;
handler.disable();
}
public static interface IScissorsHandler {
public void enable();
public void disable();
public void apply(int x, int y, int width, int height);
}
public static class NoOp implements IScissorsHandler {
public static final IScissorsHandler INSTANCE = new NoOp();
@Override
public void enable() {}
@Override
public void disable() {}
@Override
public void apply(int x, int y, int width, int height) {}
}
}
@@ -40,7 +40,7 @@ public class VertexLayout implements ObjectIterable<Element> {
private void add(Element element) {
if(mapped.put(element.name(), element) != null) throw new IllegalStateException("Duplicated Names are not allowed");
elements.add(element);
offsets.add(stride);
offsets.add(bytes);
int size = element.size();
stride += size;
bytes += element.type().size(size);
@@ -29,6 +29,7 @@ public class GLVertexBuffer extends VertexBuffer {
}
public int id() {
if(id == 0) throw new IllegalStateException("Buffer is already removed");
return id;
}
@@ -7,17 +7,24 @@ import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL30;
import org.lwjgl.opengl.GL45;
import speiger.src.coreengine.assets.AssetLocation;
import speiger.src.coreengine.assets.api.ID;
import speiger.src.coreengine.graphics.api.core.GraphicsCommandBuffer;
import speiger.src.coreengine.graphics.api.core.GraphicsCommandQueue;
import speiger.src.coreengine.graphics.api.shader.states.GraphicsDataType;
import speiger.src.coreengine.graphics.api.target.RenderTarget;
import speiger.src.coreengine.graphics.api.target.ScreenTarget;
import speiger.src.coreengine.graphics.api.target.TextureTarget;
import speiger.src.coreengine.graphics.api.texture.Texture;
import speiger.src.coreengine.graphics.api.texture.states.TextureFormat;
import speiger.src.coreengine.graphics.api.utils.PushableResource;
import speiger.src.coreengine.graphics.opengl.texture.FrameBufferCache;
import speiger.src.coreengine.graphics.opengl.texture.GLTexture;
import speiger.src.coreengine.graphics.opengl.utils.GLStates;
import speiger.src.coreengine.graphics.opengl.utils.GLUtils;
import speiger.src.coreengine.graphics.opengl.utils.ViewPortStack;
import speiger.src.coreengine.graphics.opengl.utils.states.ScissorState;
import speiger.src.coreengine.math.vector.floats.Vec4f;
import speiger.src.coreengine.math.vector.ints.Vec4i;
import speiger.src.coreengine.rendering.input.window.Window;
public class GLCommandQueue implements GraphicsCommandQueue {
@@ -25,12 +32,14 @@ public class GLCommandQueue implements GraphicsCommandQueue {
FrameBufferCache fboCache = new FrameBufferCache();
RenderTarget renderTarget = ScreenTarget.INSTANCE;
ScreenBuffer targetFBO;
int tempWriteFBO;
Vec4f clearColor = Vec4f.mutable(0F, 0F, 0F, 1F);
double clearDepth = 0D;
public GLCommandQueue(GLGraphicsDevice device) {
this.device = device;
this.targetFBO = new ScreenBuffer(0, device.getWindow().width(), device.getWindow().height());
this.targetFBO = new ScreenBuffer(0, device.window().width(), device.window().height());
tempWriteFBO = GL45.glCreateFramebuffers();
}
@Override
@@ -48,6 +57,22 @@ public class GLCommandQueue implements GraphicsCommandQueue {
return new PushableObject<>(renderTarget, setRenderTarget(target), () -> renderTarget, this::setRenderTarget);
}
@Override
public PushableResource pushViewPort(int x, int y, int width, int height) {
ViewPortStack stack = device.states.viewPort;
Vec4i top = stack.top();
Vec4i bounds = Vec4i.of(x, y, height, width);
stack.push(x, y, width, height);
return new PushableObject<>(top, bounds, stack::top, _ -> stack.pop());
}
@Override
public void submitCommands(GraphicsCommandBuffer buffer) {
if(buffer instanceof GLRecordingCommandBuffer recorder) {
recorder.execute();
}
}
private Vec4f setClearColor(Vec4f value) {
if(clearColor.equals(value)) return value;
clearColor.set(value);
@@ -67,10 +92,10 @@ public class GLCommandQueue implements GraphicsCommandQueue {
this.renderTarget = target;
targetFBO = switch(target) {
case ScreenTarget _ -> {
Window window = device.getWindow();
Window window = device.window();
yield new ScreenBuffer(0, window.width(), window.height());
}
case TextureTarget(AssetLocation id, Texture color, Texture depth) -> {
case TextureTarget(ID id, Texture color, Texture depth) -> {
int fbo = fboCache.getOrCreateFBO(id);
if(color != null) {
GL45.glNamedFramebufferTexture(fbo, GL30.GL_COLOR_ATTACHMENT0, ((GLTexture)color).id(), 0);
@@ -90,26 +115,61 @@ public class GLCommandQueue implements GraphicsCommandQueue {
@Override
public void clearTexture(int clearParam) {
GL11.glClear(((clearParam & CLEAR_COLOR) != 0 ? GL11.GL_COLOR_BUFFER_BIT : 0) | ((clearParam & CLEAR_DEPTH) != 0 ? GL11.GL_DEPTH_BUFFER_BIT : 0));
}
@Override
public void clearTexture(int x, int y, int width, int height, int clearParam) {
ScissorState state = device.states.scissors;
state.backup();
try(PushableResource _ = pushViewPort(0, 0, targetFBO.width(), targetFBO.height())) {
state.set(Vec4i.of(x, y, x + width, y + height));
clearTexture(clearParam);
}
state.restore();
}
@Override
public void clearTexture(Texture texture, int clearParam) {
if((clearParam & CLEAR_BOTH) == CLEAR_BOTH) {
//TODO implement Logging
return;
}
GL30.glBindFramebuffer(GL30.GL_DRAW_FRAMEBUFFER, tempWriteFBO);
GL45.glNamedFramebufferTexture(tempWriteFBO, GL30.GL_COLOR_ATTACHMENT0, ((GLTexture)texture).id(), 0);
clearTexture(clearParam);
GL45.glNamedFramebufferTexture(tempWriteFBO, GL30.GL_COLOR_ATTACHMENT0, 0, 0);
GL30.glBindFramebuffer(GL30.GL_DRAW_FRAMEBUFFER, targetFBO.fbo());
}
@Override
public void clearTexture(Texture texture, int x, int y, int width, int height, int clearParam) {
ScissorState state = device.states.scissors;
state.backup();
try(PushableResource _ = pushViewPort(x, y, texture.width(), texture.height())) {
state.set(Vec4i.of(x, y, x + width, y + height));
clearTexture(texture, clearParam);
}
state.restore();
}
@Override
public void writeToTexture(Texture target, int x, int y, int width, int height, long source) {
public void writeToTexture(Texture target, TextureFormat format, GraphicsDataType dataType, long source) {
GL45.glTextureSubImage2D(((GLTexture)target).id(), 0, 0, 0, target.width(), target.height(), GLUtils.toGLExternal(format), GLUtils.toGL(dataType), source);
}
@Override
public void readFromTexture(Texture source, int x, int y, int width, int height, long target) {
public void writeToTexture(Texture target, int sourceX, int sourceY, int targetX, int targetY, int width, int height, TextureFormat format, GraphicsDataType dataType, long source) {
GLStates states = device.states;
states.unpack_alignment.set(target.settings().internal().components());
states.unpack_row_length.set(target.width());
states.unpack_skip_pixel.set(sourceX);
states.unpack_skip_rows.set(sourceY);
GL45.glTextureSubImage2D(((GLTexture)target).id(), 0, targetX, targetY, width, height, GLUtils.toGLExternal(format), GLUtils.toGL(dataType), source);
states.unpack_alignment.setDefault();
states.unpack_row_length.setDefault();
states.unpack_skip_pixel.setDefault();
states.unpack_skip_rows.setDefault();
}
private record PushableObject<T>(T original, T applying, Supplier<T> current, Consumer<T> apply) implements PushableResource {
@@ -122,9 +182,4 @@ public class GLCommandQueue implements GraphicsCommandQueue {
}
private record ScreenBuffer(int fbo, int width, int height) {}
@Override
public void submitCommands(GraphicsCommandBuffer buffer) {
}
}
@@ -1,12 +1,19 @@
package speiger.src.coreengine.graphics.opengl.core;
import org.lwjgl.glfw.GLFW;
import org.lwjgl.opengl.GL;
import speiger.src.coreengine.assets.manager.AssetManager;
import speiger.src.coreengine.graphics.api.core.Graphics;
import speiger.src.coreengine.rendering.input.window.Window;
public class GLGraphics implements Graphics {
AssetManager manager;
public GLGraphics(AssetManager manager) {
this.manager = manager;
}
@Override
public String getName() {
return "OpenGL";
@@ -14,6 +21,9 @@ public class GLGraphics implements Graphics {
@Override
public void setupWindowArguments() {
GLFW.glfwDefaultWindowHints();
GLFW.glfwWindowHint(GLFW.GLFW_VISIBLE, GLFW.GLFW_FALSE);
GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_VERSION_MAJOR, 4);
GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_VERSION_MINOR, 0);
GLFW.glfwWindowHint(GLFW.GLFW_OPENGL_PROFILE, GLFW.GLFW_OPENGL_CORE_PROFILE);
@@ -22,7 +32,7 @@ public class GLGraphics implements Graphics {
@Override
public GLGraphicsDevice createDevice(Window window) {
return new GLGraphicsDevice(window);
return new GLGraphicsDevice(window, manager, GL.createCapabilities());
}
}
@@ -1,19 +1,41 @@
package speiger.src.coreengine.graphics.opengl.core;
import java.nio.ByteBuffer;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Optional;
import java.util.OptionalInt;
import java.util.function.IntPredicate;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL31;
import org.lwjgl.opengl.GL33;
import org.lwjgl.opengl.GL41;
import org.lwjgl.opengl.GL43;
import org.lwjgl.opengl.GL45;
import org.lwjgl.opengl.GL46;
import org.lwjgl.opengl.GLCapabilities;
import org.lwjgl.system.MemoryUtil;
import speiger.src.collections.ints.lists.IntArrayList;
import speiger.src.collections.ints.lists.IntList;
import speiger.src.collections.ints.maps.impl.hash.Int2ObjectLinkedOpenHashMap;
import speiger.src.collections.ints.maps.impl.hash.Int2ObjectOpenHashMap;
import speiger.src.collections.ints.maps.interfaces.Int2ObjectMap;
import speiger.src.collections.objects.maps.impl.hash.Object2IntOpenHashMap;
import speiger.src.collections.objects.maps.impl.hash.Object2ObjectOpenHashMap;
import speiger.src.collections.objects.maps.interfaces.Object2IntMap;
import speiger.src.collections.objects.maps.interfaces.Object2ObjectMap;
import speiger.src.collections.objects.utils.ObjectIterables;
import speiger.src.coreengine.assets.api.IAsset;
import speiger.src.coreengine.assets.api.IAssetProvider;
import speiger.src.coreengine.assets.api.ID;
import speiger.src.coreengine.assets.api.MultiAsset;
import speiger.src.coreengine.assets.manager.AssetManager;
import speiger.src.coreengine.graphics.api.buffer.VertexBuffer;
import speiger.src.coreengine.graphics.api.buffer.states.BufferState;
import speiger.src.coreengine.graphics.api.buffer.states.BufferType;
@@ -22,30 +44,50 @@ import speiger.src.coreengine.graphics.api.core.GraphicsDevice;
import speiger.src.coreengine.graphics.api.mesh.Mesh;
import speiger.src.coreengine.graphics.api.mesh.Mesh.LayoutInfo;
import speiger.src.coreengine.graphics.api.sampler.SamplerSettings;
import speiger.src.coreengine.graphics.api.shader.BufferAttribute;
import speiger.src.coreengine.graphics.api.shader.ShaderPipeline;
import speiger.src.coreengine.graphics.api.shader.VertexBinding;
import speiger.src.coreengine.graphics.api.shader.states.Bindings.TextureBinding;
import speiger.src.coreengine.graphics.api.shader.states.Bindings.UniformBinding;
import speiger.src.coreengine.graphics.api.shader.states.ShaderType;
import speiger.src.coreengine.graphics.api.texture.TextureSettings;
import speiger.src.coreengine.graphics.api.texture.states.SwizzleMask;
import speiger.src.coreengine.graphics.api.texture.states.TextureType;
import speiger.src.coreengine.graphics.api.utils.ExecutionType;
import speiger.src.coreengine.graphics.opengl.buffer.GLVertexBuffer;
import speiger.src.coreengine.graphics.opengl.mesh.GLMesh;
import speiger.src.coreengine.graphics.opengl.sampler.GLSampler;
import speiger.src.coreengine.graphics.opengl.shader.ShaderCache;
import speiger.src.coreengine.graphics.opengl.shader.ShaderInstance;
import speiger.src.coreengine.graphics.opengl.shader.ShaderInstance.SamplerObject;
import speiger.src.coreengine.graphics.opengl.shader.ShaderInstance.ShaderStorage;
import speiger.src.coreengine.graphics.opengl.shader.ShaderInstance.UniformObject;
import speiger.src.coreengine.graphics.opengl.texture.GLTexture;
import speiger.src.coreengine.graphics.opengl.utils.GLFunctions;
import speiger.src.coreengine.graphics.opengl.utils.GLStates;
import speiger.src.coreengine.graphics.opengl.utils.GLUtils;
import speiger.src.coreengine.rendering.input.window.Window;
public class GLGraphicsDevice implements GraphicsDevice {
static final Map<ID, String> IMPORTS = Object2ObjectMap.builder().<ID, String>map().synchronize();
Object2IntMap<ID> shaderCache = new Object2IntOpenHashMap<>();
Object2ObjectMap<ID, ShaderInstance> programCache = new Object2ObjectOpenHashMap<>();
AssetManager manager;
GLCapabilities capabilities;
GLStates states = new GLStates();
ShaderCache cache = new ShaderCache();
GLCommandQueue queue;
Window owner;
public GLGraphicsDevice(Window owner) {
public GLGraphicsDevice(Window owner, AssetManager manager, GLCapabilities capbilities) {
this.owner = owner;
this.manager = manager;
this.capabilities = capbilities;
this.queue = new GLCommandQueue(this);
}
@Override
public Window getWindow() {
public Window window() {
return owner;
}
@@ -56,11 +98,11 @@ public class GLGraphicsDevice implements GraphicsDevice {
@Override
public GraphicsCommandBuffer createCommandBuffer(ExecutionType type) {
return null;
return type == ExecutionType.IMMEDIATE ? new GLImmidateCommandBuffer(this) : new GLRecordingCommandBuffer(this);
}
@Override
public GLCommandQueue getQueue() {
public GLCommandQueue queue() {
return queue;
}
@@ -79,6 +121,7 @@ public class GLGraphicsDevice implements GraphicsDevice {
public GLTexture createTexture(TextureSettings settings, int width, int height) {
if(width < 0) throw new IllegalArgumentException("Width ["+width+"] is invalid");
if(height < 0) throw new IllegalArgumentException("Height ["+height+"] is invalid");
if(settings.type() != TextureType.TEXTURE_2D) throw new IllegalStateException("Currently only 2D textures are supported");
int id = GLFunctions.createTexture(settings.type());
settings.stencilType().ifPresent(T -> GL45.glTextureParameteri(id, GL43.GL_DEPTH_STENCIL_TEXTURE_MODE, GLUtils.toGL(T)));
settings.baseLevel().ifPresent(T -> GL45.glTextureParameteri(id, GL12.GL_TEXTURE_BASE_LEVEL, T));
@@ -92,6 +135,7 @@ public class GLGraphicsDevice implements GraphicsDevice {
GLUtils.toGL(masks.get(3).orElse(SwizzleMask.ALPHA)),
});
}
GL45.glTextureStorage2D(id, 1, GLUtils.toGLInternal(settings.internal()), width, height);
return new GLTexture(settings, id, width, height);
}
@@ -116,7 +160,162 @@ public class GLGraphicsDevice implements GraphicsDevice {
return new GLMesh(builder.info(), new Int2ObjectOpenHashMap<>(), null, null, false);
}
public ShaderInstance createShader(ShaderPipeline line, IAssetProvider provider) {
return null;
@Override
public void preloadPipelines(List<ShaderPipeline> pipelines) {
try(IAssetProvider provider = manager.get()) {
for(ShaderPipeline pipeline : pipelines) {
computeShaderProgram(pipeline, provider);
}
}
catch(Exception e) {
e.printStackTrace();
}
}
public ShaderInstance getShader(ShaderPipeline pipeline) {
return programCache.supplyIfAbsent(pipeline.id(), () -> computeShaderProgram(pipeline));
}
private ShaderInstance computeShaderProgram(ShaderPipeline pipeline) {
try(IAssetProvider provider = manager.get()) {
return computeShaderProgram(pipeline, provider);
}
catch(Exception e) {
e.printStackTrace();
return null;
}
}
private ShaderInstance computeShaderProgram(ShaderPipeline pipeline, IAssetProvider provider) {
MultiAsset assets = MultiAsset.combineNonNull(provider, pipeline.shaders().values().toArray(ID[]::new));
if(assets == null) {
//TODO log stuff
return null;
}
int program = loadOrCreateProgram(pipeline, assets, T -> {
Map<ID, IAsset> map = Object2ObjectMap.builder().map();
assets.forEach(map, (K, V) -> K.put(V.location(), V));
IntList shaders = new IntArrayList();
for(Entry<ShaderType, ID> entry : pipeline.shaders().entrySet()) {
ShaderType type = entry.getKey();
IAsset asset = map.get(entry.getValue());
int id = shaderCache.computeIfAbsent(entry.getValue(), E -> computeShader(E, asset, type, provider));
if(id == -1) {
System.out.println("Couldn't create a shader. Type["+type+"], of Id["+asset.location()+"]");
//TODO implement logging.
//Failed to compile shader
return false;
}
GL20.glAttachShader(T, id);
}
for(BufferAttribute attribute : ObjectIterables.flatMap(pipeline.attributes(), VertexBinding::attributes)) {
GL20.glBindAttribLocation(T, attribute.index(), attribute.name());
}
GL20.glLinkProgram(T);
for(int i = 0,m=shaders.size();i<m;i++) {
GL20.glDetachShader(T, shaders.getInt(i));
}
return true;
});
if(program == 0) return null;
ShaderStorage<UniformObject> uniforms = new ShaderStorage<>();
ShaderStorage<SamplerObject> samplers = new ShaderStorage<>();
for(TextureBinding binding : pipeline.textures()) {
int location = GL20.glGetUniformLocation(program, binding.name());
if(location == -1) {
System.out.println("Couldn't find Location ["+binding.name()+"] in Program ["+program+"]");
//TODO Warn that location wasn't found
continue;
}
samplers.add(binding.name(), binding.slot(), new SamplerObject(binding.slot()));
}
for(UniformBinding binding : pipeline.uniforms()) {
int location = GL31.glGetUniformBlockIndex(program, binding.name());
if(location == GL31.GL_INVALID_INDEX) {
System.out.println("Couldn't find Location ["+binding.name()+"] in Program ["+program+"]");
//TODO Warn that location wasn't found
continue;
}
uniforms.add(binding.name(), binding.slot(), new UniformObject(binding.slot()));
}
ShaderInstance instance = new ShaderInstance(program, uniforms, samplers);
states.shaders.register(instance);
return instance;
}
private int computeShader(ID id, IAsset asset, ShaderType type, IAssetProvider provider) {
StringBuilder builder = new StringBuilder(2048);
try {
for(String line : asset.lines()) {
if(line.startsWith("//")) continue;
if(line.startsWith("#import<") && line.endsWith(">")) builder.append(importLines(provider, ID.of(line.substring(8, line.length()-1))));
else builder.append(line);
builder.append("\n");
};
}
catch(Exception e) {
e.printStackTrace();
return -1;
}
int shader = GL20.glCreateShader(GLUtils.toGL(type));
GL20.glShaderSource(shader, builder);
GL20.glCompileShader(shader);
if(GL20.glGetShaderi(shader, GL20.GL_COMPILE_STATUS) == 0) {
//TODO improve logging
System.out.println(GL20.glGetShaderInfoLog(shader, GL20.glGetShaderi(shader, GL20.GL_INFO_LOG_LENGTH)));
GL20.glDeleteShader(shader);
return -1;
}
return shader;
}
private int loadOrCreateProgram(ShaderPipeline pipeline, MultiAsset assets, IntPredicate callback) {
int id = GL20.glCreateProgram();
boolean fail = true;
if((fail = !loadFromCache(id, pipeline, assets) && callback.test(id))) {
if(GL20.glGetProgrami(id, GL20.GL_LINK_STATUS) == GL11.GL_TRUE) {
cache.store(pipeline.id(), assets.crc(), getProgramBytes(id));
fail = false;
}
}
if(fail) {
GL20.glDeleteProgram(id);
return 0;
}
String error = GL20.glGetProgramInfoLog(id);
if(!error.isBlank()) {
System.out.println("Error: "+error);
}
return id;
}
private byte[] getProgramBytes(int programId) {
ByteBuffer buffer = MemoryUtil.memAlloc(GL20.glGetProgrami(programId, GL41.GL_PROGRAM_BINARY_LENGTH) + 4);
int[] format = new int[1];
GL41.glGetProgramBinary(programId, null, format, buffer.position(4));
buffer.putInt(0, format[0]).flip();
byte[] data = new byte[buffer.remaining()];
buffer.get(data);
MemoryUtil.memFree(buffer);
return data;
}
private boolean loadFromCache(int programId, ShaderPipeline pipeline, MultiAsset asset) {
ByteBuffer data = cache.get(pipeline.id(), asset::crc);
if(data == null) return false;
GL41.glProgramBinary(programId, data.getInt(), data);
MemoryUtil.memFree(data);
return GL46.glGetProgrami(programId, GL46.GL_LINK_STATUS) == GL46.GL_TRUE;
}
public static String importLines(IAssetProvider provider, ID location) {
return IMPORTS.computeIfAbsent(location, T -> {
try { return ObjectIterables.filter(provider.get(T).lines(), E -> !E.startsWith("//")).reduce(new StringBuilder(), (K, V) -> K.append(V).append("\n")).toString(); }
catch(Exception e) {
e.printStackTrace();
return "";
}
});
}
}
@@ -1,85 +1,206 @@
package speiger.src.coreengine.graphics.opengl.core;
import java.util.Objects;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL30;
import org.lwjgl.opengl.GL31;
import org.lwjgl.opengl.GL33;
import org.lwjgl.opengl.GL45;
import speiger.src.coreengine.graphics.api.buffer.VertexBuffer;
import speiger.src.coreengine.graphics.api.core.GraphicsCommandBuffer;
import speiger.src.coreengine.graphics.api.mesh.Mesh;
import speiger.src.coreengine.graphics.api.sampler.Sampler;
import speiger.src.coreengine.graphics.api.shader.ColorTarget;
import speiger.src.coreengine.graphics.api.shader.DepthTarget;
import speiger.src.coreengine.graphics.api.shader.RasterizerState;
import speiger.src.coreengine.graphics.api.shader.ShaderPipeline;
import speiger.src.coreengine.graphics.api.texture.Texture;
import speiger.src.coreengine.graphics.api.utils.ScissorsManager;
import speiger.src.coreengine.graphics.opengl.buffer.GLVertexBuffer;
import speiger.src.coreengine.graphics.opengl.mesh.GLMesh;
import speiger.src.coreengine.graphics.opengl.sampler.GLSampler;
import speiger.src.coreengine.graphics.opengl.shader.ShaderInstance;
import speiger.src.coreengine.graphics.opengl.texture.GLTexture;
import speiger.src.coreengine.graphics.opengl.utils.GLScissorsHandler;
import speiger.src.coreengine.graphics.opengl.utils.GLStates;
import speiger.src.coreengine.graphics.opengl.utils.GLUtils;
public class GLImmidateCommandBuffer implements GraphicsCommandBuffer {
GLGraphicsDevice device;
boolean removed = false;
boolean recording = false;
ShaderPipeline pipeline;
Mesh mesh;
int recorded = 0;
ScissorsManager scissors;
ShaderPipeline pipeline;
ShaderInstance shader;
GLMesh mesh;
public GLImmidateCommandBuffer(GLGraphicsDevice device) {
this.device = device;
this.scissors = new ScissorsManager(8, new GLScissorsHandler(device.states));
}
protected void ensureDrawing() {
if(removed) throw new IllegalStateException("CommandBuffer is removed");
if(!recording) throw new IllegalStateException("CommandBuffer isn't recording");
}
@Override
public boolean isRemoved() { return false; }
public boolean isRemoved() { return removed; }
@Override
public void remove() {}
public void remove() {
clear();
device = null;
removed = true;
recorded = 0;
}
@Override
public GraphicsCommandBuffer begin() {
if(removed) throw new IllegalStateException("CommandBuffer is removed");
if(recording) throw new IllegalStateException("CommandBuffer already recording");
recording = true;
recorded = 0;
return this;
}
@Override
public GraphicsCommandBuffer setScissors(int x, int y, int width, int height) {
ensureDrawing();
scissors.setRoot(x, y, width, height);
return this;
}
@Override
public GraphicsCommandBuffer pipeline(ShaderPipeline pipeline) {
Objects.requireNonNull(pipeline);
ensureDrawing();
if(this.pipeline == pipeline) return this;
this.pipeline = pipeline;
shader = device.getShader(pipeline);
if(shader == null) {
//TODO implement logging
return this;
}
GLStates states = device.states;
states.shaders.bind(shader);
RasterizerState rasterization = pipeline.rasterizer();
states.polygon.set(rasterization.fillMode());
states.backCulling.set(rasterization.cullBack());
DepthTarget depth = pipeline.depthTarget();
if(depth.enabled()) {
states.depth.enable();
states.setDepthMask(depth.write());
states.setDepthFunction(depth.function());
}
else states.depth.disable();
ColorTarget color = pipeline.colorTarget();
color.blend().ifPresentOrElse(states.blend::enableSet, states.blend::disable);
states.setColorMask(color.writeMask());
recorded++;
return this;
}
@Override
public GraphicsCommandBuffer mesh(Mesh mesh) {
this.mesh = mesh;
Objects.requireNonNull(mesh);
ensureDrawing();
if(this.mesh == mesh) return this;
this.mesh = (GLMesh)mesh;
this.mesh.bind();
// GL30.glBindVertexArray(this.mesh.id());
recorded++;
return this;
}
@Override
public GraphicsCommandBuffer texture(int set, int binding, Texture texture, Sampler sampler) {
public GraphicsCommandBuffer texture(int binding, Texture texture, Sampler sampler) {
Objects.requireNonNull(texture);
Objects.requireNonNull(sampler);
ensureDrawing();
Objects.requireNonNull(shader, "No Pipeline found");
Objects.requireNonNull(shader.samplers().byIndex(binding), "Texture["+binding+"] binding not found");
// GLStates states = device.states;
GL45.glBindTextureUnit(binding, ((GLTexture)texture).id());
GL33.glBindSampler(binding, ((GLSampler)sampler).id());
// states.textures.bind(binding, ((GLTexture)texture).id());
// states.samplers.bind(binding, ((GLSampler)sampler).id());
recorded++;
return this;
}
@Override
public GraphicsCommandBuffer uniform(int set, int binding, VertexBuffer buffer) {
public GraphicsCommandBuffer uniform(int binding, VertexBuffer buffer) {
Objects.requireNonNull(buffer);
ensureDrawing();
Objects.requireNonNull(shader, "No Pipeline found");
Objects.requireNonNull(shader.uniforms().byIndex(binding), "Uniform for binding ["+binding+"] doesn't exist");
GL30.glBindBufferBase(GL31.GL_UNIFORM_BUFFER, binding, ((GLVertexBuffer)buffer).id());
recorded++;
return this;
}
@Override
public GraphicsCommandBuffer uniform(int binding, VertexBuffer buffer, long offset, long size) {
Objects.requireNonNull(buffer);
ensureDrawing();
Objects.requireNonNull(shader, "No Pipeline found");
Objects.requireNonNull(shader.uniforms().byIndex(binding), "Uniform for binding ["+binding+"] doesn't exist");
GL30.glBindBufferRange(GL31.GL_UNIFORM_BUFFER, binding, ((GLVertexBuffer)buffer).id(), offset, size);
recorded++;
return this;
}
@Override
public GraphicsCommandBuffer drawIndexed(int start, int count) {
public GraphicsCommandBuffer drawArrays(int offset, int count) {
ensureDrawing();
Objects.requireNonNull(shader, "No Pipeline found");
GL11.glDrawArrays(GLUtils.toGL(pipeline.rasterizer().mode()), offset, count);
recorded++;
return this;
}
@Override
public GraphicsCommandBuffer drawElements(int offset, int count) {
ensureDrawing();
Objects.requireNonNull(shader, "No Pipeline found");
GL11.nglDrawElements(GLUtils.toGL(pipeline.rasterizer().mode()), count, GLUtils.toGL(mesh.indeciesType()), offset);
recorded++;
return this;
}
@Override
public GraphicsCommandBuffer pushScissors(int x, int y, int width, int height) {
ensureDrawing();
scissors.push(x, y, width, height);
return this;
}
@Override
public GraphicsCommandBuffer popScissors() {
ensureDrawing();
scissors.pop();
return this;
}
@Override
public GraphicsCommandBuffer end() {
if(!recording) throw new IllegalStateException("Already Stopped Recording");
pipeline = null;
mesh = null;
recording = false;
clear();
return this;
}
private void clear() {
recording = false;
pipeline = null;
shader = null;
mesh = null;
scissors.clear();
}
@Override
public int commandCount() { return recorded; }
}
@@ -0,0 +1,239 @@
package speiger.src.coreengine.graphics.opengl.core;
import java.util.List;
import java.util.Objects;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL30;
import org.lwjgl.opengl.GL31;
import speiger.src.collections.objects.lists.ObjectArrayList;
import speiger.src.coreengine.graphics.api.buffer.VertexBuffer;
import speiger.src.coreengine.graphics.api.buffer.states.IndeciesType;
import speiger.src.coreengine.graphics.api.core.GraphicsCommandBuffer;
import speiger.src.coreengine.graphics.api.mesh.Mesh;
import speiger.src.coreengine.graphics.api.sampler.Sampler;
import speiger.src.coreengine.graphics.api.shader.ColorTarget;
import speiger.src.coreengine.graphics.api.shader.DepthTarget;
import speiger.src.coreengine.graphics.api.shader.RasterizerState;
import speiger.src.coreengine.graphics.api.shader.ShaderPipeline;
import speiger.src.coreengine.graphics.api.shader.states.DrawMode;
import speiger.src.coreengine.graphics.api.texture.Texture;
import speiger.src.coreengine.graphics.api.utils.ScissorsManager;
import speiger.src.coreengine.graphics.api.utils.ScissorsManager.NoOp;
import speiger.src.coreengine.graphics.opengl.buffer.GLVertexBuffer;
import speiger.src.coreengine.graphics.opengl.mesh.GLMesh;
import speiger.src.coreengine.graphics.opengl.sampler.GLSampler;
import speiger.src.coreengine.graphics.opengl.shader.ShaderInstance;
import speiger.src.coreengine.graphics.opengl.texture.GLTexture;
import speiger.src.coreengine.graphics.opengl.utils.GLStates;
import speiger.src.coreengine.graphics.opengl.utils.GLUtils;
import speiger.src.coreengine.math.vector.ints.Vec4i;
public class GLRecordingCommandBuffer implements GraphicsCommandBuffer {
List<Runnable> tasks = new ObjectArrayList<>();
boolean recording = false;
boolean removed = false;
GLGraphicsDevice device;
ScissorsManager scissors = new ScissorsManager(8, NoOp.INSTANCE);
ShaderPipeline pipeline;
ShaderInstance shader;
GLMesh mesh;
public GLRecordingCommandBuffer(GLGraphicsDevice device) {
this.device = device;
}
protected void ensureDrawing() {
if(removed) throw new IllegalStateException("CommandBuffer is removed");
if(!recording) throw new IllegalStateException("CommandBuffer isn't recording");
}
@Override
public boolean isRemoved() {
return removed;
}
@Override
public void remove() {
removed = true;
clear();
recording = false;
tasks.clear();
}
@Override
public GraphicsCommandBuffer begin() {
if(removed) throw new IllegalStateException("CommandBuffer is removed");
if(recording) throw new IllegalStateException("CommandBuffer already recording");
recording = true;
tasks.clear();
return this;
}
@Override
public GraphicsCommandBuffer setScissors(int x, int y, int width, int height) {
ensureDrawing();
scissors.setRoot(x, y, width, height);
return this;
}
@Override
public GraphicsCommandBuffer pipeline(ShaderPipeline pipeline) {
Objects.requireNonNull(pipeline);
ensureDrawing();
if(this.pipeline == pipeline) return this;
this.pipeline = pipeline;
shader = device.getShader(pipeline);
tasks.add(new SetPipeline(pipeline, shader, device.states));
return this;
}
@Override
public GraphicsCommandBuffer mesh(Mesh mesh) {
Objects.requireNonNull(mesh);
ensureDrawing();
if(this.mesh == mesh) return this;
this.mesh = (GLMesh)mesh;
this.tasks.add(new SetMesh(this.mesh));
return this;
}
@Override
public GraphicsCommandBuffer texture(int binding, Texture texture, Sampler sampler) {
Objects.requireNonNull(texture);
Objects.requireNonNull(sampler);
ensureDrawing();
Objects.requireNonNull(shader, "No Pipeline found");
Objects.requireNonNull(shader.samplers().byIndex(binding), "Texture["+binding+"] binding not found");
tasks.add(new SetTexture(binding, (GLTexture)texture, (GLSampler)sampler, device.states));
return this;
}
@Override
public GraphicsCommandBuffer uniform(int binding, VertexBuffer buffer) {
Objects.requireNonNull(buffer);
ensureDrawing();
Objects.requireNonNull(shader, "No Pipeline found");
Objects.requireNonNull(shader.uniforms().byIndex(binding), "Uniform for binding ["+binding+"] doesn't exist");
tasks.add(new SetUniform(binding, (GLVertexBuffer)buffer, -1, -1));
return this;
}
@Override
public GraphicsCommandBuffer uniform(int binding, VertexBuffer buffer, long offset, long size) {
Objects.requireNonNull(buffer);
ensureDrawing();
Objects.requireNonNull(shader, "No Pipeline found");
Objects.requireNonNull(shader.uniforms().byIndex(binding), "Uniform for binding ["+binding+"] doesn't exist");
tasks.add(new SetUniform(binding, (GLVertexBuffer)buffer, offset, size));
return this;
}
@Override
public GraphicsCommandBuffer drawArrays(int offset, int count) {
ensureDrawing();
Objects.requireNonNull(shader, "No Pipeline found");
DrawMode mode = pipeline.rasterizer().mode();
tasks.add(() -> GL11.glDrawArrays(GLUtils.toGL(mode), offset, count));
return this;
}
@Override
public GraphicsCommandBuffer drawElements(int offset, int count) {
ensureDrawing();
Objects.requireNonNull(shader, "No Pipeline found");
DrawMode mode = pipeline.rasterizer().mode();
IndeciesType type = mesh.indeciesType();
tasks.add(() -> GL11.nglDrawElements(GLUtils.toGL(mode), count, GLUtils.toGL(type), offset));
return this;
}
@Override
public GraphicsCommandBuffer pushScissors(int x, int y, int width, int height) {
ensureDrawing();
scissors.push(x, y, width, height);
tasks.add(new SetScissors(scissors.top(), device.states));
return this;
}
@Override
public GraphicsCommandBuffer popScissors() {
ensureDrawing();
scissors.pop();
tasks.add(new SetScissors(scissors.top(), device.states));
return this;
}
@Override
public GraphicsCommandBuffer end() {
if(!recording) throw new IllegalStateException("Already Stopped Recording");
clear();
return this;
}
@Override
public int commandCount() {
return tasks.size();
}
public void execute() {
tasks.forEach(Runnable::run);
}
private void clear() {
recording = false;
pipeline = null;
shader = null;
mesh = null;
scissors.clear();
}
public record SetScissors(Vec4i area, GLStates states) implements Runnable {
@Override
public void run() { states.scissors.set(area); }
}
public record SetPipeline(ShaderPipeline pipeline, ShaderInstance instance, GLStates states) implements Runnable {
@Override
public void run() {
states.shaders.bind(instance);
RasterizerState rasterization = pipeline.rasterizer();
states.polygon.set(rasterization.fillMode());
states.backCulling.set(rasterization.cullBack());
DepthTarget depth = pipeline.depthTarget();
if(depth.enabled()) {
states.depth.enable();
states.setDepthMask(depth.write());
states.setDepthFunction(depth.function());
}
else states.depth.disable();
ColorTarget color = pipeline.colorTarget();
color.blend().ifPresentOrElse(states.blend::enableSet, states.blend::disable);
states.setColorMask(color.writeMask());
}
}
public record SetMesh(GLMesh mesh) implements Runnable {
@Override
public void run() { GL30.glBindVertexArray(mesh.id()); }
}
public record SetTexture(int binding, GLTexture texture, GLSampler sampler, GLStates states) implements Runnable {
@Override
public void run() {
states.textures.bind(binding, texture.id());
states.samplers.bind(binding, sampler.id());
}
}
public record SetUniform(int binding, GLVertexBuffer buffer, long offset, long size) implements Runnable {
@Override
public void run() {
if(size < 0 || offset < 0) GL30.glBindBufferBase(GL31.GL_UNIFORM_BUFFER, binding, buffer.id());
else GL30.glBindBufferRange(GL31.GL_UNIFORM_BUFFER, binding, buffer.id(), offset, size);
}
}
}
@@ -1,6 +1,7 @@
package speiger.src.coreengine.graphics.opengl.core;
import org.lwjgl.glfw.GLFW;
import org.lwjgl.opengl.GL11;
import speiger.src.coreengine.graphics.api.core.GraphicsSurface;
import speiger.src.coreengine.rendering.input.window.Window;
@@ -19,6 +20,9 @@ public class GLSurface implements GraphicsSurface {
@Override
public void beginFrame() {
GL11.glClearColor(0.2F, 0.55F, 0.66F, 1F);
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);
window.beginFrame();
GLFW.glfwPollEvents();
}
@@ -1,6 +1,7 @@
package speiger.src.coreengine.graphics.opengl.mesh;
import org.jspecify.annotations.Nullable;
import org.lwjgl.opengl.GL30;
import org.lwjgl.opengl.GL45;
import speiger.src.collections.ints.collections.IntIterator;
@@ -29,6 +30,22 @@ public class GLMesh extends Mesh {
}
}
public int id() {
return vao;
}
public void bind() {
GL30.glBindVertexArray(vao);
for(Element element : layouts.values().map(LayoutInfo::layout).flatMap(VertexLayout::elements)) {
GL45.glEnableVertexArrayAttrib(vao, element.index());
}
}
@Override
public boolean equals(Object obj) {
return obj instanceof GLMesh mesh && mesh.vao == vao;
}
@Override
public boolean isRemoved() { return vao == 0; }
@@ -47,6 +64,7 @@ public class GLMesh extends Mesh {
}
private void bindElement() {
indeciesBuffer.bind();
GL45.glVertexArrayElementBuffer(vao, ((GLVertexBuffer)indeciesBuffer).id());
}
@@ -54,6 +72,7 @@ public class GLMesh extends Mesh {
LayoutInfo info = layouts.get(binding);
VertexLayout layout = info.layout();
GLVertexBuffer buffer = (GLVertexBuffer)buffers.get(binding);
buffer.bind();
GL45.glVertexArrayVertexBuffer(vao, binding, buffer.id(), 0, layout.bytes());
if(info.instanceCount() > 0) GL45.glVertexArrayBindingDivisor(vao, binding, info.instanceCount());
int index = 0;
@@ -27,6 +27,10 @@ public class GLSampler extends Sampler {
GL45.glSamplerParameteri(id, GL11.GL_TEXTURE_MAG_FILTER, GLUtils.toGL(settings.magSample()));
}
public int id() {
return id;
}
@Override
public boolean isRemoved() {
return id == 0;
@@ -2,10 +2,14 @@ package speiger.src.coreengine.graphics.opengl.shader;
import java.io.BufferedReader;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Map;
import java.util.Objects;
import java.util.function.Supplier;
import org.lwjgl.system.MemoryUtil;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
@@ -62,12 +66,16 @@ public class ShaderCache {
catch(Exception e) { e.printStackTrace(); }
}
public byte[] get(ID shaderId, String fileHash) {
public ByteBuffer get(ID shaderId, Supplier<String> fileHash) {
if(cache == null) return null;
String known = hashCache.get(shaderId);
if(!Objects.equals(known, fileHash)) return null;
if(known == null || !Objects.equals(known, fileHash.get())) return null;
Path path = toFile(shaderId);
if(Files.notExists(path)) return null;
try { return Files.readAllBytes(path); }
try {
byte[] data = Files.readAllBytes(path);
return MemoryUtil.memAlloc(data.length).put(data).flip();
}
catch(Exception e) { e.printStackTrace(); }
return null;
}
@@ -76,6 +84,7 @@ public class ShaderCache {
Objects.requireNonNull(shaderId);
Objects.requireNonNull(fileHash);
Objects.requireNonNull(shader);
if(cache == null) return;
hashCache.put(shaderId, fileHash);
try {
Path file = toFile(shaderId);
@@ -89,6 +98,7 @@ public class ShaderCache {
}
public void delete(ID shaderId) {
if(cache == null) return;
if(hashCache.remove(shaderId) == null) return;
try { Files.deleteIfExists(toFile(shaderId)); }
catch(Exception e) { e.printStackTrace(); }
@@ -2,8 +2,29 @@ package speiger.src.coreengine.graphics.opengl.shader;
import java.util.Map;
public record ShaderInstance(int programId, Map<String, UniformObject> uniforms, Map<String, SamplerObject> samplers) {
import speiger.src.collections.ints.maps.impl.hash.Int2ObjectOpenHashMap;
import speiger.src.collections.ints.maps.interfaces.Int2ObjectMap;
import speiger.src.collections.objects.maps.impl.hash.Object2ObjectOpenHashMap;
public record ShaderInstance(int programId, ShaderStorage<UniformObject> uniforms, ShaderStorage<SamplerObject> samplers) {
public record UniformObject(int slot) {}
public record SamplerObject(int unit) {}
public static class ShaderStorage<T> {
Map<String, T> byName = new Object2ObjectOpenHashMap<>();
Int2ObjectMap<T> byIndex = new Int2ObjectOpenHashMap<>();
public void add(String name, int index, T value) {
byName.put(name, value);
byIndex.put(index, value);
}
public T byName(String name) {
return byName.get(name);
}
public T byIndex(int index) {
return byIndex.get(index);
}
}
}
@@ -0,0 +1,26 @@
package speiger.src.coreengine.graphics.opengl.shader;
import org.lwjgl.opengl.GL20;
import speiger.src.collections.objects.lists.ObjectArrayList;
import speiger.src.collections.objects.lists.ObjectList;
public class ShaderTracker {
ObjectList<ShaderInstance> knownShaders = new ObjectArrayList<ShaderInstance>();
int boundShader;
public void register(ShaderInstance instance) {
knownShaders.add(instance);
}
public void remove(ShaderInstance instance) {
knownShaders.remove(instance);
}
public void bind(ShaderInstance instance) {
int id = instance.programId();
if(id == boundShader) return;
boundShader = id;
GL20.glUseProgram(id);
}
}
@@ -4,16 +4,16 @@ import org.lwjgl.opengl.GL45;
import speiger.src.collections.objects.maps.impl.hash.Object2IntOpenHashMap;
import speiger.src.collections.objects.maps.interfaces.Object2IntMap;
import speiger.src.coreengine.assets.AssetLocation;
import speiger.src.coreengine.assets.api.ID;
public class FrameBufferCache {
Object2IntMap<AssetLocation> fboCache = new Object2IntOpenHashMap<>();
Object2IntMap<ID> fboCache = new Object2IntOpenHashMap<>();
public int getOrCreateFBO(AssetLocation id) {
public int getOrCreateFBO(ID id) {
return fboCache.supplyIntIfAbsent(id, GL45::glCreateFramebuffers);
}
public void deleteFBO(AssetLocation id) {
public void deleteFBO(ID id) {
int fbo = fboCache.rem(id);
if(fbo == -1) return;
GL45.glDeleteFramebuffers(fbo);
@@ -0,0 +1,13 @@
package speiger.src.coreengine.graphics.opengl.utils;
import speiger.src.coreengine.graphics.api.utils.ScissorsManager.IScissorsHandler;
import speiger.src.coreengine.math.vector.ints.Vec4i;
public record GLScissorsHandler(GLStates states) implements IScissorsHandler {
@Override
public void enable() { states.scissors.enable(); }
@Override
public void disable() { states.scissors.disable(); }
@Override
public void apply(int x, int y, int width, int height) { states.scissors.set(Vec4i.of(x, y, x + width, y + height)); }
}
@@ -0,0 +1,64 @@
package speiger.src.coreengine.graphics.opengl.utils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL13;
import speiger.src.coreengine.graphics.api.shader.ColorTarget;
import speiger.src.coreengine.graphics.api.utils.AlphaFunction;
import speiger.src.coreengine.graphics.opengl.shader.ShaderTracker;
import speiger.src.coreengine.graphics.opengl.utils.states.BlendState;
import speiger.src.coreengine.graphics.opengl.utils.states.CullState;
import speiger.src.coreengine.graphics.opengl.utils.states.FloatState;
import speiger.src.coreengine.graphics.opengl.utils.states.GLPolygon;
import speiger.src.coreengine.graphics.opengl.utils.states.GLState;
import speiger.src.coreengine.graphics.opengl.utils.states.PixelState;
import speiger.src.coreengine.graphics.opengl.utils.states.SamplerState;
import speiger.src.coreengine.graphics.opengl.utils.states.ScissorState;
import speiger.src.coreengine.graphics.opengl.utils.states.TextureState;
public final class GLStates {
public final GLState multiSampling = new GLState(GL13.GL_MULTISAMPLE);
public final GLState depth = new GLState(GL11.GL_DEPTH_TEST);
public final GLPolygon polygon = new GLPolygon();
public final GLState backCulling = new CullState();
public final FloatState pointSize = new FloatState(1F, GL11::glPointSize);
public final FloatState lineSize = new FloatState(1F, GL11::glLineWidth);
public final TextureState textures = new TextureState();
public final SamplerState samplers = new SamplerState();
public final BlendState blend = new BlendState();
public final ScissorState scissors = new ScissorState();
public final ViewPortStack viewPort = new ViewPortStack();
//Texture States
public final PixelState unpack_alignment = new PixelState(GL11.GL_UNPACK_ALIGNMENT, 4);
public final PixelState unpack_skip_pixel = new PixelState(GL11.GL_UNPACK_SKIP_PIXELS, 0);
public final PixelState unpack_skip_rows = new PixelState(GL11.GL_UNPACK_SKIP_ROWS, 0);
public final PixelState unpack_row_length = new PixelState(GL11.GL_UNPACK_ROW_LENGTH, 0);
public final ShaderTracker shaders = new ShaderTracker();
int colorMask = ColorTarget.ALL;
boolean depthMask = true;
AlphaFunction depthFunction = AlphaFunction.GEQUAL;
int activeVAO = 0;
int activeShader = 0;
public void setColorMask(int mask) {
if(colorMask == mask) return;
colorMask = mask;
GL11.glColorMask((mask & ColorTarget.RED) != 0, (mask & ColorTarget.GREEN) != 0, (mask & ColorTarget.BLUE) != 0, (mask & ColorTarget.ALPHA) != 0);
}
public void setDepthMask(boolean depth) {
if(depthMask == depth) return;
depthMask = depth;
GL11.glDepthMask(depth);
}
public void setDepthFunction(AlphaFunction function) {
if(depthFunction == function) return;
depthFunction = function;
GL11.glDepthFunc(GLUtils.toGL(function));
}
}
@@ -5,6 +5,7 @@ import org.lwjgl.opengl.GL12;
import org.lwjgl.opengl.GL13;
import org.lwjgl.opengl.GL14;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL21;
import org.lwjgl.opengl.GL30;
import org.lwjgl.opengl.GL31;
@@ -16,17 +17,21 @@ import org.lwjgl.opengl.GL44;
import speiger.src.coreengine.graphics.api.buffer.states.BufferState;
import speiger.src.coreengine.graphics.api.buffer.states.BufferType;
import speiger.src.coreengine.graphics.api.buffer.states.IndeciesType;
import speiger.src.coreengine.graphics.api.sampler.states.BorderMode;
import speiger.src.coreengine.graphics.api.sampler.states.SampleMode;
import speiger.src.coreengine.graphics.api.shader.states.BlendFactor;
import speiger.src.coreengine.graphics.api.shader.states.DrawMode;
import speiger.src.coreengine.graphics.api.shader.states.GraphicsDataType;
import speiger.src.coreengine.graphics.api.shader.states.PolygonMode;
import speiger.src.coreengine.graphics.api.shader.states.ShaderType;
import speiger.src.coreengine.graphics.api.texture.states.StencilType;
import speiger.src.coreengine.graphics.api.texture.states.SwizzleMask;
import speiger.src.coreengine.graphics.api.texture.states.TextureFormat;
import speiger.src.coreengine.graphics.api.texture.states.TextureType;
import speiger.src.coreengine.graphics.api.utils.AlphaFunction;
public class GLUtils {
//Buffer Begin
public class GLUtils {
public static int toGL(BufferType type) {
return switch(type) {
case ARRAY_BUFFER -> GL15.GL_ARRAY_BUFFER;
@@ -58,9 +63,7 @@ public class GLUtils {
case DYNAMIC_COPY -> GL15.GL_DYNAMIC_COPY;
};
}
//Texture Begin
public static int toGL(TextureType type) {
return switch(type) {
case TEXTURE_1D -> GL11.GL_TEXTURE_1D;
@@ -77,6 +80,45 @@ public class GLUtils {
};
}
public static int toGLInternal(TextureFormat format) {
return switch(format) {
case R -> GL11.GL_RED;
case RG -> GL30.GL_RG;
case RGB -> GL11.GL_RGB;
case RGBA -> GL11.GL_RGBA8;
case DEPTH -> GL11.GL_DEPTH_COMPONENT;
case DEPTH_STENCIL -> GL30.GL_DEPTH_STENCIL;
case LUMINANCE -> GL30.GL_R8;
case LUMINANCE_ALPHA -> GL11.GL_LUMINANCE8_ALPHA8;
};
}
public static int toGLExternal(TextureFormat format) {
return switch(format) {
case R -> GL11.GL_RED;
case RG -> GL30.GL_RG;
case RGB -> GL11.GL_RGB;
case RGBA -> GL11.GL_RGBA;
case DEPTH -> GL11.GL_DEPTH_COMPONENT;
case DEPTH_STENCIL -> GL30.GL_DEPTH_STENCIL;
case LUMINANCE -> GL30.GL_R8;
case LUMINANCE_ALPHA -> GL11.GL_LUMINANCE8_ALPHA8;
};
}
public static int components(TextureFormat format) {
return switch(format) {
case R -> 1;
case RG -> 2;
case RGB -> 3;
case RGBA -> 4;
case DEPTH -> 1;
case DEPTH_STENCIL -> 2;
case LUMINANCE -> 1;
case LUMINANCE_ALPHA -> 2;
};
}
public static int toGL(SwizzleMask mask) {
return switch(mask) {
case RED -> GL11.GL_RED;
@@ -154,4 +196,64 @@ public class GLUtils {
case UNSIGNED_INT_5_9_9_9_REV -> GL30.GL_UNSIGNED_INT_5_9_9_9_REV;
};
}
public static int toGL(BlendFactor type) {
return switch(type) {
case ZERO -> GL11.GL_ZERO;
case ONE -> GL11.GL_ONE;
case CONSTANT_COLOR -> GL15.GL_CONSTANT_COLOR;
case CONSTANT_ALPHA -> GL15.GL_CONSTANT_ALPHA;
case SRC_COLOR -> GL11.GL_SRC_COLOR;
case SRC_ALPHA -> GL11.GL_SRC_ALPHA;
case DST_COLOR -> GL11.GL_DST_COLOR;
case DST_ALPHA -> GL11.GL_DST_ALPHA;
case ONE_MINUS_SRC_COLOR -> GL11.GL_ONE_MINUS_SRC_COLOR;
case ONE_MINUS_SRC_ALPHA -> GL11.GL_ONE_MINUS_SRC_ALPHA;
case ONE_MINUS_DST_COLOR -> GL11.GL_ONE_MINUS_DST_COLOR;
case ONE_MINUS_DST_ALPHA -> GL11.GL_ONE_MINUS_DST_ALPHA;
case ONE_MINUS_CONSTANT_COLOR -> GL15.GL_ONE_MINUS_CONSTANT_COLOR;
case ONE_MINUS_CONSTANT_ALPHA -> GL15.GL_ONE_MINUS_CONSTANT_ALPHA;
case SRC_ALPHA_SATURATE -> GL11.GL_SRC_ALPHA_SATURATE;
};
}
public static int toGL(PolygonMode state) {
return switch(state) {
case FILL -> GL11.GL_FILL;
case LINE -> GL11.GL_LINE;
case POINT -> GL11.GL_POINT;
};
}
public static int toGL(ShaderType type) {
return switch(type) {
case VERTEX -> GL20.GL_VERTEX_SHADER;
case FRAGMENT -> GL20.GL_FRAGMENT_SHADER;
case GEOMETRY -> GL32.GL_GEOMETRY_SHADER;
case TESSELATION_CONTROL -> GL40.GL_TESS_CONTROL_SHADER;
case TESSELATION_EVALUATION -> GL40.GL_TESS_EVALUATION_SHADER;
};
}
public static int toGL(DrawMode mode) {
return switch(mode) {
case POINT -> GL11.GL_POINT;
case LINES -> GL11.GL_LINES;
case LINE_LOOP -> GL11.GL_LINE_LOOP;
case LINE_STRIP -> GL11.GL_LINE_STRIP;
case TRIANGLES -> GL11.GL_TRIANGLES;
case TRIANGLE_FAN -> GL11.GL_TRIANGLE_FAN;
case TRIANGLE_STRIP -> GL11.GL_TRIANGLE_STRIP;
case QUADS -> GL11.GL_QUADS;
};
}
public static int toGL(IndeciesType type) {
return switch(type) {
case BYTE -> GL11.GL_UNSIGNED_BYTE;
case SHORT -> GL11.GL_UNSIGNED_SHORT;
case INT -> GL11.GL_UNSIGNED_INT;
};
}
}
@@ -0,0 +1,36 @@
package speiger.src.coreengine.graphics.opengl.utils;
import org.lwjgl.opengl.GL11;
import speiger.src.collections.objects.lists.ObjectArrayList;
import speiger.src.collections.utils.Stack;
import speiger.src.coreengine.math.vector.ints.Vec4i;
public class ViewPortStack {
Stack<Vec4i> stack = new ObjectArrayList<>();
Vec4i defaultPort = Vec4i.ZERO;
public void setDefault(int x, int y, int w, int h) {
if(defaultPort.x() == x && defaultPort.y() == y && defaultPort.z() == w && defaultPort.w() == h) return;
defaultPort.set(x, y, w, h);
if(stack.isEmpty()) GL11.glViewport(x, y, w, h);
}
public void push(int x, int y, int w, int h) {
Vec4i current = top();
if(current.x() == x && current.y() == y && current.z() == w && current.w() == h) return;
stack.push(Vec4i.of(x, y, w, h));
GL11.glViewport(x, y, w, h);
}
public void pop() {
if(stack.isEmpty()) return;
stack.pop();
Vec4i current = top();
GL11.glViewport(current.x(), current.y(), current.z(), current.w());
}
public Vec4i top() {
return stack.isEmpty() ? defaultPort : stack.top();
}
}
@@ -0,0 +1,39 @@
package speiger.src.coreengine.graphics.opengl.utils.states;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
import speiger.src.coreengine.graphics.api.shader.states.BlendFunction;
import speiger.src.coreengine.graphics.opengl.utils.GLUtils;
public class BlendState extends GLState {
BlendFunction function = BlendFunction.DEFAULT;
public BlendState() {
super(GL11.GL_BLEND);
}
public BlendState enableSet(BlendFunction function) {
enable();
return set(function);
}
public BlendState set(BlendFunction function) {
if(this.function.equals(function)) return this;
this.function = function;
GL15.glBlendFuncSeparate(GLUtils.toGL(function.colorSource()), GLUtils.toGL(function.colorTarget()), GLUtils.toGL(function.alphaSource()), GLUtils.toGL(function.alphaTarget()));
return this;
}
@Override
public void reapply() {
super.reapply();
GL15.glBlendFuncSeparate(GLUtils.toGL(function.colorSource()), GLUtils.toGL(function.colorTarget()), GLUtils.toGL(function.alphaSource()), GLUtils.toGL(function.alphaTarget()));
}
@Override
public void setDefault() {
super.setDefault();
set(BlendFunction.DEFAULT);
}
}
@@ -0,0 +1,23 @@
package speiger.src.coreengine.graphics.opengl.utils.states;
import org.lwjgl.opengl.GL11;
public class CullState extends GLState {
public CullState() {
super(GL11.GL_CULL_FACE, false);
}
@Override
public GLState enable() {
super.enable();
GL11.glCullFace(GL11.GL_BACK);
return this;
}
@Override
public void reapply() {
super.reapply();
GL11.glCullFace(GL11.GL_BACK);
}
}
@@ -0,0 +1,40 @@
package speiger.src.coreengine.graphics.opengl.utils.states;
import speiger.src.collections.floats.functions.FloatConsumer;
public class FloatState implements IGLState {
protected final float defaultValue;
protected float value;
FloatConsumer setter;
public FloatState(float defaultValue, FloatConsumer setter) {
this.defaultValue = this.value = defaultValue;
this.setter = setter;
}
public FloatState set(float value) {
if(equalsNot(this.value, value)) {
this.value = value;
setter.accept(value);
}
return this;
}
public float get() {
return value;
}
@Override
public void setDefault() {
set(defaultValue);
}
@Override
public void reapply() {
setter.accept(defaultValue);
}
protected boolean equalsNot(float key, float value) {
return Float.floatToIntBits(key) != Float.floatToIntBits(value);
}
}
@@ -0,0 +1,30 @@
package speiger.src.coreengine.graphics.opengl.utils.states;
import org.lwjgl.opengl.GL11;
import speiger.src.coreengine.graphics.api.shader.states.PolygonMode;
import speiger.src.coreengine.graphics.opengl.utils.GLUtils;
public class GLPolygon implements IGLState
{
PolygonMode defaultState = PolygonMode.FILL;
PolygonMode state = PolygonMode.FILL;
public GLPolygon set(PolygonMode state) {
if(this.state == state) return this;
this.state = state;
GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GLUtils.toGL(state));
return this;
}
@Override
public void setDefault() {
set(defaultState);
}
@Override
public void reapply() {
GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GLUtils.toGL(state));
}
}
@@ -0,0 +1,51 @@
package speiger.src.coreengine.graphics.opengl.utils.states;
import org.lwjgl.opengl.GL11;
public class GLState implements IGLState {
final int id;
final boolean defaultValue;
boolean value;
public GLState(int id) {
this(id, GL11.glGetBoolean(id));
}
public GLState(int id, boolean defaultValue) {
this.id = id;
this.defaultValue = this.value = defaultValue;
}
public GLState enable() {
return set(true);
}
public GLState disable() {
return set(false);
}
public GLState set(boolean value) {
if(this.value != value) {
this.value = value;
setValue(value);
}
return this;
}
public boolean enabled() { return value; }
protected void setValue(boolean value) {
if(value) GL11.glEnable(id);
else GL11.glDisable(id);
}
@Override
public void reapply() {
setValue(value);
}
@Override
public void setDefault() {
set(defaultValue);
}
}
@@ -0,0 +1,7 @@
package speiger.src.coreengine.graphics.opengl.utils.states;
public interface IGLState
{
public void setDefault();
public void reapply();
}
@@ -0,0 +1,41 @@
package speiger.src.coreengine.graphics.opengl.utils.states;
import org.lwjgl.opengl.GL11;
public class PixelState implements IGLState {
final int id;
final int defaultValue;
int value;
public PixelState(int id, int defaultValue) {
this.id = id;
this.defaultValue = defaultValue;
this.value = defaultValue;
}
public PixelState set(int value) {
if(this.value != value) {
this.value = value;
setValue(value);
}
return this;
}
protected void setValue(int value) {
GL11.glPixelStorei(id, value);
}
public int get() {
return value;
}
@Override
public void setDefault() {
set(defaultValue);
}
@Override
public void reapply() {
setValue(value);
}
}
@@ -0,0 +1,51 @@
package speiger.src.coreengine.graphics.opengl.utils.states;
import java.util.Arrays;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL33;
import org.lwjgl.opengl.GL44;
public class SamplerState implements IGLState {
int[] samplers = new int[GL20.GL_TEXTURE31 - GL20.GL_TEXTURE0];
public SamplerState bind(int sampler) {
return bind(0, sampler);
}
public SamplerState bind(int unit, int sampler) {
if(samplers[unit] != sampler) {
this.samplers[unit] = sampler;
GL33.glBindSampler(unit, sampler);
}
return this;
}
public SamplerState unbind(int unit) {
if(samplers[unit] != 0) {
samplers[unit] = 0;
GL33.glBindSampler(unit, 0);
}
return this;
}
public SamplerState unbindAll() {
setDefault();
return this;
}
public int getSamplerId(int unit) {
return samplers[unit];
}
@Override
public void setDefault() {
GL44.nglBindSamplers(0, samplers.length, 0);
Arrays.fill(samplers, 0);
}
@Override
public void reapply() {
GL44.glBindSamplers(0, samplers);
}
}
@@ -0,0 +1,52 @@
package speiger.src.coreengine.graphics.opengl.utils.states;
import org.lwjgl.opengl.GL11;
import speiger.src.coreengine.math.vector.ints.Vec4i;
public class ScissorState extends GLState {
Vec4i current = Vec4i.MINUS_ONE;
Vec4i backup = null;
public ScissorState() {
super(GL11.GL_SCISSOR_TEST);
}
public ScissorState set(Vec4i bounds) {
if(current.equals(bounds)) return this;
current = bounds.asImmutable();
if(Vec4i.MINUS_ONE.equals(bounds)) {
disable();
return this;
}
enable();
GL11.glScissor(bounds.x(), bounds.y(), bounds.z() - bounds.x(), bounds.w() - bounds.y());
return this;
}
public void backup() {
backup = current;
}
public void restore() {
if(backup == null) return;
set(backup);
backup = null;
}
public Vec4i current() {
return current;
}
@Override
public void setDefault() {
super.setDefault();
current = Vec4i.MINUS_ONE;
}
@Override
public void reapply() {
super.reapply();
GL11.glScissor(current.x(), current.y(), current.z() - current.x(), current.w() - current.y());
}
}
@@ -0,0 +1,50 @@
package speiger.src.coreengine.graphics.opengl.utils.states;
import java.util.Arrays;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL45;
public class TextureState implements IGLState {
int[] textures = new int[GL20.GL_TEXTURE31 - GL20.GL_TEXTURE0];
public TextureState bind(int texture) {
return bind(0, texture);
}
public TextureState bind(int unit, int texture) {
if(textures[unit] != texture) {
this.textures[unit] = texture;
GL45.glBindTextureUnit(unit, texture);
}
return this;
}
public TextureState unbind(int unit) {
if(textures[unit] != 0) {
textures[unit] = 0;
GL45.glBindTextureUnit(unit, 0);
}
return this;
}
public TextureState unbindAll() {
setDefault();
return this;
}
public int getTextureId(int unit) {
return textures[unit];
}
@Override
public void setDefault() {
GL45.nglBindTextures(0, textures.length, 0);
Arrays.fill(textures, 0);
}
@Override
public void reapply() {
GL45.glBindTextures(0, textures);
}
}
@@ -76,8 +76,8 @@ public class NewInputTest {
Joystick.INSTANCE.init(manager, bus);
FileDrop.INSTANCE.init(bus);
manager.addDevices(Mouse.INSTANCE, Keyboard.INSTANCE, Joystick.INSTANCE, FileDrop.INSTANCE);
Window window = manager.builder().title("Testing Engine").width(800).height(600).antialis(0).build();
Window secondWindow = manager.builder().title("Second Window Engine").width(800).height(600).antialis(0).build();
Window window = manager.builder().title("Testing Engine").width(800).height(600).antialis(0).build(null);
Window secondWindow = manager.builder().title("Second Window Engine").width(800).height(600).antialis(0).build(null);
Thread.ofPlatform().start(() -> drawSecondScreen(secondWindow));
window.setupContext();
shaderTest.register();
@@ -0,0 +1,154 @@
package speiger.src.coreengine;
import java.nio.ByteBuffer;
import org.lwjgl.glfw.GLFW;
import org.lwjgl.system.Configuration;
import org.lwjgl.util.freetype.FreeType;
import speiger.src.coreengine.assets.api.IAssetPackage;
import speiger.src.coreengine.assets.api.ID;
import speiger.src.coreengine.assets.manager.AssetManager;
import speiger.src.coreengine.graphics.api.buffer.VertexBuffer;
import speiger.src.coreengine.graphics.api.buffer.states.BufferState;
import speiger.src.coreengine.graphics.api.buffer.states.BufferType;
import speiger.src.coreengine.graphics.api.core.Graphics;
import speiger.src.coreengine.graphics.api.core.GraphicsCommandBuffer;
import speiger.src.coreengine.graphics.api.core.GraphicsDevice;
import speiger.src.coreengine.graphics.api.core.GraphicsSurface;
import speiger.src.coreengine.graphics.api.mesh.Mesh;
import speiger.src.coreengine.graphics.api.sampler.Sampler;
import speiger.src.coreengine.graphics.api.sampler.SamplerSettings;
import speiger.src.coreengine.graphics.api.sampler.states.BorderMode;
import speiger.src.coreengine.graphics.api.sampler.states.SampleMode;
import speiger.src.coreengine.graphics.api.shader.ShaderPipeline;
import speiger.src.coreengine.graphics.api.shader.states.GraphicsDataType;
import speiger.src.coreengine.graphics.api.shader.states.ShaderType;
import speiger.src.coreengine.graphics.api.texture.Texture;
import speiger.src.coreengine.graphics.api.texture.TextureSettings;
import speiger.src.coreengine.graphics.api.texture.states.TextureFormat;
import speiger.src.coreengine.graphics.api.texture.states.TextureType;
import speiger.src.coreengine.graphics.api.utils.ExecutionType;
import speiger.src.coreengine.graphics.api.vertex.VertexLayout;
import speiger.src.coreengine.graphics.api.vertex.VertexLayout.Usage;
import speiger.src.coreengine.graphics.opengl.core.GLGraphics;
import speiger.src.coreengine.math.vector.matrix.Matrix4f;
import speiger.src.coreengine.rendering.input.devices.FileDrop;
import speiger.src.coreengine.rendering.input.devices.Joystick;
import speiger.src.coreengine.rendering.input.devices.Keyboard;
import speiger.src.coreengine.rendering.input.devices.Mouse;
import speiger.src.coreengine.rendering.input.window.Window;
import speiger.src.coreengine.rendering.input.window.WindowManager;
import speiger.src.coreengine.rendering.tesselation.buffer.VertexBuilder;
import speiger.src.coreengine.rendering.tesselation.format.VertexTypes;
import speiger.src.coreengine.rendering.textures.custom.Drawable;
import speiger.src.coreengine.rendering.utils.values.textures.GLTextureFormat;
import speiger.src.coreengine.utils.eventbus.EventBus;
import speiger.src.coreengine.utils.helpers.IOUtils;
public class NewRenderEngineTest {
EventBus bus = new EventBus();
WindowManager manager = new WindowManager();
AssetManager assets = AssetManager.single(IAssetPackage.asset(IOUtils.getBaseLocation()));
public static void main(String... args) {
new NewRenderEngineTest().run();
}
public void run() {
Configuration.HARFBUZZ_LIBRARY_NAME.set(FreeType.getLibrary());
GLFW.glfwInit();
manager.initialize();
Mouse.INSTANCE.init(bus);
Keyboard.INSTANCE.init(bus);
Joystick.INSTANCE.init(manager, bus);
FileDrop.INSTANCE.init(bus);
manager.addDevices(Mouse.INSTANCE, Keyboard.INSTANCE, Joystick.INSTANCE, FileDrop.INSTANCE);
Graphics graphics = new GLGraphics(assets);
Window window = manager.builder().title("Testing Engine").width(800).height(600).antialis(0).build(graphics);
GraphicsDevice device = graphics.createDevice(window);
window.setupContext();
GraphicsSurface surface = device.createSurface();
int size = 512;
int half = size >> 1;
int base = size >> 3;
Drawable drawer = new Drawable(GLTextureFormat.RGBA, size, size);
drawer.fill(0, 0, size, size, -1);
drawer.fill(0, 0, half, half, 255, 0, 0, 255);
drawer.fill(half, 0, half, half, 0, 255, 0, 255);
drawer.fill(half, half, half, half, 255, 0, 0, 255);
drawer.fill(0, half, half, half, 0, 0, 255, 255);
VertexBuilder builder = new VertexBuilder(255);
builder.start(null, VertexTypes.TESTING);
builder.pos(-0.5F, -0.5F, 0).tex(0F, 1F).rgba(-1).endVertex();
builder.pos(0.5F, -0.5F, 0).tex(1F, 1F).rgba(-1).endVertex();
builder.pos(0.5F, 0.5F, 0).tex(1F, 0F).rgba(-1).endVertex();
builder.pos(0.5F, 0.5F, 0).tex(1F, 0F).rgba(-1).endVertex();
builder.pos(-0.5F, 0.5F, 0).tex(0F, 0F).rgba(-1).endVertex();
builder.pos(-0.5F, -0.5F, 0).tex(0F, 1F).rgba(-1).endVertex();
Texture texture = device.createTexture(TextureSettings.builder().build(), size, size);
device.queue().writeToTexture(texture, TextureFormat.RGBA, GraphicsDataType.UNSIGNED_BYTE, drawer.pixels());
drawer.close();
VertexLayout layout = VertexLayout.builder()
.element("pos", 0, 3, Usage.POS)
.element("tex", 1, 2, Usage.UV)
.element("color", 2, 4, Usage.COLOR, GraphicsDataType.UNSIGNED_BYTE, true)
.build();
Mesh mesh = device.createMesh(Mesh.builder().layout(layout));
mesh.buffer(0, device.createBuffer(BufferType.ARRAY_BUFFER, BufferState.STATIC_DRAW).bind().set(builder.getBytes()).unbind());
Sampler sampler = device.createSampler(SamplerSettings.builder().sampler(SampleMode.NEAREST).wrap(BorderMode.CLAMP_TO_EDGE).build());
VertexBuffer buffer = device.createBuffer(BufferType.UNIFORM_BUFFER, BufferState.STATIC_DRAW);
ByteBuffer mat = ByteBuffer.allocate(64);
int scale = 0;
new Matrix4f().ortho(0, 0, window.width() >> scale, window.height() >> scale, 1000, -1000).store(mat);
buffer.bind().set(mat.flip().array()).unbind();
ShaderPipeline pipeline = ShaderPipeline.builder(ID.of("gui"))
.withStage(ShaderType.VERTEX, ID.of("shader/testGui/vertex.vs"))
.withStage(ShaderType.FRAGMENT, ID.of("shader/testGui/fragment.fs"))
.withTexture("texture", 0, 0, TextureType.TEXTURE_2D)
.withUniform("Camera", 0, 0, 64)
.withFormat()
.attribute("in_position", 0, 3)
.attribute("in_tex", 1, 2)
.attribute("in_color", 2, 4, GraphicsDataType.UNSIGNED_BYTE, true)
.endFormat()
.build();
window.visible(true);
GraphicsCommandBuffer queue = device.createCommandBuffer(ExecutionType.IMMEDIATE);
while(!window.shouldClose()) {
surface.beginFrame();
if(window.changed()) {
window.updateViewport();
new Matrix4f().ortho(0, 0, window.width() >> scale, window.height() >> scale, 1000, -1000).store(mat);
buffer.bind().set(mat.flip().array()).unbind();
}
queue.begin();
queue.pipeline(pipeline)
.mesh(mesh)
.texture(0, texture, sampler)
.uniform(0, buffer)
.drawArrays(0, 6)
.drawArrays(0, 6);
queue.end();
surface.finishFrame();
try {
Thread.sleep(1);
}
catch(Exception e) {
e.printStackTrace();
}
}
}
}
@@ -1,341 +1,342 @@
package speiger.src.coreengine.rendering.input.window;
import java.util.List;
import org.lwjgl.glfw.GLFW;
import org.lwjgl.opengl.GL;
import org.lwjgl.opengl.GLCapabilities;
import org.lwjgl.system.CallbackI;
import speiger.src.collections.objects.lists.ObjectArrayList;
import speiger.src.coreengine.math.BitUtil;
import speiger.src.coreengine.math.vector.ints.Vec4i;
import speiger.src.coreengine.rendering.input.window.IWindowListener.Reason;
import speiger.src.coreengine.rendering.input.window.WindowCallback.ReloadFunction;
import speiger.src.coreengine.rendering.input.window.WindowManager.WindowBuilder;
import speiger.src.coreengine.rendering.utils.GLStateTracker;
import speiger.src.coreengine.utils.collections.FlagHolder;
public class Window {
static final int VISIBLE = 1;
static final int VSYNC = 2;
static final int FOCUS = 4;
static final int CPU_FPS_CAP = 8;
static final int FULL_SCREEN = 16;
static final int MAXIMIZED = 32;
static final int BORDERLESS = 64;
static final int RESIZABLE = 128;
static final int FLOATING = 256;
static final int CLOSE = 512;
static final int WINDOW_CHANGE = 1024;
WindowManager manager;
FlagHolder flags = new FlagHolder();
long id;
VideoMode fullScreenMode;
String title = "";
int x;
int y;
int width;
int height;
int frameWidth;
int frameHeight;
Vec4i[] backup = new Vec4i[] {Vec4i.mutable(), Vec4i.mutable()};
int backupIndex = 0;
final int antialiasing;
List<WindowCallback> callbacks = new ObjectArrayList<>();
List<IWindowListener> listeners = new ObjectArrayList<>();
GLCapabilities capabilities;
protected Window(WindowBuilder builder) {
manager = builder.manager;
title = builder.title;
frameWidth = width = builder.width;
frameHeight = height = builder.height;
antialiasing = builder.antiAlis;
fullScreenMode = builder.fullScreenTarget;
flags.setFlag(BORDERLESS, builder.borderless);
flags.setFlag(FULL_SCREEN, builder.fullScreen);
flags.setFlag(FLOATING, builder.floating);
flags.setFlag(RESIZABLE, builder.resizable);
flags.setFlag(VSYNC, builder.vsync);
flags.setFlag(CPU_FPS_CAP, builder.fpsCap);
createDefaultWindowHints();
for(int i = 0,m=builder.windowHints.size();i<m;i++) {
long value = builder.windowHints.getLong(i);
GLFW.glfwWindowHint(BitUtil.intKey(value), BitUtil.intValue(value));
}
GLFW.glfwWindowHint(GLFW.GLFW_SAMPLES, antialiasing);
GLFW.glfwWindowHint(GLFW.GLFW_RESIZABLE, flags.isFlagSet(RESIZABLE) ? GLFW.GLFW_TRUE : GLFW.GLFW_FALSE);
GLFW.glfwWindowHint(GLFW.GLFW_DECORATED, flags.isFlagNotSet(FULL_SCREEN) && flags.isFlagSet(BORDERLESS) ? GLFW.GLFW_FALSE : GLFW.GLFW_TRUE);
GLFW.glfwWindowHint(GLFW.GLFW_FLOATING, flags.isFlagNotSet(FULL_SCREEN) && flags.isFlagSet(FLOATING) ? GLFW.GLFW_TRUE : GLFW.GLFW_FALSE);
GLFW.glfwWindowHint(GLFW.GLFW_MAXIMIZED, flags.isFlagNotSet(FULL_SCREEN) && flags.isFlagSet(MAXIMIZED) ? GLFW.GLFW_TRUE : GLFW.GLFW_FALSE);
Monitor monitor = manager.getMonitor(builder.monitor);
if(monitor == null) throw new IllegalStateException("Monitor is missing: "+monitor);
VideoMode mode = monitor.defaultMode();
boolean fullscreen = builder.fullScreen;
id = GLFW.glfwCreateWindow(fullscreen ? mode.width() : width, fullscreen ? mode.height() : height, title, builder.fullScreen ? monitor.id() : 0, manager.getPrimaryWindow());
if(id == 0) throw new IllegalStateException("Window Couldn't be Created");
manager.addWindow(this);
createWindowListeners();
GLFW.glfwMakeContextCurrent(id);
capabilities = GL.createCapabilities(true);
x = monitor.xOffset() + (builder.center ? (mode.width() / 2) - (width / 2) : 0);
y = monitor.yOffset() + (builder.center ? (mode.height() / 2) - (height / 2) : 0);
if(!fullscreen) GLFW.glfwSetWindowPos(id, x, y);
GLFW.glfwSwapInterval(flags.isFlagSet(VSYNC) ? 1 : 0);
fetchWindowBounds();
updateViewport();
}
protected void createDefaultWindowHints() {
GLFW.glfwDefaultWindowHints();
GLFW.glfwWindowHint(GLFW.GLFW_VISIBLE, GLFW.GLFW_FALSE);
GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_VERSION_MAJOR, 4);
GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_VERSION_MINOR, 0);
GLFW.glfwWindowHint(GLFW.GLFW_OPENGL_PROFILE, GLFW.GLFW_OPENGL_CORE_PROFILE);
GLFW.glfwWindowHint(GLFW.GLFW_OPENGL_FORWARD_COMPAT, GLFW.GLFW_TRUE);
}
protected void createWindowListeners() {
addCallback(this::framebuffer, GLFW::glfwSetFramebufferSizeCallback);
addCallback(this::focused, GLFW::glfwSetWindowFocusCallback);
addCallback(this::position, GLFW::glfwSetWindowPosCallback);
addCallback(this::bounds, GLFW::glfwSetWindowSizeCallback);
}
private void framebuffer(long window, int width, int height) {
frameWidth = width;
frameHeight = height;
flags.setFlag(WINDOW_CHANGE);
}
private void focused(long window, boolean focused) {
manager.updateFocus(this, focused);
}
private void position(long window, int x, int y) {
this.x = x;
this.y = y;
flags.setFlag(WINDOW_CHANGE);
}
private void bounds(long window, int width, int height) {
this.width = width;
this.height = height;
flags.setFlag(WINDOW_CHANGE);
}
protected void fetchWindowBounds() {
int[] width = new int[1];
int[] height = new int[1];
GLFW.glfwGetWindowSize(id, width, height);
this.frameWidth = width[0];
this.frameHeight = height[0];
}
public void updateViewport() {
flags.clearFlag(WINDOW_CHANGE);
GLStateTracker.instance().viewPort.setDefault(0, 0, frameWidth, frameHeight);
for(int i = 0,m=listeners.size();i<m;i++) {
listeners.get(i).onChanged(this, Reason.CHANGE);
}
}
public void setupContext() {
GL.setCapabilities(capabilities);
GLFW.glfwMakeContextCurrent(id);
}
public void beginFrame() {
setupContext();
if(flags.isFlagSet(WINDOW_CHANGE)) updateViewport();
}
public void handleInput() { manager.processDevices(id); }
public boolean shouldClose() { return flags.isFlagSet(CLOSE) || GLFW.glfwWindowShouldClose(id); }
public void finishFrame() { GLFW.glfwSwapBuffers(id); }
public void destroy() {
manager.removeWindow(id);
for(int i = 0,m=listeners.size();i<m;i++) {
listeners.get(i).onChanged(this, Reason.CLOSING);
}
GLFW.glfwDestroyWindow(id);
callbacks.forEach(WindowCallback::destroy);
callbacks.clear();
}
@SuppressWarnings("unchecked")
public <T extends CallbackI> WindowCallback addCallback(T listener, ReloadFunction<T> function) {
WindowCallback callback = new WindowCallback(listener, (ReloadFunction<CallbackI>)function);
callbacks.add(callback);
callback.load(id);
return callback;
}
public void removeCallback(WindowCallback callback) {
if(callbacks.remove(callback)) {
callback.destroy();
}
}
public void addListener(IWindowListener listener) {
listeners.add(listener);
}
public void removeListener(IWindowListener listener) {
listeners.remove(listener);
}
public void title(String name) {
if(name == null || title.equals(name)) return;
title = name;
GLFW.glfwSetWindowTitle(id, title);
}
public void vsync(boolean vsync) {
if(!flags.setFlag(VSYNC, vsync)) return;
GLFW.glfwSwapInterval(vsync ? 1 : 0);
}
public void fpsCap(boolean fpsCap) {
flags.setFlag(CPU_FPS_CAP, fpsCap);
}
public void visible(boolean visible) {
if(!flags.setFlag(VISIBLE, visible)) return;
if(visible) GLFW.glfwShowWindow(id);
else GLFW.glfwHideWindow(id);
}
public void floating(boolean floating) {
if(flags.isFlagNotSet(FULL_SCREEN) && flags.setFlag(FLOATING, floating)) {
GLFW.glfwSetWindowAttrib(id, GLFW.GLFW_FLOATING, floating ? GLFW.GLFW_TRUE : GLFW.GLFW_FALSE);
}
}
public void maximized(boolean maximized) {
if(flags.isFlagNotSet(FULL_SCREEN) && flags.setFlag(MAXIMIZED, maximized)) {
if(maximized) {
backupSize();
GLFW.glfwMaximizeWindow(id);
}
else {
GLFW.glfwRestoreWindow(id);
restoreSize();
}
fetchWindowBounds();
}
}
public void resizeable(boolean resizeable) {
if(flags.isFlagNotSet(FULL_SCREEN) && flags.setFlag(RESIZABLE, resizeable)) {
GLFW.glfwSetWindowAttrib(id, GLFW.GLFW_RESIZABLE, resizeable ? GLFW.GLFW_TRUE : GLFW.GLFW_FALSE);
}
}
public void borderless(boolean borderless) {
if(flags.isFlagNotSet(FULL_SCREEN) && flags.setFlag(BORDERLESS, borderless)) {
GLFW.glfwSetWindowAttrib(id, GLFW.GLFW_DECORATED, borderless ? GLFW.GLFW_FALSE : GLFW.GLFW_TRUE);
if(flags.isFlagSet(MAXIMIZED)) {
GLFW.glfwRestoreWindow(id);
GLFW.glfwMaximizeWindow(id);
fetchWindowBounds();
}
}
}
public void fullscreen(boolean fullscreen) {
if(flags.setFlag(FULL_SCREEN, fullscreen)) {
if(fullscreen) {
Monitor monitor = manager.getMonitorForWindow(this);
if(monitor == null) {
flags.clearFlag(FULL_SCREEN);
return;
}
backupSize();
VideoMode mode = fullScreenMode == null || !monitor.has(fullScreenMode) ? monitor.defaultMode() : fullScreenMode;
x = 0;
y = 0;
width = mode.width();
height = mode.height();
GLFW.glfwSetWindowMonitor(id, monitor.id(), 0, 0, mode.width(), mode.height(), mode.refreshrate());
}
else
{
restoreSize();
GLFW.glfwSetWindowMonitor(id, 0, x, y, width, height, -1);
if(flags.isFlagSet(BORDERLESS)) {
GLFW.glfwSetWindowAttrib(id, GLFW.GLFW_DECORATED, GLFW.GLFW_FALSE);
}
if(flags.isFlagSet(MAXIMIZED)) {
GLFW.glfwMaximizeWindow(id);
}
}
fetchWindowBounds();
updateViewport();
GLFW.glfwSwapInterval(flags.isFlagSet(VSYNC) ? 1 : 0);
}
}
protected void backupSize() {
if(backupIndex >= backup.length) return;
backup[backupIndex++].set(x, y, width, height);
}
protected void restoreSize() {
if(backupIndex == 0) return;
Vec4i prev = backup[--backupIndex];
x = prev.x();
y = prev.y();
width = prev.z();
height = prev.w();
}
public boolean position(int x, int y) {
if(flags.isAnyFlagSet(MAXIMIZED | FULL_SCREEN) || (this.x == x && this.y == y)) return false;
this.x = x;
this.y = y;
GLFW.glfwSetWindowPos(id, x, y);
flags.setFlag(WINDOW_CHANGE);
return true;
}
public boolean size(int width, int height) {
if(flags.isAnyFlagSet(MAXIMIZED | FULL_SCREEN) || (this.width == width && this.height == height)) return false;
this.width = width;
this.height = height;
GLFW.glfwSetWindowSize(id, width, height);
flags.setFlag(WINDOW_CHANGE);
return true;
}
public boolean width(int width) { return size(width, height); }
public boolean height(int height) { return size(width, height); }
public long id() { return id; }
public VideoMode desiredFullScreen() { return fullScreenMode; }
public int x() { return x; }
public int y() { return y; }
public int width() { return frameWidth; }
public int height() { return frameHeight; }
public int screenWidth() { return width; }
public int screenHeight() { return height; }
public boolean changed() { return flags.isFlagSet(WINDOW_CHANGE); }
public String title() { return title; }
public boolean isVsync() { return flags.isFlagSet(VSYNC); }
public boolean shouldFPSCap() { return flags.isFlagSet(CPU_FPS_CAP); }
public boolean isVisible() { return flags.isFlagSet(VISIBLE); }
public boolean isFloating() { return flags.isFlagSet(FLOATING); }
public boolean isMaximized() { return flags.isFlagSet(MAXIMIZED); }
public boolean isResizeable() { return flags.isFlagSet(RESIZABLE); }
public boolean isBorderless() { return flags.isFlagSet(BORDERLESS); }
public boolean isFullscreen() { return flags.isFlagSet(FULL_SCREEN); }
public int antialiasing() { return antialiasing; }
}
package speiger.src.coreengine.rendering.input.window;
import java.util.List;
import org.lwjgl.glfw.GLFW;
import org.lwjgl.opengl.GL;
import org.lwjgl.opengl.GLCapabilities;
import org.lwjgl.system.CallbackI;
import speiger.src.collections.objects.lists.ObjectArrayList;
import speiger.src.coreengine.graphics.api.core.Graphics;
import speiger.src.coreengine.math.BitUtil;
import speiger.src.coreengine.math.vector.ints.Vec4i;
import speiger.src.coreengine.rendering.input.window.IWindowListener.Reason;
import speiger.src.coreengine.rendering.input.window.WindowCallback.ReloadFunction;
import speiger.src.coreengine.rendering.input.window.WindowManager.WindowBuilder;
import speiger.src.coreengine.utils.collections.FlagHolder;
public class Window {
static final int VISIBLE = 1;
static final int VSYNC = 2;
static final int FOCUS = 4;
static final int CPU_FPS_CAP = 8;
static final int FULL_SCREEN = 16;
static final int MAXIMIZED = 32;
static final int BORDERLESS = 64;
static final int RESIZABLE = 128;
static final int FLOATING = 256;
static final int CLOSE = 512;
static final int WINDOW_CHANGE = 1024;
WindowManager manager;
FlagHolder flags = new FlagHolder();
long id;
VideoMode fullScreenMode;
String title = "";
int x;
int y;
int width;
int height;
int frameWidth;
int frameHeight;
Vec4i[] backup = new Vec4i[] {Vec4i.mutable(), Vec4i.mutable()};
int backupIndex = 0;
final int antialiasing;
List<WindowCallback> callbacks = new ObjectArrayList<>();
List<IWindowListener> listeners = new ObjectArrayList<>();
GLCapabilities capabilities;
protected Window(WindowBuilder builder, Graphics graphics) {
manager = builder.manager;
title = builder.title;
frameWidth = width = builder.width;
frameHeight = height = builder.height;
antialiasing = builder.antiAlis;
fullScreenMode = builder.fullScreenTarget;
flags.setFlag(BORDERLESS, builder.borderless);
flags.setFlag(FULL_SCREEN, builder.fullScreen);
flags.setFlag(FLOATING, builder.floating);
flags.setFlag(RESIZABLE, builder.resizable);
flags.setFlag(VSYNC, builder.vsync);
flags.setFlag(CPU_FPS_CAP, builder.fpsCap);
if(graphics != null) graphics.setupWindowArguments();
else createDefaultWindowHints();
for(int i = 0,m=builder.windowHints.size();i<m;i++) {
long value = builder.windowHints.getLong(i);
GLFW.glfwWindowHint(BitUtil.intKey(value), BitUtil.intValue(value));
}
GLFW.glfwWindowHint(GLFW.GLFW_SAMPLES, antialiasing);
GLFW.glfwWindowHint(GLFW.GLFW_RESIZABLE, flags.isFlagSet(RESIZABLE) ? GLFW.GLFW_TRUE : GLFW.GLFW_FALSE);
GLFW.glfwWindowHint(GLFW.GLFW_DECORATED, flags.isFlagNotSet(FULL_SCREEN) && flags.isFlagSet(BORDERLESS) ? GLFW.GLFW_FALSE : GLFW.GLFW_TRUE);
GLFW.glfwWindowHint(GLFW.GLFW_FLOATING, flags.isFlagNotSet(FULL_SCREEN) && flags.isFlagSet(FLOATING) ? GLFW.GLFW_TRUE : GLFW.GLFW_FALSE);
GLFW.glfwWindowHint(GLFW.GLFW_MAXIMIZED, flags.isFlagNotSet(FULL_SCREEN) && flags.isFlagSet(MAXIMIZED) ? GLFW.GLFW_TRUE : GLFW.GLFW_FALSE);
Monitor monitor = manager.getMonitor(builder.monitor);
if(monitor == null) throw new IllegalStateException("Monitor is missing: "+monitor);
VideoMode mode = monitor.defaultMode();
boolean fullscreen = builder.fullScreen;
id = GLFW.glfwCreateWindow(fullscreen ? mode.width() : width, fullscreen ? mode.height() : height, title, builder.fullScreen ? monitor.id() : 0, manager.getPrimaryWindow());
if(id == 0) throw new IllegalStateException("Window Couldn't be Created");
manager.addWindow(this);
createWindowListeners();
GLFW.glfwMakeContextCurrent(id);
if(graphics == null) capabilities = GL.createCapabilities(true);
x = monitor.xOffset() + (builder.center ? (mode.width() / 2) - (width / 2) : 0);
y = monitor.yOffset() + (builder.center ? (mode.height() / 2) - (height / 2) : 0);
if(!fullscreen) GLFW.glfwSetWindowPos(id, x, y);
GLFW.glfwSwapInterval(flags.isFlagSet(VSYNC) ? 1 : 0);
fetchWindowBounds();
updateViewport();
}
protected void createDefaultWindowHints() {
GLFW.glfwDefaultWindowHints();
GLFW.glfwWindowHint(GLFW.GLFW_VISIBLE, GLFW.GLFW_FALSE);
GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_VERSION_MAJOR, 4);
GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_VERSION_MINOR, 0);
GLFW.glfwWindowHint(GLFW.GLFW_OPENGL_PROFILE, GLFW.GLFW_OPENGL_CORE_PROFILE);
GLFW.glfwWindowHint(GLFW.GLFW_OPENGL_FORWARD_COMPAT, GLFW.GLFW_TRUE);
}
protected void createWindowListeners() {
addCallback(this::framebuffer, GLFW::glfwSetFramebufferSizeCallback);
addCallback(this::focused, GLFW::glfwSetWindowFocusCallback);
addCallback(this::position, GLFW::glfwSetWindowPosCallback);
addCallback(this::bounds, GLFW::glfwSetWindowSizeCallback);
}
private void framebuffer(long window, int width, int height) {
frameWidth = width;
frameHeight = height;
flags.setFlag(WINDOW_CHANGE);
}
private void focused(long window, boolean focused) {
manager.updateFocus(this, focused);
}
private void position(long window, int x, int y) {
this.x = x;
this.y = y;
flags.setFlag(WINDOW_CHANGE);
}
private void bounds(long window, int width, int height) {
this.width = width;
this.height = height;
flags.setFlag(WINDOW_CHANGE);
}
protected void fetchWindowBounds() {
int[] width = new int[1];
int[] height = new int[1];
GLFW.glfwGetWindowSize(id, width, height);
this.frameWidth = width[0];
this.frameHeight = height[0];
}
public void updateViewport() {
flags.clearFlag(WINDOW_CHANGE);
// GLStateTracker.instance().viewPort.setDefault(0, 0, frameWidth, frameHeight);
for(int i = 0,m=listeners.size();i<m;i++) {
listeners.get(i).onChanged(this, Reason.CHANGE);
}
}
public void setupContext() {
if(capabilities != null) GL.setCapabilities(capabilities);
GLFW.glfwMakeContextCurrent(id);
}
public void beginFrame() {
setupContext();
if(flags.isFlagSet(WINDOW_CHANGE)) updateViewport();
}
public void handleInput() { manager.processDevices(id); }
public boolean shouldClose() { return flags.isFlagSet(CLOSE) || GLFW.glfwWindowShouldClose(id); }
public void finishFrame() { GLFW.glfwSwapBuffers(id); }
public void destroy() {
manager.removeWindow(id);
for(int i = 0,m=listeners.size();i<m;i++) {
listeners.get(i).onChanged(this, Reason.CLOSING);
}
GLFW.glfwDestroyWindow(id);
callbacks.forEach(WindowCallback::destroy);
callbacks.clear();
}
@SuppressWarnings("unchecked")
public <T extends CallbackI> WindowCallback addCallback(T listener, ReloadFunction<T> function) {
WindowCallback callback = new WindowCallback(listener, (ReloadFunction<CallbackI>)function);
callbacks.add(callback);
callback.load(id);
return callback;
}
public void removeCallback(WindowCallback callback) {
if(callbacks.remove(callback)) {
callback.destroy();
}
}
public void addListener(IWindowListener listener) {
listeners.add(listener);
}
public void removeListener(IWindowListener listener) {
listeners.remove(listener);
}
public void title(String name) {
if(name == null || title.equals(name)) return;
title = name;
GLFW.glfwSetWindowTitle(id, title);
}
public void vsync(boolean vsync) {
if(!flags.setFlag(VSYNC, vsync)) return;
GLFW.glfwSwapInterval(vsync ? 1 : 0);
}
public void fpsCap(boolean fpsCap) {
flags.setFlag(CPU_FPS_CAP, fpsCap);
}
public void visible(boolean visible) {
if(!flags.setFlag(VISIBLE, visible)) return;
if(visible) GLFW.glfwShowWindow(id);
else GLFW.glfwHideWindow(id);
}
public void floating(boolean floating) {
if(flags.isFlagNotSet(FULL_SCREEN) && flags.setFlag(FLOATING, floating)) {
GLFW.glfwSetWindowAttrib(id, GLFW.GLFW_FLOATING, floating ? GLFW.GLFW_TRUE : GLFW.GLFW_FALSE);
}
}
public void maximized(boolean maximized) {
if(flags.isFlagNotSet(FULL_SCREEN) && flags.setFlag(MAXIMIZED, maximized)) {
if(maximized) {
backupSize();
GLFW.glfwMaximizeWindow(id);
}
else {
GLFW.glfwRestoreWindow(id);
restoreSize();
}
fetchWindowBounds();
}
}
public void resizeable(boolean resizeable) {
if(flags.isFlagNotSet(FULL_SCREEN) && flags.setFlag(RESIZABLE, resizeable)) {
GLFW.glfwSetWindowAttrib(id, GLFW.GLFW_RESIZABLE, resizeable ? GLFW.GLFW_TRUE : GLFW.GLFW_FALSE);
}
}
public void borderless(boolean borderless) {
if(flags.isFlagNotSet(FULL_SCREEN) && flags.setFlag(BORDERLESS, borderless)) {
GLFW.glfwSetWindowAttrib(id, GLFW.GLFW_DECORATED, borderless ? GLFW.GLFW_FALSE : GLFW.GLFW_TRUE);
if(flags.isFlagSet(MAXIMIZED)) {
GLFW.glfwRestoreWindow(id);
GLFW.glfwMaximizeWindow(id);
fetchWindowBounds();
}
}
}
public void fullscreen(boolean fullscreen) {
if(flags.setFlag(FULL_SCREEN, fullscreen)) {
if(fullscreen) {
Monitor monitor = manager.getMonitorForWindow(this);
if(monitor == null) {
flags.clearFlag(FULL_SCREEN);
return;
}
backupSize();
VideoMode mode = fullScreenMode == null || !monitor.has(fullScreenMode) ? monitor.defaultMode() : fullScreenMode;
x = 0;
y = 0;
width = mode.width();
height = mode.height();
GLFW.glfwSetWindowMonitor(id, monitor.id(), 0, 0, mode.width(), mode.height(), mode.refreshrate());
}
else
{
restoreSize();
GLFW.glfwSetWindowMonitor(id, 0, x, y, width, height, -1);
if(flags.isFlagSet(BORDERLESS)) {
GLFW.glfwSetWindowAttrib(id, GLFW.GLFW_DECORATED, GLFW.GLFW_FALSE);
}
if(flags.isFlagSet(MAXIMIZED)) {
GLFW.glfwMaximizeWindow(id);
}
}
fetchWindowBounds();
updateViewport();
GLFW.glfwSwapInterval(flags.isFlagSet(VSYNC) ? 1 : 0);
}
}
protected void backupSize() {
if(backupIndex >= backup.length) return;
backup[backupIndex++].set(x, y, width, height);
}
protected void restoreSize() {
if(backupIndex == 0) return;
Vec4i prev = backup[--backupIndex];
x = prev.x();
y = prev.y();
width = prev.z();
height = prev.w();
}
public boolean position(int x, int y) {
if(flags.isAnyFlagSet(MAXIMIZED | FULL_SCREEN) || (this.x == x && this.y == y)) return false;
this.x = x;
this.y = y;
GLFW.glfwSetWindowPos(id, x, y);
flags.setFlag(WINDOW_CHANGE);
return true;
}
public boolean size(int width, int height) {
if(flags.isAnyFlagSet(MAXIMIZED | FULL_SCREEN) || (this.width == width && this.height == height)) return false;
this.width = width;
this.height = height;
GLFW.glfwSetWindowSize(id, width, height);
flags.setFlag(WINDOW_CHANGE);
return true;
}
public boolean width(int width) { return size(width, height); }
public boolean height(int height) { return size(width, height); }
public long id() { return id; }
public VideoMode desiredFullScreen() { return fullScreenMode; }
public int x() { return x; }
public int y() { return y; }
public int width() { return frameWidth; }
public int height() { return frameHeight; }
public int screenWidth() { return width; }
public int screenHeight() { return height; }
public boolean changed() { return flags.isFlagSet(WINDOW_CHANGE); }
public String title() { return title; }
public boolean isVsync() { return flags.isFlagSet(VSYNC); }
public boolean shouldFPSCap() { return flags.isFlagSet(CPU_FPS_CAP); }
public boolean isVisible() { return flags.isFlagSet(VISIBLE); }
public boolean isFloating() { return flags.isFlagSet(FLOATING); }
public boolean isMaximized() { return flags.isFlagSet(MAXIMIZED); }
public boolean isResizeable() { return flags.isFlagSet(RESIZABLE); }
public boolean isBorderless() { return flags.isFlagSet(BORDERLESS); }
public boolean isFullscreen() { return flags.isFlagSet(FULL_SCREEN); }
public int antialiasing() { return antialiasing; }
}
@@ -1,266 +1,267 @@
package speiger.src.coreengine.rendering.input.window;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import org.lwjgl.glfw.GLFW;
import org.lwjgl.system.Callback;
import org.lwjgl.system.CallbackI;
import speiger.src.collections.longs.lists.LongArrayList;
import speiger.src.collections.longs.lists.LongList;
import speiger.src.collections.longs.maps.impl.concurrent.Long2ObjectConcurrentOpenHashMap;
import speiger.src.collections.longs.maps.interfaces.Long2ObjectMap;
import speiger.src.collections.objects.lists.ObjectArrayList;
import speiger.src.coreengine.math.BitUtil;
import speiger.src.coreengine.rendering.input.devices.InputDevice;
import speiger.src.coreengine.rendering.input.window.WindowCallback.SimpleReloadFunction;
public class WindowManager {
Long2ObjectMap<Monitor> monitors;
Long2ObjectMap<Window> windows = new Long2ObjectConcurrentOpenHashMap<>();
Window activeWindow;
Window primaryWindow;
List<WindowCallback> callbacks = new ObjectArrayList<>();
Callback monitorTracker;
List<InputDevice> devices = new ObjectArrayList<>();
public void initialize() {
monitors = Monitor.createMonitors();
addCallback(this::onMonitorChanged, GLFW::glfwSetMonitorCallback);
}
private void onMonitorChanged(long monitor, int event) {
switch(event) {
case GLFW.GLFW_CONNECTED -> monitors.put(monitor, new Monitor(monitor));
case GLFW.GLFW_DISCONNECTED -> monitors.remove(monitor);
}
}
public WindowBuilder builder() { return new WindowBuilder(this); }
private Window create(WindowBuilder builder) { return new Window(builder); }
public void addDevices(InputDevice...devices) {
for(InputDevice device : devices) addDevice(device);
}
public void addDevice(InputDevice device) {
devices.add(device);
windows.values().forEach(device::register);
}
public void removeDevice(InputDevice device) { devices.remove(device); }
public void processDevices(long windowId) {
for(int i = 0,m=devices.size();i<m;i++) {
devices.get(i).processInput(windowId);
}
}
@SuppressWarnings("unchecked")
public <T extends CallbackI> WindowCallback addCallback(T listener, SimpleReloadFunction<T> function) {
WindowCallback callback = new WindowCallback(listener, (SimpleReloadFunction<CallbackI>)function);
callbacks.add(callback);
callback.load(0L);
return callback;
}
public void removeCallback(WindowCallback callback) {
if(callbacks.remove(callback)) {
callback.destroy();
}
}
void addWindow(Window window) {
windows.put(window.id(), window);
for(int i = 0,m=devices.size();i<m;i++) {
devices.get(i).register(window);
}
}
void updateWindow(long oldId) {
Window prev = windows.remove(oldId);
if(prev == null) return;
windows.put(prev.id(), prev);
}
void removeWindow(long id) {
Window window = windows.remove(id);
if(window == null) return;
if(window == activeWindow) activeWindow = null;
if(window == primaryWindow) {
Iterator<Window> iter = windows.values().iterator();
primaryWindow = iter.hasNext() ? iter.next() : null;
}
}
void updateFocus(Window window, boolean focus) {
if(focus) this.activeWindow = window;
else if(activeWindow == window) activeWindow = null;
}
void updateInputs(long windowId) {
for(int i = 0,m=devices.size();i<m;i++) {
InputDevice device = devices.get(i);
device.reset(windowId);
device.processInput(windowId);
}
}
public void destroy() {
callbacks.forEach(WindowCallback::destroy);
callbacks.clear();
}
public long getActiveWindow() {
return activeWindow == null ? 0L : activeWindow.id();
}
public long getPrimaryWindow() {
return primaryWindow == null ? 0 : primaryWindow.id();
}
public Window getWindow(long window) {
return windows.get(window);
}
public Monitor getMonitor(long id) {
return monitors.get(id);
}
public Monitor getPriamryMonitor() {
return getMonitor(GLFW.glfwGetPrimaryMonitor());
}
public Monitor getMonitorForWindow(Window window) {
long current = GLFW.glfwGetWindowMonitor(window.id());
if(current != 0L) return getMonitor(current);
int minX = window.x();
int minY = window.y();
int maxX = minX + window.screenWidth();
int maxY = minY + window.screenHeight();
int largest = 0;
Monitor mon = null;
for(Monitor monitor : monitors.values()) {
int next = monitor.getOverlap(minX, minY, maxX, maxY);
if(next > largest) {
largest = next;
mon = monitor;
}
}
return mon;
}
public static class WindowBuilder {
WindowManager manager;
long monitor;
LongList windowHints = new LongArrayList();
VideoMode fullScreenTarget;
String title = "";
int width = 640;
int height = 480;
int antiAlis = 4;
boolean vsync = true;
boolean fpsCap;
boolean fullScreen;
boolean maximized;
boolean resizable = true;
boolean floating;
boolean borderless;
boolean center = true;
private WindowBuilder(WindowManager manager) {
this.manager = manager;
monitor = GLFW.glfwGetPrimaryMonitor();
}
public WindowBuilder title(String title) {
this.title = Objects.requireNonNull(title);
return this;
}
public WindowBuilder width(int width) {
this.width = Math.max(1, width);
return this;
}
public WindowBuilder height(int height) {
this.height = Math.max(1, height);
return this;
}
public WindowBuilder antialis(int antiAlis) {
this.antiAlis = Math.max(1, antiAlis);
return this;
}
public WindowBuilder fullscreen(boolean fullScreen) {
this.fullScreen = fullScreen;
borderless &= !fullScreen;
floating &= !fullScreen;
maximized &= !fullScreen;
center &= !fullScreen;
return this;
}
public WindowBuilder maximized(boolean maximized) {
this.maximized = maximized;
this.fullScreen &= !maximized;
center &= !maximized;
return this;
}
public WindowBuilder borderless(boolean borderless) {
this.borderless = borderless;
fullScreen &= !borderless;
return this;
}
public WindowBuilder floating(boolean floating) {
this.floating = floating;
fullScreen &= !floating;
return this;
}
public WindowBuilder resizeable(boolean resizable) {
this.resizable = resizable;
return this;
}
public WindowBuilder centered(boolean center) {
this.center = center;
this.maximized &= !center;
this.fullScreen &= !center;
return this;
}
public WindowBuilder monitor(Monitor monitor) {
if(monitor == null || monitor.defaultMode() == null) return this;
this.monitor = monitor.id();
return this;
}
public WindowBuilder fullScreenMode(VideoMode mode) {
this.fullScreenTarget = mode;
return this;
}
public WindowBuilder vsync(boolean value) {
vsync = value;
return this;
}
public WindowBuilder fpsCap(boolean cap) {
fpsCap = cap;
return this;
}
public WindowBuilder addCustomHint(int key, int value) {
windowHints.add(BitUtil.toLong(key, value));
return this;
}
public Window build() {
return manager.create(this);
}
}
}
package speiger.src.coreengine.rendering.input.window;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import org.lwjgl.glfw.GLFW;
import org.lwjgl.system.Callback;
import org.lwjgl.system.CallbackI;
import speiger.src.collections.longs.lists.LongArrayList;
import speiger.src.collections.longs.lists.LongList;
import speiger.src.collections.longs.maps.impl.concurrent.Long2ObjectConcurrentOpenHashMap;
import speiger.src.collections.longs.maps.interfaces.Long2ObjectMap;
import speiger.src.collections.objects.lists.ObjectArrayList;
import speiger.src.coreengine.graphics.api.core.Graphics;
import speiger.src.coreengine.math.BitUtil;
import speiger.src.coreengine.rendering.input.devices.InputDevice;
import speiger.src.coreengine.rendering.input.window.WindowCallback.SimpleReloadFunction;
public class WindowManager {
Long2ObjectMap<Monitor> monitors;
Long2ObjectMap<Window> windows = new Long2ObjectConcurrentOpenHashMap<>();
Window activeWindow;
Window primaryWindow;
List<WindowCallback> callbacks = new ObjectArrayList<>();
Callback monitorTracker;
List<InputDevice> devices = new ObjectArrayList<>();
public void initialize() {
monitors = Monitor.createMonitors();
addCallback(this::onMonitorChanged, GLFW::glfwSetMonitorCallback);
}
private void onMonitorChanged(long monitor, int event) {
switch(event) {
case GLFW.GLFW_CONNECTED -> monitors.put(monitor, new Monitor(monitor));
case GLFW.GLFW_DISCONNECTED -> monitors.remove(monitor);
}
}
public WindowBuilder builder() { return new WindowBuilder(this); }
private Window create(WindowBuilder builder, Graphics graphics) { return new Window(builder, graphics); }
public void addDevices(InputDevice...devices) {
for(InputDevice device : devices) addDevice(device);
}
public void addDevice(InputDevice device) {
devices.add(device);
windows.values().forEach(device::register);
}
public void removeDevice(InputDevice device) { devices.remove(device); }
public void processDevices(long windowId) {
for(int i = 0,m=devices.size();i<m;i++) {
devices.get(i).processInput(windowId);
}
}
@SuppressWarnings("unchecked")
public <T extends CallbackI> WindowCallback addCallback(T listener, SimpleReloadFunction<T> function) {
WindowCallback callback = new WindowCallback(listener, (SimpleReloadFunction<CallbackI>)function);
callbacks.add(callback);
callback.load(0L);
return callback;
}
public void removeCallback(WindowCallback callback) {
if(callbacks.remove(callback)) {
callback.destroy();
}
}
void addWindow(Window window) {
windows.put(window.id(), window);
for(int i = 0,m=devices.size();i<m;i++) {
devices.get(i).register(window);
}
}
void updateWindow(long oldId) {
Window prev = windows.remove(oldId);
if(prev == null) return;
windows.put(prev.id(), prev);
}
void removeWindow(long id) {
Window window = windows.remove(id);
if(window == null) return;
if(window == activeWindow) activeWindow = null;
if(window == primaryWindow) {
Iterator<Window> iter = windows.values().iterator();
primaryWindow = iter.hasNext() ? iter.next() : null;
}
}
void updateFocus(Window window, boolean focus) {
if(focus) this.activeWindow = window;
else if(activeWindow == window) activeWindow = null;
}
void updateInputs(long windowId) {
for(int i = 0,m=devices.size();i<m;i++) {
InputDevice device = devices.get(i);
device.reset(windowId);
device.processInput(windowId);
}
}
public void destroy() {
callbacks.forEach(WindowCallback::destroy);
callbacks.clear();
}
public long getActiveWindow() {
return activeWindow == null ? 0L : activeWindow.id();
}
public long getPrimaryWindow() {
return primaryWindow == null ? 0 : primaryWindow.id();
}
public Window getWindow(long window) {
return windows.get(window);
}
public Monitor getMonitor(long id) {
return monitors.get(id);
}
public Monitor getPriamryMonitor() {
return getMonitor(GLFW.glfwGetPrimaryMonitor());
}
public Monitor getMonitorForWindow(Window window) {
long current = GLFW.glfwGetWindowMonitor(window.id());
if(current != 0L) return getMonitor(current);
int minX = window.x();
int minY = window.y();
int maxX = minX + window.screenWidth();
int maxY = minY + window.screenHeight();
int largest = 0;
Monitor mon = null;
for(Monitor monitor : monitors.values()) {
int next = monitor.getOverlap(minX, minY, maxX, maxY);
if(next > largest) {
largest = next;
mon = monitor;
}
}
return mon;
}
public static class WindowBuilder {
WindowManager manager;
long monitor;
LongList windowHints = new LongArrayList();
VideoMode fullScreenTarget;
String title = "";
int width = 640;
int height = 480;
int antiAlis = 4;
boolean vsync = true;
boolean fpsCap;
boolean fullScreen;
boolean maximized;
boolean resizable = true;
boolean floating;
boolean borderless;
boolean center = true;
private WindowBuilder(WindowManager manager) {
this.manager = manager;
monitor = GLFW.glfwGetPrimaryMonitor();
}
public WindowBuilder title(String title) {
this.title = Objects.requireNonNull(title);
return this;
}
public WindowBuilder width(int width) {
this.width = Math.max(1, width);
return this;
}
public WindowBuilder height(int height) {
this.height = Math.max(1, height);
return this;
}
public WindowBuilder antialis(int antiAlis) {
this.antiAlis = Math.max(1, antiAlis);
return this;
}
public WindowBuilder fullscreen(boolean fullScreen) {
this.fullScreen = fullScreen;
borderless &= !fullScreen;
floating &= !fullScreen;
maximized &= !fullScreen;
center &= !fullScreen;
return this;
}
public WindowBuilder maximized(boolean maximized) {
this.maximized = maximized;
this.fullScreen &= !maximized;
center &= !maximized;
return this;
}
public WindowBuilder borderless(boolean borderless) {
this.borderless = borderless;
fullScreen &= !borderless;
return this;
}
public WindowBuilder floating(boolean floating) {
this.floating = floating;
fullScreen &= !floating;
return this;
}
public WindowBuilder resizeable(boolean resizable) {
this.resizable = resizable;
return this;
}
public WindowBuilder centered(boolean center) {
this.center = center;
this.maximized &= !center;
this.fullScreen &= !center;
return this;
}
public WindowBuilder monitor(Monitor monitor) {
if(monitor == null || monitor.defaultMode() == null) return this;
this.monitor = monitor.id();
return this;
}
public WindowBuilder fullScreenMode(VideoMode mode) {
this.fullScreenTarget = mode;
return this;
}
public WindowBuilder vsync(boolean value) {
vsync = value;
return this;
}
public WindowBuilder fpsCap(boolean cap) {
fpsCap = cap;
return this;
}
public WindowBuilder addCustomHint(int key, int value) {
windowHints.add(BitUtil.toLong(key, value));
return this;
}
public Window build(Graphics graphics) {
return manager.create(this, graphics);
}
}
}
@@ -1,192 +1,192 @@
package speiger.src.coreengine.rendering.textures.custom;
import org.lwjgl.system.MemoryUtil;
import speiger.src.collections.ints.collections.IntIterator;
import speiger.src.collections.ints.sets.IntLinkedOpenHashSet;
import speiger.src.collections.ints.sets.IntSet;
import speiger.src.coreengine.assets.base.IAssetProvider;
import speiger.src.coreengine.math.BitUtil;
import speiger.src.coreengine.math.misc.ColorSpaces;
import speiger.src.coreengine.rendering.textures.base.BaseTexture;
import speiger.src.coreengine.rendering.textures.base.TextureMetadata;
import speiger.src.coreengine.rendering.utils.GLFunctions;
import speiger.src.coreengine.rendering.utils.GLStateTracker;
import speiger.src.coreengine.rendering.utils.values.GLDataType;
import speiger.src.coreengine.rendering.utils.values.textures.GLTextureFormat;
import speiger.src.coreengine.rendering.utils.values.textures.GLTextureParameter;
import speiger.src.coreengine.rendering.utils.values.textures.GLTextureValue;
public class DynamicTexture extends BaseTexture implements IDynamicTexture {
public static final TextureMetadata DEFAULT_PARAMETERS = TextureMetadata.builder().internalFormat(GLTextureFormat.RGBAI).externalFormat(GLTextureFormat.RGBA).dataFormat(GLDataType.UNSIGNED_BYTE)
.arguement(GLTextureParameter.MIN_FILTER, GLTextureValue.NEAREST).arguement(GLTextureParameter.MAG_FILTER, GLTextureValue.NEAREST)
.arguement(GLTextureParameter.WRAP_S, GLTextureValue.CLAMP_TO_EDGE).arguement(GLTextureParameter.WRAP_T, GLTextureValue.CLAMP_TO_EDGE).build();
IntSet dirtySections = new IntLinkedOpenHashSet();
TextureMetadata metadata;
int width;
int height;
long data;
public DynamicTexture(int width, int height, TextureMetadata metadata) {
this.metadata = metadata;
this.width = width;
this.height = height;
if(width % 16 != 0 || height % 16 != 0) throw new IllegalArgumentException("Texture must be a power of 16");
data = MemoryUtil.nmemAllocChecked(4L * width * height);
track();
}
@Override
public void load(IAssetProvider provider) {
createTexture();
metadata.applyArguments(id());
GLFunctions.prepare2DImage(id(), 1, metadata, width, height);
dirtySections.clear();
}
@Override
public void delete(boolean untrack) {
super.delete(untrack);
if(untrack && data != 0L) {
MemoryUtil.nmemFree(data);
data = 0L;
}
}
@Override
public int width() { return width; }
@Override
public int height() { return height; }
@Override
public ColorSpaces colorspace() { return ColorSpaces.ABGR; }
@Override
public boolean isDirty() { return !dirtySections.isEmpty(); }
@Override
public void process(boolean full) {
if(full) {
GLFunctions.upload2DSubImage(id(), 0, 0, 0, width, height, GLTextureFormat.RGBA, GLDataType.UNSIGNED_BYTE, data);
dirtySections.clear();
return;
}
GLStateTracker tracker = GLStateTracker.instance();
tracker.unpack_row_length.set(width());
Thread thread = Thread.currentThread();
for(IntIterator iter = dirtySections.iterator();iter.hasNext() && !thread.isInterrupted();iter.remove()) {
int key = iter.nextInt();
uploadPixels(tracker, BitUtil.shortKey(key) * 16, BitUtil.shortValue(key) * 16);
}
tracker.unpack_row_length.setDefault();
tracker.unpack_skip_pixel.setDefault();
tracker.unpack_skip_rows.setDefault();
}
protected void uploadPixels(GLStateTracker tracker, int x, int y) {
tracker.unpack_skip_pixel.set(x);
tracker.unpack_skip_rows.set(y);
GLFunctions.upload2DSubImage(id(), 0, x, y, 16, 16, GLTextureFormat.RGBA, GLDataType.UNSIGNED_BYTE, data);
}
protected long offset(int x, int y) {
return ((y * width()) + x) << 2L;
}
protected void ensureValid(int index) {
ensureValid(index % width, index / width);
}
protected void ensureValid(int x, int y) {
if(x < 0 || y < 0) throw new ArrayIndexOutOfBoundsException("Index out of bounds: X=["+x+"], Y=["+y+"]");
if(x > width || y > height) throw new ArrayIndexOutOfBoundsException("Index out of bounds: X=["+x+"], Y=["+y+"], width=["+width+"], height=["+height+"]");
if(data == 0L) throw new IllegalStateException("Texture Data isn't bound");
}
@Override
public void fill(int x, int y, int width, int height, int data) {
ensureValid(x, y);
ensureValid(x+width, y+height);
for(int xOff = 0;xOff<width;xOff++) {
for(int yOff = 0;yOff<height;yOff++) {
MemoryUtil.memPutInt(this.data + offset(x+xOff, y+yOff), data);
}
}
if(id() == 0) return;
for(int i = x>>4,m=width>>4;i<m;i++) {
for(int j = y>>4,n=height>>4;j<n;j++) {
dirtySections.add(BitUtil.toInt(i, j));
}
}
}
@Override
public void dirty(int x, int y) {
if(id() == 0) return;
ensureValid(x, y);
dirtySections.add(BitUtil.toInt(x >> 4, y >> 4));
}
@Override
public void set(int index, int data) {
ensureValid(index);
MemoryUtil.memPutInt(this.data + index * 4L, data);
dirty(index);
}
@Override
public void setR(int index, int red) {
ensureValid(index);
MemoryUtil.memPutByte(data + index * 4L, (byte)(red & 0xFF));
dirty(index);
}
@Override
public void setG(int index, int green) {
ensureValid(index);
MemoryUtil.memPutByte(data + index * 4L + 1L, (byte)(green & 0xFF));
dirty(index);
}
@Override
public void setB(int index, int blue) {
ensureValid(index);
MemoryUtil.memPutByte(data + index * 4L + 2L, (byte)(blue & 0xFF));
dirty(index);
}
@Override
public void setA(int index, int alpha) {
ensureValid(index);
MemoryUtil.memPutByte(data + index * 4L + 3L, (byte)(alpha & 0xFF));
dirty(index);
}
@Override
public int get(int index) {
ensureValid(index);
return MemoryUtil.memGetInt(data + index * 4L);
}
@Override
public int getR(int index) {
ensureValid(index);
return MemoryUtil.memGetByte(data + index * 4L);
}
@Override
public int getG(int index) {
ensureValid(index);
return MemoryUtil.memGetByte(data + index * 4L + 1L);
}
@Override
public int getB(int index) {
ensureValid(index);
return MemoryUtil.memGetByte(data + index * 4L + 2L);
}
@Override
public int getA(int index) {
ensureValid(index);
return MemoryUtil.memGetByte(data + index * 4L + 3L);
}
package speiger.src.coreengine.rendering.textures.custom;
import org.lwjgl.system.MemoryUtil;
import speiger.src.collections.ints.collections.IntIterator;
import speiger.src.collections.ints.sets.IntLinkedOpenHashSet;
import speiger.src.collections.ints.sets.IntSet;
import speiger.src.coreengine.assets.base.IAssetProvider;
import speiger.src.coreengine.math.BitUtil;
import speiger.src.coreengine.math.misc.ColorSpaces;
import speiger.src.coreengine.rendering.textures.base.BaseTexture;
import speiger.src.coreengine.rendering.textures.base.TextureMetadata;
import speiger.src.coreengine.rendering.utils.GLFunctions;
import speiger.src.coreengine.rendering.utils.GLStateTracker;
import speiger.src.coreengine.rendering.utils.values.GLDataType;
import speiger.src.coreengine.rendering.utils.values.textures.GLTextureFormat;
import speiger.src.coreengine.rendering.utils.values.textures.GLTextureParameter;
import speiger.src.coreengine.rendering.utils.values.textures.GLTextureValue;
public class DynamicTexture extends BaseTexture implements IDynamicTexture {
public static final TextureMetadata DEFAULT_PARAMETERS = TextureMetadata.builder().internalFormat(GLTextureFormat.RGBAI).externalFormat(GLTextureFormat.RGBA).dataFormat(GLDataType.UNSIGNED_BYTE)
.arguement(GLTextureParameter.MIN_FILTER, GLTextureValue.NEAREST).arguement(GLTextureParameter.MAG_FILTER, GLTextureValue.NEAREST)
.arguement(GLTextureParameter.WRAP_S, GLTextureValue.CLAMP_TO_EDGE).arguement(GLTextureParameter.WRAP_T, GLTextureValue.CLAMP_TO_EDGE).build();
IntSet dirtySections = new IntLinkedOpenHashSet();
TextureMetadata metadata;
int width;
int height;
long data;
public DynamicTexture(int width, int height, TextureMetadata metadata) {
this.metadata = metadata;
this.width = width;
this.height = height;
if(width % 16 != 0 || height % 16 != 0) throw new IllegalArgumentException("Texture must be a power of 16");
data = MemoryUtil.nmemAllocChecked(4L * width * height);
track();
}
@Override
public void load(IAssetProvider provider) {
createTexture();
metadata.applyArguments(id());
GLFunctions.prepare2DImage(id(), 1, metadata, width, height);
dirtySections.clear();
}
@Override
public void delete(boolean untrack) {
super.delete(untrack);
if(untrack && data != 0L) {
MemoryUtil.nmemFree(data);
data = 0L;
}
}
@Override
public int width() { return width; }
@Override
public int height() { return height; }
@Override
public ColorSpaces colorspace() { return ColorSpaces.ABGR; }
@Override
public boolean isDirty() { return !dirtySections.isEmpty(); }
@Override
public void process(boolean full) {
if(full) {
GLFunctions.upload2DSubImage(id(), 0, 0, 0, width, height, GLTextureFormat.RGBA, GLDataType.UNSIGNED_BYTE, data);
dirtySections.clear();
return;
}
GLStateTracker tracker = GLStateTracker.instance();
tracker.unpack_row_length.set(width());
Thread thread = Thread.currentThread();
for(IntIterator iter = dirtySections.iterator();iter.hasNext() && !thread.isInterrupted();iter.remove()) {
int key = iter.nextInt();
uploadPixels(tracker, BitUtil.shortKey(key) * 16, BitUtil.shortValue(key) * 16);
}
tracker.unpack_row_length.setDefault();
tracker.unpack_skip_pixel.setDefault();
tracker.unpack_skip_rows.setDefault();
}
protected void uploadPixels(GLStateTracker tracker, int x, int y) {
tracker.unpack_skip_pixel.set(x);
tracker.unpack_skip_rows.set(y);
GLFunctions.upload2DSubImage(id(), 0, x, y, 16, 16, GLTextureFormat.RGBA, GLDataType.UNSIGNED_BYTE, data);
}
protected long offset(int x, int y) {
return ((y * width()) + x) << 2L;
}
protected void ensureValid(int index) {
ensureValid(index % width, index / width);
}
protected void ensureValid(int x, int y) {
if(x < 0 || y < 0) throw new ArrayIndexOutOfBoundsException("Index out of bounds: X=["+x+"], Y=["+y+"]");
if(x > width || y > height) throw new ArrayIndexOutOfBoundsException("Index out of bounds: X=["+x+"], Y=["+y+"], width=["+width+"], height=["+height+"]");
if(data == 0L) throw new IllegalStateException("Texture Data isn't bound");
}
@Override
public void fill(int x, int y, int width, int height, int data) {
ensureValid(x, y);
ensureValid(x+width, y+height);
for(int xOff = 0;xOff<width;xOff++) {
for(int yOff = 0;yOff<height;yOff++) {
MemoryUtil.memPutInt(this.data + offset(x+xOff, y+yOff), data);
}
}
if(id() == 0) return;
for(int i = x>>4,m=width>>4;i<m;i++) {
for(int j = y>>4,n=height>>4;j<n;j++) {
dirtySections.add(BitUtil.toInt(i, j));
}
}
}
@Override
public void dirty(int x, int y) {
if(id() == 0) return;
ensureValid(x, y);
dirtySections.add(BitUtil.toInt(x >> 4, y >> 4));
}
@Override
public void set(int index, int data) {
ensureValid(index);
MemoryUtil.memPutInt(this.data + index * 4L, data);
dirty(index);
}
@Override
public void setR(int index, int red) {
ensureValid(index);
MemoryUtil.memPutByte(data + index * 4L, (byte)(red & 0xFF));
dirty(index);
}
@Override
public void setG(int index, int green) {
ensureValid(index);
MemoryUtil.memPutByte(data + index * 4L + 1L, (byte)(green & 0xFF));
dirty(index);
}
@Override
public void setB(int index, int blue) {
ensureValid(index);
MemoryUtil.memPutByte(data + index * 4L + 2L, (byte)(blue & 0xFF));
dirty(index);
}
@Override
public void setA(int index, int alpha) {
ensureValid(index);
MemoryUtil.memPutByte(data + index * 4L + 3L, (byte)(alpha & 0xFF));
dirty(index);
}
@Override
public int get(int index) {
ensureValid(index);
return MemoryUtil.memGetInt(data + index * 4L);
}
@Override
public int getR(int index) {
ensureValid(index);
return MemoryUtil.memGetByte(data + index * 4L);
}
@Override
public int getG(int index) {
ensureValid(index);
return MemoryUtil.memGetByte(data + index * 4L + 1L);
}
@Override
public int getB(int index) {
ensureValid(index);
return MemoryUtil.memGetByte(data + index * 4L + 2L);
}
@Override
public int getA(int index) {
ensureValid(index);
return MemoryUtil.memGetByte(data + index * 4L + 3L);
}
}
@@ -1,14 +1,14 @@
#version 330
in vec4 pass_color;
in vec2 pass_tex;
out vec4 frag_color;
uniform sampler2D texture;
void main()
{
vec4 color = texture2D(texture, pass_tex);
frag_color = pass_color * vec4(1, 1, 1, color.r);
#version 420
in vec4 pass_color;
in vec2 pass_tex;
out vec4 frag_color;
layout(binding = 0) uniform sampler2D texture;
void main()
{
vec4 color = texture2D(texture, pass_tex);
frag_color = pass_color * vec4(1, 1, 1, color.r);
}
@@ -1,17 +1,19 @@
#version 330
layout(location = 0) in vec3 in_position;
layout(location = 1) in vec2 in_tex;
layout(location = 2) in vec4 in_color;
out vec4 pass_color;
out vec2 pass_tex;
uniform mat4 proViewMatrix;
void main()
{
gl_Position = proViewMatrix * vec4(in_position, 1.0);
pass_color = in_color;
pass_tex = in_tex;
#version 420
layout(location = 0) in vec3 in_position;
layout(location = 1) in vec2 in_tex;
layout(location = 2) in vec4 in_color;
out vec4 pass_color;
out vec2 pass_tex;
layout(binding = 0) uniform Camera {
mat4 proViewMatrix;
} camera;
void main()
{
gl_Position = camera.proViewMatrix * vec4(in_position, 1.0);
pass_color = in_color;
pass_tex = in_tex;
}