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= connection.project.dir=
eclipse.preferences.version=1 eclipse.preferences.version=1
gradle.user.home= gradle.user.home=
java.home=C\:/Program Files/Eclipse Adoptium/jdk25 java.home=C\:/Program Files/Java/jdk25
jvm.arguments= jvm.arguments=
offline.mode=false offline.mode=false
override.workspace.settings=true override.workspace.settings=true
+1 -1
View File
@@ -1,4 +1,4 @@
org.gradle.jvmargs=-Xmx2G org.gradle.jvmargs=-Xmx2G
lwjglVersion = 3.3.4 lwjglVersion = 3.4.2
lwjglNatives = natives-windows lwjglNatives = natives-windows
@@ -1,6 +1,8 @@
package speiger.src.coreengine.assets.api; package speiger.src.coreengine.assets.api;
import java.io.IOException; import java.io.IOException;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.BasicFileAttributes;
@@ -12,7 +14,9 @@ import java.util.function.Consumer;
import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable; 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.FolderWatcher.ChangeType;
import speiger.src.coreengine.assets.impl.ZipAssetPackage;
@NullMarked @NullMarked
public interface IAssetPackage extends AutoCloseable { public interface IAssetPackage extends AutoCloseable {
@@ -23,6 +27,23 @@ public interface IAssetPackage extends AutoCloseable {
IAsset get(ID location); IAsset get(ID location);
void getAll(ID folder, BiConsumer<ID, IAsset> assets); 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 record AssetIdentity(long size, FileTime lastModified) {
public AssetIdentity(Path path) throws IOException { 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) { public static final ID of(String location) {
Objects.requireNonNull(location); Objects.requireNonNull(location);
int index = location.indexOf(":"); int index = location.indexOf(":");
String domain = location.substring(0, index); String domain = index == -1 ? "base" : location.substring(0, index);
String path = location.substring(index + 1); 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(!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+"]"); if(!isValidPath(path)) throw new IllegalArgumentException("Non [a-zA-Z0-9/_.-] Character found in path of location ["+domain+":"+path+"]");
return new ID(domain, path); return new ID(domain, path);
@@ -18,7 +18,7 @@ public class Checksums {
public static class CRC implements IAssetParser<String> { public static class CRC implements IAssetParser<String> {
@Override @Override
public String parse(Path path) throws IOException { 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); if(files.containsKey("crc")) return Long.toHexString(((Long)files.get("crc")) & 0xFFFFFFFFL);
CRC32C crc = new CRC32C(); CRC32C crc = new CRC32C();
crc.update(Files.readAllBytes(path)); crc.update(Files.readAllBytes(path));
@@ -4,6 +4,7 @@ import java.nio.file.Path;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Predicate; import java.util.function.Predicate;
@@ -27,6 +28,7 @@ public class AssetManager {
List<IAssetPackage> packages; List<IAssetPackage> packages;
ReloadTracker tracker = new ReloadTracker(); ReloadTracker tracker = new ReloadTracker();
ReloadManager manager; ReloadManager manager;
AtomicInteger InUse = new AtomicInteger();
public AssetManager(BaseProvider provider) { public AssetManager(BaseProvider provider) {
setPackages(provider); setPackages(provider);
@@ -89,21 +91,29 @@ public class AssetManager {
} }
public IAssetProvider get() { 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 { public final class WrappedProvider implements IAssetProvider {
BaseProvider provider; BaseProvider provider;
AutoCloseable closer;
public WrappedProvider(BaseProvider provider) { public WrappedProvider(BaseProvider provider, AutoCloseable closer) {
this.provider = provider; this.provider = provider;
this.closer = closer;
} }
@Override @Override
public void close() throws Exception { public void close() throws Exception {
BaseProvider provider = this.provider; if(provider == null) return;
this.provider = null; this.provider = null;
provider.close(); closer.close();
} }
@Override @Override
@@ -5,6 +5,6 @@ import java.util.List;
import speiger.src.coreengine.assets.api.IAssetPackage; import speiger.src.coreengine.assets.api.IAssetPackage;
import speiger.src.coreengine.assets.api.IAssetProvider; import speiger.src.coreengine.assets.api.IAssetProvider;
public interface BaseProvider extends IAssetProvider, AutoCloseable { public interface BaseProvider extends IAssetProvider {
List<IAssetPackage> packages(); List<IAssetPackage> packages();
} }
@@ -38,13 +38,21 @@ public abstract class VertexBuffer implements GraphicsResource {
public abstract VertexBuffer allocate(int totalBytes); public abstract VertexBuffer allocate(int totalBytes);
public abstract VertexBuffer set(long pointer, int totalBytes); public abstract VertexBuffer set(long pointer, int totalBytes);
public VertexBuffer set(ByteBuffer buffer) { public VertexBuffer set(ByteBuffer buffer) { return set(MemoryUtil.memAddress(buffer), buffer.remaining()); }
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 abstract VertexBuffer fill(long pointer, int totalBytes, int offset);
public VertexBuffer fill(int offset, ByteBuffer buffer) { public VertexBuffer fill(int offset, ByteBuffer buffer) { return fill(MemoryUtil.memAddress(buffer), buffer.remaining(), offset); }
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); public abstract VertexBuffer read(long pointer, int totalBytes, int offset);
@@ -13,10 +13,13 @@ public interface GraphicsCommandBuffer extends GraphicsResource {
GraphicsCommandBuffer pipeline(ShaderPipeline pipeline); GraphicsCommandBuffer pipeline(ShaderPipeline pipeline);
GraphicsCommandBuffer mesh(Mesh mesh); GraphicsCommandBuffer mesh(Mesh mesh);
GraphicsCommandBuffer texture(int set, int binding, Texture texture, Sampler sampler); GraphicsCommandBuffer texture(int binding, Texture texture, Sampler sampler);
GraphicsCommandBuffer uniform(int set, int binding, VertexBuffer buffer);
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 pushScissors(int x, int y, int width, int height);
GraphicsCommandBuffer popScissors(); GraphicsCommandBuffer popScissors();
@@ -1,34 +1,31 @@
package speiger.src.coreengine.graphics.api.core; 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.target.RenderTarget;
import speiger.src.coreengine.graphics.api.texture.Texture; 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.api.utils.PushableResource;
import speiger.src.coreengine.math.vector.floats.Vec4f; import speiger.src.coreengine.math.vector.floats.Vec4f;
public interface GraphicsCommandQueue { public interface GraphicsCommandQueue {
public static final int CLEAR_COLOR = 1; public static final int CLEAR_COLOR = 1;
public static final int CLEAR_DEPTH = 2; public static final int CLEAR_DEPTH = 2;
public static final int CLEAR_BOTH = 3;
public PushableResource putClearColor(Vec4f color); public PushableResource putClearColor(Vec4f color);
public PushableResource putClearDepth(double value); public PushableResource putClearDepth(double value);
public PushableResource putRenderTarget(RenderTarget target); 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 clearParam);
public void clearTexture(int x, int y, int width, int height, 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 clearParam);
public void clearTexture(Texture texture, int x, int y, int width, int height, 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 writeToTexture(Texture target, TextureFormat format, GraphicsDataType dataType, long source);
public void readFromTexture(Texture source, int x, int y, int width, int height, long target); 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 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; 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.VertexBuffer;
import speiger.src.coreengine.graphics.api.buffer.states.BufferState; 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.BufferType;
import speiger.src.coreengine.graphics.api.mesh.Mesh; import speiger.src.coreengine.graphics.api.mesh.Mesh;
import speiger.src.coreengine.graphics.api.sampler.Sampler; import speiger.src.coreengine.graphics.api.sampler.Sampler;
import speiger.src.coreengine.graphics.api.sampler.SamplerSettings; 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.Texture;
import speiger.src.coreengine.graphics.api.texture.TextureSettings; import speiger.src.coreengine.graphics.api.texture.TextureSettings;
import speiger.src.coreengine.graphics.api.utils.ExecutionType; import speiger.src.coreengine.graphics.api.utils.ExecutionType;
import speiger.src.coreengine.rendering.input.window.Window; import speiger.src.coreengine.rendering.input.window.Window;
public interface GraphicsDevice { public interface GraphicsDevice {
public Window getWindow(); Window window();
public GraphicsSurface createSurface(); GraphicsSurface createSurface();
public GraphicsCommandBuffer createCommandBuffer(ExecutionType type); GraphicsCommandBuffer createCommandBuffer(ExecutionType type);
public GraphicsCommandQueue getQueue(); GraphicsCommandQueue queue();
public VertexBuffer createBuffer(BufferType type, BufferState state); VertexBuffer createBuffer(BufferType type, BufferState state);
public VertexBuffer createBuffer(BufferType type, BufferState state, int allocatedBytes); VertexBuffer createBuffer(BufferType type, BufferState state, int allocatedBytes);
public Texture createTexture(TextureSettings data, int width, int height); Texture createTexture(TextureSettings data, int width, int height);
public Sampler createSampler(SamplerSettings settings); Sampler createSampler(SamplerSettings settings);
public Mesh createMesh(Mesh.Builder builder); 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); Objects.requireNonNull(magSample);
} }
public static Builder builder() {
return new Builder();
}
public static class Builder { public static class Builder {
BorderMode wrapS; BorderMode wrapS;
BorderMode wrapT; BorderMode wrapT;
@@ -23,6 +27,19 @@ public record SamplerSettings(BorderMode wrapS, BorderMode wrapT, Optional<Borde
SampleMode minSample; SampleMode minSample;
SampleMode magSample; 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) { public Builder wrapS(BorderMode mode) {
wrapS = Objects.requireNonNull(mode); wrapS = Objects.requireNonNull(mode);
return this; return this;
@@ -38,6 +55,13 @@ public record SamplerSettings(BorderMode wrapS, BorderMode wrapT, Optional<Borde
return this; 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) { public Builder minSample(SampleMode mode) {
minSample = Objects.requireNonNull(mode); minSample = Objects.requireNonNull(mode);
return this; return this;
@@ -4,8 +4,8 @@ import java.util.Objects;
import speiger.src.coreengine.graphics.api.utils.AlphaFunction; import speiger.src.coreengine.graphics.api.utils.AlphaFunction;
public record DepthTarget(AlphaFunction function, boolean write) { public record DepthTarget(boolean enabled, AlphaFunction function, boolean write) {
public static final DepthTarget DEFAULT = new DepthTarget(AlphaFunction.GEQUAL, true); public static final DepthTarget DEFAULT = new DepthTarget(true, AlphaFunction.GEQUAL, true);
public DepthTarget { public DepthTarget {
Objects.requireNonNull(function); 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; return this;
} }
public Builder withUniform(String name, int binding, int set, int bytes) { public Builder withUniform(String name, int binding, int slot, int bytes) {
uniforms.add(new UniformBinding(name, binding, set, bytes)); uniforms.add(new UniformBinding(name, binding, slot, bytes));
return this; return this;
} }
public Builder withTexture(String name, int binding, int set, TextureType type) { public Builder withTexture(String name, int binding, int slot, TextureType type) {
textures.add(new TextureBinding(name, binding, set, type)); textures.add(new TextureBinding(name, binding, slot, type));
return this; return this;
} }
@@ -5,13 +5,13 @@ import java.util.Objects;
import speiger.src.coreengine.graphics.api.texture.states.TextureType; import speiger.src.coreengine.graphics.api.texture.states.TextureType;
public class Bindings { 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 { public UniformBinding {
Objects.requireNonNull(name, "A name has to be provided"); 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 { public TextureBinding {
Objects.requireNonNull(name, "A name has to be provided"); Objects.requireNonNull(name, "A name has to be provided");
Objects.requireNonNull(type, "A TextureType 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; package speiger.src.coreengine.graphics.api.shader.states;
public record BlendFunction(BlendFactor colorSource, BlendFactor alphaSource, BlendFactor colorTarget, BlendFactor alphaTarget) { 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) { public BlendFunction(BlendFactor source, BlendFactor target) {
this(source, BlendFactor.ONE, target, BlendFactor.ZERO); this(source, BlendFactor.ONE, target, BlendFactor.ZERO);
} }
@@ -1,10 +1,10 @@
package speiger.src.coreengine.graphics.api.target; 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.Texture;
import speiger.src.coreengine.graphics.api.texture.states.TextureType; 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 { public TextureTarget {
if(color == null && depth == null) throw new IllegalArgumentException("At least either texture has to be nonNull"); if(color == null && depth == null) throw new IllegalArgumentException("At least either texture has to be nonNull");
if(color != null && depth != null) { 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.TextureFormat;
import speiger.src.coreengine.graphics.api.texture.states.TextureType; 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 { public TextureSettings {
Objects.requireNonNull(type, "Texture Type shouldn't be null"); Objects.requireNonNull(type, "Texture Type shouldn't be null");
Objects.requireNonNull(internal, "Internal Format 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(baseLevel);
Objects.requireNonNull(maxLevel); Objects.requireNonNull(maxLevel);
Objects.requireNonNull(swizzle); Objects.requireNonNull(swizzle);
@@ -33,9 +32,8 @@ public record TextureSettings(TextureType type, TextureFormat internal, TextureF
} }
public static class Builder { public static class Builder {
TextureFormat internalFormat; TextureFormat internalFormat = TextureFormat.RGBA;
TextureFormat externalFormat; TextureType type = TextureType.TEXTURE_2D;
TextureType type;
boolean generateMipmapping = false; boolean generateMipmapping = false;
OptionalInt baseLevel = OptionalInt.empty(); OptionalInt baseLevel = OptionalInt.empty();
OptionalInt maxLevel = OptionalInt.empty(); OptionalInt maxLevel = OptionalInt.empty();
@@ -47,7 +45,6 @@ public record TextureSettings(TextureType type, TextureFormat internal, TextureF
private Builder(TextureSettings settings) { private Builder(TextureSettings settings) {
type = settings.type; type = settings.type;
internalFormat = settings.internal; internalFormat = settings.internal;
externalFormat = settings.external;
generateMipmapping = settings.generateMipmapping; generateMipmapping = settings.generateMipmapping;
baseLevel = settings.baseLevel; baseLevel = settings.baseLevel;
maxLevel = settings.maxLevel; maxLevel = settings.maxLevel;
@@ -58,8 +55,7 @@ public record TextureSettings(TextureType type, TextureFormat internal, TextureF
public TextureSettings build() { public TextureSettings build() {
return new TextureSettings( return new TextureSettings(
Objects.requireNonNull(type, "Texture Type shouldn't be null"), Objects.requireNonNull(type, "Texture Type shouldn't be null"),
Objects.requireNonNull(internalFormat, "Internal Format shouldn't be null"), Objects.requireNonNull(internalFormat, "Internal Format shouldn't be null"),
Objects.requireNonNull(externalFormat, "External Format shouldn't be null"),
generateMipmapping, baseLevel, maxLevel, new ImmutableObjectList<>(swizzle), stencilType); generateMipmapping, baseLevel, maxLevel, new ImmutableObjectList<>(swizzle), stencilType);
} }
@@ -70,7 +66,6 @@ public record TextureSettings(TextureType type, TextureFormat internal, TextureF
public Builder format(TextureFormat format) { public Builder format(TextureFormat format) {
this.internalFormat = format; this.internalFormat = format;
this.externalFormat = format;
return this; return this;
} }
@@ -79,11 +74,6 @@ public record TextureSettings(TextureType type, TextureFormat internal, TextureF
return this; return this;
} }
public Builder external(TextureFormat format) {
this.externalFormat = format;
return this;
}
public Builder swizzle(SwizzleMask red, SwizzleMask green, SwizzleMask blue, SwizzleMask alpha) { public Builder swizzle(SwizzleMask red, SwizzleMask green, SwizzleMask blue, SwizzleMask alpha) {
swizzle[0] = Optional.ofNullable(red); swizzle[0] = Optional.ofNullable(red);
swizzle[1] = Optional.ofNullable(green); swizzle[1] = Optional.ofNullable(green);
@@ -1,11 +1,22 @@
package speiger.src.coreengine.graphics.api.texture.states; package speiger.src.coreengine.graphics.api.texture.states;
public enum TextureFormat { public enum TextureFormat {
R, R(1),
RGB, RG(2),
RGBA, RGB(3),
DEPTH, RGBA(4),
DEPTH_STENCIL, DEPTH(1),
LUMINANCE, DEPTH_STENCIL(2),
LUMINANCE_ALPHA 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; 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.objects.lists.ObjectArrayList;
import speiger.src.collections.utils.Stack; import speiger.src.collections.utils.Stack;
import speiger.src.coreengine.math.vector.ints.Vec4i; import speiger.src.coreengine.math.vector.ints.Vec4i;
public class ScissorsManager { public class ScissorsManager {
IScissorsHandler handler;
Stack<Vec4i> stack = new ObjectArrayList<>(); Stack<Vec4i> stack = new ObjectArrayList<>();
boolean withRoot = false;
int limit; int limit;
public ScissorsManager(int limit) { public ScissorsManager(int limit, IScissorsHandler handler) {
if(limit <= 0) throw new IllegalStateException("Limit is negative"); if(limit <= 0) throw new IllegalStateException("Limit is negative");
this.limit = limit; 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) { public void push(int x, int y, int width, int height) {
@@ -22,12 +33,18 @@ public class ScissorsManager {
} }
if(stack.isEmpty()) { if(stack.isEmpty()) {
stack.push(Vec4i.of(x, y, x + width, y + height)); stack.push(Vec4i.of(x, y, x + width, y + height));
GL11.glEnable(GL11.GL_SCISSOR_TEST); handler.enable();
GL11.glScissor(x, y, width, height); handler.apply(x, y, width, height);
return; return;
} }
Vec4i top = stack.top(); 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) { public boolean contains(int x, int y, int width, int height) {
@@ -39,14 +56,39 @@ public class ScissorsManager {
} }
public void pop() { public void pop() {
if(stack.isEmpty()) { if(stack.size() <= (withRoot ? 1 : 0)) {
//TODO implement logging //TODO implement logging
return; return;
} }
stack.pop(); 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() { public void clear() {
stack.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) { private void add(Element element) {
if(mapped.put(element.name(), element) != null) throw new IllegalStateException("Duplicated Names are not allowed"); if(mapped.put(element.name(), element) != null) throw new IllegalStateException("Duplicated Names are not allowed");
elements.add(element); elements.add(element);
offsets.add(stride); offsets.add(bytes);
int size = element.size(); int size = element.size();
stride += size; stride += size;
bytes += element.type().size(size); bytes += element.type().size(size);
@@ -29,6 +29,7 @@ public class GLVertexBuffer extends VertexBuffer {
} }
public int id() { public int id() {
if(id == 0) throw new IllegalStateException("Buffer is already removed");
return id; return id;
} }
@@ -7,17 +7,24 @@ import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL30; import org.lwjgl.opengl.GL30;
import org.lwjgl.opengl.GL45; 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.GraphicsCommandBuffer;
import speiger.src.coreengine.graphics.api.core.GraphicsCommandQueue; 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.RenderTarget;
import speiger.src.coreengine.graphics.api.target.ScreenTarget; import speiger.src.coreengine.graphics.api.target.ScreenTarget;
import speiger.src.coreengine.graphics.api.target.TextureTarget; import speiger.src.coreengine.graphics.api.target.TextureTarget;
import speiger.src.coreengine.graphics.api.texture.Texture; 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.api.utils.PushableResource;
import speiger.src.coreengine.graphics.opengl.texture.FrameBufferCache; import speiger.src.coreengine.graphics.opengl.texture.FrameBufferCache;
import speiger.src.coreengine.graphics.opengl.texture.GLTexture; 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.floats.Vec4f;
import speiger.src.coreengine.math.vector.ints.Vec4i;
import speiger.src.coreengine.rendering.input.window.Window; import speiger.src.coreengine.rendering.input.window.Window;
public class GLCommandQueue implements GraphicsCommandQueue { public class GLCommandQueue implements GraphicsCommandQueue {
@@ -25,12 +32,14 @@ public class GLCommandQueue implements GraphicsCommandQueue {
FrameBufferCache fboCache = new FrameBufferCache(); FrameBufferCache fboCache = new FrameBufferCache();
RenderTarget renderTarget = ScreenTarget.INSTANCE; RenderTarget renderTarget = ScreenTarget.INSTANCE;
ScreenBuffer targetFBO; ScreenBuffer targetFBO;
int tempWriteFBO;
Vec4f clearColor = Vec4f.mutable(0F, 0F, 0F, 1F); Vec4f clearColor = Vec4f.mutable(0F, 0F, 0F, 1F);
double clearDepth = 0D; double clearDepth = 0D;
public GLCommandQueue(GLGraphicsDevice device) { public GLCommandQueue(GLGraphicsDevice device) {
this.device = 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 @Override
@@ -48,6 +57,22 @@ public class GLCommandQueue implements GraphicsCommandQueue {
return new PushableObject<>(renderTarget, setRenderTarget(target), () -> renderTarget, this::setRenderTarget); 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) { private Vec4f setClearColor(Vec4f value) {
if(clearColor.equals(value)) return value; if(clearColor.equals(value)) return value;
clearColor.set(value); clearColor.set(value);
@@ -67,10 +92,10 @@ public class GLCommandQueue implements GraphicsCommandQueue {
this.renderTarget = target; this.renderTarget = target;
targetFBO = switch(target) { targetFBO = switch(target) {
case ScreenTarget _ -> { case ScreenTarget _ -> {
Window window = device.getWindow(); Window window = device.window();
yield new ScreenBuffer(0, window.width(), window.height()); 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); int fbo = fboCache.getOrCreateFBO(id);
if(color != null) { if(color != null) {
GL45.glNamedFramebufferTexture(fbo, GL30.GL_COLOR_ATTACHMENT0, ((GLTexture)color).id(), 0); GL45.glNamedFramebufferTexture(fbo, GL30.GL_COLOR_ATTACHMENT0, ((GLTexture)color).id(), 0);
@@ -90,26 +115,61 @@ public class GLCommandQueue implements GraphicsCommandQueue {
@Override @Override
public void clearTexture(int clearParam) { 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 @Override
public void clearTexture(int x, int y, int width, int height, int clearParam) { 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 @Override
public void clearTexture(Texture texture, int clearParam) { 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 @Override
public void clearTexture(Texture texture, int x, int y, int width, int height, int clearParam) { 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 @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 @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 { 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) {} 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; package speiger.src.coreengine.graphics.opengl.core;
import org.lwjgl.glfw.GLFW; 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.graphics.api.core.Graphics;
import speiger.src.coreengine.rendering.input.window.Window; import speiger.src.coreengine.rendering.input.window.Window;
public class GLGraphics implements Graphics { public class GLGraphics implements Graphics {
AssetManager manager;
public GLGraphics(AssetManager manager) {
this.manager = manager;
}
@Override @Override
public String getName() { public String getName() {
return "OpenGL"; return "OpenGL";
@@ -14,6 +21,9 @@ public class GLGraphics implements Graphics {
@Override @Override
public void setupWindowArguments() { 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_MAJOR, 4);
GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_VERSION_MINOR, 0); GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_VERSION_MINOR, 0);
GLFW.glfwWindowHint(GLFW.GLFW_OPENGL_PROFILE, GLFW.GLFW_OPENGL_CORE_PROFILE); GLFW.glfwWindowHint(GLFW.GLFW_OPENGL_PROFILE, GLFW.GLFW_OPENGL_CORE_PROFILE);
@@ -22,7 +32,7 @@ public class GLGraphics implements Graphics {
@Override @Override
public GLGraphicsDevice createDevice(Window window) { 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; package speiger.src.coreengine.graphics.opengl.core;
import java.nio.ByteBuffer;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects; import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.OptionalInt; import java.util.OptionalInt;
import java.util.function.IntPredicate;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12; import org.lwjgl.opengl.GL12;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL31;
import org.lwjgl.opengl.GL33; import org.lwjgl.opengl.GL33;
import org.lwjgl.opengl.GL41;
import org.lwjgl.opengl.GL43; import org.lwjgl.opengl.GL43;
import org.lwjgl.opengl.GL45; 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.Int2ObjectLinkedOpenHashMap;
import speiger.src.collections.ints.maps.impl.hash.Int2ObjectOpenHashMap; import speiger.src.collections.ints.maps.impl.hash.Int2ObjectOpenHashMap;
import speiger.src.collections.ints.maps.interfaces.Int2ObjectMap; 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.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.VertexBuffer;
import speiger.src.coreengine.graphics.api.buffer.states.BufferState; 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.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;
import speiger.src.coreengine.graphics.api.mesh.Mesh.LayoutInfo; import speiger.src.coreengine.graphics.api.mesh.Mesh.LayoutInfo;
import speiger.src.coreengine.graphics.api.sampler.SamplerSettings; 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.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.TextureSettings;
import speiger.src.coreengine.graphics.api.texture.states.SwizzleMask; 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.api.utils.ExecutionType;
import speiger.src.coreengine.graphics.opengl.buffer.GLVertexBuffer; import speiger.src.coreengine.graphics.opengl.buffer.GLVertexBuffer;
import speiger.src.coreengine.graphics.opengl.mesh.GLMesh; import speiger.src.coreengine.graphics.opengl.mesh.GLMesh;
import speiger.src.coreengine.graphics.opengl.sampler.GLSampler; 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;
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.texture.GLTexture;
import speiger.src.coreengine.graphics.opengl.utils.GLFunctions; 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.graphics.opengl.utils.GLUtils;
import speiger.src.coreengine.rendering.input.window.Window; import speiger.src.coreengine.rendering.input.window.Window;
public class GLGraphicsDevice implements GraphicsDevice { 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; GLCommandQueue queue;
Window owner; Window owner;
public GLGraphicsDevice(Window owner) { public GLGraphicsDevice(Window owner, AssetManager manager, GLCapabilities capbilities) {
this.owner = owner; this.owner = owner;
this.manager = manager;
this.capabilities = capbilities;
this.queue = new GLCommandQueue(this); this.queue = new GLCommandQueue(this);
} }
@Override @Override
public Window getWindow() { public Window window() {
return owner; return owner;
} }
@@ -56,11 +98,11 @@ public class GLGraphicsDevice implements GraphicsDevice {
@Override @Override
public GraphicsCommandBuffer createCommandBuffer(ExecutionType type) { public GraphicsCommandBuffer createCommandBuffer(ExecutionType type) {
return null; return type == ExecutionType.IMMEDIATE ? new GLImmidateCommandBuffer(this) : new GLRecordingCommandBuffer(this);
} }
@Override @Override
public GLCommandQueue getQueue() { public GLCommandQueue queue() {
return queue; return queue;
} }
@@ -79,6 +121,7 @@ public class GLGraphicsDevice implements GraphicsDevice {
public GLTexture createTexture(TextureSettings settings, int width, int height) { public GLTexture createTexture(TextureSettings settings, int width, int height) {
if(width < 0) throw new IllegalArgumentException("Width ["+width+"] is invalid"); if(width < 0) throw new IllegalArgumentException("Width ["+width+"] is invalid");
if(height < 0) throw new IllegalArgumentException("Height ["+height+"] 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()); int id = GLFunctions.createTexture(settings.type());
settings.stencilType().ifPresent(T -> GL45.glTextureParameteri(id, GL43.GL_DEPTH_STENCIL_TEXTURE_MODE, GLUtils.toGL(T))); 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)); 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)), 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); 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); return new GLMesh(builder.info(), new Int2ObjectOpenHashMap<>(), null, null, false);
} }
public ShaderInstance createShader(ShaderPipeline line, IAssetProvider provider) { @Override
return null; 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; 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.buffer.VertexBuffer;
import speiger.src.coreengine.graphics.api.core.GraphicsCommandBuffer; import speiger.src.coreengine.graphics.api.core.GraphicsCommandBuffer;
import speiger.src.coreengine.graphics.api.mesh.Mesh; import speiger.src.coreengine.graphics.api.mesh.Mesh;
import speiger.src.coreengine.graphics.api.sampler.Sampler; 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.ShaderPipeline;
import speiger.src.coreengine.graphics.api.texture.Texture; 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 { public class GLImmidateCommandBuffer implements GraphicsCommandBuffer {
GLGraphicsDevice device;
boolean removed = false;
boolean recording = false; boolean recording = false;
ShaderPipeline pipeline;
Mesh mesh;
int recorded = 0; 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() { protected void ensureDrawing() {
if(removed) throw new IllegalStateException("CommandBuffer is removed");
if(!recording) throw new IllegalStateException("CommandBuffer isn't recording"); if(!recording) throw new IllegalStateException("CommandBuffer isn't recording");
} }
@Override @Override
public boolean isRemoved() { return false; } public boolean isRemoved() { return removed; }
@Override @Override
public void remove() {} public void remove() {
clear();
device = null;
removed = true;
recorded = 0;
}
@Override @Override
public GraphicsCommandBuffer begin() { public GraphicsCommandBuffer begin() {
if(removed) throw new IllegalStateException("CommandBuffer is removed");
if(recording) throw new IllegalStateException("CommandBuffer already recording"); if(recording) throw new IllegalStateException("CommandBuffer already recording");
recording = true; recording = true;
recorded = 0;
return this; return this;
} }
@Override @Override
public GraphicsCommandBuffer setScissors(int x, int y, int width, int height) { public GraphicsCommandBuffer setScissors(int x, int y, int width, int height) {
ensureDrawing();
scissors.setRoot(x, y, width, height);
return this; return this;
} }
@Override @Override
public GraphicsCommandBuffer pipeline(ShaderPipeline pipeline) { public GraphicsCommandBuffer pipeline(ShaderPipeline pipeline) {
Objects.requireNonNull(pipeline);
ensureDrawing();
if(this.pipeline == pipeline) return this;
this.pipeline = pipeline; 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; return this;
} }
@Override @Override
public GraphicsCommandBuffer mesh(Mesh mesh) { 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; return this;
} }
@Override @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; return this;
} }
@Override @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; return this;
} }
@Override @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; return this;
} }
@Override @Override
public GraphicsCommandBuffer pushScissors(int x, int y, int width, int height) { public GraphicsCommandBuffer pushScissors(int x, int y, int width, int height) {
ensureDrawing();
scissors.push(x, y, width, height);
return this; return this;
} }
@Override @Override
public GraphicsCommandBuffer popScissors() { public GraphicsCommandBuffer popScissors() {
ensureDrawing();
scissors.pop();
return this; return this;
} }
@Override @Override
public GraphicsCommandBuffer end() { public GraphicsCommandBuffer end() {
if(!recording) throw new IllegalStateException("Already Stopped Recording"); if(!recording) throw new IllegalStateException("Already Stopped Recording");
pipeline = null; clear();
mesh = null;
recording = false;
return this; return this;
} }
private void clear() {
recording = false;
pipeline = null;
shader = null;
mesh = null;
scissors.clear();
}
@Override @Override
public int commandCount() { return recorded; } 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; package speiger.src.coreengine.graphics.opengl.core;
import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFW;
import org.lwjgl.opengl.GL11;
import speiger.src.coreengine.graphics.api.core.GraphicsSurface; import speiger.src.coreengine.graphics.api.core.GraphicsSurface;
import speiger.src.coreengine.rendering.input.window.Window; import speiger.src.coreengine.rendering.input.window.Window;
@@ -19,6 +20,9 @@ public class GLSurface implements GraphicsSurface {
@Override @Override
public void beginFrame() { public void beginFrame() {
GL11.glClearColor(0.2F, 0.55F, 0.66F, 1F);
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);
window.beginFrame();
GLFW.glfwPollEvents(); GLFW.glfwPollEvents();
} }
@@ -1,6 +1,7 @@
package speiger.src.coreengine.graphics.opengl.mesh; package speiger.src.coreengine.graphics.opengl.mesh;
import org.jspecify.annotations.Nullable; import org.jspecify.annotations.Nullable;
import org.lwjgl.opengl.GL30;
import org.lwjgl.opengl.GL45; import org.lwjgl.opengl.GL45;
import speiger.src.collections.ints.collections.IntIterator; 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 @Override
public boolean isRemoved() { return vao == 0; } public boolean isRemoved() { return vao == 0; }
@@ -47,6 +64,7 @@ public class GLMesh extends Mesh {
} }
private void bindElement() { private void bindElement() {
indeciesBuffer.bind();
GL45.glVertexArrayElementBuffer(vao, ((GLVertexBuffer)indeciesBuffer).id()); GL45.glVertexArrayElementBuffer(vao, ((GLVertexBuffer)indeciesBuffer).id());
} }
@@ -54,6 +72,7 @@ public class GLMesh extends Mesh {
LayoutInfo info = layouts.get(binding); LayoutInfo info = layouts.get(binding);
VertexLayout layout = info.layout(); VertexLayout layout = info.layout();
GLVertexBuffer buffer = (GLVertexBuffer)buffers.get(binding); GLVertexBuffer buffer = (GLVertexBuffer)buffers.get(binding);
buffer.bind();
GL45.glVertexArrayVertexBuffer(vao, binding, buffer.id(), 0, layout.bytes()); GL45.glVertexArrayVertexBuffer(vao, binding, buffer.id(), 0, layout.bytes());
if(info.instanceCount() > 0) GL45.glVertexArrayBindingDivisor(vao, binding, info.instanceCount()); if(info.instanceCount() > 0) GL45.glVertexArrayBindingDivisor(vao, binding, info.instanceCount());
int index = 0; int index = 0;
@@ -27,6 +27,10 @@ public class GLSampler extends Sampler {
GL45.glSamplerParameteri(id, GL11.GL_TEXTURE_MAG_FILTER, GLUtils.toGL(settings.magSample())); GL45.glSamplerParameteri(id, GL11.GL_TEXTURE_MAG_FILTER, GLUtils.toGL(settings.magSample()));
} }
public int id() {
return id;
}
@Override @Override
public boolean isRemoved() { public boolean isRemoved() {
return id == 0; return id == 0;
@@ -2,10 +2,14 @@ package speiger.src.coreengine.graphics.opengl.shader;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.function.Supplier;
import org.lwjgl.system.MemoryUtil;
import com.google.gson.JsonArray; import com.google.gson.JsonArray;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
@@ -62,12 +66,16 @@ public class ShaderCache {
catch(Exception e) { e.printStackTrace(); } 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); 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); Path path = toFile(shaderId);
if(Files.notExists(path)) return null; 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(); } catch(Exception e) { e.printStackTrace(); }
return null; return null;
} }
@@ -76,6 +84,7 @@ public class ShaderCache {
Objects.requireNonNull(shaderId); Objects.requireNonNull(shaderId);
Objects.requireNonNull(fileHash); Objects.requireNonNull(fileHash);
Objects.requireNonNull(shader); Objects.requireNonNull(shader);
if(cache == null) return;
hashCache.put(shaderId, fileHash); hashCache.put(shaderId, fileHash);
try { try {
Path file = toFile(shaderId); Path file = toFile(shaderId);
@@ -89,6 +98,7 @@ public class ShaderCache {
} }
public void delete(ID shaderId) { public void delete(ID shaderId) {
if(cache == null) return;
if(hashCache.remove(shaderId) == null) return; if(hashCache.remove(shaderId) == null) return;
try { Files.deleteIfExists(toFile(shaderId)); } try { Files.deleteIfExists(toFile(shaderId)); }
catch(Exception e) { e.printStackTrace(); } catch(Exception e) { e.printStackTrace(); }
@@ -2,8 +2,29 @@ package speiger.src.coreengine.graphics.opengl.shader;
import java.util.Map; 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 UniformObject(int slot) {}
public record SamplerObject(int unit) {} 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.impl.hash.Object2IntOpenHashMap;
import speiger.src.collections.objects.maps.interfaces.Object2IntMap; import speiger.src.collections.objects.maps.interfaces.Object2IntMap;
import speiger.src.coreengine.assets.AssetLocation; import speiger.src.coreengine.assets.api.ID;
public class FrameBufferCache { 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); return fboCache.supplyIntIfAbsent(id, GL45::glCreateFramebuffers);
} }
public void deleteFBO(AssetLocation id) { public void deleteFBO(ID id) {
int fbo = fboCache.rem(id); int fbo = fboCache.rem(id);
if(fbo == -1) return; if(fbo == -1) return;
GL45.glDeleteFramebuffers(fbo); 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.GL13;
import org.lwjgl.opengl.GL14; import org.lwjgl.opengl.GL14;
import org.lwjgl.opengl.GL15; import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL21; import org.lwjgl.opengl.GL21;
import org.lwjgl.opengl.GL30; import org.lwjgl.opengl.GL30;
import org.lwjgl.opengl.GL31; 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.BufferState;
import speiger.src.coreengine.graphics.api.buffer.states.BufferType; 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.BorderMode;
import speiger.src.coreengine.graphics.api.sampler.states.SampleMode; 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.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.StencilType;
import speiger.src.coreengine.graphics.api.texture.states.SwizzleMask; 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.texture.states.TextureType;
import speiger.src.coreengine.graphics.api.utils.AlphaFunction; import speiger.src.coreengine.graphics.api.utils.AlphaFunction;
public class GLUtils { public class GLUtils {
//Buffer Begin
public static int toGL(BufferType type) { public static int toGL(BufferType type) {
return switch(type) { return switch(type) {
case ARRAY_BUFFER -> GL15.GL_ARRAY_BUFFER; case ARRAY_BUFFER -> GL15.GL_ARRAY_BUFFER;
@@ -58,9 +63,7 @@ public class GLUtils {
case DYNAMIC_COPY -> GL15.GL_DYNAMIC_COPY; case DYNAMIC_COPY -> GL15.GL_DYNAMIC_COPY;
}; };
} }
//Texture Begin
public static int toGL(TextureType type) { public static int toGL(TextureType type) {
return switch(type) { return switch(type) {
case TEXTURE_1D -> GL11.GL_TEXTURE_1D; 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) { public static int toGL(SwizzleMask mask) {
return switch(mask) { return switch(mask) {
case RED -> GL11.GL_RED; 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; 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); Joystick.INSTANCE.init(manager, bus);
FileDrop.INSTANCE.init(bus); FileDrop.INSTANCE.init(bus);
manager.addDevices(Mouse.INSTANCE, Keyboard.INSTANCE, Joystick.INSTANCE, FileDrop.INSTANCE); 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 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(); Window secondWindow = manager.builder().title("Second Window Engine").width(800).height(600).antialis(0).build(null);
Thread.ofPlatform().start(() -> drawSecondScreen(secondWindow)); Thread.ofPlatform().start(() -> drawSecondScreen(secondWindow));
window.setupContext(); window.setupContext();
shaderTest.register(); 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; package speiger.src.coreengine.rendering.input.window;
import java.util.List; import java.util.List;
import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFW;
import org.lwjgl.opengl.GL; import org.lwjgl.opengl.GL;
import org.lwjgl.opengl.GLCapabilities; import org.lwjgl.opengl.GLCapabilities;
import org.lwjgl.system.CallbackI; import org.lwjgl.system.CallbackI;
import speiger.src.collections.objects.lists.ObjectArrayList; import speiger.src.collections.objects.lists.ObjectArrayList;
import speiger.src.coreengine.math.BitUtil; import speiger.src.coreengine.graphics.api.core.Graphics;
import speiger.src.coreengine.math.vector.ints.Vec4i; import speiger.src.coreengine.math.BitUtil;
import speiger.src.coreengine.rendering.input.window.IWindowListener.Reason; import speiger.src.coreengine.math.vector.ints.Vec4i;
import speiger.src.coreengine.rendering.input.window.WindowCallback.ReloadFunction; import speiger.src.coreengine.rendering.input.window.IWindowListener.Reason;
import speiger.src.coreengine.rendering.input.window.WindowManager.WindowBuilder; import speiger.src.coreengine.rendering.input.window.WindowCallback.ReloadFunction;
import speiger.src.coreengine.rendering.utils.GLStateTracker; import speiger.src.coreengine.rendering.input.window.WindowManager.WindowBuilder;
import speiger.src.coreengine.utils.collections.FlagHolder; import speiger.src.coreengine.utils.collections.FlagHolder;
public class Window { public class Window {
static final int VISIBLE = 1; static final int VISIBLE = 1;
static final int VSYNC = 2; static final int VSYNC = 2;
static final int FOCUS = 4; static final int FOCUS = 4;
static final int CPU_FPS_CAP = 8; static final int CPU_FPS_CAP = 8;
static final int FULL_SCREEN = 16; static final int FULL_SCREEN = 16;
static final int MAXIMIZED = 32; static final int MAXIMIZED = 32;
static final int BORDERLESS = 64; static final int BORDERLESS = 64;
static final int RESIZABLE = 128; static final int RESIZABLE = 128;
static final int FLOATING = 256; static final int FLOATING = 256;
static final int CLOSE = 512; static final int CLOSE = 512;
static final int WINDOW_CHANGE = 1024; static final int WINDOW_CHANGE = 1024;
WindowManager manager; WindowManager manager;
FlagHolder flags = new FlagHolder(); FlagHolder flags = new FlagHolder();
long id; long id;
VideoMode fullScreenMode; VideoMode fullScreenMode;
String title = ""; String title = "";
int x; int x;
int y; int y;
int width; int width;
int height; int height;
int frameWidth; int frameWidth;
int frameHeight; int frameHeight;
Vec4i[] backup = new Vec4i[] {Vec4i.mutable(), Vec4i.mutable()}; Vec4i[] backup = new Vec4i[] {Vec4i.mutable(), Vec4i.mutable()};
int backupIndex = 0; int backupIndex = 0;
final int antialiasing; final int antialiasing;
List<WindowCallback> callbacks = new ObjectArrayList<>(); List<WindowCallback> callbacks = new ObjectArrayList<>();
List<IWindowListener> listeners = new ObjectArrayList<>(); List<IWindowListener> listeners = new ObjectArrayList<>();
GLCapabilities capabilities; GLCapabilities capabilities;
protected Window(WindowBuilder builder) { protected Window(WindowBuilder builder, Graphics graphics) {
manager = builder.manager; manager = builder.manager;
title = builder.title; title = builder.title;
frameWidth = width = builder.width; frameWidth = width = builder.width;
frameHeight = height = builder.height; frameHeight = height = builder.height;
antialiasing = builder.antiAlis; antialiasing = builder.antiAlis;
fullScreenMode = builder.fullScreenTarget; fullScreenMode = builder.fullScreenTarget;
flags.setFlag(BORDERLESS, builder.borderless); flags.setFlag(BORDERLESS, builder.borderless);
flags.setFlag(FULL_SCREEN, builder.fullScreen); flags.setFlag(FULL_SCREEN, builder.fullScreen);
flags.setFlag(FLOATING, builder.floating); flags.setFlag(FLOATING, builder.floating);
flags.setFlag(RESIZABLE, builder.resizable); flags.setFlag(RESIZABLE, builder.resizable);
flags.setFlag(VSYNC, builder.vsync); flags.setFlag(VSYNC, builder.vsync);
flags.setFlag(CPU_FPS_CAP, builder.fpsCap); flags.setFlag(CPU_FPS_CAP, builder.fpsCap);
createDefaultWindowHints(); if(graphics != null) graphics.setupWindowArguments();
for(int i = 0,m=builder.windowHints.size();i<m;i++) { else createDefaultWindowHints();
long value = builder.windowHints.getLong(i); for(int i = 0,m=builder.windowHints.size();i<m;i++) {
GLFW.glfwWindowHint(BitUtil.intKey(value), BitUtil.intValue(value)); 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_SAMPLES, antialiasing);
GLFW.glfwWindowHint(GLFW.GLFW_DECORATED, flags.isFlagNotSet(FULL_SCREEN) && flags.isFlagSet(BORDERLESS) ? GLFW.GLFW_FALSE : GLFW.GLFW_TRUE); GLFW.glfwWindowHint(GLFW.GLFW_RESIZABLE, flags.isFlagSet(RESIZABLE) ? GLFW.GLFW_TRUE : GLFW.GLFW_FALSE);
GLFW.glfwWindowHint(GLFW.GLFW_FLOATING, flags.isFlagNotSet(FULL_SCREEN) && flags.isFlagSet(FLOATING) ? 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_MAXIMIZED, flags.isFlagNotSet(FULL_SCREEN) && flags.isFlagSet(MAXIMIZED) ? GLFW.GLFW_TRUE : GLFW.GLFW_FALSE); GLFW.glfwWindowHint(GLFW.GLFW_FLOATING, flags.isFlagNotSet(FULL_SCREEN) && flags.isFlagSet(FLOATING) ? GLFW.GLFW_TRUE : GLFW.GLFW_FALSE);
Monitor monitor = manager.getMonitor(builder.monitor); GLFW.glfwWindowHint(GLFW.GLFW_MAXIMIZED, flags.isFlagNotSet(FULL_SCREEN) && flags.isFlagSet(MAXIMIZED) ? GLFW.GLFW_TRUE : GLFW.GLFW_FALSE);
if(monitor == null) throw new IllegalStateException("Monitor is missing: "+monitor); Monitor monitor = manager.getMonitor(builder.monitor);
VideoMode mode = monitor.defaultMode(); if(monitor == null) throw new IllegalStateException("Monitor is missing: "+monitor);
boolean fullscreen = builder.fullScreen; VideoMode mode = monitor.defaultMode();
id = GLFW.glfwCreateWindow(fullscreen ? mode.width() : width, fullscreen ? mode.height() : height, title, builder.fullScreen ? monitor.id() : 0, manager.getPrimaryWindow()); boolean fullscreen = builder.fullScreen;
if(id == 0) throw new IllegalStateException("Window Couldn't be Created"); id = GLFW.glfwCreateWindow(fullscreen ? mode.width() : width, fullscreen ? mode.height() : height, title, builder.fullScreen ? monitor.id() : 0, manager.getPrimaryWindow());
manager.addWindow(this); if(id == 0) throw new IllegalStateException("Window Couldn't be Created");
createWindowListeners(); manager.addWindow(this);
GLFW.glfwMakeContextCurrent(id); createWindowListeners();
capabilities = GL.createCapabilities(true); GLFW.glfwMakeContextCurrent(id);
x = monitor.xOffset() + (builder.center ? (mode.width() / 2) - (width / 2) : 0); if(graphics == null) capabilities = GL.createCapabilities(true);
y = monitor.yOffset() + (builder.center ? (mode.height() / 2) - (height / 2) : 0); x = monitor.xOffset() + (builder.center ? (mode.width() / 2) - (width / 2) : 0);
if(!fullscreen) GLFW.glfwSetWindowPos(id, x, y); y = monitor.yOffset() + (builder.center ? (mode.height() / 2) - (height / 2) : 0);
GLFW.glfwSwapInterval(flags.isFlagSet(VSYNC) ? 1 : 0); if(!fullscreen) GLFW.glfwSetWindowPos(id, x, y);
fetchWindowBounds(); GLFW.glfwSwapInterval(flags.isFlagSet(VSYNC) ? 1 : 0);
updateViewport(); fetchWindowBounds();
} updateViewport();
}
protected void createDefaultWindowHints() {
GLFW.glfwDefaultWindowHints(); protected void createDefaultWindowHints() {
GLFW.glfwWindowHint(GLFW.GLFW_VISIBLE, GLFW.GLFW_FALSE); GLFW.glfwDefaultWindowHints();
GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_VERSION_MAJOR, 4); GLFW.glfwWindowHint(GLFW.GLFW_VISIBLE, GLFW.GLFW_FALSE);
GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_VERSION_MINOR, 0); GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_VERSION_MAJOR, 4);
GLFW.glfwWindowHint(GLFW.GLFW_OPENGL_PROFILE, GLFW.GLFW_OPENGL_CORE_PROFILE); GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_VERSION_MINOR, 0);
GLFW.glfwWindowHint(GLFW.GLFW_OPENGL_FORWARD_COMPAT, GLFW.GLFW_TRUE); 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); protected void createWindowListeners() {
addCallback(this::focused, GLFW::glfwSetWindowFocusCallback); addCallback(this::framebuffer, GLFW::glfwSetFramebufferSizeCallback);
addCallback(this::position, GLFW::glfwSetWindowPosCallback); addCallback(this::focused, GLFW::glfwSetWindowFocusCallback);
addCallback(this::bounds, GLFW::glfwSetWindowSizeCallback); addCallback(this::position, GLFW::glfwSetWindowPosCallback);
} addCallback(this::bounds, GLFW::glfwSetWindowSizeCallback);
}
private void framebuffer(long window, int width, int height) {
frameWidth = width; private void framebuffer(long window, int width, int height) {
frameHeight = height; frameWidth = width;
flags.setFlag(WINDOW_CHANGE); frameHeight = height;
} flags.setFlag(WINDOW_CHANGE);
}
private void focused(long window, boolean focused) {
manager.updateFocus(this, focused); private void focused(long window, boolean focused) {
} manager.updateFocus(this, focused);
}
private void position(long window, int x, int y) {
this.x = x; private void position(long window, int x, int y) {
this.y = y; this.x = x;
flags.setFlag(WINDOW_CHANGE); this.y = y;
} flags.setFlag(WINDOW_CHANGE);
}
private void bounds(long window, int width, int height) {
this.width = width; private void bounds(long window, int width, int height) {
this.height = height; this.width = width;
flags.setFlag(WINDOW_CHANGE); this.height = height;
} flags.setFlag(WINDOW_CHANGE);
}
protected void fetchWindowBounds() {
int[] width = new int[1]; protected void fetchWindowBounds() {
int[] height = new int[1]; int[] width = new int[1];
GLFW.glfwGetWindowSize(id, width, height); int[] height = new int[1];
this.frameWidth = width[0]; GLFW.glfwGetWindowSize(id, width, height);
this.frameHeight = height[0]; this.frameWidth = width[0];
} this.frameHeight = height[0];
}
public void updateViewport() {
flags.clearFlag(WINDOW_CHANGE); public void updateViewport() {
GLStateTracker.instance().viewPort.setDefault(0, 0, frameWidth, frameHeight); flags.clearFlag(WINDOW_CHANGE);
for(int i = 0,m=listeners.size();i<m;i++) { // GLStateTracker.instance().viewPort.setDefault(0, 0, frameWidth, frameHeight);
listeners.get(i).onChanged(this, Reason.CHANGE); for(int i = 0,m=listeners.size();i<m;i++) {
} listeners.get(i).onChanged(this, Reason.CHANGE);
} }
}
public void setupContext() {
GL.setCapabilities(capabilities); public void setupContext() {
GLFW.glfwMakeContextCurrent(id); if(capabilities != null) GL.setCapabilities(capabilities);
GLFW.glfwMakeContextCurrent(id);
}
}
public void beginFrame() {
setupContext(); public void beginFrame() {
if(flags.isFlagSet(WINDOW_CHANGE)) updateViewport(); 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 handleInput() { manager.processDevices(id); }
public void finishFrame() { GLFW.glfwSwapBuffers(id); } public boolean shouldClose() { return flags.isFlagSet(CLOSE) || GLFW.glfwWindowShouldClose(id); }
public void finishFrame() { GLFW.glfwSwapBuffers(id); }
public void destroy() {
manager.removeWindow(id); public void destroy() {
for(int i = 0,m=listeners.size();i<m;i++) { manager.removeWindow(id);
listeners.get(i).onChanged(this, Reason.CLOSING); for(int i = 0,m=listeners.size();i<m;i++) {
} listeners.get(i).onChanged(this, Reason.CLOSING);
GLFW.glfwDestroyWindow(id); }
callbacks.forEach(WindowCallback::destroy); GLFW.glfwDestroyWindow(id);
callbacks.clear(); callbacks.forEach(WindowCallback::destroy);
} callbacks.clear();
}
@SuppressWarnings("unchecked")
public <T extends CallbackI> WindowCallback addCallback(T listener, ReloadFunction<T> function) { @SuppressWarnings("unchecked")
WindowCallback callback = new WindowCallback(listener, (ReloadFunction<CallbackI>)function); public <T extends CallbackI> WindowCallback addCallback(T listener, ReloadFunction<T> function) {
callbacks.add(callback); WindowCallback callback = new WindowCallback(listener, (ReloadFunction<CallbackI>)function);
callback.load(id); callbacks.add(callback);
return callback; callback.load(id);
} return callback;
}
public void removeCallback(WindowCallback callback) {
if(callbacks.remove(callback)) { public void removeCallback(WindowCallback callback) {
callback.destroy(); if(callbacks.remove(callback)) {
} callback.destroy();
} }
}
public void addListener(IWindowListener listener) {
listeners.add(listener); public void addListener(IWindowListener listener) {
} listeners.add(listener);
}
public void removeListener(IWindowListener listener) {
listeners.remove(listener); public void removeListener(IWindowListener listener) {
} listeners.remove(listener);
}
public void title(String name) {
if(name == null || title.equals(name)) return; public void title(String name) {
title = name; if(name == null || title.equals(name)) return;
GLFW.glfwSetWindowTitle(id, title); title = name;
} GLFW.glfwSetWindowTitle(id, title);
}
public void vsync(boolean vsync) {
if(!flags.setFlag(VSYNC, vsync)) return; public void vsync(boolean vsync) {
GLFW.glfwSwapInterval(vsync ? 1 : 0); if(!flags.setFlag(VSYNC, vsync)) return;
} GLFW.glfwSwapInterval(vsync ? 1 : 0);
}
public void fpsCap(boolean fpsCap) {
flags.setFlag(CPU_FPS_CAP, fpsCap); public void fpsCap(boolean fpsCap) {
} flags.setFlag(CPU_FPS_CAP, fpsCap);
}
public void visible(boolean visible) {
if(!flags.setFlag(VISIBLE, visible)) return; public void visible(boolean visible) {
if(visible) GLFW.glfwShowWindow(id); if(!flags.setFlag(VISIBLE, visible)) return;
else GLFW.glfwHideWindow(id); if(visible) GLFW.glfwShowWindow(id);
} else GLFW.glfwHideWindow(id);
}
public void floating(boolean floating) {
if(flags.isFlagNotSet(FULL_SCREEN) && flags.setFlag(FLOATING, floating)) { public void floating(boolean floating) {
GLFW.glfwSetWindowAttrib(id, GLFW.GLFW_FLOATING, floating ? GLFW.GLFW_TRUE : GLFW.GLFW_FALSE); 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)) { public void maximized(boolean maximized) {
if(maximized) { if(flags.isFlagNotSet(FULL_SCREEN) && flags.setFlag(MAXIMIZED, maximized)) {
backupSize(); if(maximized) {
GLFW.glfwMaximizeWindow(id); backupSize();
} GLFW.glfwMaximizeWindow(id);
else { }
GLFW.glfwRestoreWindow(id); else {
restoreSize(); GLFW.glfwRestoreWindow(id);
} restoreSize();
fetchWindowBounds(); }
} fetchWindowBounds();
} }
}
public void resizeable(boolean resizeable) {
if(flags.isFlagNotSet(FULL_SCREEN) && flags.setFlag(RESIZABLE, resizeable)) { public void resizeable(boolean resizeable) {
GLFW.glfwSetWindowAttrib(id, GLFW.GLFW_RESIZABLE, resizeable ? GLFW.GLFW_TRUE : GLFW.GLFW_FALSE); 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)) { public void borderless(boolean borderless) {
GLFW.glfwSetWindowAttrib(id, GLFW.GLFW_DECORATED, borderless ? GLFW.GLFW_FALSE : GLFW.GLFW_TRUE); if(flags.isFlagNotSet(FULL_SCREEN) && flags.setFlag(BORDERLESS, borderless)) {
if(flags.isFlagSet(MAXIMIZED)) { GLFW.glfwSetWindowAttrib(id, GLFW.GLFW_DECORATED, borderless ? GLFW.GLFW_FALSE : GLFW.GLFW_TRUE);
GLFW.glfwRestoreWindow(id); if(flags.isFlagSet(MAXIMIZED)) {
GLFW.glfwMaximizeWindow(id); GLFW.glfwRestoreWindow(id);
fetchWindowBounds(); GLFW.glfwMaximizeWindow(id);
} fetchWindowBounds();
} }
} }
}
public void fullscreen(boolean fullscreen) {
if(flags.setFlag(FULL_SCREEN, fullscreen)) { public void fullscreen(boolean fullscreen) {
if(fullscreen) { if(flags.setFlag(FULL_SCREEN, fullscreen)) {
Monitor monitor = manager.getMonitorForWindow(this); if(fullscreen) {
if(monitor == null) { Monitor monitor = manager.getMonitorForWindow(this);
flags.clearFlag(FULL_SCREEN); if(monitor == null) {
return; flags.clearFlag(FULL_SCREEN);
} return;
backupSize(); }
VideoMode mode = fullScreenMode == null || !monitor.has(fullScreenMode) ? monitor.defaultMode() : fullScreenMode; backupSize();
x = 0; VideoMode mode = fullScreenMode == null || !monitor.has(fullScreenMode) ? monitor.defaultMode() : fullScreenMode;
y = 0; x = 0;
width = mode.width(); y = 0;
height = mode.height(); width = mode.width();
GLFW.glfwSetWindowMonitor(id, monitor.id(), 0, 0, mode.width(), mode.height(), mode.refreshrate()); height = mode.height();
} GLFW.glfwSetWindowMonitor(id, monitor.id(), 0, 0, mode.width(), mode.height(), mode.refreshrate());
else }
{ else
restoreSize(); {
GLFW.glfwSetWindowMonitor(id, 0, x, y, width, height, -1); restoreSize();
if(flags.isFlagSet(BORDERLESS)) { GLFW.glfwSetWindowMonitor(id, 0, x, y, width, height, -1);
GLFW.glfwSetWindowAttrib(id, GLFW.GLFW_DECORATED, GLFW.GLFW_FALSE); if(flags.isFlagSet(BORDERLESS)) {
} GLFW.glfwSetWindowAttrib(id, GLFW.GLFW_DECORATED, GLFW.GLFW_FALSE);
if(flags.isFlagSet(MAXIMIZED)) { }
GLFW.glfwMaximizeWindow(id); if(flags.isFlagSet(MAXIMIZED)) {
} GLFW.glfwMaximizeWindow(id);
} }
fetchWindowBounds(); }
updateViewport(); fetchWindowBounds();
GLFW.glfwSwapInterval(flags.isFlagSet(VSYNC) ? 1 : 0); updateViewport();
} GLFW.glfwSwapInterval(flags.isFlagSet(VSYNC) ? 1 : 0);
} }
}
protected void backupSize() {
if(backupIndex >= backup.length) return; protected void backupSize() {
backup[backupIndex++].set(x, y, width, height); if(backupIndex >= backup.length) return;
} backup[backupIndex++].set(x, y, width, height);
}
protected void restoreSize() {
if(backupIndex == 0) return; protected void restoreSize() {
Vec4i prev = backup[--backupIndex]; if(backupIndex == 0) return;
x = prev.x(); Vec4i prev = backup[--backupIndex];
y = prev.y(); x = prev.x();
width = prev.z(); y = prev.y();
height = prev.w(); 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; public boolean position(int x, int y) {
this.x = x; if(flags.isAnyFlagSet(MAXIMIZED | FULL_SCREEN) || (this.x == x && this.y == y)) return false;
this.y = y; this.x = x;
GLFW.glfwSetWindowPos(id, x, y); this.y = y;
flags.setFlag(WINDOW_CHANGE); GLFW.glfwSetWindowPos(id, x, y);
return true; 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; public boolean size(int width, int height) {
this.width = width; if(flags.isAnyFlagSet(MAXIMIZED | FULL_SCREEN) || (this.width == width && this.height == height)) return false;
this.height = height; this.width = width;
GLFW.glfwSetWindowSize(id, width, height); this.height = height;
flags.setFlag(WINDOW_CHANGE); GLFW.glfwSetWindowSize(id, width, height);
return true; 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 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 long id() { return id; }
public VideoMode desiredFullScreen() { return fullScreenMode; }
public int x() { return x; }
public int y() { return y; } public int x() { return x; }
public int width() { return frameWidth; } public int y() { return y; }
public int height() { return frameHeight; } public int width() { return frameWidth; }
public int screenWidth() { return width; } public int height() { return frameHeight; }
public int screenHeight() { return height; } public int screenWidth() { return width; }
public boolean changed() { return flags.isFlagSet(WINDOW_CHANGE); } 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 String title() { return title; }
public boolean shouldFPSCap() { return flags.isFlagSet(CPU_FPS_CAP); } public boolean isVsync() { return flags.isFlagSet(VSYNC); }
public boolean isVisible() { return flags.isFlagSet(VISIBLE); } public boolean shouldFPSCap() { return flags.isFlagSet(CPU_FPS_CAP); }
public boolean isFloating() { return flags.isFlagSet(FLOATING); } public boolean isVisible() { return flags.isFlagSet(VISIBLE); }
public boolean isMaximized() { return flags.isFlagSet(MAXIMIZED); } public boolean isFloating() { return flags.isFlagSet(FLOATING); }
public boolean isResizeable() { return flags.isFlagSet(RESIZABLE); } public boolean isMaximized() { return flags.isFlagSet(MAXIMIZED); }
public boolean isBorderless() { return flags.isFlagSet(BORDERLESS); } public boolean isResizeable() { return flags.isFlagSet(RESIZABLE); }
public boolean isFullscreen() { return flags.isFlagSet(FULL_SCREEN); } public boolean isBorderless() { return flags.isFlagSet(BORDERLESS); }
public int antialiasing() { return antialiasing; } public boolean isFullscreen() { return flags.isFlagSet(FULL_SCREEN); }
} public int antialiasing() { return antialiasing; }
}
@@ -1,266 +1,267 @@
package speiger.src.coreengine.rendering.input.window; package speiger.src.coreengine.rendering.input.window;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFW;
import org.lwjgl.system.Callback; import org.lwjgl.system.Callback;
import org.lwjgl.system.CallbackI; import org.lwjgl.system.CallbackI;
import speiger.src.collections.longs.lists.LongArrayList; import speiger.src.collections.longs.lists.LongArrayList;
import speiger.src.collections.longs.lists.LongList; import speiger.src.collections.longs.lists.LongList;
import speiger.src.collections.longs.maps.impl.concurrent.Long2ObjectConcurrentOpenHashMap; import speiger.src.collections.longs.maps.impl.concurrent.Long2ObjectConcurrentOpenHashMap;
import speiger.src.collections.longs.maps.interfaces.Long2ObjectMap; import speiger.src.collections.longs.maps.interfaces.Long2ObjectMap;
import speiger.src.collections.objects.lists.ObjectArrayList; import speiger.src.collections.objects.lists.ObjectArrayList;
import speiger.src.coreengine.math.BitUtil; import speiger.src.coreengine.graphics.api.core.Graphics;
import speiger.src.coreengine.rendering.input.devices.InputDevice; import speiger.src.coreengine.math.BitUtil;
import speiger.src.coreengine.rendering.input.window.WindowCallback.SimpleReloadFunction; import speiger.src.coreengine.rendering.input.devices.InputDevice;
import speiger.src.coreengine.rendering.input.window.WindowCallback.SimpleReloadFunction;
public class WindowManager {
Long2ObjectMap<Monitor> monitors; public class WindowManager {
Long2ObjectMap<Window> windows = new Long2ObjectConcurrentOpenHashMap<>(); Long2ObjectMap<Monitor> monitors;
Window activeWindow; Long2ObjectMap<Window> windows = new Long2ObjectConcurrentOpenHashMap<>();
Window primaryWindow; Window activeWindow;
List<WindowCallback> callbacks = new ObjectArrayList<>(); Window primaryWindow;
Callback monitorTracker; List<WindowCallback> callbacks = new ObjectArrayList<>();
List<InputDevice> devices = new ObjectArrayList<>(); Callback monitorTracker;
List<InputDevice> devices = new ObjectArrayList<>();
public void initialize() {
monitors = Monitor.createMonitors(); public void initialize() {
addCallback(this::onMonitorChanged, GLFW::glfwSetMonitorCallback); monitors = Monitor.createMonitors();
} addCallback(this::onMonitorChanged, GLFW::glfwSetMonitorCallback);
}
private void onMonitorChanged(long monitor, int event) {
switch(event) { private void onMonitorChanged(long monitor, int event) {
case GLFW.GLFW_CONNECTED -> monitors.put(monitor, new Monitor(monitor)); switch(event) {
case GLFW.GLFW_DISCONNECTED -> monitors.remove(monitor); 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 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 addDevices(InputDevice...devices) {
} for(InputDevice device : devices) addDevice(device);
public void addDevice(InputDevice device) { }
devices.add(device); public void addDevice(InputDevice device) {
windows.values().forEach(device::register); devices.add(device);
} windows.values().forEach(device::register);
public void removeDevice(InputDevice device) { devices.remove(device); } }
public void processDevices(long windowId) { public void removeDevice(InputDevice device) { devices.remove(device); }
for(int i = 0,m=devices.size();i<m;i++) { public void processDevices(long windowId) {
devices.get(i).processInput(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) { @SuppressWarnings("unchecked")
WindowCallback callback = new WindowCallback(listener, (SimpleReloadFunction<CallbackI>)function); public <T extends CallbackI> WindowCallback addCallback(T listener, SimpleReloadFunction<T> function) {
callbacks.add(callback); WindowCallback callback = new WindowCallback(listener, (SimpleReloadFunction<CallbackI>)function);
callback.load(0L); callbacks.add(callback);
return callback; callback.load(0L);
} return callback;
}
public void removeCallback(WindowCallback callback) {
if(callbacks.remove(callback)) { public void removeCallback(WindowCallback callback) {
callback.destroy(); if(callbacks.remove(callback)) {
} callback.destroy();
} }
}
void addWindow(Window window) {
windows.put(window.id(), window); void addWindow(Window window) {
for(int i = 0,m=devices.size();i<m;i++) { windows.put(window.id(), window);
devices.get(i).register(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); void updateWindow(long oldId) {
if(prev == null) return; Window prev = windows.remove(oldId);
windows.put(prev.id(), prev); if(prev == null) return;
} windows.put(prev.id(), prev);
}
void removeWindow(long id) {
Window window = windows.remove(id); void removeWindow(long id) {
if(window == null) return; Window window = windows.remove(id);
if(window == activeWindow) activeWindow = null; if(window == null) return;
if(window == primaryWindow) { if(window == activeWindow) activeWindow = null;
Iterator<Window> iter = windows.values().iterator(); if(window == primaryWindow) {
primaryWindow = iter.hasNext() ? iter.next() : null; Iterator<Window> iter = windows.values().iterator();
} primaryWindow = iter.hasNext() ? iter.next() : null;
} }
}
void updateFocus(Window window, boolean focus) {
if(focus) this.activeWindow = window; void updateFocus(Window window, boolean focus) {
else if(activeWindow == window) activeWindow = null; 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++) { void updateInputs(long windowId) {
InputDevice device = devices.get(i); for(int i = 0,m=devices.size();i<m;i++) {
device.reset(windowId); InputDevice device = devices.get(i);
device.processInput(windowId); device.reset(windowId);
} device.processInput(windowId);
} }
}
public void destroy() {
callbacks.forEach(WindowCallback::destroy); public void destroy() {
callbacks.clear(); callbacks.forEach(WindowCallback::destroy);
} callbacks.clear();
}
public long getActiveWindow() {
return activeWindow == null ? 0L : activeWindow.id(); public long getActiveWindow() {
} return activeWindow == null ? 0L : activeWindow.id();
}
public long getPrimaryWindow() {
return primaryWindow == null ? 0 : primaryWindow.id(); public long getPrimaryWindow() {
} return primaryWindow == null ? 0 : primaryWindow.id();
}
public Window getWindow(long window) {
return windows.get(window); public Window getWindow(long window) {
} return windows.get(window);
}
public Monitor getMonitor(long id) {
return monitors.get(id); public Monitor getMonitor(long id) {
} return monitors.get(id);
}
public Monitor getPriamryMonitor() {
return getMonitor(GLFW.glfwGetPrimaryMonitor()); public Monitor getPriamryMonitor() {
} return getMonitor(GLFW.glfwGetPrimaryMonitor());
}
public Monitor getMonitorForWindow(Window window) {
long current = GLFW.glfwGetWindowMonitor(window.id()); public Monitor getMonitorForWindow(Window window) {
if(current != 0L) return getMonitor(current); long current = GLFW.glfwGetWindowMonitor(window.id());
int minX = window.x(); if(current != 0L) return getMonitor(current);
int minY = window.y(); int minX = window.x();
int maxX = minX + window.screenWidth(); int minY = window.y();
int maxY = minY + window.screenHeight(); int maxX = minX + window.screenWidth();
int largest = 0; int maxY = minY + window.screenHeight();
Monitor mon = null; int largest = 0;
for(Monitor monitor : monitors.values()) { Monitor mon = null;
int next = monitor.getOverlap(minX, minY, maxX, maxY); for(Monitor monitor : monitors.values()) {
if(next > largest) { int next = monitor.getOverlap(minX, minY, maxX, maxY);
largest = next; if(next > largest) {
mon = monitor; largest = next;
} mon = monitor;
} }
return mon; }
} return mon;
}
public static class WindowBuilder {
WindowManager manager; public static class WindowBuilder {
long monitor; WindowManager manager;
LongList windowHints = new LongArrayList(); long monitor;
VideoMode fullScreenTarget; LongList windowHints = new LongArrayList();
String title = ""; VideoMode fullScreenTarget;
int width = 640; String title = "";
int height = 480; int width = 640;
int antiAlis = 4; int height = 480;
boolean vsync = true; int antiAlis = 4;
boolean fpsCap; boolean vsync = true;
boolean fullScreen; boolean fpsCap;
boolean maximized; boolean fullScreen;
boolean resizable = true; boolean maximized;
boolean floating; boolean resizable = true;
boolean borderless; boolean floating;
boolean center = true; boolean borderless;
boolean center = true;
private WindowBuilder(WindowManager manager) {
this.manager = manager; private WindowBuilder(WindowManager manager) {
monitor = GLFW.glfwGetPrimaryMonitor(); this.manager = manager;
} monitor = GLFW.glfwGetPrimaryMonitor();
}
public WindowBuilder title(String title) {
this.title = Objects.requireNonNull(title); public WindowBuilder title(String title) {
return this; this.title = Objects.requireNonNull(title);
} return this;
}
public WindowBuilder width(int width) {
this.width = Math.max(1, width); public WindowBuilder width(int width) {
return this; this.width = Math.max(1, width);
} return this;
}
public WindowBuilder height(int height) {
this.height = Math.max(1, height); public WindowBuilder height(int height) {
return this; this.height = Math.max(1, height);
} return this;
}
public WindowBuilder antialis(int antiAlis) {
this.antiAlis = Math.max(1, antiAlis); public WindowBuilder antialis(int antiAlis) {
return this; this.antiAlis = Math.max(1, antiAlis);
} return this;
}
public WindowBuilder fullscreen(boolean fullScreen) {
this.fullScreen = fullScreen; public WindowBuilder fullscreen(boolean fullScreen) {
borderless &= !fullScreen; this.fullScreen = fullScreen;
floating &= !fullScreen; borderless &= !fullScreen;
maximized &= !fullScreen; floating &= !fullScreen;
center &= !fullScreen; maximized &= !fullScreen;
return this; center &= !fullScreen;
} return this;
}
public WindowBuilder maximized(boolean maximized) {
this.maximized = maximized; public WindowBuilder maximized(boolean maximized) {
this.fullScreen &= !maximized; this.maximized = maximized;
center &= !maximized; this.fullScreen &= !maximized;
return this; center &= !maximized;
} return this;
}
public WindowBuilder borderless(boolean borderless) {
this.borderless = borderless; public WindowBuilder borderless(boolean borderless) {
fullScreen &= !borderless; this.borderless = borderless;
return this; fullScreen &= !borderless;
} return this;
}
public WindowBuilder floating(boolean floating) {
this.floating = floating; public WindowBuilder floating(boolean floating) {
fullScreen &= !floating; this.floating = floating;
return this; fullScreen &= !floating;
} return this;
}
public WindowBuilder resizeable(boolean resizable) {
this.resizable = resizable; public WindowBuilder resizeable(boolean resizable) {
return this; this.resizable = resizable;
} return this;
}
public WindowBuilder centered(boolean center) {
this.center = center; public WindowBuilder centered(boolean center) {
this.maximized &= !center; this.center = center;
this.fullScreen &= !center; this.maximized &= !center;
return this; this.fullScreen &= !center;
} return this;
}
public WindowBuilder monitor(Monitor monitor) {
if(monitor == null || monitor.defaultMode() == null) return this; public WindowBuilder monitor(Monitor monitor) {
this.monitor = monitor.id(); if(monitor == null || monitor.defaultMode() == null) return this;
return this; this.monitor = monitor.id();
} return this;
}
public WindowBuilder fullScreenMode(VideoMode mode) {
this.fullScreenTarget = mode; public WindowBuilder fullScreenMode(VideoMode mode) {
return this; this.fullScreenTarget = mode;
} return this;
}
public WindowBuilder vsync(boolean value) {
vsync = value; public WindowBuilder vsync(boolean value) {
return this; vsync = value;
} return this;
}
public WindowBuilder fpsCap(boolean cap) {
fpsCap = cap; public WindowBuilder fpsCap(boolean cap) {
return this; fpsCap = cap;
} return this;
}
public WindowBuilder addCustomHint(int key, int value) {
windowHints.add(BitUtil.toLong(key, value)); public WindowBuilder addCustomHint(int key, int value) {
return this; windowHints.add(BitUtil.toLong(key, value));
} return this;
}
public Window build() {
return manager.create(this); public Window build(Graphics graphics) {
} return manager.create(this, graphics);
}
}
} }
}
@@ -1,192 +1,192 @@
package speiger.src.coreengine.rendering.textures.custom; package speiger.src.coreengine.rendering.textures.custom;
import org.lwjgl.system.MemoryUtil; import org.lwjgl.system.MemoryUtil;
import speiger.src.collections.ints.collections.IntIterator; import speiger.src.collections.ints.collections.IntIterator;
import speiger.src.collections.ints.sets.IntLinkedOpenHashSet; import speiger.src.collections.ints.sets.IntLinkedOpenHashSet;
import speiger.src.collections.ints.sets.IntSet; import speiger.src.collections.ints.sets.IntSet;
import speiger.src.coreengine.assets.base.IAssetProvider; import speiger.src.coreengine.assets.base.IAssetProvider;
import speiger.src.coreengine.math.BitUtil; import speiger.src.coreengine.math.BitUtil;
import speiger.src.coreengine.math.misc.ColorSpaces; import speiger.src.coreengine.math.misc.ColorSpaces;
import speiger.src.coreengine.rendering.textures.base.BaseTexture; import speiger.src.coreengine.rendering.textures.base.BaseTexture;
import speiger.src.coreengine.rendering.textures.base.TextureMetadata; import speiger.src.coreengine.rendering.textures.base.TextureMetadata;
import speiger.src.coreengine.rendering.utils.GLFunctions; import speiger.src.coreengine.rendering.utils.GLFunctions;
import speiger.src.coreengine.rendering.utils.GLStateTracker; import speiger.src.coreengine.rendering.utils.GLStateTracker;
import speiger.src.coreengine.rendering.utils.values.GLDataType; 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.GLTextureFormat;
import speiger.src.coreengine.rendering.utils.values.textures.GLTextureParameter; import speiger.src.coreengine.rendering.utils.values.textures.GLTextureParameter;
import speiger.src.coreengine.rendering.utils.values.textures.GLTextureValue; import speiger.src.coreengine.rendering.utils.values.textures.GLTextureValue;
public class DynamicTexture extends BaseTexture implements IDynamicTexture { 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) 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.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(); .arguement(GLTextureParameter.WRAP_S, GLTextureValue.CLAMP_TO_EDGE).arguement(GLTextureParameter.WRAP_T, GLTextureValue.CLAMP_TO_EDGE).build();
IntSet dirtySections = new IntLinkedOpenHashSet(); IntSet dirtySections = new IntLinkedOpenHashSet();
TextureMetadata metadata; TextureMetadata metadata;
int width; int width;
int height; int height;
long data; long data;
public DynamicTexture(int width, int height, TextureMetadata metadata) { public DynamicTexture(int width, int height, TextureMetadata metadata) {
this.metadata = metadata; this.metadata = metadata;
this.width = width; this.width = width;
this.height = height; this.height = height;
if(width % 16 != 0 || height % 16 != 0) throw new IllegalArgumentException("Texture must be a power of 16"); if(width % 16 != 0 || height % 16 != 0) throw new IllegalArgumentException("Texture must be a power of 16");
data = MemoryUtil.nmemAllocChecked(4L * width * height); data = MemoryUtil.nmemAllocChecked(4L * width * height);
track(); track();
} }
@Override @Override
public void load(IAssetProvider provider) { public void load(IAssetProvider provider) {
createTexture(); createTexture();
metadata.applyArguments(id()); metadata.applyArguments(id());
GLFunctions.prepare2DImage(id(), 1, metadata, width, height); GLFunctions.prepare2DImage(id(), 1, metadata, width, height);
dirtySections.clear(); dirtySections.clear();
} }
@Override @Override
public void delete(boolean untrack) { public void delete(boolean untrack) {
super.delete(untrack); super.delete(untrack);
if(untrack && data != 0L) { if(untrack && data != 0L) {
MemoryUtil.nmemFree(data); MemoryUtil.nmemFree(data);
data = 0L; data = 0L;
} }
} }
@Override @Override
public int width() { return width; } public int width() { return width; }
@Override @Override
public int height() { return height; } public int height() { return height; }
@Override @Override
public ColorSpaces colorspace() { return ColorSpaces.ABGR; } public ColorSpaces colorspace() { return ColorSpaces.ABGR; }
@Override @Override
public boolean isDirty() { return !dirtySections.isEmpty(); } public boolean isDirty() { return !dirtySections.isEmpty(); }
@Override @Override
public void process(boolean full) { public void process(boolean full) {
if(full) { if(full) {
GLFunctions.upload2DSubImage(id(), 0, 0, 0, width, height, GLTextureFormat.RGBA, GLDataType.UNSIGNED_BYTE, data); GLFunctions.upload2DSubImage(id(), 0, 0, 0, width, height, GLTextureFormat.RGBA, GLDataType.UNSIGNED_BYTE, data);
dirtySections.clear(); dirtySections.clear();
return; return;
} }
GLStateTracker tracker = GLStateTracker.instance(); GLStateTracker tracker = GLStateTracker.instance();
tracker.unpack_row_length.set(width()); tracker.unpack_row_length.set(width());
Thread thread = Thread.currentThread(); Thread thread = Thread.currentThread();
for(IntIterator iter = dirtySections.iterator();iter.hasNext() && !thread.isInterrupted();iter.remove()) { for(IntIterator iter = dirtySections.iterator();iter.hasNext() && !thread.isInterrupted();iter.remove()) {
int key = iter.nextInt(); int key = iter.nextInt();
uploadPixels(tracker, BitUtil.shortKey(key) * 16, BitUtil.shortValue(key) * 16); uploadPixels(tracker, BitUtil.shortKey(key) * 16, BitUtil.shortValue(key) * 16);
} }
tracker.unpack_row_length.setDefault(); tracker.unpack_row_length.setDefault();
tracker.unpack_skip_pixel.setDefault(); tracker.unpack_skip_pixel.setDefault();
tracker.unpack_skip_rows.setDefault(); tracker.unpack_skip_rows.setDefault();
} }
protected void uploadPixels(GLStateTracker tracker, int x, int y) { protected void uploadPixels(GLStateTracker tracker, int x, int y) {
tracker.unpack_skip_pixel.set(x); tracker.unpack_skip_pixel.set(x);
tracker.unpack_skip_rows.set(y); tracker.unpack_skip_rows.set(y);
GLFunctions.upload2DSubImage(id(), 0, x, y, 16, 16, GLTextureFormat.RGBA, GLDataType.UNSIGNED_BYTE, data); GLFunctions.upload2DSubImage(id(), 0, x, y, 16, 16, GLTextureFormat.RGBA, GLDataType.UNSIGNED_BYTE, data);
} }
protected long offset(int x, int y) { protected long offset(int x, int y) {
return ((y * width()) + x) << 2L; return ((y * width()) + x) << 2L;
} }
protected void ensureValid(int index) { protected void ensureValid(int index) {
ensureValid(index % width, index / width); ensureValid(index % width, index / width);
} }
protected void ensureValid(int x, int y) { 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 < 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(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"); if(data == 0L) throw new IllegalStateException("Texture Data isn't bound");
} }
@Override @Override
public void fill(int x, int y, int width, int height, int data) { public void fill(int x, int y, int width, int height, int data) {
ensureValid(x, y); ensureValid(x, y);
ensureValid(x+width, y+height); ensureValid(x+width, y+height);
for(int xOff = 0;xOff<width;xOff++) { for(int xOff = 0;xOff<width;xOff++) {
for(int yOff = 0;yOff<height;yOff++) { for(int yOff = 0;yOff<height;yOff++) {
MemoryUtil.memPutInt(this.data + offset(x+xOff, y+yOff), data); MemoryUtil.memPutInt(this.data + offset(x+xOff, y+yOff), data);
} }
} }
if(id() == 0) return; if(id() == 0) return;
for(int i = x>>4,m=width>>4;i<m;i++) { for(int i = x>>4,m=width>>4;i<m;i++) {
for(int j = y>>4,n=height>>4;j<n;j++) { for(int j = y>>4,n=height>>4;j<n;j++) {
dirtySections.add(BitUtil.toInt(i, j)); dirtySections.add(BitUtil.toInt(i, j));
} }
} }
} }
@Override @Override
public void dirty(int x, int y) { public void dirty(int x, int y) {
if(id() == 0) return; if(id() == 0) return;
ensureValid(x, y); ensureValid(x, y);
dirtySections.add(BitUtil.toInt(x >> 4, y >> 4)); dirtySections.add(BitUtil.toInt(x >> 4, y >> 4));
} }
@Override @Override
public void set(int index, int data) { public void set(int index, int data) {
ensureValid(index); ensureValid(index);
MemoryUtil.memPutInt(this.data + index * 4L, data); MemoryUtil.memPutInt(this.data + index * 4L, data);
dirty(index); dirty(index);
} }
@Override @Override
public void setR(int index, int red) { public void setR(int index, int red) {
ensureValid(index); ensureValid(index);
MemoryUtil.memPutByte(data + index * 4L, (byte)(red & 0xFF)); MemoryUtil.memPutByte(data + index * 4L, (byte)(red & 0xFF));
dirty(index); dirty(index);
} }
@Override @Override
public void setG(int index, int green) { public void setG(int index, int green) {
ensureValid(index); ensureValid(index);
MemoryUtil.memPutByte(data + index * 4L + 1L, (byte)(green & 0xFF)); MemoryUtil.memPutByte(data + index * 4L + 1L, (byte)(green & 0xFF));
dirty(index); dirty(index);
} }
@Override @Override
public void setB(int index, int blue) { public void setB(int index, int blue) {
ensureValid(index); ensureValid(index);
MemoryUtil.memPutByte(data + index * 4L + 2L, (byte)(blue & 0xFF)); MemoryUtil.memPutByte(data + index * 4L + 2L, (byte)(blue & 0xFF));
dirty(index); dirty(index);
} }
@Override @Override
public void setA(int index, int alpha) { public void setA(int index, int alpha) {
ensureValid(index); ensureValid(index);
MemoryUtil.memPutByte(data + index * 4L + 3L, (byte)(alpha & 0xFF)); MemoryUtil.memPutByte(data + index * 4L + 3L, (byte)(alpha & 0xFF));
dirty(index); dirty(index);
} }
@Override @Override
public int get(int index) { public int get(int index) {
ensureValid(index); ensureValid(index);
return MemoryUtil.memGetInt(data + index * 4L); return MemoryUtil.memGetInt(data + index * 4L);
} }
@Override @Override
public int getR(int index) { public int getR(int index) {
ensureValid(index); ensureValid(index);
return MemoryUtil.memGetByte(data + index * 4L); return MemoryUtil.memGetByte(data + index * 4L);
} }
@Override @Override
public int getG(int index) { public int getG(int index) {
ensureValid(index); ensureValid(index);
return MemoryUtil.memGetByte(data + index * 4L + 1L); return MemoryUtil.memGetByte(data + index * 4L + 1L);
} }
@Override @Override
public int getB(int index) { public int getB(int index) {
ensureValid(index); ensureValid(index);
return MemoryUtil.memGetByte(data + index * 4L + 2L); return MemoryUtil.memGetByte(data + index * 4L + 2L);
} }
@Override @Override
public int getA(int index) { public int getA(int index) {
ensureValid(index); ensureValid(index);
return MemoryUtil.memGetByte(data + index * 4L + 3L); return MemoryUtil.memGetByte(data + index * 4L + 3L);
} }
} }
@@ -1,14 +1,14 @@
#version 330 #version 420
in vec4 pass_color; in vec4 pass_color;
in vec2 pass_tex; in vec2 pass_tex;
out vec4 frag_color; out vec4 frag_color;
uniform sampler2D texture; layout(binding = 0) uniform sampler2D texture;
void main() void main()
{ {
vec4 color = texture2D(texture, pass_tex); vec4 color = texture2D(texture, pass_tex);
frag_color = pass_color * vec4(1, 1, 1, color.r); frag_color = pass_color * vec4(1, 1, 1, color.r);
} }
@@ -1,17 +1,19 @@
#version 330 #version 420
layout(location = 0) in vec3 in_position; layout(location = 0) in vec3 in_position;
layout(location = 1) in vec2 in_tex; layout(location = 1) in vec2 in_tex;
layout(location = 2) in vec4 in_color; layout(location = 2) in vec4 in_color;
out vec4 pass_color; out vec4 pass_color;
out vec2 pass_tex; out vec2 pass_tex;
uniform mat4 proViewMatrix; layout(binding = 0) uniform Camera {
mat4 proViewMatrix;
void main() } camera;
{
gl_Position = proViewMatrix * vec4(in_position, 1.0); void main()
pass_color = in_color; {
pass_tex = in_tex; gl_Position = camera.proViewMatrix * vec4(in_position, 1.0);
pass_color = in_color;
pass_tex = in_tex;
} }