More work done.
- Added: New Window System (old one ported to the new system) - Added: More support for textures - Added: Compute Pipeline - Added: Fences & Queries - Added: More Support for vulkan (Buffers now define the inputted buffer ownership)
This commit is contained in:
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
org.gradle.jvmargs=-Xmx2G
|
org.gradle.jvmargs=-Xmx2G
|
||||||
|
|
||||||
lwjglVersion = 3.4.2
|
lwjglVersion = 3.3.4
|
||||||
lwjglNatives = natives-windows
|
lwjglNatives = natives-windows
|
||||||
@@ -4,11 +4,13 @@ import java.nio.Buffer;
|
|||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.NonNull;
|
||||||
import org.lwjgl.system.MemoryUtil;
|
import org.lwjgl.system.MemoryUtil;
|
||||||
|
|
||||||
import speiger.src.collections.ints.misc.pairs.IntObjectPair;
|
import speiger.src.collections.ints.misc.pairs.IntObjectPair;
|
||||||
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.utils.BufferOwner;
|
||||||
import speiger.src.coreengine.graphics.api.utils.GraphicsResource;
|
import speiger.src.coreengine.graphics.api.utils.GraphicsResource;
|
||||||
|
|
||||||
public abstract class VertexBuffer implements GraphicsResource {
|
public abstract class VertexBuffer implements GraphicsResource {
|
||||||
@@ -37,30 +39,18 @@ public abstract class VertexBuffer implements GraphicsResource {
|
|||||||
public abstract VertexBuffer unbind();
|
public abstract VertexBuffer unbind();
|
||||||
|
|
||||||
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, BufferOwner owner);
|
||||||
public VertexBuffer set(ByteBuffer buffer) { return set(MemoryUtil.memAddress(buffer), buffer.remaining()); }
|
public VertexBuffer set(ByteBuffer buffer, BufferOwner owner) { return set(MemoryUtil.memAddress(buffer), buffer.remaining(), owner); }
|
||||||
public VertexBuffer set(byte[] data) {
|
public VertexBuffer set(byte[] data) { return set(MemoryUtil.memAlloc(data.length).put(data).flip(), BufferOwner.GIVEN); }
|
||||||
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, BufferOwner owner);
|
||||||
public VertexBuffer fill(int offset, ByteBuffer buffer) { return fill(MemoryUtil.memAddress(buffer), buffer.remaining(), offset); }
|
public VertexBuffer fill(int offset, ByteBuffer buffer, BufferOwner owner) { return fill(MemoryUtil.memAddress(buffer), buffer.remaining(), offset, owner); }
|
||||||
public VertexBuffer fill(int offset, byte[] data) {
|
public VertexBuffer fill(int offset, byte[] data) {
|
||||||
ByteBuffer buffer = MemoryUtil.memAlloc(data.length).put(data).flip();
|
return fill(offset, MemoryUtil.memAlloc(data.length).put(data).flip(), BufferOwner.GIVEN);
|
||||||
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, @NonNull Runnable completion);
|
||||||
public VertexBuffer read(Buffer buffer, int totalBytes, int offset) {
|
public VertexBuffer read(Buffer buffer, int totalBytes, int offset, @NonNull Runnable completion) { return read(MemoryUtil.memAddress(buffer), totalBytes, offset, completion); }
|
||||||
read(MemoryUtil.memAddress(buffer), totalBytes, offset);
|
|
||||||
buffer.flip();
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public VertexBuffer fill(List<IntObjectPair<byte[]>> data) { return fill(0, data); }
|
public VertexBuffer fill(List<IntObjectPair<byte[]>> data) { return fill(0, data); }
|
||||||
public abstract VertexBuffer fill(int offset, List<IntObjectPair<byte[]>> data);
|
public abstract VertexBuffer fill(int offset, List<IntObjectPair<byte[]>> data);
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package speiger.src.coreengine.graphics.api.compute;
|
||||||
|
|
||||||
|
public enum BarrierType {
|
||||||
|
MESH_BUFFER,
|
||||||
|
SHADER_TEXTURE,
|
||||||
|
SHADER_IMAGE,
|
||||||
|
SHADER_STORAGE,
|
||||||
|
SHADER_UNIFORM,
|
||||||
|
INDIRECT_COMMAND,
|
||||||
|
FRAMEBUFFER,
|
||||||
|
CPU;
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
package speiger.src.coreengine.graphics.api.compute;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import speiger.src.collections.objects.lists.ObjectArrayList;
|
||||||
|
import speiger.src.collections.objects.lists.ObjectList;
|
||||||
|
import speiger.src.coreengine.assets.api.ID;
|
||||||
|
import speiger.src.coreengine.graphics.api.shader.states.Bindings.BufferBinding;
|
||||||
|
import speiger.src.coreengine.graphics.api.shader.states.Bindings.BufferMode;
|
||||||
|
import speiger.src.coreengine.graphics.api.shader.states.Bindings.TextureBinding;
|
||||||
|
import speiger.src.coreengine.graphics.api.shader.states.Bindings.TextureMode;
|
||||||
|
import speiger.src.coreengine.graphics.api.texture.states.TextureType;
|
||||||
|
|
||||||
|
public record ComputePipeline(ID id, ID shader, List<BufferBinding> uniforms, List<TextureBinding> textures) {
|
||||||
|
|
||||||
|
public static Builder builder(ID id) {
|
||||||
|
return new Builder(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Builder {
|
||||||
|
ID id;
|
||||||
|
ID shader;
|
||||||
|
ObjectList<BufferBinding> uniforms = new ObjectArrayList<>();
|
||||||
|
ObjectList<TextureBinding> textures = new ObjectArrayList<>();
|
||||||
|
|
||||||
|
private Builder(ID id) {
|
||||||
|
this.id = Objects.requireNonNull(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder withShader(ID shader) {
|
||||||
|
this.shader = Objects.requireNonNull(shader);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder withUniform(String name, int binding, int slot, int bytes) {
|
||||||
|
uniforms.add(new BufferBinding(Objects.requireNonNull(name), binding, slot, bytes, BufferMode.UNIFORM));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder withBuffer(String name, int binding, int slot, int bytes) {
|
||||||
|
uniforms.add(new BufferBinding(Objects.requireNonNull(name), binding, slot, bytes, BufferMode.STORAGE));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder withTexture(String name, int binding, int slot, TextureType type) {
|
||||||
|
textures.add(new TextureBinding(Objects.requireNonNull(name), binding, slot, type, TextureMode.SAMPLER));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder withStorage(String name, int binding, int slot, TextureType type) {
|
||||||
|
textures.add(new TextureBinding(Objects.requireNonNull(name), binding, slot, type, TextureMode.IMAGE_BUFFER));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ComputePipeline build() {
|
||||||
|
return new ComputePipeline(id, Objects.requireNonNull(shader, "A Shader has to exist"), uniforms, textures);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package speiger.src.coreengine.graphics.api.core;
|
||||||
|
|
||||||
|
public interface CommandBuffer {
|
||||||
|
int commandCount();
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package speiger.src.coreengine.graphics.api.core;
|
||||||
|
|
||||||
|
import speiger.src.coreengine.graphics.api.buffer.VertexBuffer;
|
||||||
|
import speiger.src.coreengine.graphics.api.compute.BarrierType;
|
||||||
|
import speiger.src.coreengine.graphics.api.compute.ComputePipeline;
|
||||||
|
import speiger.src.coreengine.graphics.api.sampler.Sampler;
|
||||||
|
import speiger.src.coreengine.graphics.api.texture.Texture;
|
||||||
|
import speiger.src.coreengine.graphics.api.utils.AccessType;
|
||||||
|
import speiger.src.coreengine.graphics.api.utils.GraphicsResource;
|
||||||
|
|
||||||
|
public interface ComputeCommandBuffer extends GraphicsResource, CommandBuffer {
|
||||||
|
ComputeCommandBuffer begin();
|
||||||
|
ComputeCommandBuffer pipeline(ComputePipeline pipeline);
|
||||||
|
|
||||||
|
|
||||||
|
ComputeCommandBuffer texture(int binding, Texture texture, Sampler sampler);
|
||||||
|
ComputeCommandBuffer texture(int binding, Texture texture, AccessType access);
|
||||||
|
|
||||||
|
ComputeCommandBuffer uniform(int binding, VertexBuffer buffer);
|
||||||
|
ComputeCommandBuffer uniform(int binding, VertexBuffer buffer, long offset, long size);
|
||||||
|
|
||||||
|
ComputeCommandBuffer storage(int binding, VertexBuffer buffer);
|
||||||
|
ComputeCommandBuffer storage(int binding, VertexBuffer buffer, long offset, long size);
|
||||||
|
|
||||||
|
ComputeCommandBuffer dispatch(int numGroupsX, int numGroupsY, int numGroupsZ);
|
||||||
|
|
||||||
|
ComputeCommandBuffer barrier(BarrierType... types);
|
||||||
|
|
||||||
|
ComputeCommandBuffer end();
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package speiger.src.coreengine.graphics.api.core;
|
package speiger.src.coreengine.graphics.api.core;
|
||||||
|
|
||||||
import speiger.src.coreengine.rendering.input.window.Window;
|
import speiger.src.coreengine.input.window.Window;
|
||||||
|
|
||||||
public interface Graphics {
|
public interface Graphics {
|
||||||
public String getName();
|
public String getName();
|
||||||
|
|||||||
+6
-2
@@ -5,19 +5,24 @@ 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.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.AccessType;
|
||||||
import speiger.src.coreengine.graphics.api.utils.GraphicsResource;
|
import speiger.src.coreengine.graphics.api.utils.GraphicsResource;
|
||||||
|
|
||||||
public interface GraphicsCommandBuffer extends GraphicsResource {
|
public interface GraphicsCommandBuffer extends GraphicsResource, CommandBuffer {
|
||||||
GraphicsCommandBuffer begin();
|
GraphicsCommandBuffer begin();
|
||||||
GraphicsCommandBuffer setScissors(int x, int y, int width, int height);
|
GraphicsCommandBuffer setScissors(int x, int y, int width, int height);
|
||||||
|
|
||||||
GraphicsCommandBuffer pipeline(ShaderPipeline pipeline);
|
GraphicsCommandBuffer pipeline(ShaderPipeline pipeline);
|
||||||
GraphicsCommandBuffer mesh(Mesh mesh);
|
GraphicsCommandBuffer mesh(Mesh mesh);
|
||||||
GraphicsCommandBuffer texture(int binding, Texture texture, Sampler sampler);
|
GraphicsCommandBuffer texture(int binding, Texture texture, Sampler sampler);
|
||||||
|
GraphicsCommandBuffer texture(int binding, Texture texture, AccessType access);
|
||||||
|
|
||||||
GraphicsCommandBuffer uniform(int binding, VertexBuffer buffer);
|
GraphicsCommandBuffer uniform(int binding, VertexBuffer buffer);
|
||||||
GraphicsCommandBuffer uniform(int binding, VertexBuffer buffer, long offset, long size);
|
GraphicsCommandBuffer uniform(int binding, VertexBuffer buffer, long offset, long size);
|
||||||
|
|
||||||
|
GraphicsCommandBuffer storage(int binding, VertexBuffer buffer);
|
||||||
|
GraphicsCommandBuffer storage(int binding, VertexBuffer buffer, long offset, long size);
|
||||||
|
|
||||||
GraphicsCommandBuffer drawArrays(int offset, int count);
|
GraphicsCommandBuffer drawArrays(int offset, int count);
|
||||||
GraphicsCommandBuffer drawElements(int offset, int count);
|
GraphicsCommandBuffer drawElements(int offset, int count);
|
||||||
|
|
||||||
@@ -25,5 +30,4 @@ public interface GraphicsCommandBuffer extends GraphicsResource {
|
|||||||
GraphicsCommandBuffer popScissors();
|
GraphicsCommandBuffer popScissors();
|
||||||
GraphicsCommandBuffer end();
|
GraphicsCommandBuffer end();
|
||||||
|
|
||||||
int commandCount();
|
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-4
@@ -22,10 +22,14 @@ public interface GraphicsCommandQueue {
|
|||||||
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, TextureFormat format, GraphicsDataType dataType, long source);
|
public void writeToTexture(Texture target, TextureFormat format, GraphicsDataType dataType, long source);
|
||||||
public default void writeToTexture(Texture target, int x, int y, int width, int height, TextureFormat format, GraphicsDataType dataType, long source) {
|
public default void writeToTexture(Texture target, int level, 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);
|
writeToTexture(target, level, 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 writeToTexture(Texture target, int level, 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(CommandBuffer buffer);
|
||||||
|
|
||||||
|
public GraphicsFence createFence();
|
||||||
|
public long currentFrame();
|
||||||
|
public void submitFrame();
|
||||||
}
|
}
|
||||||
@@ -5,6 +5,7 @@ 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.compute.ComputePipeline;
|
||||||
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;
|
||||||
@@ -12,7 +13,7 @@ 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.input.window.Window;
|
||||||
|
|
||||||
public interface GraphicsDevice {
|
public interface GraphicsDevice {
|
||||||
Window window();
|
Window window();
|
||||||
@@ -24,5 +25,6 @@ public interface GraphicsDevice {
|
|||||||
Texture createTexture(TextureSettings data, int width, int height);
|
Texture createTexture(TextureSettings data, int width, int height);
|
||||||
Sampler createSampler(SamplerSettings settings);
|
Sampler createSampler(SamplerSettings settings);
|
||||||
Mesh createMesh(Mesh.Builder builder);
|
Mesh createMesh(Mesh.Builder builder);
|
||||||
void preloadPipelines(List<ShaderPipeline> pipelines);
|
TimeQueryPool createQueryPool(int size);
|
||||||
|
void preloadPipelines(List<ShaderPipeline> graphics, List<ComputePipeline> compute);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package speiger.src.coreengine.graphics.api.core;
|
||||||
|
|
||||||
|
import speiger.src.coreengine.graphics.api.utils.GraphicsResource;
|
||||||
|
|
||||||
|
public interface GraphicsFence extends GraphicsResource {
|
||||||
|
boolean awaitCompletion(long timeout);
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package speiger.src.coreengine.graphics.api.core;
|
||||||
|
|
||||||
|
import java.util.OptionalLong;
|
||||||
|
|
||||||
|
import speiger.src.coreengine.graphics.api.utils.GraphicsResource;
|
||||||
|
|
||||||
|
public interface TimeQueryPool extends GraphicsResource {
|
||||||
|
public int size();
|
||||||
|
public void write(int index);
|
||||||
|
public OptionalLong get(int index);
|
||||||
|
}
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
package speiger.src.coreengine.graphics.api.shader;
|
|
||||||
|
|
||||||
public interface CompiledPipeline {
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -6,23 +6,25 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
import speiger.src.collections.objects.lists.ImmutableObjectList;
|
|
||||||
import speiger.src.collections.objects.lists.ObjectArrayList;
|
import speiger.src.collections.objects.lists.ObjectArrayList;
|
||||||
import speiger.src.collections.objects.lists.ObjectList;
|
import speiger.src.collections.objects.lists.ObjectList;
|
||||||
import speiger.src.coreengine.assets.api.ID;
|
import speiger.src.coreengine.assets.api.ID;
|
||||||
|
import speiger.src.coreengine.graphics.api.shader.states.Bindings.BufferMode;
|
||||||
import speiger.src.coreengine.graphics.api.shader.states.Bindings.TextureBinding;
|
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.Bindings.TextureMode;
|
||||||
|
import speiger.src.coreengine.graphics.api.shader.states.Bindings.BufferBinding;
|
||||||
import speiger.src.coreengine.graphics.api.shader.states.DrawMode;
|
import speiger.src.coreengine.graphics.api.shader.states.DrawMode;
|
||||||
import speiger.src.coreengine.graphics.api.shader.states.GraphicsDataType;
|
|
||||||
import speiger.src.coreengine.graphics.api.shader.states.PolygonMode;
|
import speiger.src.coreengine.graphics.api.shader.states.PolygonMode;
|
||||||
import speiger.src.coreengine.graphics.api.shader.states.ShaderType;
|
import speiger.src.coreengine.graphics.api.shader.states.ShaderType;
|
||||||
import speiger.src.coreengine.graphics.api.texture.states.TextureType;
|
import speiger.src.coreengine.graphics.api.texture.states.TextureType;
|
||||||
|
import speiger.src.coreengine.graphics.api.vertex.VertexLayout;
|
||||||
|
|
||||||
public record ShaderPipeline(ID id, Map<ShaderType, ID> shaders, List<VertexBinding> attributes, List<UniformBinding> uniforms, List<TextureBinding> textures, RasterizerState rasterizer, ColorTarget colorTarget, DepthTarget depthTarget) {
|
public record ShaderPipeline(ID id, Map<ShaderType, ID> shaders, List<VertexBinding> attributes, List<BufferBinding> uniforms, List<TextureBinding> textures, RasterizerState rasterizer, ColorTarget colorTarget, DepthTarget depthTarget) {
|
||||||
|
|
||||||
public ShaderPipeline {
|
public ShaderPipeline {
|
||||||
Objects.requireNonNull(id);
|
Objects.requireNonNull(id);
|
||||||
if(shaders.isEmpty()) throw new IllegalStateException("Shaders isn't allowed to be empty");
|
if(shaders.isEmpty()) throw new IllegalStateException("Shaders isn't allowed to be empty");
|
||||||
|
if(shaders.containsKey(ShaderType.COMPUTE)) throw new IllegalStateException("Graphics Pipelines are not allowed to have Compute Shaders");
|
||||||
Objects.requireNonNull(uniforms);
|
Objects.requireNonNull(uniforms);
|
||||||
Objects.requireNonNull(textures);
|
Objects.requireNonNull(textures);
|
||||||
Objects.requireNonNull(rasterizer);
|
Objects.requireNonNull(rasterizer);
|
||||||
@@ -34,15 +36,11 @@ public record ShaderPipeline(ID id, Map<ShaderType, ID> shaders, List<VertexBind
|
|||||||
return new Builder(id);
|
return new Builder(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder copy(ID id) {
|
|
||||||
return new Builder(id, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class Builder {
|
public static class Builder {
|
||||||
ID id;
|
ID id;
|
||||||
EnumMap<ShaderType, ID> shaders = new EnumMap<>(ShaderType.class);
|
EnumMap<ShaderType, ID> shaders = new EnumMap<>(ShaderType.class);
|
||||||
ObjectList<VertexBinding> attributes = new ObjectArrayList<>();
|
ObjectList<VertexBinding> attributes = new ObjectArrayList<>();
|
||||||
ObjectList<UniformBinding> uniforms = new ObjectArrayList<>();
|
ObjectList<BufferBinding> uniforms = new ObjectArrayList<>();
|
||||||
ObjectList<TextureBinding> textures = new ObjectArrayList<>();
|
ObjectList<TextureBinding> textures = new ObjectArrayList<>();
|
||||||
RasterizerState rasterizer = RasterizerState.DEFAULT;
|
RasterizerState rasterizer = RasterizerState.DEFAULT;
|
||||||
ColorTarget colorTarget = ColorTarget.DEFAULT;
|
ColorTarget colorTarget = ColorTarget.DEFAULT;
|
||||||
@@ -50,18 +48,6 @@ public record ShaderPipeline(ID id, Map<ShaderType, ID> shaders, List<VertexBind
|
|||||||
|
|
||||||
private Builder(ID id) {
|
private Builder(ID id) {
|
||||||
this.id = Objects.requireNonNull(id);
|
this.id = Objects.requireNonNull(id);
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private Builder(ID id, ShaderPipeline line) {
|
|
||||||
this(id);
|
|
||||||
shaders.putAll(line.shaders());
|
|
||||||
attributes.addAll(line.attributes());
|
|
||||||
uniforms.addAll(line.uniforms());
|
|
||||||
textures.addAll(line.textures());
|
|
||||||
rasterizer = line.rasterizer();
|
|
||||||
colorTarget = line.colorTarget();
|
|
||||||
depthTarget = line.depthTarget();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder withStage(ShaderType type, ID location) {
|
public Builder withStage(ShaderType type, ID location) {
|
||||||
@@ -70,38 +56,49 @@ public record ShaderPipeline(ID id, Map<ShaderType, ID> shaders, List<VertexBind
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Builder withUniform(String name, int binding, int slot, int bytes) {
|
public Builder withUniform(String name, int binding, int slot, int bytes) {
|
||||||
uniforms.add(new UniformBinding(name, binding, slot, bytes));
|
uniforms.add(new BufferBinding(Objects.requireNonNull(name), binding, slot, bytes, BufferMode.UNIFORM));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder withBuffer(String name, int binding, int slot, int bytes) {
|
||||||
|
uniforms.add(new BufferBinding(Objects.requireNonNull(name), binding, slot, bytes, BufferMode.STORAGE));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder withTexture(String name, int binding, int slot, TextureType type) {
|
public Builder withTexture(String name, int binding, int slot, TextureType type) {
|
||||||
textures.add(new TextureBinding(name, binding, slot, type));
|
textures.add(new TextureBinding(Objects.requireNonNull(name), binding, slot, type, TextureMode.SAMPLER));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder withStorage(String name, int binding, int slot, TextureType type) {
|
||||||
|
textures.add(new TextureBinding(Objects.requireNonNull(name), binding, slot, type, TextureMode.IMAGE_BUFFER));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder withRasterizer(DrawMode draw, PolygonMode polygon, boolean cullBack) {
|
public Builder withRasterizer(DrawMode draw, PolygonMode polygon, boolean cullBack) {
|
||||||
rasterizer = new RasterizerState(draw, polygon, cullBack);
|
rasterizer = new RasterizerState(Objects.requireNonNull(draw), Objects.requireNonNull(polygon), cullBack);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AttributeBuilder withFormat() {
|
public Builder withFormat(VertexLayout layout) {
|
||||||
return withFormat(attributes.size());
|
return withFormat(attributes.size(), layout);
|
||||||
}
|
}
|
||||||
|
|
||||||
public AttributeBuilder withInstanceFormat() {
|
public Builder withInstanceFormat(VertexLayout layout) {
|
||||||
return withFormat(attributes.size(), 1);
|
return withFormat(attributes.size(), layout, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public AttributeBuilder withInstanceFormat(int instanceCount) {
|
public Builder withInstanceFormat(VertexLayout layout, int instanceCount) {
|
||||||
return withFormat(attributes.size(), instanceCount);
|
return withFormat(attributes.size(), layout, instanceCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
public AttributeBuilder withFormat(int bindingIndex) {
|
public Builder withFormat(int bindingIndex, VertexLayout layout) {
|
||||||
return withFormat(bindingIndex, 0);
|
return withFormat(bindingIndex, layout, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public AttributeBuilder withFormat(int bindingIndex, int instanceCount) {
|
public Builder withFormat(int bindingIndex, VertexLayout layout, int instanceCount) {
|
||||||
return new AttributeBuilder(this, bindingIndex, instanceCount);
|
attributes.add(new VertexBinding(bindingIndex, Objects.requireNonNull(layout), instanceCount));
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder withColorTarget(ColorTarget target) {
|
public Builder withColorTarget(ColorTarget target) {
|
||||||
@@ -116,52 +113,8 @@ public record ShaderPipeline(ID id, Map<ShaderType, ID> shaders, List<VertexBind
|
|||||||
|
|
||||||
public ShaderPipeline build() {
|
public ShaderPipeline build() {
|
||||||
if(shaders.isEmpty()) throw new IllegalStateException("Shaders must be provided in a Shader Pipeline");
|
if(shaders.isEmpty()) throw new IllegalStateException("Shaders must be provided in a Shader Pipeline");
|
||||||
|
if(shaders.containsKey(ShaderType.COMPUTE)) throw new IllegalStateException("Graphics Pipelines are not allowed to have Compute Shaders");
|
||||||
return new ShaderPipeline(id, Collections.unmodifiableMap(shaders), attributes.unmodifiable(), uniforms.unmodifiable(), textures.unmodifiable(), Objects.requireNonNull(rasterizer, "A Rasterizer should be defined"), colorTarget, depthTarget);
|
return new ShaderPipeline(id, Collections.unmodifiableMap(shaders), attributes.unmodifiable(), uniforms.unmodifiable(), textures.unmodifiable(), Objects.requireNonNull(rasterizer, "A Rasterizer should be defined"), colorTarget, depthTarget);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class AttributeBuilder {
|
|
||||||
final Builder owner;
|
|
||||||
final List<BufferAttribute> attributes = new ObjectArrayList<>();
|
|
||||||
final int bindingIndex;
|
|
||||||
final int instanceCount;
|
|
||||||
int stride;
|
|
||||||
|
|
||||||
private AttributeBuilder(Builder owner, int bindingIndex, int instanceCount) {
|
|
||||||
this.owner = owner;
|
|
||||||
this.bindingIndex = bindingIndex;
|
|
||||||
this.instanceCount = instanceCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
public AttributeBuilder attribute(String name, int index, int size) {
|
|
||||||
return attribute(name, index, size, GraphicsDataType.FLOAT, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public AttributeBuilder attribute(String name, int index, int size, GraphicsDataType type) {
|
|
||||||
return attribute(name, index, size, type, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public AttributeBuilder attribute(String name, int index, int size, GraphicsDataType type, boolean normalized) {
|
|
||||||
attributes.add(new BufferAttribute(name, index, size, type, normalized));
|
|
||||||
stride += type.size(size);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public AttributeBuilder array(String baseName, int startIndex, int width, int size, GraphicsDataType type) {
|
|
||||||
return array(baseName, startIndex, width, size, type, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public AttributeBuilder array(String baseName, int startIndex, int width, int size, GraphicsDataType type, boolean normalized) {
|
|
||||||
Objects.requireNonNull(baseName);
|
|
||||||
for(int i = 0;i<width;i++) {
|
|
||||||
attribute(baseName+" ["+i+"]", startIndex + i, size, type, normalized);
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder endFormat() {
|
|
||||||
owner.attributes.add(new VertexBinding(bindingIndex, stride, instanceCount, new ImmutableObjectList<>(attributes)));
|
|
||||||
return owner;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
package speiger.src.coreengine.graphics.api.shader;
|
package speiger.src.coreengine.graphics.api.shader;
|
||||||
|
|
||||||
import java.util.List;
|
import speiger.src.collections.objects.collections.ObjectIterable;
|
||||||
|
import speiger.src.coreengine.graphics.api.vertex.VertexLayout;
|
||||||
public record VertexBinding(int bindingIndex, int stide, int instanceOffset, List<BufferAttribute> attributes) {
|
import speiger.src.coreengine.graphics.api.vertex.VertexLayout.Element;
|
||||||
|
|
||||||
|
public record VertexBinding(int bindingIndex, VertexLayout layout, int instanceOffset) {
|
||||||
|
public ObjectIterable<Element> elements() {
|
||||||
|
return layout.elements();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,17 +5,28 @@ 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 slot, int bytes) {
|
public record BufferBinding(String name, int binding, int slot, int bytes, BufferMode mode) {
|
||||||
public UniformBinding {
|
public BufferBinding {
|
||||||
Objects.requireNonNull(name, "A name has to be provided");
|
Objects.requireNonNull(name, "A name has to be provided");
|
||||||
|
Objects.requireNonNull(mode, "A Mode has to be provided");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public record TextureBinding(String name, int binding, int slot, TextureType type) {
|
public record TextureBinding(String name, int binding, int slot, TextureType type, TextureMode mode) {
|
||||||
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");
|
||||||
|
Objects.requireNonNull(mode, "A Mode has to be provided");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static enum BufferMode {
|
||||||
|
UNIFORM,
|
||||||
|
STORAGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static enum TextureMode {
|
||||||
|
SAMPLER,
|
||||||
|
IMAGE_BUFFER;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,5 +5,6 @@ public enum ShaderType {
|
|||||||
FRAGMENT,
|
FRAGMENT,
|
||||||
GEOMETRY,
|
GEOMETRY,
|
||||||
TESSELATION_CONTROL,
|
TESSELATION_CONTROL,
|
||||||
TESSELATION_EVALUATION;
|
TESSELATION_EVALUATION,
|
||||||
|
COMPUTE;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,201 @@
|
|||||||
|
package speiger.src.coreengine.graphics.api.texture.drawable;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import org.lwjgl.stb.STBImage;
|
||||||
|
import org.lwjgl.stb.STBTTFontinfo;
|
||||||
|
import org.lwjgl.stb.STBTruetype;
|
||||||
|
import org.lwjgl.system.MemoryUtil;
|
||||||
|
import org.lwjgl.util.freetype.FT_Bitmap;
|
||||||
|
import org.lwjgl.util.freetype.FT_Face;
|
||||||
|
import org.lwjgl.util.freetype.FT_GlyphSlot;
|
||||||
|
import org.lwjgl.util.freetype.FreeType;
|
||||||
|
|
||||||
|
import speiger.src.coreengine.graphics.api.core.GraphicsCommandQueue;
|
||||||
|
import speiger.src.coreengine.graphics.api.shader.states.GraphicsDataType;
|
||||||
|
import speiger.src.coreengine.graphics.api.texture.Texture;
|
||||||
|
import speiger.src.coreengine.graphics.api.texture.states.TextureFormat;
|
||||||
|
import speiger.src.coreengine.math.color.ColorSpaces;
|
||||||
|
|
||||||
|
public class Drawable implements IDrawable, AutoCloseable {
|
||||||
|
TextureFormat format;
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
long components;
|
||||||
|
long pixels;
|
||||||
|
boolean isSTB;
|
||||||
|
|
||||||
|
public Drawable(TextureFormat format, int width, int height) {
|
||||||
|
this(format, width, height, MemoryUtil.nmemAllocChecked(format.components() * width * height), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Drawable(TextureFormat format, int width, int height, long pixels, boolean isSTB) {
|
||||||
|
this.format = format;
|
||||||
|
this.width = width;
|
||||||
|
this.height = height;
|
||||||
|
this.pixels = pixels;
|
||||||
|
this.components = format.components();
|
||||||
|
this.isSTB = isSTB;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ColorSpaces colorspace() { return ColorSpaces.ABGR; }
|
||||||
|
@Override
|
||||||
|
public int width() { return width; }
|
||||||
|
@Override
|
||||||
|
public int height() { return height; }
|
||||||
|
public long pixels() { return pixels; }
|
||||||
|
public boolean isSTBImage() { return isSTB; }
|
||||||
|
public TextureFormat format() { return format; }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
if(pixels == 0L) return;
|
||||||
|
if(isSTB) STBImage.nstbi_image_free(pixels);
|
||||||
|
else MemoryUtil.nmemFree(pixels);
|
||||||
|
pixels = 0L;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected long offset(int x, int y) {
|
||||||
|
return ((y * width()) + x) * components;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void ensureValid(int index) {
|
||||||
|
ensureValid(index % width, index / width);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void ensureValid(int x, int y) {
|
||||||
|
if(x < 0 || y < 0) throw new ArrayIndexOutOfBoundsException("Index out of bounds: X=["+x+"], Y=["+y+"]");
|
||||||
|
if(x > width || y > height) throw new ArrayIndexOutOfBoundsException("Index out of bounds: X=["+x+"], Y=["+y+"], width=["+width+"], height=["+height+"]");
|
||||||
|
if(pixels == 0L) throw new IllegalStateException("Pixel Data doesn't exist");
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean drawFont(FT_Face face, int glyth) {
|
||||||
|
ensureValid(0);
|
||||||
|
if(components != 1) throw new IllegalStateException("Format has to be 1 component");
|
||||||
|
if(FreeType.FT_Load_Glyph(face, glyth, 4) != 0) return false;
|
||||||
|
|
||||||
|
FT_GlyphSlot slot = Objects.requireNonNull(face.glyph());
|
||||||
|
FT_Bitmap map = slot.bitmap();
|
||||||
|
if(map.pixel_mode() != FreeType.FT_PIXEL_MODE_GRAY) throw new IllegalStateException("Pixel isn't a grayscale picture");
|
||||||
|
if(map.width() != width() || map.rows() != height()) throw new IllegalStateException("Bounds do not match");
|
||||||
|
int size = map.width() * map.rows();
|
||||||
|
ByteBuffer buffer = Objects.requireNonNull(map.buffer(size));
|
||||||
|
MemoryUtil.memCopy(MemoryUtil.memAddress(buffer), pixels(), size);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void drawFont(STBTTFontinfo info, int glyth, float sourceX, float sourceY, int targetX, int targetY, int width, int height, float scaleX, float scaleY) {
|
||||||
|
ensureValid(targetX, targetY);
|
||||||
|
ensureValid(targetX + width, targetY + height);
|
||||||
|
if(components != 1) throw new IllegalStateException("Format has to be 1 component");
|
||||||
|
STBTruetype.nstbtt_MakeGlyphBitmapSubpixel(info.address(), pixels + offset(targetX, targetY), width, height, width(), scaleX, scaleY, sourceX, sourceY, glyth);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void upload(GraphicsCommandQueue queue, Texture texture) {
|
||||||
|
if(texture.width() != width || texture.height() != height) throw new IllegalStateException("Texture[w="+texture.width()+",h="+texture.height()+"] bounds does not match the drawable [w="+width+",h="+height+"]");
|
||||||
|
queue.writeToTexture(texture, format, GraphicsDataType.UNSIGNED_BYTE, pixels);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void upload(GraphicsCommandQueue queue, Texture texture, int targetX, int targetY, int sourceX, int sourceY, int width, int height) {
|
||||||
|
upload(queue, texture, 0, targetX, targetY, sourceX, sourceY, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void upload(GraphicsCommandQueue queue, Texture texture, int level, int targetX, int targetY, int sourceX, int sourceY, int width, int height) {
|
||||||
|
ensureValid(sourceX, sourceY);
|
||||||
|
ensureValid(sourceX + width, sourceY + height);
|
||||||
|
queue.writeToTexture(texture, level, sourceX, sourceY, targetX, targetY, width, height, format, GraphicsDataType.UNSIGNED_BYTE, pixels);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void set(int index, int data) {
|
||||||
|
ensureValid(index);
|
||||||
|
if(components != 4) throw new IllegalStateException("Format has to be 4 components");
|
||||||
|
MemoryUtil.memPutInt(this.pixels + index * 4L, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setR(int index, int red) {
|
||||||
|
if(!format.hasRed()) throw new IllegalArgumentException("Format doesn't support Red/Luminance");
|
||||||
|
ensureValid(index);
|
||||||
|
MemoryUtil.memPutByte(this.pixels + index * components + format.redOffset(), (byte)(red & 0xFF));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setG(int index, int green) {
|
||||||
|
if(!format.hasGreen()) throw new IllegalArgumentException("Format doesn't support Green");
|
||||||
|
ensureValid(index);
|
||||||
|
MemoryUtil.memPutByte(this.pixels + index * components + format.greenOffset(), (byte)(green & 0xFF));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setB(int index, int blue) {
|
||||||
|
if(!format.hasBlue()) throw new IllegalArgumentException("Format doesn't support Blue");
|
||||||
|
ensureValid(index);
|
||||||
|
MemoryUtil.memPutByte(this.pixels + index * components + format.blueOffset(), (byte)(blue & 0xFF));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setA(int index, int alpha) {
|
||||||
|
if(!format.hasAlpha()) throw new IllegalArgumentException("Format doesn't support Alpha");
|
||||||
|
ensureValid(index);
|
||||||
|
MemoryUtil.memPutByte(this.pixels + index * components + format.alphaOffset(), (byte)(alpha & 0xFF));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void fill(int x, int y, int width, int height, int data) {
|
||||||
|
if(components != 4 && components != 1) throw new IllegalStateException("Format has to be 1 or 4 components");
|
||||||
|
ensureValid(x, y);
|
||||||
|
ensureValid(x+width, y+height);
|
||||||
|
if(components == 1) {
|
||||||
|
for(int xOff = 0;xOff<width;xOff++) {
|
||||||
|
for(int yOff = 0;yOff<height;yOff++) {
|
||||||
|
MemoryUtil.memPutByte(this.pixels + offset(x+xOff, y+yOff), (byte)(data & 0xFF));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int xOff = 0;xOff<width;xOff++) {
|
||||||
|
for(int yOff = 0;yOff<height;yOff++) {
|
||||||
|
MemoryUtil.memPutInt(this.pixels + offset(x+xOff, y+yOff), data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int get(int index) {
|
||||||
|
if(components != 4) throw new IllegalStateException("Format has to be 4 components");
|
||||||
|
ensureValid(index);
|
||||||
|
return MemoryUtil.memGetInt(this.pixels + index * 4L);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getR(int index) {
|
||||||
|
if(!format.hasRed()) throw new IllegalArgumentException("Format doesn't support Red/Luminance");
|
||||||
|
ensureValid(index);
|
||||||
|
return MemoryUtil.memGetByte(this.pixels + index * components + format.redOffset());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getG(int index) {
|
||||||
|
if(!format.hasGreen()) throw new IllegalArgumentException("Format doesn't support Green");
|
||||||
|
ensureValid(index);
|
||||||
|
return MemoryUtil.memGetByte(this.pixels + index * components + format.greenOffset());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getB(int index) {
|
||||||
|
if(!format.hasBlue()) throw new IllegalArgumentException("Format doesn't support Blue");
|
||||||
|
ensureValid(index);
|
||||||
|
return MemoryUtil.memGetByte(this.pixels + index * components + format.blueOffset());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getA(int index) {
|
||||||
|
if(!format.hasAlpha()) throw new IllegalArgumentException("Format doesn't support Alpha");
|
||||||
|
ensureValid(index);
|
||||||
|
return MemoryUtil.memGetByte(this.pixels + index * components + format.alphaOffset());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
package speiger.src.coreengine.graphics.api.texture.drawable;
|
||||||
|
|
||||||
|
import speiger.src.coreengine.math.color.ColorSpaces;
|
||||||
|
|
||||||
|
public interface IDrawable {
|
||||||
|
public ColorSpaces colorspace();
|
||||||
|
public int width();
|
||||||
|
public int height();
|
||||||
|
|
||||||
|
public void set(int index, int data);
|
||||||
|
public default void set(int x, int y, int data) { set((y * width()) + x, data); }
|
||||||
|
|
||||||
|
public default void set(int index, int red, int green, int blue, int alpha) { set(index, colorspace().color(red, green, blue, alpha)); }
|
||||||
|
public default void set(int x, int y, int red, int green, int blue, int alpha) { set((y * width()) + x, colorspace().color(red, green, blue, alpha)); }
|
||||||
|
|
||||||
|
public void setR(int index, int red);
|
||||||
|
public default void setR(int x, int y, int red) { setR((y * width()) + x, red); }
|
||||||
|
|
||||||
|
public void setG(int index, int green);
|
||||||
|
public default void setG(int x, int y, int green) { setG((y * width()) + x, green); }
|
||||||
|
|
||||||
|
public void setB(int index, int blue);
|
||||||
|
public default void setB(int x, int y, int blue) { setB((y * width()) + x, blue); }
|
||||||
|
|
||||||
|
public void setA(int index, int alpha);
|
||||||
|
public default void setA(int x, int y, int alpha) { setA((y * width()) + x, alpha); }
|
||||||
|
|
||||||
|
public void fill(int x, int y, int width, int height, int data);
|
||||||
|
public default void fill(int x, int y, int width, int height, int red, int green, int blue, int alpha) { fill(x, y, width, height, colorspace().color(red, green, blue, alpha)); }
|
||||||
|
|
||||||
|
public int get(int index);
|
||||||
|
public default int get(int x, int y) { return get((y * width()) + x); }
|
||||||
|
|
||||||
|
public int getR(int index);
|
||||||
|
public default int getR(int x, int y) { return getR((y * width()) + x); }
|
||||||
|
public int getG(int index);
|
||||||
|
public default int getG(int x, int y) { return getG((y * width()) + x); }
|
||||||
|
public int getB(int index);
|
||||||
|
public default int getB(int x, int y) { return getB((y * width()) + x); }
|
||||||
|
public int getA(int index);
|
||||||
|
public default int getA(int x, int y) { return getA((y * width()) + x); }
|
||||||
|
|
||||||
|
}
|
||||||
+30
-12
@@ -1,22 +1,40 @@
|
|||||||
package speiger.src.coreengine.graphics.api.texture.states;
|
package speiger.src.coreengine.graphics.api.texture.states;
|
||||||
|
|
||||||
public enum TextureFormat {
|
public enum TextureFormat {
|
||||||
R(1),
|
R(1, 0, -1, -1, -1),
|
||||||
RG(2),
|
RG(2, 0, 1, -1, -1),
|
||||||
RGB(3),
|
RGB(3, 0, 1, 2, -1),
|
||||||
RGBA(4),
|
RGBA(4, 0, 1, 2, 3),
|
||||||
DEPTH(1),
|
DEPTH(1, 0, -1, -1, -1),
|
||||||
DEPTH_STENCIL(2),
|
DEPTH_STENCIL(2, 0, 1, -1, -1),
|
||||||
LUMINANCE(1),
|
LUMINANCE(1, 0, 0, 0, 0),
|
||||||
LUMINANCE_ALPHA(2);
|
LUMINANCE_ALPHA(2, 0, -1, -1, 1);
|
||||||
|
|
||||||
int components;
|
int components;
|
||||||
|
int componentMask;
|
||||||
|
int redOffset;
|
||||||
|
int greenOffset;
|
||||||
|
int blueOffset;
|
||||||
|
int alphaOffset;
|
||||||
|
|
||||||
private TextureFormat(int components) {
|
private TextureFormat(int components, int redOffset, int greenOffset, int blueOffset, int alphaOffset) {
|
||||||
this.components = components;
|
this.components = components;
|
||||||
|
this.componentMask = (1 << (components * 8)) - 1;
|
||||||
|
this.redOffset = redOffset;
|
||||||
|
this.greenOffset = greenOffset;
|
||||||
|
this.blueOffset = blueOffset;
|
||||||
|
this.alphaOffset = alphaOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int components() {
|
public int components() { return components; }
|
||||||
return components;
|
public int componentMask() { return componentMask; }
|
||||||
}
|
public int redOffset() { return redOffset; }
|
||||||
|
public int greenOffset() { return greenOffset; }
|
||||||
|
public int blueOffset() { return blueOffset; }
|
||||||
|
public int alphaOffset() { return alphaOffset; }
|
||||||
|
|
||||||
|
public boolean hasRed() { return redOffset != -1; }
|
||||||
|
public boolean hasGreen() { return greenOffset != -1; }
|
||||||
|
public boolean hasBlue() { return blueOffset != -1; }
|
||||||
|
public boolean hasAlpha() { return alphaOffset != -1; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package speiger.src.coreengine.graphics.api.utils;
|
||||||
|
|
||||||
|
public enum AccessType {
|
||||||
|
READ,
|
||||||
|
WRITE,
|
||||||
|
BOTH;
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package speiger.src.coreengine.graphics.api.utils;
|
||||||
|
|
||||||
|
public enum BufferOwner {
|
||||||
|
KEPT,
|
||||||
|
GIVEN;
|
||||||
|
}
|
||||||
+10
-11
@@ -2,7 +2,9 @@ package speiger.src.coreengine.graphics.opengl.buffer;
|
|||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.NonNull;
|
||||||
import org.lwjgl.opengl.GL15;
|
import org.lwjgl.opengl.GL15;
|
||||||
import org.lwjgl.opengl.GL45;
|
import org.lwjgl.opengl.GL45;
|
||||||
import org.lwjgl.system.MemoryUtil;
|
import org.lwjgl.system.MemoryUtil;
|
||||||
@@ -11,6 +13,7 @@ import speiger.src.collections.ints.misc.pairs.IntObjectPair;
|
|||||||
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.utils.BufferOwner;
|
||||||
import speiger.src.coreengine.graphics.opengl.utils.GLUtils;
|
import speiger.src.coreengine.graphics.opengl.utils.GLUtils;
|
||||||
import speiger.src.coreengine.rendering.utils.AllocationTracker;
|
import speiger.src.coreengine.rendering.utils.AllocationTracker;
|
||||||
import speiger.src.coreengine.utils.io.GameLog;
|
import speiger.src.coreengine.utils.io.GameLog;
|
||||||
@@ -65,21 +68,23 @@ public class GLVertexBuffer extends VertexBuffer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GLVertexBuffer set(long pointer, int totalBytes) {
|
public GLVertexBuffer set(long pointer, int totalBytes, BufferOwner ownership) {
|
||||||
this.size = totalBytes;
|
this.size = totalBytes;
|
||||||
GL45.nglNamedBufferData(id, totalBytes, pointer, GLUtils.toGL(usage));
|
GL45.nglNamedBufferData(id, totalBytes, pointer, GLUtils.toGL(usage));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GLVertexBuffer fill(long pointer, int totalBytes, int offset) {
|
public GLVertexBuffer fill(long pointer, int totalBytes, int offset, BufferOwner ownerhip) {
|
||||||
GL45.nglNamedBufferSubData(id, offset, totalBytes, pointer);
|
GL45.nglNamedBufferSubData(id, offset, totalBytes, pointer);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GLVertexBuffer read(long pointer, int totalBytes, int offset) {
|
public GLVertexBuffer read(long pointer, int totalBytes, int offset, @NonNull Runnable completion) {
|
||||||
|
Objects.requireNonNull(completion, "Completion has to be required otherwise you can't see when the data is there");
|
||||||
GL45.nglGetNamedBufferSubData(id, offset, totalBytes, pointer);
|
GL45.nglGetNamedBufferSubData(id, offset, totalBytes, pointer);
|
||||||
|
completion.run();
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,10 +112,7 @@ public class GLVertexBuffer extends VertexBuffer {
|
|||||||
MemoryUtil.memCopy(MemoryUtil.memAddress(buffer), MemoryUtil.memAddress(newBuff), newSize);
|
MemoryUtil.memCopy(MemoryUtil.memAddress(buffer), MemoryUtil.memAddress(newBuff), newSize);
|
||||||
newBuff.position(newSize).flip();
|
newBuff.position(newSize).flip();
|
||||||
GL45.glUnmapNamedBuffer(id);
|
GL45.glUnmapNamedBuffer(id);
|
||||||
|
return set(MemoryUtil.memAddress(newBuff), newSize, BufferOwner.GIVEN);
|
||||||
set(MemoryUtil.memAddress(newBuff), newSize);
|
|
||||||
MemoryUtil.memFree(newBuff);
|
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -122,10 +124,7 @@ public class GLVertexBuffer extends VertexBuffer {
|
|||||||
MemoryUtil.memCopy(MemoryUtil.memAddress(buffer), MemoryUtil.memAddress(newBuff), size);
|
MemoryUtil.memCopy(MemoryUtil.memAddress(buffer), MemoryUtil.memAddress(newBuff), size);
|
||||||
newBuff.position(newSize).flip();
|
newBuff.position(newSize).flip();
|
||||||
GL45.glUnmapNamedBuffer(id);
|
GL45.glUnmapNamedBuffer(id);
|
||||||
|
return set(MemoryUtil.memAddress(newBuff), newSize, BufferOwner.GIVEN);
|
||||||
set(MemoryUtil.memAddress(newBuff), newSize);
|
|
||||||
MemoryUtil.memFree(newBuff);
|
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -1,14 +1,17 @@
|
|||||||
package speiger.src.coreengine.graphics.opengl.core;
|
package speiger.src.coreengine.graphics.opengl.core;
|
||||||
|
|
||||||
|
import java.util.ArrayDeque;
|
||||||
|
import java.util.Deque;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
import org.lwjgl.opengl.GL11;
|
import org.lwjgl.opengl.GL11;
|
||||||
import org.lwjgl.opengl.GL30;
|
import org.lwjgl.opengl.GL30;
|
||||||
|
import org.lwjgl.opengl.GL32;
|
||||||
import org.lwjgl.opengl.GL45;
|
import org.lwjgl.opengl.GL45;
|
||||||
|
|
||||||
import speiger.src.coreengine.assets.api.ID;
|
import speiger.src.coreengine.assets.api.ID;
|
||||||
import speiger.src.coreengine.graphics.api.core.GraphicsCommandBuffer;
|
import speiger.src.coreengine.graphics.api.core.CommandBuffer;
|
||||||
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.shader.states.GraphicsDataType;
|
||||||
import speiger.src.coreengine.graphics.api.target.RenderTarget;
|
import speiger.src.coreengine.graphics.api.target.RenderTarget;
|
||||||
@@ -23,9 +26,9 @@ 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.graphics.opengl.utils.ViewPortStack;
|
import speiger.src.coreengine.graphics.opengl.utils.ViewPortStack;
|
||||||
import speiger.src.coreengine.graphics.opengl.utils.states.ScissorState;
|
import speiger.src.coreengine.graphics.opengl.utils.states.ScissorState;
|
||||||
|
import speiger.src.coreengine.input.window.Window;
|
||||||
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.math.vector.ints.Vec4i;
|
||||||
import speiger.src.coreengine.rendering.input.window.Window;
|
|
||||||
|
|
||||||
public class GLCommandQueue implements GraphicsCommandQueue {
|
public class GLCommandQueue implements GraphicsCommandQueue {
|
||||||
GLGraphicsDevice device;
|
GLGraphicsDevice device;
|
||||||
@@ -33,6 +36,8 @@ public class GLCommandQueue implements GraphicsCommandQueue {
|
|||||||
RenderTarget renderTarget = ScreenTarget.INSTANCE;
|
RenderTarget renderTarget = ScreenTarget.INSTANCE;
|
||||||
ScreenBuffer targetFBO;
|
ScreenBuffer targetFBO;
|
||||||
int tempWriteFBO;
|
int tempWriteFBO;
|
||||||
|
Deque<Frame> frames = new ArrayDeque<>(4);
|
||||||
|
long currentFrame = 0L;
|
||||||
Vec4f clearColor = Vec4f.mutable(0F, 0F, 0F, 1F);
|
Vec4f clearColor = Vec4f.mutable(0F, 0F, 0F, 1F);
|
||||||
double clearDepth = 0D;
|
double clearDepth = 0D;
|
||||||
|
|
||||||
@@ -40,6 +45,8 @@ public class GLCommandQueue implements GraphicsCommandQueue {
|
|||||||
this.device = device;
|
this.device = device;
|
||||||
this.targetFBO = new ScreenBuffer(0, device.window().width(), device.window().height());
|
this.targetFBO = new ScreenBuffer(0, device.window().width(), device.window().height());
|
||||||
tempWriteFBO = GL45.glCreateFramebuffers();
|
tempWriteFBO = GL45.glCreateFramebuffers();
|
||||||
|
frames.add(new Frame(-2));
|
||||||
|
frames.add(new Frame(-1));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -67,10 +74,33 @@ public class GLCommandQueue implements GraphicsCommandQueue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void submitCommands(GraphicsCommandBuffer buffer) {
|
public void submitCommands(CommandBuffer buffer) {
|
||||||
if(buffer instanceof GLRecordingCommandBuffer recorder) {
|
if(buffer instanceof GLRecordingCommandBuffer recorder) {
|
||||||
recorder.execute();
|
recorder.execute();
|
||||||
}
|
}
|
||||||
|
else if(buffer instanceof GLRecordingComputeCommandBuffer recorder) {
|
||||||
|
recorder.execute();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long currentFrame() {
|
||||||
|
return currentFrame;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void submitFrame() {
|
||||||
|
frames.getFirst().fenceIn();
|
||||||
|
currentFrame++;
|
||||||
|
frames.add(new Frame(currentFrame));
|
||||||
|
if(!frames.pop().isCompleted(Long.MAX_VALUE)) {
|
||||||
|
throw new IllegalStateException("Couldn't complete frames");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GLFence createFence() {
|
||||||
|
return new GLFence(frames.getFirst());
|
||||||
}
|
}
|
||||||
|
|
||||||
private Vec4f setClearColor(Vec4f value) {
|
private Vec4f setClearColor(Vec4f value) {
|
||||||
@@ -159,13 +189,13 @@ public class GLCommandQueue implements GraphicsCommandQueue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
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 writeToTexture(Texture target, int level, int sourceX, int sourceY, int targetX, int targetY, int width, int height, TextureFormat format, GraphicsDataType dataType, long source) {
|
||||||
GLStates states = device.states;
|
GLStates states = device.states;
|
||||||
states.unpack_alignment.set(target.settings().internal().components());
|
states.unpack_alignment.set(target.settings().internal().components());
|
||||||
states.unpack_row_length.set(target.width());
|
states.unpack_row_length.set(target.width());
|
||||||
states.unpack_skip_pixel.set(sourceX);
|
states.unpack_skip_pixel.set(sourceX);
|
||||||
states.unpack_skip_rows.set(sourceY);
|
states.unpack_skip_rows.set(sourceY);
|
||||||
GL45.glTextureSubImage2D(((GLTexture)target).id(), 0, targetX, targetY, width, height, GLUtils.toGLExternal(format), GLUtils.toGL(dataType), source);
|
GL45.glTextureSubImage2D(((GLTexture)target).id(), level, targetX, targetY, width, height, GLUtils.toGLExternal(format), GLUtils.toGL(dataType), source);
|
||||||
states.unpack_alignment.setDefault();
|
states.unpack_alignment.setDefault();
|
||||||
states.unpack_row_length.setDefault();
|
states.unpack_row_length.setDefault();
|
||||||
states.unpack_skip_pixel.setDefault();
|
states.unpack_skip_pixel.setDefault();
|
||||||
@@ -182,4 +212,33 @@ public class GLCommandQueue implements GraphicsCommandQueue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private record ScreenBuffer(int fbo, int width, int height) {}
|
private record ScreenBuffer(int fbo, int width, int height) {}
|
||||||
|
public class Frame {
|
||||||
|
final long frame;
|
||||||
|
boolean hasBegun;
|
||||||
|
long fenceId;
|
||||||
|
|
||||||
|
public Frame(long frame) {
|
||||||
|
this.frame = frame;
|
||||||
|
hasBegun = frame < 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void fenceIn() {
|
||||||
|
hasBegun = true;
|
||||||
|
fenceId = GL32.glFenceSync(GL32.GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isCompleted(long timeout) {
|
||||||
|
if(!hasBegun) {
|
||||||
|
if(timeout == 0) return false;
|
||||||
|
throw new IllegalStateException("Can't wait on something you are supposed to submit to");
|
||||||
|
}
|
||||||
|
if(fenceId == 0) return true;
|
||||||
|
int result = GL32.glClientWaitSync(fenceId, GL32.GL_SYNC_FLUSH_COMMANDS_BIT, timeout);
|
||||||
|
if(result == GL32.GL_TIMEOUT_EXPIRED) return false;
|
||||||
|
if(result == GL32.GL_WAIT_FAILED) throw new IllegalStateException("Waiting has failed?");
|
||||||
|
GL32.glDeleteSync(fenceId);
|
||||||
|
fenceId = 0L;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package speiger.src.coreengine.graphics.opengl.core;
|
||||||
|
|
||||||
|
import speiger.src.coreengine.graphics.api.core.GraphicsFence;
|
||||||
|
import speiger.src.coreengine.graphics.opengl.core.GLCommandQueue.Frame;
|
||||||
|
|
||||||
|
public class GLFence implements GraphicsFence {
|
||||||
|
boolean removed = false;
|
||||||
|
Frame frame;
|
||||||
|
|
||||||
|
public GLFence(Frame frame) {
|
||||||
|
this.frame = frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRemoved() {
|
||||||
|
return removed;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove() {
|
||||||
|
removed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean awaitCompletion(long timeout) {
|
||||||
|
return removed || frame.isCompleted(timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -5,7 +5,7 @@ import org.lwjgl.opengl.GL;
|
|||||||
|
|
||||||
import speiger.src.coreengine.assets.manager.AssetManager;
|
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.input.window.Window;
|
||||||
|
|
||||||
public class GLGraphics implements Graphics {
|
public class GLGraphics implements Graphics {
|
||||||
AssetManager manager;
|
AssetManager manager;
|
||||||
|
|||||||
+82
-27
@@ -8,6 +8,7 @@ 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 java.util.function.IntPredicate;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
import org.lwjgl.opengl.GL11;
|
import org.lwjgl.opengl.GL11;
|
||||||
import org.lwjgl.opengl.GL12;
|
import org.lwjgl.opengl.GL12;
|
||||||
@@ -39,34 +40,38 @@ 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;
|
||||||
|
import speiger.src.coreengine.graphics.api.compute.ComputePipeline;
|
||||||
import speiger.src.coreengine.graphics.api.core.GraphicsCommandBuffer;
|
import speiger.src.coreengine.graphics.api.core.GraphicsCommandBuffer;
|
||||||
import speiger.src.coreengine.graphics.api.core.GraphicsDevice;
|
import speiger.src.coreengine.graphics.api.core.GraphicsDevice;
|
||||||
|
import speiger.src.coreengine.graphics.api.core.TimeQueryPool;
|
||||||
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.VertexBinding;
|
||||||
|
import speiger.src.coreengine.graphics.api.shader.states.Bindings.BufferBinding;
|
||||||
|
import speiger.src.coreengine.graphics.api.shader.states.Bindings.BufferMode;
|
||||||
import speiger.src.coreengine.graphics.api.shader.states.Bindings.TextureBinding;
|
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.Bindings.TextureMode;
|
||||||
import speiger.src.coreengine.graphics.api.shader.states.ShaderType;
|
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.texture.states.TextureType;
|
||||||
import speiger.src.coreengine.graphics.api.utils.ExecutionType;
|
import speiger.src.coreengine.graphics.api.utils.ExecutionType;
|
||||||
|
import speiger.src.coreengine.graphics.api.vertex.VertexLayout.Element;
|
||||||
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.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.BufferObject;
|
||||||
import speiger.src.coreengine.graphics.opengl.shader.ShaderInstance.ShaderStorage;
|
import speiger.src.coreengine.graphics.opengl.shader.ShaderInstance.ShaderStorage;
|
||||||
import speiger.src.coreengine.graphics.opengl.shader.ShaderInstance.UniformObject;
|
import speiger.src.coreengine.graphics.opengl.shader.ShaderInstance.TextureObject;
|
||||||
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.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.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();
|
static final Map<ID, String> IMPORTS = Object2ObjectMap.builder().<ID, String>map().synchronize();
|
||||||
@@ -93,7 +98,7 @@ public class GLGraphicsDevice implements GraphicsDevice {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GLSurface createSurface() {
|
public GLSurface createSurface() {
|
||||||
return new GLSurface(owner);
|
return new GLSurface(owner, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -161,10 +166,18 @@ public class GLGraphicsDevice implements GraphicsDevice {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void preloadPipelines(List<ShaderPipeline> pipelines) {
|
public TimeQueryPool createQueryPool(int size) {
|
||||||
|
return new GLTimeQueryPool(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void preloadPipelines(List<ShaderPipeline> graphics, List<ComputePipeline> compute) {
|
||||||
try(IAssetProvider provider = manager.get()) {
|
try(IAssetProvider provider = manager.get()) {
|
||||||
for(ShaderPipeline pipeline : pipelines) {
|
for(ShaderPipeline pipeline : graphics) {
|
||||||
computeShaderProgram(pipeline, provider);
|
programCache.put(pipeline.id(), computeShaderProgram(pipeline, provider));
|
||||||
|
}
|
||||||
|
for(ComputePipeline pipeline : compute) {
|
||||||
|
programCache.put(pipeline.id(), computeShaderProgram(pipeline, provider));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch(Exception e) {
|
catch(Exception e) {
|
||||||
@@ -186,13 +199,52 @@ public class GLGraphicsDevice implements GraphicsDevice {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ShaderInstance getShader(ComputePipeline pipeline) {
|
||||||
|
return programCache.supplyIfAbsent(pipeline.id(), () -> computeShaderProgram(pipeline));
|
||||||
|
}
|
||||||
|
|
||||||
|
private ShaderInstance computeShaderProgram(ComputePipeline pipeline) {
|
||||||
|
try(IAssetProvider provider = manager.get()) {
|
||||||
|
return computeShaderProgram(pipeline, provider);
|
||||||
|
}
|
||||||
|
catch(Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private ShaderInstance computeShaderProgram(ComputePipeline pipeline, IAssetProvider provider) {
|
||||||
|
MultiAsset assets = MultiAsset.combineNonNull(provider, pipeline.shader());
|
||||||
|
if(assets == null) {
|
||||||
|
//TODO log stuff
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
int program = loadOrCreateProgram(pipeline.id(), assets::crc, T -> {
|
||||||
|
int id = shaderCache.computeIfAbsent(pipeline.shader(), E -> computeShader(E, assets.get(0), ShaderType.COMPUTE, provider));
|
||||||
|
if(id == -1) {
|
||||||
|
System.out.println("Couldn't create a compute shader Id["+pipeline.shader()+"]");
|
||||||
|
//TODO implement logging.
|
||||||
|
//Failed to compile shader
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
GL20.glAttachShader(T, id);
|
||||||
|
GL20.glLinkProgram(T);
|
||||||
|
GL20.glDetachShader(T, id);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
if(program == 0) return null;
|
||||||
|
return generateShaderInstance(program, pipeline::textures, pipeline::uniforms);
|
||||||
|
}
|
||||||
|
|
||||||
private ShaderInstance computeShaderProgram(ShaderPipeline pipeline, IAssetProvider provider) {
|
private ShaderInstance computeShaderProgram(ShaderPipeline pipeline, IAssetProvider provider) {
|
||||||
MultiAsset assets = MultiAsset.combineNonNull(provider, pipeline.shaders().values().toArray(ID[]::new));
|
MultiAsset assets = MultiAsset.combineNonNull(provider, pipeline.shaders().values().toArray(ID[]::new));
|
||||||
if(assets == null) {
|
if(assets == null) {
|
||||||
//TODO log stuff
|
//TODO log stuff
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
int program = loadOrCreateProgram(pipeline, assets, T -> {
|
int program = loadOrCreateProgram(pipeline.id(), assets::crc, T -> {
|
||||||
Map<ID, IAsset> map = Object2ObjectMap.builder().map();
|
Map<ID, IAsset> map = Object2ObjectMap.builder().map();
|
||||||
assets.forEach(map, (K, V) -> K.put(V.location(), V));
|
assets.forEach(map, (K, V) -> K.put(V.location(), V));
|
||||||
IntList shaders = new IntArrayList();
|
IntList shaders = new IntArrayList();
|
||||||
@@ -209,8 +261,8 @@ public class GLGraphicsDevice implements GraphicsDevice {
|
|||||||
GL20.glAttachShader(T, id);
|
GL20.glAttachShader(T, id);
|
||||||
shaders.add(id);
|
shaders.add(id);
|
||||||
}
|
}
|
||||||
for(BufferAttribute attribute : ObjectIterables.flatMap(pipeline.attributes(), VertexBinding::attributes)) {
|
for(Element element : ObjectIterables.flatMap(pipeline.attributes(), VertexBinding::elements)) {
|
||||||
GL20.glBindAttribLocation(T, attribute.index(), attribute.name());
|
GL20.glBindAttribLocation(T, element.index(), element.name());
|
||||||
}
|
}
|
||||||
GL20.glLinkProgram(T);
|
GL20.glLinkProgram(T);
|
||||||
for(int i = 0,m=shaders.size();i<m;i++) {
|
for(int i = 0,m=shaders.size();i<m;i++) {
|
||||||
@@ -219,31 +271,34 @@ public class GLGraphicsDevice implements GraphicsDevice {
|
|||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
if(program == 0) return null;
|
if(program == 0) return null;
|
||||||
ShaderStorage<UniformObject> uniforms = new ShaderStorage<>();
|
return generateShaderInstance(program, pipeline::textures, pipeline::uniforms);
|
||||||
ShaderStorage<SamplerObject> samplers = new ShaderStorage<>();
|
}
|
||||||
for(TextureBinding binding : pipeline.textures()) {
|
|
||||||
|
private ShaderInstance generateShaderInstance(int program, Supplier<List<TextureBinding>> textureProvider, Supplier<List<BufferBinding>> uniformProvider) {
|
||||||
|
ShaderStorage<BufferObject> uniforms = new ShaderStorage<>();
|
||||||
|
ShaderStorage<BufferObject> storages = new ShaderStorage<>();
|
||||||
|
ShaderStorage<TextureObject> samplers = new ShaderStorage<>();
|
||||||
|
ShaderStorage<TextureObject> buffers = new ShaderStorage<>();
|
||||||
|
for(TextureBinding binding : textureProvider.get()) {
|
||||||
int location = GL20.glGetUniformLocation(program, binding.name());
|
int location = GL20.glGetUniformLocation(program, binding.name());
|
||||||
if(location == -1) {
|
if(location == -1) {
|
||||||
System.out.println("Couldn't find Location ["+binding.name()+"] in Program ["+program+"]");
|
System.out.println("Couldn't find Location ["+binding.name()+"] in Program ["+program+"]");
|
||||||
//TODO Warn that location wasn't found
|
//TODO Warn that location wasn't found
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
samplers.add(binding.name(), binding.slot(), new SamplerObject(binding.slot()));
|
(binding.mode() == TextureMode.SAMPLER ? samplers : buffers).add(binding.name(), binding.slot(), new TextureObject(binding.slot()));
|
||||||
}
|
}
|
||||||
for(UniformBinding binding : pipeline.uniforms()) {
|
for(BufferBinding binding : uniformProvider.get()) {
|
||||||
int location = GL31.glGetUniformBlockIndex(program, binding.name());
|
int location = GL31.glGetUniformBlockIndex(program, binding.name());
|
||||||
if(location == GL31.GL_INVALID_INDEX) {
|
if(location == GL31.GL_INVALID_INDEX) {
|
||||||
System.out.println("Couldn't find Location ["+binding.name()+"] in Program ["+program+"]");
|
System.out.println("Couldn't find Location ["+binding.name()+"] in Program ["+program+"]");
|
||||||
|
|
||||||
//TODO Warn that location wasn't found
|
//TODO Warn that location wasn't found
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
GL31.glUniformBlockBinding(program, location, binding.slot());
|
GL31.glUniformBlockBinding(program, location, binding.slot());
|
||||||
uniforms.add(binding.name(), binding.slot(), new UniformObject(binding.slot()));
|
(binding.mode() == BufferMode.UNIFORM ? uniforms : storages).add(binding.name(), binding.slot(), new BufferObject(binding.slot()));
|
||||||
}
|
}
|
||||||
ShaderInstance instance = new ShaderInstance(program, uniforms, samplers);
|
return new ShaderInstance(program, uniforms, storages, samplers, buffers);
|
||||||
states.shaders.register(instance);
|
|
||||||
return instance;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private int computeShader(ID id, IAsset asset, ShaderType type, IAssetProvider provider) {
|
private int computeShader(ID id, IAsset asset, ShaderType type, IAssetProvider provider) {
|
||||||
@@ -272,12 +327,12 @@ public class GLGraphicsDevice implements GraphicsDevice {
|
|||||||
return shader;
|
return shader;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int loadOrCreateProgram(ShaderPipeline pipeline, MultiAsset assets, IntPredicate callback) {
|
private int loadOrCreateProgram(ID pipeline, Supplier<String> crc, IntPredicate callback) {
|
||||||
int id = GL20.glCreateProgram();
|
int id = GL20.glCreateProgram();
|
||||||
boolean fail = true;
|
boolean fail = true;
|
||||||
if((fail = !loadFromCache(id, pipeline, assets) && callback.test(id))) {
|
if((fail = !loadFromCache(id, pipeline, crc) && callback.test(id))) {
|
||||||
if(GL20.glGetProgrami(id, GL20.GL_LINK_STATUS) == GL11.GL_TRUE) {
|
if(GL20.glGetProgrami(id, GL20.GL_LINK_STATUS) == GL11.GL_TRUE) {
|
||||||
cache.store(pipeline.id(), assets.crc(), getProgramBytes(id));
|
cache.store(pipeline, crc.get(), getProgramBytes(id));
|
||||||
fail = false;
|
fail = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -303,8 +358,8 @@ public class GLGraphicsDevice implements GraphicsDevice {
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean loadFromCache(int programId, ShaderPipeline pipeline, MultiAsset asset) {
|
private boolean loadFromCache(int programId, ID pipeline, Supplier<String> crc) {
|
||||||
ByteBuffer data = cache.get(pipeline.id(), asset::crc);
|
ByteBuffer data = cache.get(pipeline, crc);
|
||||||
if(data == null) return false;
|
if(data == null) return false;
|
||||||
GL41.glProgramBinary(programId, data.getInt(), data);
|
GL41.glProgramBinary(programId, data.getInt(), data);
|
||||||
MemoryUtil.memFree(data);
|
MemoryUtil.memFree(data);
|
||||||
|
|||||||
+45
-8
@@ -5,10 +5,11 @@ import java.util.Objects;
|
|||||||
import org.lwjgl.opengl.GL11;
|
import org.lwjgl.opengl.GL11;
|
||||||
import org.lwjgl.opengl.GL30;
|
import org.lwjgl.opengl.GL30;
|
||||||
import org.lwjgl.opengl.GL31;
|
import org.lwjgl.opengl.GL31;
|
||||||
import org.lwjgl.opengl.GL33;
|
import org.lwjgl.opengl.GL43;
|
||||||
import org.lwjgl.opengl.GL45;
|
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.buffer.states.BufferType;
|
||||||
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;
|
||||||
@@ -17,6 +18,7 @@ import speiger.src.coreengine.graphics.api.shader.DepthTarget;
|
|||||||
import speiger.src.coreengine.graphics.api.shader.RasterizerState;
|
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.AccessType;
|
||||||
import speiger.src.coreengine.graphics.api.utils.ScissorsManager;
|
import speiger.src.coreengine.graphics.api.utils.ScissorsManager;
|
||||||
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;
|
||||||
@@ -110,8 +112,7 @@ public class GLImmidateCommandBuffer implements GraphicsCommandBuffer {
|
|||||||
ensureDrawing();
|
ensureDrawing();
|
||||||
if(this.mesh == mesh) return this;
|
if(this.mesh == mesh) return this;
|
||||||
this.mesh = (GLMesh)mesh;
|
this.mesh = (GLMesh)mesh;
|
||||||
this.mesh.bind();
|
GL30.glBindVertexArray(this.mesh.id());
|
||||||
// GL30.glBindVertexArray(this.mesh.id());
|
|
||||||
recorded++;
|
recorded++;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -123,11 +124,21 @@ public class GLImmidateCommandBuffer implements GraphicsCommandBuffer {
|
|||||||
ensureDrawing();
|
ensureDrawing();
|
||||||
Objects.requireNonNull(shader, "No Pipeline found");
|
Objects.requireNonNull(shader, "No Pipeline found");
|
||||||
Objects.requireNonNull(shader.samplers().byIndex(binding), "Texture["+binding+"] binding not found");
|
Objects.requireNonNull(shader.samplers().byIndex(binding), "Texture["+binding+"] binding not found");
|
||||||
// GLStates states = device.states;
|
GLStates states = device.states;
|
||||||
GL45.glBindTextureUnit(binding, ((GLTexture)texture).id());
|
states.textures.bind(binding, ((GLTexture)texture).id());
|
||||||
GL33.glBindSampler(binding, ((GLSampler)sampler).id());
|
states.samplers.bind(binding, ((GLSampler)sampler).id());
|
||||||
// states.textures.bind(binding, ((GLTexture)texture).id());
|
recorded++;
|
||||||
// states.samplers.bind(binding, ((GLSampler)sampler).id());
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GraphicsCommandBuffer texture(int binding, Texture texture, AccessType access) {
|
||||||
|
Objects.requireNonNull(texture);
|
||||||
|
Objects.requireNonNull(access);
|
||||||
|
ensureDrawing();
|
||||||
|
Objects.requireNonNull(shader, "No Pipeline found");
|
||||||
|
Objects.requireNonNull(shader.buffers().byIndex(binding), "Texture["+binding+"] binding not found");
|
||||||
|
GL45.glBindImageTexture(0, binding, ((GLTexture)texture).id(), false, 0, GLUtils.toGL(access), GLUtils.toGLInternal(texture.settings().internal()));
|
||||||
recorded++;
|
recorded++;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -135,6 +146,7 @@ public class GLImmidateCommandBuffer implements GraphicsCommandBuffer {
|
|||||||
@Override
|
@Override
|
||||||
public GraphicsCommandBuffer uniform(int binding, VertexBuffer buffer) {
|
public GraphicsCommandBuffer uniform(int binding, VertexBuffer buffer) {
|
||||||
Objects.requireNonNull(buffer);
|
Objects.requireNonNull(buffer);
|
||||||
|
if(buffer.type() != BufferType.UNIFORM_BUFFER) throw new IllegalStateException("Buffer isn't a Uniform Buffer");
|
||||||
ensureDrawing();
|
ensureDrawing();
|
||||||
Objects.requireNonNull(shader, "No Pipeline found");
|
Objects.requireNonNull(shader, "No Pipeline found");
|
||||||
Objects.requireNonNull(shader.uniforms().byIndex(binding), "Uniform for binding ["+binding+"] doesn't exist");
|
Objects.requireNonNull(shader.uniforms().byIndex(binding), "Uniform for binding ["+binding+"] doesn't exist");
|
||||||
@@ -146,6 +158,7 @@ public class GLImmidateCommandBuffer implements GraphicsCommandBuffer {
|
|||||||
@Override
|
@Override
|
||||||
public GraphicsCommandBuffer uniform(int binding, VertexBuffer buffer, long offset, long size) {
|
public GraphicsCommandBuffer uniform(int binding, VertexBuffer buffer, long offset, long size) {
|
||||||
Objects.requireNonNull(buffer);
|
Objects.requireNonNull(buffer);
|
||||||
|
if(buffer.type() != BufferType.UNIFORM_BUFFER) throw new IllegalStateException("Buffer isn't a Uniform Buffer");
|
||||||
ensureDrawing();
|
ensureDrawing();
|
||||||
Objects.requireNonNull(shader, "No Pipeline found");
|
Objects.requireNonNull(shader, "No Pipeline found");
|
||||||
Objects.requireNonNull(shader.uniforms().byIndex(binding), "Uniform for binding ["+binding+"] doesn't exist");
|
Objects.requireNonNull(shader.uniforms().byIndex(binding), "Uniform for binding ["+binding+"] doesn't exist");
|
||||||
@@ -154,6 +167,30 @@ public class GLImmidateCommandBuffer implements GraphicsCommandBuffer {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GraphicsCommandBuffer storage(int binding, VertexBuffer buffer) {
|
||||||
|
Objects.requireNonNull(buffer);
|
||||||
|
if(buffer.type() != BufferType.SHADER_STORAGE_BUFFER) throw new IllegalStateException("Buffer isn't a Shader Storage Buffer");
|
||||||
|
ensureDrawing();
|
||||||
|
Objects.requireNonNull(shader, "No Pipeline found");
|
||||||
|
Objects.requireNonNull(shader.storages().byIndex(binding), "Shader Storage for binding ["+binding+"] doesn't exist");
|
||||||
|
GL30.glBindBufferBase(GL43.GL_SHADER_STORAGE_BUFFER, binding, ((GLVertexBuffer)buffer).id());
|
||||||
|
recorded++;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GraphicsCommandBuffer storage(int binding, VertexBuffer buffer, long offset, long size) {
|
||||||
|
Objects.requireNonNull(buffer);
|
||||||
|
if(buffer.type() != BufferType.SHADER_STORAGE_BUFFER) throw new IllegalStateException("Buffer isn't a Shader Storage Buffer");
|
||||||
|
ensureDrawing();
|
||||||
|
Objects.requireNonNull(shader, "No Pipeline found");
|
||||||
|
Objects.requireNonNull(shader.storages().byIndex(binding), "Shader Storage for binding ["+binding+"] doesn't exist");
|
||||||
|
GL30.glBindBufferRange(GL43.GL_SHADER_STORAGE_BUFFER, binding, ((GLVertexBuffer)buffer).id(), offset, size);
|
||||||
|
recorded++;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GraphicsCommandBuffer drawArrays(int offset, int count) {
|
public GraphicsCommandBuffer drawArrays(int offset, int count) {
|
||||||
ensureDrawing();
|
ensureDrawing();
|
||||||
|
|||||||
+192
@@ -0,0 +1,192 @@
|
|||||||
|
package speiger.src.coreengine.graphics.opengl.core;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import org.lwjgl.opengl.GL30;
|
||||||
|
import org.lwjgl.opengl.GL31;
|
||||||
|
import org.lwjgl.opengl.GL42;
|
||||||
|
import org.lwjgl.opengl.GL43;
|
||||||
|
import org.lwjgl.opengl.GL45;
|
||||||
|
|
||||||
|
import speiger.src.coreengine.graphics.api.buffer.VertexBuffer;
|
||||||
|
import speiger.src.coreengine.graphics.api.buffer.states.BufferType;
|
||||||
|
import speiger.src.coreengine.graphics.api.compute.BarrierType;
|
||||||
|
import speiger.src.coreengine.graphics.api.compute.ComputePipeline;
|
||||||
|
import speiger.src.coreengine.graphics.api.core.ComputeCommandBuffer;
|
||||||
|
import speiger.src.coreengine.graphics.api.sampler.Sampler;
|
||||||
|
import speiger.src.coreengine.graphics.api.texture.Texture;
|
||||||
|
import speiger.src.coreengine.graphics.api.utils.AccessType;
|
||||||
|
import speiger.src.coreengine.graphics.opengl.buffer.GLVertexBuffer;
|
||||||
|
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;
|
||||||
|
|
||||||
|
public class GLImmidateComputeCommandBuffer implements ComputeCommandBuffer {
|
||||||
|
GLGraphicsDevice device;
|
||||||
|
int recorded = 0;
|
||||||
|
boolean recording = false;
|
||||||
|
boolean removed = false;
|
||||||
|
|
||||||
|
ComputePipeline pipeline;
|
||||||
|
ShaderInstance shader;
|
||||||
|
|
||||||
|
public GLImmidateComputeCommandBuffer(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() {
|
||||||
|
clear();
|
||||||
|
device = null;
|
||||||
|
removed = true;
|
||||||
|
recorded = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ComputeCommandBuffer begin() {
|
||||||
|
if(removed) throw new IllegalStateException("CommandBuffer is removed");
|
||||||
|
if(recording) throw new IllegalStateException("CommandBuffer already recording");
|
||||||
|
recording = true;
|
||||||
|
recorded = 0;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ComputeCommandBuffer pipeline(ComputePipeline pipeline) {
|
||||||
|
Objects.requireNonNull(pipeline);
|
||||||
|
ensureDrawing();
|
||||||
|
if(this.pipeline == pipeline) return this;
|
||||||
|
this.pipeline = pipeline;
|
||||||
|
shader = device.getShader(pipeline);
|
||||||
|
if(shader == null) {
|
||||||
|
//TODO implement logging
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
GLStates states = device.states;
|
||||||
|
states.shaders.bind(shader);
|
||||||
|
recorded++;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ComputeCommandBuffer 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;
|
||||||
|
states.textures.bind(binding, ((GLTexture)texture).id());
|
||||||
|
states.samplers.bind(binding, ((GLSampler)sampler).id());
|
||||||
|
recorded++;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ComputeCommandBuffer texture(int binding, Texture texture, AccessType access) {
|
||||||
|
Objects.requireNonNull(texture);
|
||||||
|
Objects.requireNonNull(access);
|
||||||
|
ensureDrawing();
|
||||||
|
Objects.requireNonNull(shader, "No Pipeline found");
|
||||||
|
Objects.requireNonNull(shader.buffers().byIndex(binding), "Texture["+binding+"] binding not found");
|
||||||
|
GL45.glBindImageTexture(0, binding, ((GLTexture)texture).id(), false, 0, GLUtils.toGL(access), GLUtils.toGLInternal(texture.settings().internal()));
|
||||||
|
recorded++;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ComputeCommandBuffer uniform(int binding, VertexBuffer buffer) {
|
||||||
|
Objects.requireNonNull(buffer);
|
||||||
|
if(buffer.type() != BufferType.UNIFORM_BUFFER) throw new IllegalStateException("Buffer isn't a Uniform 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 ComputeCommandBuffer uniform(int binding, VertexBuffer buffer, long offset, long size) {
|
||||||
|
Objects.requireNonNull(buffer);
|
||||||
|
if(buffer.type() != BufferType.UNIFORM_BUFFER) throw new IllegalStateException("Buffer isn't a Uniform Buffer");
|
||||||
|
ensureDrawing();
|
||||||
|
Objects.requireNonNull(shader, "No Pipeline found");
|
||||||
|
Objects.requireNonNull(shader.uniforms().byIndex(binding), "Uniform for binding ["+binding+"] doesn't exist");
|
||||||
|
GL30.glBindBufferRange(GL31.GL_UNIFORM_BUFFER, binding, ((GLVertexBuffer)buffer).id(), offset, size);
|
||||||
|
recorded++;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ComputeCommandBuffer storage(int binding, VertexBuffer buffer) {
|
||||||
|
Objects.requireNonNull(buffer);
|
||||||
|
if(buffer.type() != BufferType.SHADER_STORAGE_BUFFER) throw new IllegalStateException("Buffer isn't a Shader Storage Buffer");
|
||||||
|
ensureDrawing();
|
||||||
|
Objects.requireNonNull(shader, "No Pipeline found");
|
||||||
|
Objects.requireNonNull(shader.storages().byIndex(binding), "Shader Storage for binding ["+binding+"] doesn't exist");
|
||||||
|
GL30.glBindBufferBase(GL43.GL_SHADER_STORAGE_BUFFER, binding, ((GLVertexBuffer)buffer).id());
|
||||||
|
recorded++;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ComputeCommandBuffer storage(int binding, VertexBuffer buffer, long offset, long size) {
|
||||||
|
Objects.requireNonNull(buffer);
|
||||||
|
if(buffer.type() != BufferType.SHADER_STORAGE_BUFFER) throw new IllegalStateException("Buffer isn't a Shader Storage Buffer");
|
||||||
|
ensureDrawing();
|
||||||
|
Objects.requireNonNull(shader, "No Pipeline found");
|
||||||
|
Objects.requireNonNull(shader.storages().byIndex(binding), "Shader Storage for binding ["+binding+"] doesn't exist");
|
||||||
|
GL30.glBindBufferRange(GL43.GL_SHADER_STORAGE_BUFFER, binding, ((GLVertexBuffer)buffer).id(), offset, size);
|
||||||
|
recorded++;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ComputeCommandBuffer dispatch(int numGroupsX, int numGroupsY, int numGroupsZ) {
|
||||||
|
Objects.requireNonNull(shader);
|
||||||
|
if(numGroupsX < 1) throw new IllegalArgumentException("X has to be 1 or more");
|
||||||
|
if(numGroupsY < 1) throw new IllegalArgumentException("Y has to be 1 or more");
|
||||||
|
if(numGroupsZ < 1) throw new IllegalArgumentException("Z has to be 1 or more");
|
||||||
|
GL45.glDispatchCompute(numGroupsX, numGroupsY, numGroupsZ);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ComputeCommandBuffer barrier(BarrierType... types) {
|
||||||
|
int result = 0;
|
||||||
|
for(BarrierType type : types) {
|
||||||
|
result |= GLUtils.toGL(type);
|
||||||
|
}
|
||||||
|
GL42.glMemoryBarrier(result);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ComputeCommandBuffer end() {
|
||||||
|
clear();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void clear() {
|
||||||
|
recording = false;
|
||||||
|
pipeline = null;
|
||||||
|
shader = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int commandCount() {
|
||||||
|
return recorded;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+55
-5
@@ -6,9 +6,12 @@ import java.util.Objects;
|
|||||||
import org.lwjgl.opengl.GL11;
|
import org.lwjgl.opengl.GL11;
|
||||||
import org.lwjgl.opengl.GL30;
|
import org.lwjgl.opengl.GL30;
|
||||||
import org.lwjgl.opengl.GL31;
|
import org.lwjgl.opengl.GL31;
|
||||||
|
import org.lwjgl.opengl.GL43;
|
||||||
|
import org.lwjgl.opengl.GL45;
|
||||||
|
|
||||||
import speiger.src.collections.objects.lists.ObjectArrayList;
|
import speiger.src.collections.objects.lists.ObjectArrayList;
|
||||||
import speiger.src.coreengine.graphics.api.buffer.VertexBuffer;
|
import speiger.src.coreengine.graphics.api.buffer.VertexBuffer;
|
||||||
|
import speiger.src.coreengine.graphics.api.buffer.states.BufferType;
|
||||||
import speiger.src.coreengine.graphics.api.buffer.states.IndeciesType;
|
import speiger.src.coreengine.graphics.api.buffer.states.IndeciesType;
|
||||||
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;
|
||||||
@@ -19,6 +22,7 @@ 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.shader.states.DrawMode;
|
import speiger.src.coreengine.graphics.api.shader.states.DrawMode;
|
||||||
import speiger.src.coreengine.graphics.api.texture.Texture;
|
import speiger.src.coreengine.graphics.api.texture.Texture;
|
||||||
|
import speiger.src.coreengine.graphics.api.utils.AccessType;
|
||||||
import speiger.src.coreengine.graphics.api.utils.ScissorsManager;
|
import speiger.src.coreengine.graphics.api.utils.ScissorsManager;
|
||||||
import speiger.src.coreengine.graphics.api.utils.ScissorsManager.NoOp;
|
import speiger.src.coreengine.graphics.api.utils.ScissorsManager.NoOp;
|
||||||
import speiger.src.coreengine.graphics.opengl.buffer.GLVertexBuffer;
|
import speiger.src.coreengine.graphics.opengl.buffer.GLVertexBuffer;
|
||||||
@@ -87,6 +91,10 @@ public class GLRecordingCommandBuffer implements GraphicsCommandBuffer {
|
|||||||
if(this.pipeline == pipeline) return this;
|
if(this.pipeline == pipeline) return this;
|
||||||
this.pipeline = pipeline;
|
this.pipeline = pipeline;
|
||||||
shader = device.getShader(pipeline);
|
shader = device.getShader(pipeline);
|
||||||
|
if(shader == null) {
|
||||||
|
//TODO implement logging
|
||||||
|
return this;
|
||||||
|
}
|
||||||
tasks.add(new SetPipeline(pipeline, shader, device.states));
|
tasks.add(new SetPipeline(pipeline, shader, device.states));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -112,23 +120,58 @@ public class GLRecordingCommandBuffer implements GraphicsCommandBuffer {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GraphicsCommandBuffer texture(int binding, Texture texture, AccessType access) {
|
||||||
|
Objects.requireNonNull(texture);
|
||||||
|
Objects.requireNonNull(access);
|
||||||
|
ensureDrawing();
|
||||||
|
Objects.requireNonNull(shader, "No Pipeline found");
|
||||||
|
Objects.requireNonNull(shader.buffers().byIndex(binding), "Texture["+binding+"] binding not found");
|
||||||
|
tasks.add(new SetImageStorage(binding, (GLTexture)texture, access));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GraphicsCommandBuffer uniform(int binding, VertexBuffer buffer) {
|
public GraphicsCommandBuffer uniform(int binding, VertexBuffer buffer) {
|
||||||
Objects.requireNonNull(buffer);
|
Objects.requireNonNull(buffer);
|
||||||
|
if(buffer.type() != BufferType.UNIFORM_BUFFER) throw new IllegalStateException("Buffer isn't a Uniform Buffer");
|
||||||
ensureDrawing();
|
ensureDrawing();
|
||||||
Objects.requireNonNull(shader, "No Pipeline found");
|
Objects.requireNonNull(shader, "No Pipeline found");
|
||||||
Objects.requireNonNull(shader.uniforms().byIndex(binding), "Uniform for binding ["+binding+"] doesn't exist");
|
Objects.requireNonNull(shader.uniforms().byIndex(binding), "Uniform for binding ["+binding+"] doesn't exist");
|
||||||
tasks.add(new SetUniform(binding, (GLVertexBuffer)buffer, -1, -1));
|
tasks.add(new SetUniform(binding, (GLVertexBuffer)buffer, -1, -1, false));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GraphicsCommandBuffer uniform(int binding, VertexBuffer buffer, long offset, long size) {
|
public GraphicsCommandBuffer uniform(int binding, VertexBuffer buffer, long offset, long size) {
|
||||||
Objects.requireNonNull(buffer);
|
Objects.requireNonNull(buffer);
|
||||||
|
if(buffer.type() != BufferType.UNIFORM_BUFFER) throw new IllegalStateException("Buffer isn't a Uniform Buffer");
|
||||||
ensureDrawing();
|
ensureDrawing();
|
||||||
Objects.requireNonNull(shader, "No Pipeline found");
|
Objects.requireNonNull(shader, "No Pipeline found");
|
||||||
Objects.requireNonNull(shader.uniforms().byIndex(binding), "Uniform for binding ["+binding+"] doesn't exist");
|
Objects.requireNonNull(shader.uniforms().byIndex(binding), "Uniform for binding ["+binding+"] doesn't exist");
|
||||||
tasks.add(new SetUniform(binding, (GLVertexBuffer)buffer, offset, size));
|
tasks.add(new SetUniform(binding, (GLVertexBuffer)buffer, offset, size, false));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GraphicsCommandBuffer storage(int binding, VertexBuffer buffer) {
|
||||||
|
Objects.requireNonNull(buffer);
|
||||||
|
if(buffer.type() != BufferType.SHADER_STORAGE_BUFFER) throw new IllegalStateException("Buffer isn't a Shader Storage Buffer");
|
||||||
|
ensureDrawing();
|
||||||
|
Objects.requireNonNull(shader, "No Pipeline found");
|
||||||
|
Objects.requireNonNull(shader.storages().byIndex(binding), "Shader Storage for binding ["+binding+"] doesn't exist");
|
||||||
|
tasks.add(new SetUniform(binding, (GLVertexBuffer)buffer, -1, -1, true));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GraphicsCommandBuffer storage(int binding, VertexBuffer buffer, long offset, long size) {
|
||||||
|
Objects.requireNonNull(buffer);
|
||||||
|
if(buffer.type() != BufferType.SHADER_STORAGE_BUFFER) throw new IllegalStateException("Buffer isn't a Shader Storage Buffer");
|
||||||
|
ensureDrawing();
|
||||||
|
Objects.requireNonNull(shader, "No Pipeline found");
|
||||||
|
Objects.requireNonNull(shader.storages().byIndex(binding), "Shader Storage for binding ["+binding+"] doesn't exist");
|
||||||
|
tasks.add(new SetUniform(binding, (GLVertexBuffer)buffer, offset, size, true));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -229,11 +272,18 @@ public class GLRecordingCommandBuffer implements GraphicsCommandBuffer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public record SetUniform(int binding, GLVertexBuffer buffer, long offset, long size) implements Runnable {
|
public record SetImageStorage(int binding, GLTexture texture, AccessType type) implements Runnable {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
if(size < 0 || offset < 0) GL30.glBindBufferBase(GL31.GL_UNIFORM_BUFFER, binding, buffer.id());
|
GL45.glBindImageTexture(0, binding, texture.id(), false, 0, GLUtils.toGL(type), GLUtils.toGLInternal(texture.settings().internal()));
|
||||||
else GL30.glBindBufferRange(GL31.GL_UNIFORM_BUFFER, binding, buffer.id(), offset, size);
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public record SetUniform(int binding, GLVertexBuffer buffer, long offset, long size, boolean shader) implements Runnable {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if(size < 0 || offset < 0) GL30.glBindBufferBase(shader ? GL43.GL_SHADER_STORAGE_BUFFER : GL31.GL_UNIFORM_BUFFER, binding, buffer.id());
|
||||||
|
else GL30.glBindBufferRange(shader ? GL43.GL_SHADER_STORAGE_BUFFER : GL31.GL_UNIFORM_BUFFER, binding, buffer.id(), offset, size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+227
@@ -0,0 +1,227 @@
|
|||||||
|
package speiger.src.coreengine.graphics.opengl.core;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import org.lwjgl.opengl.GL30;
|
||||||
|
import org.lwjgl.opengl.GL31;
|
||||||
|
import org.lwjgl.opengl.GL42;
|
||||||
|
import org.lwjgl.opengl.GL43;
|
||||||
|
import org.lwjgl.opengl.GL45;
|
||||||
|
|
||||||
|
import speiger.src.collections.objects.lists.ObjectArrayList;
|
||||||
|
import speiger.src.coreengine.graphics.api.buffer.VertexBuffer;
|
||||||
|
import speiger.src.coreengine.graphics.api.buffer.states.BufferType;
|
||||||
|
import speiger.src.coreengine.graphics.api.compute.BarrierType;
|
||||||
|
import speiger.src.coreengine.graphics.api.compute.ComputePipeline;
|
||||||
|
import speiger.src.coreengine.graphics.api.core.ComputeCommandBuffer;
|
||||||
|
import speiger.src.coreengine.graphics.api.sampler.Sampler;
|
||||||
|
import speiger.src.coreengine.graphics.api.texture.Texture;
|
||||||
|
import speiger.src.coreengine.graphics.api.utils.AccessType;
|
||||||
|
import speiger.src.coreengine.graphics.opengl.buffer.GLVertexBuffer;
|
||||||
|
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;
|
||||||
|
|
||||||
|
public class GLRecordingComputeCommandBuffer implements ComputeCommandBuffer {
|
||||||
|
List<Runnable> tasks = new ObjectArrayList<>();
|
||||||
|
boolean recording = false;
|
||||||
|
boolean removed = false;
|
||||||
|
GLGraphicsDevice device;
|
||||||
|
|
||||||
|
ComputePipeline pipeline;
|
||||||
|
ShaderInstance shader;
|
||||||
|
|
||||||
|
public GLRecordingComputeCommandBuffer(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 int commandCount() {
|
||||||
|
return tasks.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ComputeCommandBuffer 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 ComputeCommandBuffer pipeline(ComputePipeline pipeline) {
|
||||||
|
Objects.requireNonNull(pipeline);
|
||||||
|
ensureDrawing();
|
||||||
|
if(this.pipeline == pipeline) return this;
|
||||||
|
this.pipeline = pipeline;
|
||||||
|
shader = device.getShader(pipeline);
|
||||||
|
if(shader == null) {
|
||||||
|
//TODO implement logging
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
tasks.add(new SetPipeline(shader, device.states));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ComputeCommandBuffer 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 ComputeCommandBuffer texture(int binding, Texture texture, AccessType access) {
|
||||||
|
Objects.requireNonNull(texture);
|
||||||
|
Objects.requireNonNull(access);
|
||||||
|
ensureDrawing();
|
||||||
|
Objects.requireNonNull(shader, "No Pipeline found");
|
||||||
|
Objects.requireNonNull(shader.buffers().byIndex(binding), "Texture["+binding+"] binding not found");
|
||||||
|
tasks.add(new SetImageStorage(binding, (GLTexture)texture, access));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ComputeCommandBuffer uniform(int binding, VertexBuffer buffer) {
|
||||||
|
Objects.requireNonNull(buffer);
|
||||||
|
if(buffer.type() != BufferType.UNIFORM_BUFFER) throw new IllegalStateException("Buffer isn't a Uniform 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, false));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ComputeCommandBuffer uniform(int binding, VertexBuffer buffer, long offset, long size) {
|
||||||
|
Objects.requireNonNull(buffer);
|
||||||
|
if(buffer.type() != BufferType.UNIFORM_BUFFER) throw new IllegalStateException("Buffer isn't a Uniform 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, false));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ComputeCommandBuffer storage(int binding, VertexBuffer buffer) {
|
||||||
|
Objects.requireNonNull(buffer);
|
||||||
|
if(buffer.type() != BufferType.SHADER_STORAGE_BUFFER) throw new IllegalStateException("Buffer isn't a Shader Storage Buffer");
|
||||||
|
ensureDrawing();
|
||||||
|
Objects.requireNonNull(shader, "No Pipeline found");
|
||||||
|
Objects.requireNonNull(shader.storages().byIndex(binding), "Shader Storage for binding ["+binding+"] doesn't exist");
|
||||||
|
tasks.add(new SetUniform(binding, (GLVertexBuffer)buffer, -1, -1, true));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ComputeCommandBuffer storage(int binding, VertexBuffer buffer, long offset, long size) {
|
||||||
|
Objects.requireNonNull(buffer);
|
||||||
|
if(buffer.type() != BufferType.SHADER_STORAGE_BUFFER) throw new IllegalStateException("Buffer isn't a Shader Storage Buffer");
|
||||||
|
ensureDrawing();
|
||||||
|
Objects.requireNonNull(shader, "No Pipeline found");
|
||||||
|
Objects.requireNonNull(shader.storages().byIndex(binding), "Shader Storage for binding ["+binding+"] doesn't exist");
|
||||||
|
tasks.add(new SetUniform(binding, (GLVertexBuffer)buffer, offset, size, true));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ComputeCommandBuffer dispatch(int numGroupsX, int numGroupsY, int numGroupsZ) {
|
||||||
|
Objects.requireNonNull(shader);
|
||||||
|
if(numGroupsX < 1) throw new IllegalArgumentException("X has to be 1 or more");
|
||||||
|
if(numGroupsY < 1) throw new IllegalArgumentException("Y has to be 1 or more");
|
||||||
|
if(numGroupsZ < 1) throw new IllegalArgumentException("Z has to be 1 or more");
|
||||||
|
tasks.add(() -> GL45.glDispatchCompute(numGroupsX, numGroupsY, numGroupsZ));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ComputeCommandBuffer barrier(BarrierType... types) {
|
||||||
|
int result = 0;
|
||||||
|
for(BarrierType type : types) {
|
||||||
|
result |= GLUtils.toGL(type);
|
||||||
|
}
|
||||||
|
tasks.add(new SetBarrier(result));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ComputeCommandBuffer end() {
|
||||||
|
if(!recording) throw new IllegalStateException("Already Stopped Recording");
|
||||||
|
clear();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void execute() {
|
||||||
|
tasks.forEach(Runnable::run);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void clear() {
|
||||||
|
recording = false;
|
||||||
|
pipeline = null;
|
||||||
|
shader = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public record SetPipeline(ShaderInstance instance, GLStates states) implements Runnable {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
states.shaders.bind(instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 SetImageStorage(int binding, GLTexture texture, AccessType type) implements Runnable {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
GL45.glBindImageTexture(0, binding, texture.id(), false, 0, GLUtils.toGL(type), GLUtils.toGLInternal(texture.settings().internal()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public record SetUniform(int binding, GLVertexBuffer buffer, long offset, long size, boolean shader) implements Runnable {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if(size < 0 || offset < 0) GL30.glBindBufferBase(shader ? GL43.GL_SHADER_STORAGE_BUFFER : GL31.GL_UNIFORM_BUFFER, binding, buffer.id());
|
||||||
|
else GL30.glBindBufferRange(shader ? GL43.GL_SHADER_STORAGE_BUFFER : GL31.GL_UNIFORM_BUFFER, binding, buffer.id(), offset, size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public record SetBarrier(int barrierBits) implements Runnable {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
GL42.glMemoryBarrier(barrierBits);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,16 +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 org.lwjgl.opengl.GL11;
|
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.input.window.Window;
|
||||||
|
|
||||||
public class GLSurface implements GraphicsSurface {
|
public class GLSurface implements GraphicsSurface {
|
||||||
|
GLGraphicsDevice device;
|
||||||
Window window;
|
Window window;
|
||||||
|
|
||||||
public GLSurface(Window window) {
|
public GLSurface(Window window, GLGraphicsDevice device) {
|
||||||
this.window = window;
|
this.window = window;
|
||||||
|
this.device = device;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -23,12 +26,14 @@ public class GLSurface implements GraphicsSurface {
|
|||||||
GL11.glClearColor(0.2F, 0.55F, 0.66F, 1F);
|
GL11.glClearColor(0.2F, 0.55F, 0.66F, 1F);
|
||||||
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
|
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
|
||||||
window.beginFrame();
|
window.beginFrame();
|
||||||
GLFW.glfwPollEvents();
|
GL.setCapabilities(device.capabilities);
|
||||||
|
if(window.isPrimaryWindow()) window.manager().joinOnWindowThread(GLFW::glfwPollEvents);
|
||||||
|
Thread.onSpinWait();
|
||||||
|
window.handleInput();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void finishFrame() {
|
public void finishFrame() {
|
||||||
window.handleInput();
|
|
||||||
window.finishFrame();
|
window.finishFrame();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package speiger.src.coreengine.graphics.opengl.core;
|
||||||
|
|
||||||
|
import java.util.OptionalLong;
|
||||||
|
|
||||||
|
import org.lwjgl.opengl.GL15;
|
||||||
|
import org.lwjgl.opengl.GL33;
|
||||||
|
import org.lwjgl.opengl.GL45;
|
||||||
|
|
||||||
|
import speiger.src.coreengine.graphics.api.core.TimeQueryPool;
|
||||||
|
|
||||||
|
public class GLTimeQueryPool implements TimeQueryPool {
|
||||||
|
boolean removed = false;
|
||||||
|
int[] queries;
|
||||||
|
|
||||||
|
public GLTimeQueryPool(int size) {
|
||||||
|
this.queries = new int[size];
|
||||||
|
GL45.glCreateQueries(GL33.GL_TIMESTAMP, queries);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRemoved() {
|
||||||
|
return removed;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove() {
|
||||||
|
if(removed) return;
|
||||||
|
GL15.glDeleteQueries(queries);
|
||||||
|
removed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return queries.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(int index) {
|
||||||
|
GL33.glQueryCounter(queries[index], GL33.GL_TIMESTAMP);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OptionalLong get(int index) {
|
||||||
|
if(GL15.glGetQueryObjectui(queries[index], GL15.GL_QUERY_RESULT_AVAILABLE) == 1) {
|
||||||
|
return OptionalLong.of(GL33.glGetQueryObjectui64(queries[index], GL15.GL_QUERY_RESULT));
|
||||||
|
}
|
||||||
|
return OptionalLong.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
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;
|
||||||
@@ -34,13 +33,6 @@ public class GLMesh extends Mesh {
|
|||||||
return vao;
|
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
|
@Override
|
||||||
public boolean equals(Object obj) {
|
public boolean equals(Object obj) {
|
||||||
return obj instanceof GLMesh mesh && mesh.vao == vao;
|
return obj instanceof GLMesh mesh && mesh.vao == vao;
|
||||||
@@ -79,6 +71,7 @@ public class GLMesh extends Mesh {
|
|||||||
for(Element element : layout) {
|
for(Element element : layout) {
|
||||||
GL45.glVertexArrayAttribFormat(vao, element.index(), element.size(), GLUtils.toGL(element.type()), element.normalized(), layout.offset(index++));
|
GL45.glVertexArrayAttribFormat(vao, element.index(), element.size(), GLUtils.toGL(element.type()), element.normalized(), layout.offset(index++));
|
||||||
GL45.glVertexArrayAttribBinding(vao, element.index(), binding);
|
GL45.glVertexArrayAttribBinding(vao, element.index(), binding);
|
||||||
|
GL45.glEnableVertexArrayAttrib(vao, element.index());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ 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.Object2ObjectOpenHashMap;
|
import speiger.src.collections.objects.maps.impl.hash.Object2ObjectOpenHashMap;
|
||||||
|
|
||||||
public record ShaderInstance(int programId, ShaderStorage<UniformObject> uniforms, ShaderStorage<SamplerObject> samplers) {
|
public record ShaderInstance(int programId, ShaderStorage<BufferObject> uniforms, ShaderStorage<BufferObject> storages, ShaderStorage<TextureObject> samplers, ShaderStorage<TextureObject> buffers) {
|
||||||
public record UniformObject(int slot) {}
|
public record BufferObject(int slot) {}
|
||||||
public record SamplerObject(int unit) {}
|
public record TextureObject(int unit) {}
|
||||||
|
|
||||||
public static class ShaderStorage<T> {
|
public static class ShaderStorage<T> {
|
||||||
Map<String, T> byName = new Object2ObjectOpenHashMap<>();
|
Map<String, T> byName = new Object2ObjectOpenHashMap<>();
|
||||||
|
|||||||
@@ -2,21 +2,9 @@ package speiger.src.coreengine.graphics.opengl.shader;
|
|||||||
|
|
||||||
import org.lwjgl.opengl.GL20;
|
import org.lwjgl.opengl.GL20;
|
||||||
|
|
||||||
import speiger.src.collections.objects.lists.ObjectArrayList;
|
|
||||||
import speiger.src.collections.objects.lists.ObjectList;
|
|
||||||
|
|
||||||
public class ShaderTracker {
|
public class ShaderTracker {
|
||||||
ObjectList<ShaderInstance> knownShaders = new ObjectArrayList<ShaderInstance>();
|
|
||||||
int boundShader;
|
int boundShader;
|
||||||
|
|
||||||
public void register(ShaderInstance instance) {
|
|
||||||
knownShaders.add(instance);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void remove(ShaderInstance instance) {
|
|
||||||
knownShaders.remove(instance);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void bind(ShaderInstance instance) {
|
public void bind(ShaderInstance instance) {
|
||||||
int id = instance.programId();
|
int id = instance.programId();
|
||||||
if(id == boundShader) return;
|
if(id == boundShader) return;
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ 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.buffer.states.IndeciesType;
|
||||||
|
import speiger.src.coreengine.graphics.api.compute.BarrierType;
|
||||||
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.BlendFactor;
|
||||||
@@ -29,6 +30,7 @@ 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.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.AccessType;
|
||||||
import speiger.src.coreengine.graphics.api.utils.AlphaFunction;
|
import speiger.src.coreengine.graphics.api.utils.AlphaFunction;
|
||||||
|
|
||||||
public class GLUtils {
|
public class GLUtils {
|
||||||
@@ -119,6 +121,14 @@ public class GLUtils {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static int toGL(AccessType type) {
|
||||||
|
return switch(type) {
|
||||||
|
case READ -> GL15.GL_READ_ONLY;
|
||||||
|
case WRITE -> GL15.GL_WRITE_ONLY;
|
||||||
|
case BOTH -> GL15.GL_READ_WRITE;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
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;
|
||||||
@@ -232,7 +242,7 @@ public class GLUtils {
|
|||||||
case GEOMETRY -> GL32.GL_GEOMETRY_SHADER;
|
case GEOMETRY -> GL32.GL_GEOMETRY_SHADER;
|
||||||
case TESSELATION_CONTROL -> GL40.GL_TESS_CONTROL_SHADER;
|
case TESSELATION_CONTROL -> GL40.GL_TESS_CONTROL_SHADER;
|
||||||
case TESSELATION_EVALUATION -> GL40.GL_TESS_EVALUATION_SHADER;
|
case TESSELATION_EVALUATION -> GL40.GL_TESS_EVALUATION_SHADER;
|
||||||
|
case COMPUTE -> GL43.GL_COMPUTE_SHADER;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -256,4 +266,17 @@ public class GLUtils {
|
|||||||
case INT -> GL11.GL_UNSIGNED_INT;
|
case INT -> GL11.GL_UNSIGNED_INT;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static int toGL(BarrierType type) {
|
||||||
|
return switch(type) {
|
||||||
|
case MESH_BUFFER -> GL42.GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT | GL42.GL_ELEMENT_ARRAY_BARRIER_BIT;
|
||||||
|
case SHADER_TEXTURE -> GL42.GL_TEXTURE_FETCH_BARRIER_BIT;
|
||||||
|
case SHADER_IMAGE -> GL42.GL_SHADER_IMAGE_ACCESS_BARRIER_BIT;
|
||||||
|
case SHADER_STORAGE -> GL43.GL_SHADER_STORAGE_BARRIER_BIT;
|
||||||
|
case SHADER_UNIFORM -> GL42.GL_UNIFORM_BARRIER_BIT;
|
||||||
|
case INDIRECT_COMMAND -> GL42.GL_COMMAND_BARRIER_BIT;
|
||||||
|
case FRAMEBUFFER -> GL42.GL_FRAMEBUFFER_BARRIER_BIT;
|
||||||
|
case CPU -> GL42.GL_BUFFER_UPDATE_BARRIER_BIT | GL44.GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT;
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,76 @@
|
|||||||
|
package speiger.src.coreengine.input.device;
|
||||||
|
|
||||||
|
import java.util.Deque;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.concurrent.ConcurrentLinkedDeque;
|
||||||
|
|
||||||
|
import speiger.src.collections.longs.collections.LongIterable;
|
||||||
|
import speiger.src.collections.longs.maps.impl.concurrent.Long2ObjectConcurrentOpenHashMap;
|
||||||
|
import speiger.src.collections.longs.maps.interfaces.Long2ObjectMap;
|
||||||
|
import speiger.src.coreengine.input.window.IWindowListener.Reason;
|
||||||
|
import speiger.src.coreengine.input.window.Window;
|
||||||
|
import speiger.src.coreengine.utils.eventbus.Event;
|
||||||
|
import speiger.src.coreengine.utils.eventbus.EventBus;
|
||||||
|
|
||||||
|
public abstract class AbstractDevice<T, E> implements InputDevice {
|
||||||
|
protected Long2ObjectMap<Deque<E>> queues = new Long2ObjectConcurrentOpenHashMap<>();
|
||||||
|
protected Long2ObjectMap<T> windowData = new Long2ObjectConcurrentOpenHashMap<>();
|
||||||
|
protected EventBus bus;
|
||||||
|
|
||||||
|
public void init(EventBus bus) {
|
||||||
|
this.bus = bus;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected LongIterable knownWindows() {
|
||||||
|
return queues.keySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void register(Window window) {
|
||||||
|
registerCallbacks(window);
|
||||||
|
long id = window.id();
|
||||||
|
queues.putIfAbsent(id, new ConcurrentLinkedDeque<>());
|
||||||
|
T data = createData(id);
|
||||||
|
if(data == null) return;
|
||||||
|
windowData.put(id, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void registerCallbacks(Window window) {
|
||||||
|
window.addListener(this::onClose);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onClose(Window window, Reason reason) {
|
||||||
|
if(reason == Reason.CLOSING) {
|
||||||
|
queues.remove(window.id());
|
||||||
|
windowData.remove(window.id());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract T createData(long windowId);
|
||||||
|
protected abstract void process(E task);
|
||||||
|
|
||||||
|
protected boolean pushEvent(Event event) {
|
||||||
|
if(bus == null) return true;
|
||||||
|
bus.post(event);
|
||||||
|
return event.isCancelable() && event.isCanceled();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void push(long windowId, E task) {
|
||||||
|
Objects.requireNonNull(task);
|
||||||
|
Deque<E> queue = queues.get(windowId);
|
||||||
|
if(queue == null) return;
|
||||||
|
queue.add(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void processInput(long windowId) {
|
||||||
|
Deque<E> queue = queues.get(windowId);
|
||||||
|
if(queue == null) return;
|
||||||
|
while(!queue.isEmpty()) {
|
||||||
|
process(queue.poll());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public T get(long windowId) {
|
||||||
|
return windowData.get(windowId);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package speiger.src.coreengine.input.device;
|
||||||
|
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.lwjgl.glfw.GLFW;
|
||||||
|
import org.lwjgl.glfw.GLFWDropCallback;
|
||||||
|
|
||||||
|
import speiger.src.collections.objects.lists.ObjectArrayList;
|
||||||
|
import speiger.src.coreengine.input.device.FileDrop.DropTask;
|
||||||
|
import speiger.src.coreengine.input.events.FileEvents;
|
||||||
|
import speiger.src.coreengine.input.window.Window;
|
||||||
|
|
||||||
|
public class FileDrop extends AbstractDevice<Void, DropTask> {
|
||||||
|
public static final FileDrop INSTANCE = new FileDrop();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void registerCallbacks(Window window) {
|
||||||
|
super.registerCallbacks(window);
|
||||||
|
window.addCallback(this::drop, GLFW::glfwSetDropCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void drop(long windowId, int count, long names) {
|
||||||
|
List<Path> paths = new ObjectArrayList<>(count);
|
||||||
|
for(int i = 0;i<count;i++) {
|
||||||
|
Path path = Path.of(GLFWDropCallback.getName(names, i));
|
||||||
|
if(Files.notExists(path)) continue;
|
||||||
|
paths.add(path);
|
||||||
|
}
|
||||||
|
if(paths.isEmpty()) return;
|
||||||
|
push(windowId, new DropTask(windowId, paths.toArray(Path[]::new)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void reset(long windowId) {}
|
||||||
|
@Override
|
||||||
|
protected Void createData(long windowId) { return null; }
|
||||||
|
@Override
|
||||||
|
protected void process(DropTask task) {
|
||||||
|
int x = Mouse.INSTANCE.x(task.windowId());
|
||||||
|
int y = Mouse.INSTANCE.y(task.windowId());
|
||||||
|
pushEvent(new FileEvents.Drop(task.windowId(), x, y, task.files()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public record DropTask(long windowId, Path[] files) {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package speiger.src.coreengine.input.device;
|
||||||
|
|
||||||
|
import speiger.src.coreengine.input.window.Window;
|
||||||
|
|
||||||
|
public interface InputDevice {
|
||||||
|
public void register(Window window);
|
||||||
|
public void processInput(long windowId);
|
||||||
|
public void reset(long windowId);
|
||||||
|
}
|
||||||
@@ -0,0 +1,139 @@
|
|||||||
|
package speiger.src.coreengine.input.device;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.FloatBuffer;
|
||||||
|
import java.util.BitSet;
|
||||||
|
|
||||||
|
import org.lwjgl.glfw.GLFW;
|
||||||
|
|
||||||
|
import speiger.src.collections.floats.lists.FloatArrayList;
|
||||||
|
import speiger.src.collections.floats.lists.FloatList;
|
||||||
|
import speiger.src.collections.ints.collections.IntIterator;
|
||||||
|
import speiger.src.collections.ints.maps.impl.hash.Int2ObjectOpenHashMap;
|
||||||
|
import speiger.src.collections.ints.maps.interfaces.Int2ObjectMap;
|
||||||
|
import speiger.src.collections.ints.sets.IntLinkedOpenHashSet;
|
||||||
|
import speiger.src.collections.ints.sets.IntSet;
|
||||||
|
import speiger.src.collections.longs.collections.LongIterator;
|
||||||
|
import speiger.src.collections.longs.sets.LongOpenHashSet;
|
||||||
|
import speiger.src.collections.longs.sets.LongSet;
|
||||||
|
import speiger.src.coreengine.input.device.Joystick.JoyStickData;
|
||||||
|
import speiger.src.coreengine.input.device.Joystick.JoyStickTask;
|
||||||
|
import speiger.src.coreengine.input.events.JoystickEvent;
|
||||||
|
import speiger.src.coreengine.input.window.WindowManager;
|
||||||
|
import speiger.src.coreengine.utils.eventbus.EventBus;
|
||||||
|
|
||||||
|
public class Joystick extends AbstractDevice<JoyStickData, JoyStickTask> {
|
||||||
|
public static final Joystick INSTANCE = new Joystick();
|
||||||
|
WindowManager manager;
|
||||||
|
IntSet presentJoysticks = new IntLinkedOpenHashSet();
|
||||||
|
LongSet alwaysProcessInputs = new LongOpenHashSet();
|
||||||
|
|
||||||
|
public void init(WindowManager manager, EventBus bus) {
|
||||||
|
this.manager = manager;
|
||||||
|
super.init(bus);
|
||||||
|
manager.addCallback(this::plugin, GLFW::glfwSetJoystickCallback);
|
||||||
|
for(int i = 0,m=GLFW.GLFW_JOYSTICK_LAST;i<=m;i++) {
|
||||||
|
if(GLFW.glfwJoystickPresent(i)) {
|
||||||
|
plugin(i, GLFW.GLFW_CONNECTED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(EventBus bus) { throw new UnsupportedOperationException("Use init(WindowManager, EventBus) instead"); }
|
||||||
|
|
||||||
|
public void setWindowAlwaysProcessInputs(long windowId, boolean value) {
|
||||||
|
if(value) alwaysProcessInputs.add(windowId);
|
||||||
|
else alwaysProcessInputs.remove(windowId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isWindowAlwaysProcessingInputs(long windowId) {
|
||||||
|
return alwaysProcessInputs.contains(windowId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void plugin(int jid, int event) {
|
||||||
|
if(event == GLFW.GLFW_CONNECTED) presentJoysticks.add(jid);
|
||||||
|
else presentJoysticks.remove(jid);
|
||||||
|
for(LongIterator iter = knownWindows().iterator();iter.hasNext();) {
|
||||||
|
long window = iter.nextLong();
|
||||||
|
push(window, new Plugin(window, jid, event));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void reset(long windowId) {}
|
||||||
|
@Override
|
||||||
|
protected JoyStickData createData(long windowId) { return new JoyStickData(presentJoysticks); }
|
||||||
|
@Override
|
||||||
|
protected void process(JoyStickTask task) { task.process(this); }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void processInput(long windowId) {
|
||||||
|
super.processInput(windowId);
|
||||||
|
if(manager.getActiveWindow() != windowId && !alwaysProcessInputs.contains(windowId)) return;
|
||||||
|
JoyStickData data = get(windowId);
|
||||||
|
if(data == null) return;
|
||||||
|
for(IntIterator iter = presentJoysticks.iterator();iter.hasNext();) {
|
||||||
|
int jid = iter.nextInt();
|
||||||
|
ButtonData buttons = data.data.get(jid);
|
||||||
|
if(buttons == null) continue;
|
||||||
|
handleJoystick(windowId, jid, buttons);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handleJoystick(long window, int jid, ButtonData data) {
|
||||||
|
ByteBuffer buttons = GLFW.glfwGetJoystickButtons(jid);
|
||||||
|
for(int i = 0;buttons.hasRemaining();i++) {
|
||||||
|
boolean state = buttons.get() == GLFW.GLFW_PRESS;
|
||||||
|
if(data.buttons.get(i) != state) {
|
||||||
|
data.buttons.set(i, state);
|
||||||
|
pushEvent(new JoystickEvent.Button(window, jid, i, state));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FloatBuffer axis = GLFW.glfwGetJoystickAxes(jid);
|
||||||
|
for(int i = 0;axis.hasRemaining();i++) {
|
||||||
|
float state = axis.get();
|
||||||
|
if(i >= data.axis.size()) data.axis.add(state);
|
||||||
|
else data.axis.set(i, state);
|
||||||
|
boolean negative = state < 0F;
|
||||||
|
boolean max = state < -0.95F || state > 0.95F;
|
||||||
|
boolean min = (state < -0.5F || state > 0.5F) && !max;
|
||||||
|
if(data.axisState.get(i*3) != negative || data.axisState.get(i*3+1) != max || data.axisState.get(i*3+2) != min) {
|
||||||
|
data.axisState.set(i*3, negative);
|
||||||
|
data.axisState.set(i*3+1, max);
|
||||||
|
data.axisState.set(i*3+2, min);
|
||||||
|
pushEvent(new JoystickEvent.Axis(window, jid, i, state, negative, min, max));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class JoyStickData {
|
||||||
|
Int2ObjectMap<ButtonData> data = new Int2ObjectOpenHashMap<>();
|
||||||
|
|
||||||
|
public JoyStickData(IntSet knownJoysticks) {
|
||||||
|
for(IntIterator iter = knownJoysticks.iterator();iter.hasNext();) {
|
||||||
|
data.put(iter.nextInt(), new ButtonData());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ButtonData {
|
||||||
|
BitSet buttons = new BitSet();
|
||||||
|
FloatList axis = new FloatArrayList();
|
||||||
|
BitSet axisState = new BitSet();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static interface JoyStickTask {
|
||||||
|
public void process(Joystick stick);
|
||||||
|
}
|
||||||
|
|
||||||
|
public record Plugin(long window, int jid, int event) implements JoyStickTask {
|
||||||
|
@Override
|
||||||
|
public void process(Joystick stick) {
|
||||||
|
JoyStickData data = stick.get(window);
|
||||||
|
if(event == GLFW.GLFW_CONNECTED) data.data.put(jid, new ButtonData());
|
||||||
|
else if(event == GLFW.GLFW_DISCONNECTED) data.data.remove(jid);
|
||||||
|
stick.pushEvent(new JoystickEvent.Connected(window, jid, event == GLFW.GLFW_CONNECTED));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
package speiger.src.coreengine.input.device;
|
||||||
|
|
||||||
|
import java.util.function.BiPredicate;
|
||||||
|
|
||||||
|
import org.lwjgl.glfw.GLFW;
|
||||||
|
|
||||||
|
import speiger.src.coreengine.input.device.Keyboard.KeyData;
|
||||||
|
import speiger.src.coreengine.input.device.Keyboard.KeyTask;
|
||||||
|
import speiger.src.coreengine.input.events.KeyEvent;
|
||||||
|
import speiger.src.coreengine.input.events.KeyEvent.Key;
|
||||||
|
import speiger.src.coreengine.input.window.Window;
|
||||||
|
|
||||||
|
public class Keyboard extends AbstractDevice<KeyData, KeyTask> {
|
||||||
|
public static final Keyboard INSTANCE = new Keyboard();
|
||||||
|
@Override
|
||||||
|
public void reset(long windowId) {}
|
||||||
|
@Override
|
||||||
|
protected KeyData createData(long windowId) { return new KeyData(); }
|
||||||
|
@Override
|
||||||
|
protected void process(KeyTask task) { task.process(this); }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void registerCallbacks(Window window) {
|
||||||
|
super.registerCallbacks(window);
|
||||||
|
window.addCallback(this::keyPress, GLFW::glfwSetKeyCallback);
|
||||||
|
window.addCallback(this::charTyped, GLFW::glfwSetCharCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void keyPress(long window, int key, int scancode, int action, int mods) { push(window, new KeyPressed(window, key, scancode, action, mods)); }
|
||||||
|
private void charTyped(long window, int codepoint) { push(window, new CharTyped(window, codepoint)); }
|
||||||
|
|
||||||
|
public static class KeyData {
|
||||||
|
BiPredicate<Integer, Keyboard> filter;
|
||||||
|
boolean[] pressedKeys = new boolean[350];
|
||||||
|
boolean[] nonConsumedKeys = new boolean[350];
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
filter = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static interface KeyTask {
|
||||||
|
public void process(Keyboard board);
|
||||||
|
}
|
||||||
|
|
||||||
|
public record KeyPressed(long window, int key, int scancode, int action, int mods) implements KeyTask {
|
||||||
|
@Override
|
||||||
|
public void process(Keyboard board) {
|
||||||
|
if(key < 0 || key >= 350) return;
|
||||||
|
KeyData data = board.get(window);
|
||||||
|
if(action >= 1) {
|
||||||
|
data.pressedKeys[key] = true;
|
||||||
|
onKeyPressed(board, data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
data.pressedKeys[key] = false;
|
||||||
|
data.nonConsumedKeys[key] = false;
|
||||||
|
board.pushEvent(new Key(window, key, scancode, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onKeyPressed(Keyboard board, KeyData data) {
|
||||||
|
if(data.filter == null || data.filter.test(key, board)) {
|
||||||
|
if(board.pushEvent(new Key(window, key, scancode, true))) return;
|
||||||
|
data.nonConsumedKeys[key] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public record CharTyped(long window, int codepoint) implements KeyTask {
|
||||||
|
@Override
|
||||||
|
public void process(Keyboard board) {
|
||||||
|
board.pushEvent(new KeyEvent.Char(window, codepoint));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,145 @@
|
|||||||
|
package speiger.src.coreengine.input.device;
|
||||||
|
|
||||||
|
import org.lwjgl.glfw.GLFW;
|
||||||
|
|
||||||
|
import speiger.src.collections.ints.sets.IntOpenHashSet;
|
||||||
|
import speiger.src.collections.ints.sets.IntSet;
|
||||||
|
import speiger.src.coreengine.input.device.Mouse.MouseData;
|
||||||
|
import speiger.src.coreengine.input.device.Mouse.MouseTask;
|
||||||
|
import speiger.src.coreengine.input.events.MouseEvent;
|
||||||
|
import speiger.src.coreengine.input.window.Window;
|
||||||
|
import speiger.src.coreengine.math.vector.ints.Vec2i;
|
||||||
|
|
||||||
|
public class Mouse extends AbstractDevice<MouseData, MouseTask> {
|
||||||
|
public static final Mouse INSTANCE = new Mouse();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected MouseData createData(long windowId) { return new MouseData(); }
|
||||||
|
@Override
|
||||||
|
public void reset(long windowId) { get(windowId).reset(); }
|
||||||
|
protected void process(MouseTask task) { task.process(this); }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void registerCallbacks(Window window) {
|
||||||
|
super.registerCallbacks(window);
|
||||||
|
window.addCallback(this::move, GLFW::glfwSetCursorPosCallback);
|
||||||
|
window.addCallback(this::click, GLFW::glfwSetMouseButtonCallback);
|
||||||
|
window.addCallback(this::enter, GLFW::glfwSetCursorEnterCallback);
|
||||||
|
window.addCallback(this::scroll, GLFW::glfwSetScrollCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
//@formatter:off
|
||||||
|
private void move(long window, double x, double y) { push(window, new Move(window, x, y)); }
|
||||||
|
private void click(long window, int button, int action, int mods) { push(window, new Click(window, button, action, mods)); }
|
||||||
|
private void enter(long window, boolean enter) { push(window, new Enter(window, enter)); }
|
||||||
|
private void scroll(long window, double xoffset, double yoffset) { push(window, new Scroll(window, xoffset, yoffset)); }
|
||||||
|
//@formatter:on
|
||||||
|
|
||||||
|
public static class MouseData {
|
||||||
|
IntSet buttons = new IntOpenHashSet();
|
||||||
|
Vec2i position = Vec2i.mutable();
|
||||||
|
Vec2i motion = Vec2i.mutable();
|
||||||
|
Vec2i scroll = Vec2i.mutable();
|
||||||
|
boolean active = true;
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
motion.negate();
|
||||||
|
position.negate();
|
||||||
|
scroll.negate();
|
||||||
|
}
|
||||||
|
|
||||||
|
//@formatter:off
|
||||||
|
public int x() { return position.x(); }
|
||||||
|
public int y() { return position.y(); }
|
||||||
|
public boolean pressed(int button) { return buttons.contains(button); }
|
||||||
|
public Vec2i motion() { return motion; }
|
||||||
|
public Vec2i scroll() { return scroll; }
|
||||||
|
//@formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
public int x(long windowId) {
|
||||||
|
MouseData data = get(windowId);
|
||||||
|
return data == null ? 0 : data.x();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int y(long windowId) {
|
||||||
|
MouseData data = get(windowId);
|
||||||
|
return data == null ? 0 : data.y();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean pressed(long windowId, int button) {
|
||||||
|
MouseData data = get(windowId);
|
||||||
|
return data != null && data.pressed(button);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vec2i motion(long windowId) {
|
||||||
|
MouseData data = get(windowId);
|
||||||
|
return data == null ? Vec2i.ZERO : data.motion();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vec2i scroll(long windowId) {
|
||||||
|
MouseData data = get(windowId);
|
||||||
|
return data == null ? Vec2i.ZERO : data.scroll();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPosition(long windowId, int x, int y) {
|
||||||
|
MouseData data = get(windowId);
|
||||||
|
if(data == null || (data.position.x() == x && data.position.y() == y)) return;
|
||||||
|
data.position.set(x, y);
|
||||||
|
GLFW.glfwSetCursorPos(windowId, x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void pressButton(long window, int button, boolean press) { click(window, button, press ? GLFW.GLFW_PRESS : GLFW.GLFW_RELEASE, 0); }
|
||||||
|
public void scrollMouse(long window, int xScroll, int yScroll) { scroll(window, xScroll, yScroll); }
|
||||||
|
|
||||||
|
public interface MouseTask {
|
||||||
|
public void process(Mouse mouse);
|
||||||
|
}
|
||||||
|
|
||||||
|
private record Move(long window, double x, double y) implements MouseTask {
|
||||||
|
@Override
|
||||||
|
public void process(Mouse mouse) {
|
||||||
|
MouseData data = mouse.get(window);
|
||||||
|
int xOff = (int)(x - data.position.x());
|
||||||
|
int yOff = (int)(y - data.position.y());
|
||||||
|
data.position.set((int)x, (int)y);
|
||||||
|
if(!mouse.pushEvent(new MouseEvent.Move(window, (int)x, (int)y, xOff, yOff))) {
|
||||||
|
data.motion.add(xOff, yOff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private record Click(long window, int button, int action, int mods) implements MouseTask {
|
||||||
|
@Override
|
||||||
|
public void process(Mouse mouse) {
|
||||||
|
MouseData data = mouse.get(window);
|
||||||
|
if(action == GLFW.GLFW_PRESS) data.buttons.add(action);
|
||||||
|
else if(action == GLFW.GLFW_RELEASE) data.buttons.remove(action);
|
||||||
|
mouse.pushEvent(new MouseEvent.Click(window, data.position.x(), data.position.y(), button, action == GLFW.GLFW_PRESS));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private record Scroll(long window, double x, double y) implements MouseTask {
|
||||||
|
@Override
|
||||||
|
public void process(Mouse mouse) {
|
||||||
|
MouseData data = mouse.get(window);
|
||||||
|
if(!mouse.pushEvent(new MouseEvent.Scroll(window, data.position.x(), data.position.y(), (int)x, (int)y))) {
|
||||||
|
data.scroll.add((int)x, (int)y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private record Enter(long window, boolean enter) implements MouseTask {
|
||||||
|
@Override
|
||||||
|
public void process(Mouse mouse) {
|
||||||
|
MouseData data = mouse.get(window);
|
||||||
|
data.active = enter;
|
||||||
|
if(!enter) {
|
||||||
|
//TODO decide if this should also push phantom events clearing the pressed keys?
|
||||||
|
data.buttons.clear();
|
||||||
|
data.position.negate();
|
||||||
|
data.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package speiger.src.coreengine.input.events;
|
||||||
|
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
import speiger.src.collections.objects.utils.ObjectIterators;
|
||||||
|
|
||||||
|
public class FileEvents {
|
||||||
|
public static class Drop extends MouseEvent implements Iterable<DroppedFile> {
|
||||||
|
DroppedFile[] files;
|
||||||
|
|
||||||
|
public Drop(long window, int x, int y, Path[] files) {
|
||||||
|
super(window, x, y);
|
||||||
|
this.files = new DroppedFile[files.length];
|
||||||
|
for(int i = 0,m=files.length;i<m;i++) {
|
||||||
|
String name = files[i].getFileName().toString();
|
||||||
|
this.files[i] = new DroppedFile(files[i], name, name.substring(name.lastIndexOf(".")+1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<DroppedFile> iterator() { return ObjectIterators.wrap(files); }
|
||||||
|
public int size() { return files.length; }
|
||||||
|
public DroppedFile get() { return files[0]; }
|
||||||
|
public DroppedFile get(int index) { return files[index]; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public record DroppedFile(Path path, String name, String extension) {
|
||||||
|
public boolean isFolder() { return Files.isDirectory(path); }
|
||||||
|
public boolean isFile() { return Files.isRegularFile(path); }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
package speiger.src.coreengine.input.events;
|
||||||
|
|
||||||
|
import speiger.src.coreengine.utils.eventbus.Event;
|
||||||
|
|
||||||
|
public class JoystickEvent extends Event {
|
||||||
|
final long window;
|
||||||
|
final int jid;
|
||||||
|
|
||||||
|
public JoystickEvent(long window, int jid) {
|
||||||
|
this.window = window;
|
||||||
|
this.jid = jid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long window() { return window; }
|
||||||
|
public int jid() { return jid; }
|
||||||
|
|
||||||
|
public static class Connected extends JoystickEvent {
|
||||||
|
boolean connected;
|
||||||
|
|
||||||
|
public Connected(long window, int jid, boolean connected) {
|
||||||
|
super(window, jid);
|
||||||
|
this.connected = connected;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean connected() { return connected; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Button extends JoystickEvent {
|
||||||
|
final int button;
|
||||||
|
final boolean press;
|
||||||
|
|
||||||
|
public Button(long window, int jid, int button, boolean press) {
|
||||||
|
super(window, jid);
|
||||||
|
this.button = button;
|
||||||
|
this.press = press;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int button() { return button; }
|
||||||
|
public boolean press() { return press; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Axis extends JoystickEvent {
|
||||||
|
int axis;
|
||||||
|
float value;
|
||||||
|
boolean negative;
|
||||||
|
boolean halfPress;
|
||||||
|
boolean fullPress;
|
||||||
|
|
||||||
|
public Axis(long window, int jid, int axis, float value, boolean negative, boolean halfPress, boolean fullPress) {
|
||||||
|
super(window, jid);
|
||||||
|
this.axis = axis;
|
||||||
|
this.value = value;
|
||||||
|
this.negative = negative;
|
||||||
|
this.halfPress = halfPress;
|
||||||
|
this.fullPress = fullPress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int axis() { return axis; }
|
||||||
|
public float value() { return value; }
|
||||||
|
public float absoluteValue() { return Math.abs(value); }
|
||||||
|
public boolean negative() { return negative; }
|
||||||
|
public boolean halfPress() { return halfPress; }
|
||||||
|
public boolean fullPress() { return fullPress; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
package speiger.src.coreengine.input.events;
|
||||||
|
|
||||||
|
import speiger.src.coreengine.utils.eventbus.Event;
|
||||||
|
|
||||||
|
public abstract class KeyEvent extends Event {
|
||||||
|
final long window;
|
||||||
|
|
||||||
|
public KeyEvent(long window) {
|
||||||
|
this.window = window;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCancelable() { return true; }
|
||||||
|
public long window() { return window; }
|
||||||
|
|
||||||
|
public static class Key extends KeyEvent {
|
||||||
|
final int key;
|
||||||
|
final int scancode;
|
||||||
|
final boolean press;
|
||||||
|
|
||||||
|
public Key(long window, int key, int scancode, boolean press) {
|
||||||
|
super(window);
|
||||||
|
this.key = key;
|
||||||
|
this.scancode = scancode;
|
||||||
|
this.press = press;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int key() { return key; }
|
||||||
|
public int scancode() { return scancode; }
|
||||||
|
public boolean press() { return press; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Char extends KeyEvent {
|
||||||
|
final int codepoint;
|
||||||
|
|
||||||
|
public Char(long window, int codepoint) {
|
||||||
|
super(window);
|
||||||
|
this.codepoint = codepoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int codepoint() { return codepoint; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
package speiger.src.coreengine.input.events;
|
||||||
|
|
||||||
|
import speiger.src.coreengine.utils.eventbus.Event;
|
||||||
|
|
||||||
|
public abstract class MouseEvent extends Event {
|
||||||
|
final long window;
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
|
final int originX;
|
||||||
|
final int originY;
|
||||||
|
|
||||||
|
public MouseEvent(long window, int x, int y) {
|
||||||
|
this.window = window;
|
||||||
|
this.originX = this.x = x;
|
||||||
|
this.originY = this.y = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long windowId() { return window; }
|
||||||
|
public int x() { return x; }
|
||||||
|
public void x(int x) { this.x = x; }
|
||||||
|
public int y() { return y; }
|
||||||
|
public void y(int y) { this.y = y; }
|
||||||
|
public int originX() { return originX; }
|
||||||
|
public int originY() { return originY; }
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
x = originX;
|
||||||
|
y = originY;
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO implement support for Scaling
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCancelable() { return true; }
|
||||||
|
public boolean isForced() { return false; }
|
||||||
|
|
||||||
|
public static class Click extends MouseEvent {
|
||||||
|
final int button;
|
||||||
|
final boolean press;
|
||||||
|
|
||||||
|
public Click(long window, int x, int y, int button, boolean press) {
|
||||||
|
super(window, x, y);
|
||||||
|
this.button = button;
|
||||||
|
this.press = press;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isForced() { return !press; }
|
||||||
|
public int button() { return button; }
|
||||||
|
public boolean press() { return press; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Move extends MouseEvent {
|
||||||
|
final int xMove;
|
||||||
|
final int yMove;
|
||||||
|
|
||||||
|
public Move(long window, int x, int y, int xMove, int yMove) {
|
||||||
|
super(window, x, y);
|
||||||
|
this.xMove = xMove;
|
||||||
|
this.yMove = yMove;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int xMove() { return xMove; }
|
||||||
|
public int yMove() { return yMove; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Scroll extends MouseEvent {
|
||||||
|
final int scrollX;
|
||||||
|
final int scrollY;
|
||||||
|
|
||||||
|
public Scroll(long window, int x, int y, int scrollX, int scrollY) {
|
||||||
|
super(window, x, y);
|
||||||
|
this.scrollX = scrollX;
|
||||||
|
this.scrollY = scrollY;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int scrollX() { return scrollX; }
|
||||||
|
public int scrollY() { return scrollY; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package speiger.src.coreengine.input.window;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.NonNull;
|
||||||
|
import org.lwjgl.PointerBuffer;
|
||||||
|
import org.lwjgl.glfw.GLFW;
|
||||||
|
import org.lwjgl.system.MemoryStack;
|
||||||
|
import org.lwjgl.system.MemoryUtil;
|
||||||
|
|
||||||
|
public class GLFWUtil {
|
||||||
|
@NonNull
|
||||||
|
public static String glfwError(int error, long description) {
|
||||||
|
return switch(error) {
|
||||||
|
case GLFW.GLFW_NO_ERROR -> "GLFW_NO_ERROR";
|
||||||
|
case GLFW.GLFW_NOT_INITIALIZED -> "GLFW_NOT_INITIALIZED";
|
||||||
|
case GLFW.GLFW_NO_CURRENT_CONTEXT -> "GLFW_NO_CURRENT_CONTEXT";
|
||||||
|
case GLFW.GLFW_INVALID_ENUM -> "GLFW_INVALID_ENUM";
|
||||||
|
case GLFW.GLFW_INVALID_VALUE -> "GLFW_INVALID_VALUE";
|
||||||
|
case GLFW.GLFW_OUT_OF_MEMORY -> "GLFW_OUT_OF_MEMORY";
|
||||||
|
case GLFW.GLFW_API_UNAVAILABLE -> "GLFW_API_UNAVAILABLE";
|
||||||
|
case GLFW.GLFW_VERSION_UNAVAILABLE -> "GLFW_VERSION_UNAVAILABLE";
|
||||||
|
case GLFW.GLFW_PLATFORM_ERROR -> "GLFW_PLATFORM_ERROR";
|
||||||
|
case GLFW.GLFW_FORMAT_UNAVAILABLE -> "GLFW_FORMAT_UNAVAILABLE";
|
||||||
|
case GLFW.GLFW_NO_WINDOW_CONTEXT -> "GLFW_NO_WINDOW_CONTEXT";
|
||||||
|
case GLFW.GLFW_CURSOR_UNAVAILABLE -> "GLFW_CURSOR_UNAVAILABLE";
|
||||||
|
case GLFW.GLFW_FEATURE_UNAVAILABLE -> "GLFW_FEATURE_UNAVAILABLE";
|
||||||
|
case GLFW.GLFW_FEATURE_UNIMPLEMENTED -> "GLFW_FEATURE_UNIMPLEMENTED";
|
||||||
|
case GLFW.GLFW_PLATFORM_UNAVAILABLE -> "GLFW_PLATFORM_UNAVAILABLE";
|
||||||
|
default -> "UNKNOWN (" + error + ')';
|
||||||
|
} + (description != MemoryUtil.NULL ? ": " + MemoryUtil.memUTF8(description) : "");
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static String glfwError() {
|
||||||
|
try(MemoryStack stack = MemoryStack.stackPush()) {
|
||||||
|
PointerBuffer description = stack.callocPointer(1);
|
||||||
|
return glfwError(GLFW.glfwGetError(description), description.get(0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package speiger.src.coreengine.input.window;
|
||||||
|
|
||||||
|
public interface IWindowListener {
|
||||||
|
|
||||||
|
public void onChanged(Window window, Reason reason);
|
||||||
|
|
||||||
|
public static enum Reason {
|
||||||
|
CHANGE,
|
||||||
|
CLOSING;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
package speiger.src.coreengine.input.window;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.lwjgl.PointerBuffer;
|
||||||
|
import org.lwjgl.glfw.GLFW;
|
||||||
|
import org.lwjgl.glfw.GLFWVidMode;
|
||||||
|
|
||||||
|
import speiger.src.collections.longs.maps.impl.hash.Long2ObjectLinkedOpenHashMap;
|
||||||
|
import speiger.src.collections.longs.maps.interfaces.Long2ObjectMap;
|
||||||
|
import speiger.src.collections.objects.lists.ObjectArrayList;
|
||||||
|
import speiger.src.collections.objects.lists.ObjectList;
|
||||||
|
|
||||||
|
public class Monitor
|
||||||
|
{
|
||||||
|
final long id;
|
||||||
|
ObjectList<VideoMode> modes = new ObjectArrayList<>();
|
||||||
|
VideoMode defaultMode;
|
||||||
|
int xOffset;
|
||||||
|
int yOffset;
|
||||||
|
|
||||||
|
public Monitor(long id) {
|
||||||
|
this.id = id;
|
||||||
|
GLFWVidMode.Buffer buffer = GLFW.glfwGetVideoModes(id);
|
||||||
|
for(int i = buffer.limit() - 1;i >= 0;--i) {
|
||||||
|
VideoMode videomode = new VideoMode(buffer.position(i));
|
||||||
|
if(videomode.redBits() >= 8 && videomode.greenBits() >= 8 && videomode.blueBits() >= 8)
|
||||||
|
modes.add(videomode);
|
||||||
|
}
|
||||||
|
defaultMode = new VideoMode(GLFW.glfwGetVideoMode(id));
|
||||||
|
int[] xPos = new int[1];
|
||||||
|
int[] yPos = new int[1];
|
||||||
|
GLFW.glfwGetMonitorPos(id, xPos, yPos);
|
||||||
|
xOffset = xPos[0];
|
||||||
|
yOffset = yPos[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
//@formatter:off
|
||||||
|
public long id() { return id; }
|
||||||
|
public String name() { return GLFW.glfwGetMonitorName(id); }
|
||||||
|
public int size() { return modes.size(); }
|
||||||
|
public VideoMode getMode(int index) { return modes.get(index); }
|
||||||
|
public boolean has(VideoMode mode) { return modes.indexOf(mode) != -1; }
|
||||||
|
public List<VideoMode> videoModes() { return modes.unmodifiable(); }
|
||||||
|
public VideoMode defaultMode() { return defaultMode; }
|
||||||
|
public int xOffset() { return xOffset; }
|
||||||
|
public int yOffset() { return yOffset; }
|
||||||
|
//@formatter:on
|
||||||
|
|
||||||
|
public int getOverlap(int minX, int minY, int maxX, int maxY) {
|
||||||
|
int x = Math.max(0, Math.clamp(maxX, xOffset, xOffset + defaultMode.width()) - Math.clamp(minX, xOffset, xOffset + defaultMode.width()));
|
||||||
|
int y = Math.max(0, Math.clamp(maxY, yOffset, yOffset + defaultMode.height()) - Math.clamp(minY, yOffset, yOffset + defaultMode.height()));
|
||||||
|
return x * y;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) { return obj instanceof Monitor mon && mon.id == id; }
|
||||||
|
@Override
|
||||||
|
public int hashCode() { return Long.hashCode(id); }
|
||||||
|
@Override
|
||||||
|
public String toString() { return "Monitor[Name=\""+name()+"\", Id="+id+", Width="+defaultMode.width()+", Height="+defaultMode.height()+", Modes="+size()+"]"; }
|
||||||
|
|
||||||
|
|
||||||
|
public static Long2ObjectMap<Monitor> createMonitors() {
|
||||||
|
Long2ObjectMap<Monitor> monitors = new Long2ObjectLinkedOpenHashMap<>();
|
||||||
|
PointerBuffer buffer = GLFW.glfwGetMonitors();
|
||||||
|
for(int i = 0,m=buffer.limit();i<m;i++) {
|
||||||
|
long id = buffer.get();
|
||||||
|
monitors.put(id, new Monitor(id));
|
||||||
|
}
|
||||||
|
return monitors;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package speiger.src.coreengine.input.window;
|
||||||
|
|
||||||
|
import org.lwjgl.glfw.GLFWVidMode;
|
||||||
|
|
||||||
|
public record VideoMode(int width, int height, int redBits, int greenBits, int blueBits, int refreshrate) {
|
||||||
|
public VideoMode(GLFWVidMode buffer) {
|
||||||
|
this(buffer.width(), buffer.height(), buffer.redBits(), buffer.greenBits(), buffer.blueBits(), buffer.refreshRate());
|
||||||
|
}
|
||||||
|
|
||||||
|
public VideoMode(GLFWVidMode.Buffer buffer) {
|
||||||
|
this(buffer.width(), buffer.height(), buffer.redBits(), buffer.greenBits(), buffer.blueBits(), buffer.refreshRate());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,349 @@
|
|||||||
|
package speiger.src.coreengine.input.window;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.lwjgl.glfw.GLFW;
|
||||||
|
import org.lwjgl.system.CallbackI;
|
||||||
|
|
||||||
|
import speiger.src.collections.objects.lists.ObjectArrayList;
|
||||||
|
import speiger.src.coreengine.graphics.api.core.Graphics;
|
||||||
|
import speiger.src.coreengine.graphics.api.core.GraphicsDevice;
|
||||||
|
import speiger.src.coreengine.graphics.api.core.GraphicsSurface;
|
||||||
|
import speiger.src.coreengine.input.window.IWindowListener.Reason;
|
||||||
|
import speiger.src.coreengine.input.window.WindowCallback.ReloadFunction;
|
||||||
|
import speiger.src.coreengine.math.BitUtil;
|
||||||
|
import speiger.src.coreengine.math.vector.ints.Vec2i;
|
||||||
|
import speiger.src.coreengine.math.vector.ints.Vec4i;
|
||||||
|
import speiger.src.coreengine.utils.collections.FlagHolder;
|
||||||
|
|
||||||
|
public class Window {
|
||||||
|
static final int VISIBLE = 1;
|
||||||
|
static final int VSYNC = 2;
|
||||||
|
static final int FOCUS = 4;
|
||||||
|
static final int CPU_FPS_CAP = 8;
|
||||||
|
static final int FULL_SCREEN = 16;
|
||||||
|
static final int MAXIMIZED = 32;
|
||||||
|
static final int BORDERLESS = 64;
|
||||||
|
static final int RESIZABLE = 128;
|
||||||
|
static final int FLOATING = 256;
|
||||||
|
static final int CLOSE = 512;
|
||||||
|
static final int WINDOW_CHANGE = 1024;
|
||||||
|
WindowManager manager;
|
||||||
|
GraphicsDevice device;
|
||||||
|
GraphicsSurface surface;
|
||||||
|
FlagHolder flags = new FlagHolder();
|
||||||
|
long id;
|
||||||
|
VideoMode fullScreenMode;
|
||||||
|
String title = "";
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
|
||||||
|
int frameWidth;
|
||||||
|
int frameHeight;
|
||||||
|
|
||||||
|
Vec4i[] backup = new Vec4i[] {Vec4i.mutable(), Vec4i.mutable()};
|
||||||
|
int backupIndex = 0;
|
||||||
|
|
||||||
|
final int antialiasing;
|
||||||
|
List<WindowCallback> callbacks = new ObjectArrayList<>();
|
||||||
|
List<IWindowListener> listeners = new ObjectArrayList<>();
|
||||||
|
|
||||||
|
protected Window(WindowBuilder builder, Graphics graphics) {
|
||||||
|
manager = builder.manager;
|
||||||
|
title = builder.title;
|
||||||
|
frameWidth = width = builder.width;
|
||||||
|
frameHeight = height = builder.height;
|
||||||
|
antialiasing = builder.antiAlis;
|
||||||
|
fullScreenMode = builder.fullScreenTarget;
|
||||||
|
flags.setFlag(BORDERLESS, builder.borderless);
|
||||||
|
flags.setFlag(FULL_SCREEN, builder.fullScreen);
|
||||||
|
flags.setFlag(FLOATING, builder.floating);
|
||||||
|
flags.setFlag(RESIZABLE, builder.resizable);
|
||||||
|
flags.setFlag(VSYNC, builder.vsync);
|
||||||
|
flags.setFlag(CPU_FPS_CAP, builder.fpsCap);
|
||||||
|
Monitor monitor = manager.getMonitor(builder.monitor);
|
||||||
|
if(monitor == null) throw new IllegalStateException("Monitor is missing: "+monitor);
|
||||||
|
boolean fullscreen = builder.fullScreen;
|
||||||
|
VideoMode mode = monitor.defaultMode();
|
||||||
|
id = manager.joinOnWindowThread(() -> {
|
||||||
|
graphics.setupWindowArguments();
|
||||||
|
for(int i = 0,m=builder.windowHints.size();i<m;i++) {
|
||||||
|
long value = builder.windowHints.getLong(i);
|
||||||
|
GLFW.glfwWindowHint(BitUtil.intKey(value), BitUtil.intValue(value));
|
||||||
|
}
|
||||||
|
GLFW.glfwWindowHint(GLFW.GLFW_SAMPLES, antialiasing);
|
||||||
|
GLFW.glfwWindowHint(GLFW.GLFW_RESIZABLE, flags.isFlagSet(RESIZABLE) ? GLFW.GLFW_TRUE : GLFW.GLFW_FALSE);
|
||||||
|
GLFW.glfwWindowHint(GLFW.GLFW_DECORATED, flags.isFlagNotSet(FULL_SCREEN) && flags.isFlagSet(BORDERLESS) ? GLFW.GLFW_FALSE : GLFW.GLFW_TRUE);
|
||||||
|
GLFW.glfwWindowHint(GLFW.GLFW_FLOATING, flags.isFlagNotSet(FULL_SCREEN) && flags.isFlagSet(FLOATING) ? GLFW.GLFW_TRUE : GLFW.GLFW_FALSE);
|
||||||
|
GLFW.glfwWindowHint(GLFW.GLFW_MAXIMIZED, flags.isFlagNotSet(FULL_SCREEN) && flags.isFlagSet(MAXIMIZED) ? GLFW.GLFW_TRUE : GLFW.GLFW_FALSE);
|
||||||
|
return GLFW.glfwCreateWindow(fullscreen ? mode.width() : width, fullscreen ? mode.height() : height, title, builder.fullScreen ? monitor.id() : 0, manager.getPrimaryWindow());
|
||||||
|
});
|
||||||
|
if(id == 0) throw new IllegalStateException("Window Couldn't be Created");
|
||||||
|
manager.addWindow(this);
|
||||||
|
manager.joinOnWindowThread(this::createWindowListeners);
|
||||||
|
GLFW.glfwMakeContextCurrent(id);
|
||||||
|
device = graphics.createDevice(this);
|
||||||
|
surface = device.createSurface();
|
||||||
|
x = monitor.xOffset() + (builder.center ? (mode.width() / 2) - (width / 2) : 0);
|
||||||
|
y = monitor.yOffset() + (builder.center ? (mode.height() / 2) - (height / 2) : 0);
|
||||||
|
if(!fullscreen) manager.joinOnWindowThread(() -> GLFW.glfwSetWindowPos(id, x, y));
|
||||||
|
GLFW.glfwSwapInterval(flags.isFlagSet(VSYNC) ? 1 : 0);
|
||||||
|
fetchWindowBounds();
|
||||||
|
updateViewport();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void createWindowListeners() {
|
||||||
|
addCallback(this::framebuffer, GLFW::glfwSetFramebufferSizeCallback);
|
||||||
|
addCallback(this::focused, GLFW::glfwSetWindowFocusCallback);
|
||||||
|
addCallback(this::position, GLFW::glfwSetWindowPosCallback);
|
||||||
|
addCallback(this::bounds, GLFW::glfwSetWindowSizeCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void framebuffer(long window, int width, int height) {
|
||||||
|
frameWidth = width;
|
||||||
|
frameHeight = height;
|
||||||
|
flags.setFlag(WINDOW_CHANGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void focused(long window, boolean focused) {
|
||||||
|
manager.updateFocus(this, focused);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void position(long window, int x, int y) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
flags.setFlag(WINDOW_CHANGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void bounds(long window, int width, int height) {
|
||||||
|
this.width = width;
|
||||||
|
this.height = height;
|
||||||
|
flags.setFlag(WINDOW_CHANGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void fetchWindowBounds() {
|
||||||
|
Vec2i result = manager.joinOnWindowThread(() -> {
|
||||||
|
int[] width = new int[1];
|
||||||
|
int[] height = new int[1];
|
||||||
|
GLFW.glfwGetWindowSize(id, width, height);
|
||||||
|
return Vec2i.of(width[0], height[0]);
|
||||||
|
});
|
||||||
|
this.frameWidth = result.x();
|
||||||
|
this.frameHeight = result.y();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateViewport() {
|
||||||
|
flags.clearFlag(WINDOW_CHANGE);
|
||||||
|
// GLStateTracker.instance().viewPort.setDefault(0, 0, frameWidth, frameHeight);
|
||||||
|
for(int i = 0,m=listeners.size();i<m;i++) {
|
||||||
|
listeners.get(i).onChanged(this, Reason.CHANGE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setupContext() {
|
||||||
|
GLFW.glfwMakeContextCurrent(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void beginFrame() {
|
||||||
|
setupContext();
|
||||||
|
if(flags.isFlagSet(WINDOW_CHANGE)) updateViewport();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handleInput() { manager.processDevices(id); }
|
||||||
|
public boolean shouldClose() { return flags.isFlagSet(CLOSE) || GLFW.glfwWindowShouldClose(id); }
|
||||||
|
public void finishFrame() { GLFW.glfwSwapBuffers(id); }
|
||||||
|
|
||||||
|
public void destroy() {
|
||||||
|
manager.removeWindow(id);
|
||||||
|
for(int i = 0,m=listeners.size();i<m;i++) {
|
||||||
|
listeners.get(i).onChanged(this, Reason.CLOSING);
|
||||||
|
}
|
||||||
|
manager.joinOnWindowThread(() -> {
|
||||||
|
GLFW.glfwDestroyWindow(id);
|
||||||
|
callbacks.forEach(WindowCallback::destroy);
|
||||||
|
callbacks.clear();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <T extends CallbackI> WindowCallback addCallback(T listener, ReloadFunction<T> function) {
|
||||||
|
if(!manager.isManagerThread()) return manager.joinOnWindowThread(() -> addCallback(listener, function));
|
||||||
|
WindowCallback callback = new WindowCallback(listener, (ReloadFunction<CallbackI>)function);
|
||||||
|
callbacks.add(callback);
|
||||||
|
callback.load(id);
|
||||||
|
return callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeCallback(WindowCallback callback) {
|
||||||
|
if(callbacks.remove(callback)) {
|
||||||
|
manager.joinOnWindowThread(callback::destroy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addListener(IWindowListener listener) {
|
||||||
|
listeners.add(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeListener(IWindowListener listener) {
|
||||||
|
listeners.remove(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void title(String name) {
|
||||||
|
if(name == null || title.equals(name)) return;
|
||||||
|
title = name;
|
||||||
|
manager.joinOnWindowThread(() -> GLFW.glfwSetWindowTitle(id, title));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void vsync(boolean vsync) {
|
||||||
|
if(!flags.setFlag(VSYNC, vsync)) return;
|
||||||
|
GLFW.glfwSwapInterval(vsync ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void fpsCap(boolean fpsCap) {
|
||||||
|
flags.setFlag(CPU_FPS_CAP, fpsCap);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void visible(boolean visible) {
|
||||||
|
if(!flags.setFlag(VISIBLE, visible)) return;
|
||||||
|
if(visible) manager.joinOnWindowThread(() -> GLFW.glfwShowWindow(id));
|
||||||
|
else manager.joinOnWindowThread(() -> GLFW.glfwHideWindow(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void floating(boolean floating) {
|
||||||
|
if(flags.isFlagNotSet(FULL_SCREEN) && flags.setFlag(FLOATING, floating)) {
|
||||||
|
manager.joinOnWindowThread(() -> GLFW.glfwSetWindowAttrib(id, GLFW.GLFW_FLOATING, floating ? GLFW.GLFW_TRUE : GLFW.GLFW_FALSE));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void maximized(boolean maximized) {
|
||||||
|
if(flags.isFlagNotSet(FULL_SCREEN) && flags.setFlag(MAXIMIZED, maximized)) {
|
||||||
|
if(maximized) {
|
||||||
|
backupSize();
|
||||||
|
manager.joinOnWindowThread(() -> GLFW.glfwMaximizeWindow(id));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
manager.joinOnWindowThread(() -> GLFW.glfwRestoreWindow(id));
|
||||||
|
restoreSize();
|
||||||
|
}
|
||||||
|
fetchWindowBounds();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void resizeable(boolean resizeable) {
|
||||||
|
if(flags.isFlagNotSet(FULL_SCREEN) && flags.setFlag(RESIZABLE, resizeable)) {
|
||||||
|
manager.joinOnWindowThread(() -> 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)) {
|
||||||
|
manager.joinOnWindowThread(() -> GLFW.glfwSetWindowAttrib(id, GLFW.GLFW_DECORATED, borderless ? GLFW.GLFW_FALSE : GLFW.GLFW_TRUE));
|
||||||
|
if(flags.isFlagSet(MAXIMIZED)) {
|
||||||
|
manager.joinOnWindowThread(() -> {
|
||||||
|
GLFW.glfwRestoreWindow(id);
|
||||||
|
GLFW.glfwMaximizeWindow(id);
|
||||||
|
});
|
||||||
|
fetchWindowBounds();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void fullscreen(boolean fullscreen) {
|
||||||
|
if(flags.setFlag(FULL_SCREEN, fullscreen)) {
|
||||||
|
if(fullscreen) {
|
||||||
|
Monitor monitor = manager.getMonitorForWindow(this);
|
||||||
|
if(monitor == null) {
|
||||||
|
flags.clearFlag(FULL_SCREEN);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
backupSize();
|
||||||
|
VideoMode mode = fullScreenMode == null || !monitor.has(fullScreenMode) ? monitor.defaultMode() : fullScreenMode;
|
||||||
|
x = 0;
|
||||||
|
y = 0;
|
||||||
|
width = mode.width();
|
||||||
|
height = mode.height();
|
||||||
|
manager.joinOnWindowThread(() -> GLFW.glfwSetWindowMonitor(id, monitor.id(), 0, 0, mode.width(), mode.height(), mode.refreshrate()));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
restoreSize();
|
||||||
|
manager.joinOnWindowThread(() -> {
|
||||||
|
GLFW.glfwSetWindowMonitor(id, 0, x, y, width, height, -1);
|
||||||
|
if(flags.isFlagSet(BORDERLESS)) {
|
||||||
|
GLFW.glfwSetWindowAttrib(id, GLFW.GLFW_DECORATED, GLFW.GLFW_FALSE);
|
||||||
|
}
|
||||||
|
if(flags.isFlagSet(MAXIMIZED)) {
|
||||||
|
GLFW.glfwMaximizeWindow(id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
fetchWindowBounds();
|
||||||
|
updateViewport();
|
||||||
|
GLFW.glfwSwapInterval(flags.isFlagSet(VSYNC) ? 1 : 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void backupSize() {
|
||||||
|
if(backupIndex >= backup.length) return;
|
||||||
|
backup[backupIndex++].set(x, y, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void restoreSize() {
|
||||||
|
if(backupIndex == 0) return;
|
||||||
|
Vec4i prev = backup[--backupIndex];
|
||||||
|
x = prev.x();
|
||||||
|
y = prev.y();
|
||||||
|
width = prev.z();
|
||||||
|
height = prev.w();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean position(int x, int y) {
|
||||||
|
if(flags.isAnyFlagSet(MAXIMIZED | FULL_SCREEN) || (this.x == x && this.y == y)) return false;
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
manager.joinOnWindowThread(() -> GLFW.glfwSetWindowPos(id, x, y));
|
||||||
|
flags.setFlag(WINDOW_CHANGE);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean size(int width, int height) {
|
||||||
|
if(flags.isAnyFlagSet(MAXIMIZED | FULL_SCREEN) || (this.width == width && this.height == height)) return false;
|
||||||
|
this.width = width;
|
||||||
|
this.height = height;
|
||||||
|
manager.joinOnWindowThread(() -> GLFW.glfwSetWindowSize(id, width, height));
|
||||||
|
flags.setFlag(WINDOW_CHANGE);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean width(int width) { return size(width, height); }
|
||||||
|
public boolean height(int height) { return size(width, height); }
|
||||||
|
|
||||||
|
public long id() { return id; }
|
||||||
|
public boolean isPrimaryWindow() { return manager.primaryWindow == this; }
|
||||||
|
public VideoMode desiredFullScreen() { return fullScreenMode; }
|
||||||
|
|
||||||
|
public WindowManager manager() { return manager; }
|
||||||
|
public GraphicsDevice device() { return device; }
|
||||||
|
public GraphicsSurface surface() { return surface; }
|
||||||
|
|
||||||
|
public int x() { return x; }
|
||||||
|
public int y() { return y; }
|
||||||
|
public int width() { return frameWidth; }
|
||||||
|
public int height() { return frameHeight; }
|
||||||
|
public int screenWidth() { return width; }
|
||||||
|
public int screenHeight() { return height; }
|
||||||
|
public boolean changed() { return flags.isFlagSet(WINDOW_CHANGE); }
|
||||||
|
|
||||||
|
public String title() { return title; }
|
||||||
|
public boolean isVsync() { return flags.isFlagSet(VSYNC); }
|
||||||
|
public boolean shouldFPSCap() { return flags.isFlagSet(CPU_FPS_CAP); }
|
||||||
|
public boolean isVisible() { return flags.isFlagSet(VISIBLE); }
|
||||||
|
public boolean isFloating() { return flags.isFlagSet(FLOATING); }
|
||||||
|
public boolean isMaximized() { return flags.isFlagSet(MAXIMIZED); }
|
||||||
|
public boolean isResizeable() { return flags.isFlagSet(RESIZABLE); }
|
||||||
|
public boolean isBorderless() { return flags.isFlagSet(BORDERLESS); }
|
||||||
|
public boolean isFullscreen() { return flags.isFlagSet(FULL_SCREEN); }
|
||||||
|
public int antialiasing() { return antialiasing; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
package speiger.src.coreengine.input.window;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import org.lwjgl.glfw.GLFW;
|
||||||
|
|
||||||
|
import speiger.src.collections.longs.lists.LongArrayList;
|
||||||
|
import speiger.src.collections.longs.lists.LongList;
|
||||||
|
import speiger.src.coreengine.math.BitUtil;
|
||||||
|
|
||||||
|
public class WindowBuilder {
|
||||||
|
WindowManager manager;
|
||||||
|
long monitor;
|
||||||
|
LongList windowHints = new LongArrayList();
|
||||||
|
VideoMode fullScreenTarget;
|
||||||
|
String title = "";
|
||||||
|
int width = 640;
|
||||||
|
int height = 480;
|
||||||
|
int antiAlis = 4;
|
||||||
|
boolean vsync = true;
|
||||||
|
boolean fpsCap;
|
||||||
|
boolean fullScreen;
|
||||||
|
boolean maximized;
|
||||||
|
boolean resizable = true;
|
||||||
|
boolean floating;
|
||||||
|
boolean borderless;
|
||||||
|
boolean center = true;
|
||||||
|
|
||||||
|
WindowBuilder(WindowManager manager) {
|
||||||
|
this.manager = manager;
|
||||||
|
monitor = GLFW.glfwGetPrimaryMonitor();
|
||||||
|
}
|
||||||
|
|
||||||
|
public WindowBuilder title(String title) {
|
||||||
|
this.title = Objects.requireNonNull(title);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public WindowBuilder width(int width) {
|
||||||
|
this.width = Math.max(1, width);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public WindowBuilder height(int height) {
|
||||||
|
this.height = Math.max(1, height);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public WindowBuilder antialis(int antiAlis) {
|
||||||
|
this.antiAlis = Math.max(1, antiAlis);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public WindowBuilder fullscreen(boolean fullScreen) {
|
||||||
|
this.fullScreen = fullScreen;
|
||||||
|
borderless &= !fullScreen;
|
||||||
|
floating &= !fullScreen;
|
||||||
|
maximized &= !fullScreen;
|
||||||
|
center &= !fullScreen;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public WindowBuilder maximized(boolean maximized) {
|
||||||
|
this.maximized = maximized;
|
||||||
|
this.fullScreen &= !maximized;
|
||||||
|
center &= !maximized;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public WindowBuilder borderless(boolean borderless) {
|
||||||
|
this.borderless = borderless;
|
||||||
|
fullScreen &= !borderless;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public WindowBuilder floating(boolean floating) {
|
||||||
|
this.floating = floating;
|
||||||
|
fullScreen &= !floating;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public WindowBuilder resizeable(boolean resizable) {
|
||||||
|
this.resizable = resizable;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public WindowBuilder centered(boolean center) {
|
||||||
|
this.center = center;
|
||||||
|
this.maximized &= !center;
|
||||||
|
this.fullScreen &= !center;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public WindowBuilder monitor(Monitor monitor) {
|
||||||
|
if(monitor == null || monitor.defaultMode() == null) return this;
|
||||||
|
this.monitor = monitor.id();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public WindowBuilder fullScreenMode(VideoMode mode) {
|
||||||
|
this.fullScreenTarget = mode;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public WindowBuilder vsync(boolean value) {
|
||||||
|
vsync = value;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public WindowBuilder fpsCap(boolean cap) {
|
||||||
|
fpsCap = cap;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public WindowBuilder addCustomHint(int key, int value) {
|
||||||
|
windowHints.add(BitUtil.toLong(key, value));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Window build() {
|
||||||
|
return manager.create(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package speiger.src.coreengine.input.window;
|
||||||
|
|
||||||
|
import org.lwjgl.system.Callback;
|
||||||
|
import org.lwjgl.system.CallbackI;
|
||||||
|
|
||||||
|
public class WindowCallback {
|
||||||
|
ReloadFunction<CallbackI> function;
|
||||||
|
CallbackI listener;
|
||||||
|
Callback callback;
|
||||||
|
|
||||||
|
public WindowCallback(CallbackI listener, ReloadFunction<CallbackI> function) {
|
||||||
|
this.function = function;
|
||||||
|
this.listener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reload(long windowId) {
|
||||||
|
destroy();
|
||||||
|
load(windowId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void load(long windowId) {
|
||||||
|
callback = function.applyListener(windowId, listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void destroy() {
|
||||||
|
if(callback == null) return;
|
||||||
|
callback.free();
|
||||||
|
callback = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static interface ReloadFunction<T extends CallbackI> {
|
||||||
|
Callback applyListener(long window, T listener);
|
||||||
|
|
||||||
|
}
|
||||||
|
public static interface SimpleReloadFunction<T extends CallbackI> extends ReloadFunction<T> {
|
||||||
|
Callback applyListener(T listener);
|
||||||
|
@Override
|
||||||
|
default Callback applyListener(long window, T listener) { return applyListener(listener); }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,236 @@
|
|||||||
|
package speiger.src.coreengine.input.window;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.BlockingQueue;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
import org.lwjgl.glfw.GLFW;
|
||||||
|
import org.lwjgl.system.Callback;
|
||||||
|
import org.lwjgl.system.CallbackI;
|
||||||
|
import org.lwjgl.system.Platform;
|
||||||
|
|
||||||
|
import speiger.src.collections.longs.maps.impl.concurrent.Long2ObjectConcurrentOpenHashMap;
|
||||||
|
import speiger.src.collections.longs.maps.interfaces.Long2ObjectMap;
|
||||||
|
import speiger.src.collections.objects.lists.ObjectArrayList;
|
||||||
|
import speiger.src.coreengine.graphics.api.core.Graphics;
|
||||||
|
import speiger.src.coreengine.input.device.InputDevice;
|
||||||
|
import speiger.src.coreengine.input.window.WindowCallback.SimpleReloadFunction;
|
||||||
|
|
||||||
|
public class WindowManager {
|
||||||
|
public static final ScopedValue<WindowManager> MANAGER = ScopedValue.newInstance();
|
||||||
|
BlockingQueue<Runnable> tasks = new LinkedBlockingQueue<>();
|
||||||
|
Graphics graphcis;
|
||||||
|
|
||||||
|
Long2ObjectMap<Window> windows = new Long2ObjectConcurrentOpenHashMap<>();
|
||||||
|
Long2ObjectMap<Monitor> monitors;
|
||||||
|
Window activeWindow;
|
||||||
|
Window primaryWindow;
|
||||||
|
|
||||||
|
|
||||||
|
List<WindowCallback> callbacks = new ObjectArrayList<>();
|
||||||
|
Callback monitorTracker;
|
||||||
|
List<InputDevice> devices = new ObjectArrayList<>();
|
||||||
|
|
||||||
|
boolean closing = false;
|
||||||
|
Thread manager;
|
||||||
|
|
||||||
|
public void setup(Runnable runnable, Graphics graphics) {
|
||||||
|
if(this.graphcis != null) throw new IllegalStateException("We already init the Graphis Pipeline");
|
||||||
|
this.graphcis = graphics;
|
||||||
|
int[] major = new int[1];
|
||||||
|
int[] minor = new int[1];
|
||||||
|
int[] revision = new int[1];
|
||||||
|
GLFW.glfwGetVersion(major, minor, revision);
|
||||||
|
System.out.println("GLFW Version ["+major[0]+"."+minor[0]+"."+revision[0]+"] found");
|
||||||
|
GLFW.glfwSetErrorCallback((E, D) -> System.out.println(GLFWUtil.glfwError(E, D)));
|
||||||
|
|
||||||
|
manager = Thread.currentThread();
|
||||||
|
manager.setName("Window Manager");
|
||||||
|
|
||||||
|
if(Platform.get() == Platform.LINUX) {
|
||||||
|
GLFW.glfwInitHint(GLFW.GLFW_PLATFORM, GLFW.glfwPlatformSupported(GLFW.GLFW_PLATFORM_WAYLAND) ? GLFW.GLFW_PLATFORM_WAYLAND : GLFW.GLFW_PLATFORM_X11);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!GLFW.glfwInit()) {
|
||||||
|
throw new IllegalStateException("GLFW Couldn't init. Reason: "+GLFWUtil.glfwError());
|
||||||
|
}
|
||||||
|
|
||||||
|
monitors = Monitor.createMonitors();
|
||||||
|
addCallback(this::onMonitorChanged, GLFW::glfwSetMonitorCallback);
|
||||||
|
|
||||||
|
Thread.ofPlatform().start(() -> ScopedValue.where(MANAGER, this).run(runnable));
|
||||||
|
while(!closing) {
|
||||||
|
Runnable task = null;
|
||||||
|
try { task = tasks.take(); }
|
||||||
|
catch(Exception e) { e.printStackTrace(); }
|
||||||
|
if(task == null) continue;
|
||||||
|
try { task.run(); }
|
||||||
|
catch(Exception e) { e.printStackTrace(); }
|
||||||
|
}
|
||||||
|
while(!tasks.isEmpty()) {
|
||||||
|
try { tasks.poll().run(); }
|
||||||
|
catch(Exception e) { e.printStackTrace(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks.forEach(WindowCallback::destroy);
|
||||||
|
callbacks.clear();
|
||||||
|
|
||||||
|
System.out.println("GLFW Shutting down");
|
||||||
|
GLFW.glfwTerminate();
|
||||||
|
GLFW.glfwSetErrorCallback(null).free();
|
||||||
|
|
||||||
|
System.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static WindowManager get() {
|
||||||
|
return MANAGER.orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isManagerThread() {
|
||||||
|
return manager.threadId() == Thread.currentThread().threadId();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void joinOnWindowThread(Runnable run) {
|
||||||
|
CompletableFuture.runAsync(run, tasks::add).join();
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> T joinOnWindowThread(Supplier<T> provider) {
|
||||||
|
return CompletableFuture.supplyAsync(provider, tasks::add).join();
|
||||||
|
}
|
||||||
|
|
||||||
|
public CompletableFuture<Void> runOnWindowThread(Runnable run) {
|
||||||
|
return CompletableFuture.runAsync(run, tasks::add);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> CompletableFuture<T> runOnWindowThread(Supplier<T> provider) {
|
||||||
|
return CompletableFuture.supplyAsync(provider, tasks::add);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void shutdown() {
|
||||||
|
closing = true;
|
||||||
|
tasks.add(() -> {});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onMonitorChanged(long monitor, int event) {
|
||||||
|
switch(event) {
|
||||||
|
case GLFW.GLFW_CONNECTED -> monitors.put(monitor, new Monitor(monitor));
|
||||||
|
case GLFW.GLFW_DISCONNECTED -> monitors.remove(monitor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public WindowBuilder builder() { return new WindowBuilder(this); }
|
||||||
|
Window create(WindowBuilder builder) { return new Window(builder, graphcis); }
|
||||||
|
|
||||||
|
public void addDevices(InputDevice...devices) {
|
||||||
|
for(InputDevice device : devices) addDevice(device);
|
||||||
|
}
|
||||||
|
public void addDevice(InputDevice device) {
|
||||||
|
devices.add(device);
|
||||||
|
windows.values().forEach(device::register);
|
||||||
|
}
|
||||||
|
public void removeDevice(InputDevice device) { devices.remove(device); }
|
||||||
|
public void processDevices(long windowId) {
|
||||||
|
for(int i = 0,m=devices.size();i<m;i++) {
|
||||||
|
devices.get(i).processInput(windowId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <T extends CallbackI> WindowCallback addCallback(T listener, SimpleReloadFunction<T> function) {
|
||||||
|
WindowCallback callback = new WindowCallback(listener, (SimpleReloadFunction<CallbackI>)function);
|
||||||
|
callbacks.add(callback);
|
||||||
|
callback.load(0L);
|
||||||
|
return callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeCallback(WindowCallback callback) {
|
||||||
|
if(callbacks.remove(callback)) {
|
||||||
|
callback.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void addWindow(Window window) {
|
||||||
|
windows.put(window.id(), window);
|
||||||
|
if(primaryWindow == null) primaryWindow = window;
|
||||||
|
for(int i = 0,m=devices.size();i<m;i++) {
|
||||||
|
devices.get(i).register(window);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateWindow(long oldId) {
|
||||||
|
Window prev = windows.remove(oldId);
|
||||||
|
if(prev == null) return;
|
||||||
|
windows.put(prev.id(), prev);
|
||||||
|
}
|
||||||
|
|
||||||
|
void removeWindow(long id) {
|
||||||
|
Window window = windows.remove(id);
|
||||||
|
if(window == null) return;
|
||||||
|
if(window == activeWindow) activeWindow = null;
|
||||||
|
if(window == primaryWindow) {
|
||||||
|
Iterator<Window> iter = windows.values().iterator();
|
||||||
|
primaryWindow = iter.hasNext() ? iter.next() : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateFocus(Window window, boolean focus) {
|
||||||
|
if(focus) this.activeWindow = window;
|
||||||
|
else if(activeWindow == window) activeWindow = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateInputs(long windowId) {
|
||||||
|
for(int i = 0,m=devices.size();i<m;i++) {
|
||||||
|
InputDevice device = devices.get(i);
|
||||||
|
device.reset(windowId);
|
||||||
|
device.processInput(windowId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Graphics graphics() {
|
||||||
|
return graphcis;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getActiveWindow() {
|
||||||
|
return activeWindow == null ? 0L : activeWindow.id();
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getPrimaryWindow() {
|
||||||
|
return primaryWindow == null ? 0 : primaryWindow.id();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Window getWindow(long window) {
|
||||||
|
return windows.get(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Monitor getMonitor(long id) {
|
||||||
|
return monitors.get(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Monitor getPriamryMonitor() {
|
||||||
|
return getMonitor(GLFW.glfwGetPrimaryMonitor());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Monitor getMonitorForWindow(Window window) {
|
||||||
|
long current = GLFW.glfwGetWindowMonitor(window.id());
|
||||||
|
if(current != 0L) return getMonitor(current);
|
||||||
|
int minX = window.x();
|
||||||
|
int minY = window.y();
|
||||||
|
int maxX = minX + window.screenWidth();
|
||||||
|
int maxY = minY + window.screenHeight();
|
||||||
|
int largest = 0;
|
||||||
|
Monitor mon = null;
|
||||||
|
for(Monitor monitor : monitors.values()) {
|
||||||
|
int next = monitor.getOverlap(minX, minY, maxX, maxY);
|
||||||
|
if(next > largest) {
|
||||||
|
largest = next;
|
||||||
|
mon = monitor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mon;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -70,14 +70,14 @@ public class NewInputTest {
|
|||||||
public void run() {
|
public void run() {
|
||||||
Configuration.HARFBUZZ_LIBRARY_NAME.set(FreeType.getLibrary());
|
Configuration.HARFBUZZ_LIBRARY_NAME.set(FreeType.getLibrary());
|
||||||
GLFW.glfwInit();
|
GLFW.glfwInit();
|
||||||
manager.initialize();
|
manager.initialize(null);
|
||||||
Mouse.INSTANCE.init(bus);
|
Mouse.INSTANCE.init(bus);
|
||||||
Keyboard.INSTANCE.init(bus);
|
Keyboard.INSTANCE.init(bus);
|
||||||
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(null);
|
Window window = manager.builder().title("Testing Engine").width(800).height(600).antialis(0).build();
|
||||||
Window secondWindow = manager.builder().title("Second Window Engine").width(800).height(600).antialis(0).build(null);
|
Window secondWindow = manager.builder().title("Second Window Engine").width(800).height(600).antialis(0).build();
|
||||||
Thread.ofPlatform().start(() -> drawSecondScreen(secondWindow));
|
Thread.ofPlatform().start(() -> drawSecondScreen(secondWindow));
|
||||||
window.setupContext();
|
window.setupContext();
|
||||||
shaderTest.register();
|
shaderTest.register();
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package speiger.src.coreengine;
|
|||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.ByteOrder;
|
import java.nio.ByteOrder;
|
||||||
|
|
||||||
import org.lwjgl.glfw.GLFW;
|
|
||||||
import org.lwjgl.opengl.GL11;
|
import org.lwjgl.opengl.GL11;
|
||||||
import org.lwjgl.system.Configuration;
|
import org.lwjgl.system.Configuration;
|
||||||
import org.lwjgl.util.freetype.FreeType;
|
import org.lwjgl.util.freetype.FreeType;
|
||||||
@@ -14,7 +13,6 @@ 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;
|
||||||
import speiger.src.coreengine.graphics.api.core.Graphics;
|
|
||||||
import speiger.src.coreengine.graphics.api.core.GraphicsCommandBuffer;
|
import speiger.src.coreengine.graphics.api.core.GraphicsCommandBuffer;
|
||||||
import speiger.src.coreengine.graphics.api.core.GraphicsDevice;
|
import speiger.src.coreengine.graphics.api.core.GraphicsDevice;
|
||||||
import speiger.src.coreengine.graphics.api.core.GraphicsSurface;
|
import speiger.src.coreengine.graphics.api.core.GraphicsSurface;
|
||||||
@@ -34,13 +32,13 @@ import speiger.src.coreengine.graphics.api.utils.ExecutionType;
|
|||||||
import speiger.src.coreengine.graphics.api.vertex.VertexLayout;
|
import speiger.src.coreengine.graphics.api.vertex.VertexLayout;
|
||||||
import speiger.src.coreengine.graphics.api.vertex.VertexLayout.Usage;
|
import speiger.src.coreengine.graphics.api.vertex.VertexLayout.Usage;
|
||||||
import speiger.src.coreengine.graphics.opengl.core.GLGraphics;
|
import speiger.src.coreengine.graphics.opengl.core.GLGraphics;
|
||||||
|
import speiger.src.coreengine.input.device.FileDrop;
|
||||||
|
import speiger.src.coreengine.input.device.Joystick;
|
||||||
|
import speiger.src.coreengine.input.device.Keyboard;
|
||||||
|
import speiger.src.coreengine.input.device.Mouse;
|
||||||
|
import speiger.src.coreengine.input.window.Window;
|
||||||
|
import speiger.src.coreengine.input.window.WindowManager;
|
||||||
import speiger.src.coreengine.math.vector.matrix.Matrix4f;
|
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.buffer.VertexBuilder;
|
||||||
import speiger.src.coreengine.rendering.tesselation.format.VertexTypes;
|
import speiger.src.coreengine.rendering.tesselation.format.VertexTypes;
|
||||||
import speiger.src.coreengine.rendering.textures.custom.Drawable;
|
import speiger.src.coreengine.rendering.textures.custom.Drawable;
|
||||||
@@ -54,24 +52,20 @@ public class NewRenderEngineTest {
|
|||||||
AssetManager assets = AssetManager.single(IAssetPackage.asset(IOUtils.getBaseLocation()));
|
AssetManager assets = AssetManager.single(IAssetPackage.asset(IOUtils.getBaseLocation()));
|
||||||
|
|
||||||
public static void main(String... args) {
|
public static void main(String... args) {
|
||||||
new NewRenderEngineTest().run();
|
Configuration.HARFBUZZ_LIBRARY_NAME.set(FreeType.getLibrary());
|
||||||
|
NewRenderEngineTest test = new NewRenderEngineTest();
|
||||||
|
test.manager.setup(test::run, new GLGraphics(test.assets));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void run() {
|
public void run() {
|
||||||
Configuration.HARFBUZZ_LIBRARY_NAME.set(FreeType.getLibrary());
|
|
||||||
GLFW.glfwInit();
|
|
||||||
manager.initialize();
|
|
||||||
Mouse.INSTANCE.init(bus);
|
Mouse.INSTANCE.init(bus);
|
||||||
Keyboard.INSTANCE.init(bus);
|
Keyboard.INSTANCE.init(bus);
|
||||||
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);
|
||||||
Graphics graphics = new GLGraphics(assets);
|
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(graphics);
|
Thread.currentThread().setName("Main Thread");
|
||||||
GraphicsDevice device = graphics.createDevice(window);
|
GraphicsDevice device = window.device();
|
||||||
window.setupContext();
|
|
||||||
|
|
||||||
GraphicsSurface surface = device.createSurface();
|
|
||||||
int size = 512;
|
int size = 512;
|
||||||
int half = size >> 1;
|
int half = size >> 1;
|
||||||
int base = size >> 3;
|
int base = size >> 3;
|
||||||
@@ -84,15 +78,6 @@ public class NewRenderEngineTest {
|
|||||||
|
|
||||||
VertexBuilder builder = new VertexBuilder(255);
|
VertexBuilder builder = new VertexBuilder(255);
|
||||||
builder.start(null, VertexTypes.TESTING);
|
builder.start(null, VertexTypes.TESTING);
|
||||||
|
|
||||||
// builder.pos(350F, 250F, 0).tex(0F, 1F).rgba(-1).endVertex(); // Unten Links
|
|
||||||
// builder.pos(450F, 350F, 0).tex(1F, 0F).rgba(-1).endVertex(); // Oben Rechts
|
|
||||||
// builder.pos(450F, 250F, 0).tex(1F, 1F).rgba(-1).endVertex(); // Unten Rechts
|
|
||||||
//
|
|
||||||
// builder.pos(450F, 350F, 0).tex(1F, 0F).rgba(-1).endVertex(); // Oben Rechts
|
|
||||||
// builder.pos(350F, 250F, 0).tex(0F, 1F).rgba(-1).endVertex(); // Unten Links
|
|
||||||
// builder.pos(350F, 350F, 0).tex(0F, 0F).rgba(-1).endVertex(); // Oben Links
|
|
||||||
|
|
||||||
builder.pos(-0.5F, -0.5F, 0).tex(0F, 1F).rgba(-1).endVertex();
|
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, 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();
|
||||||
@@ -126,16 +111,22 @@ public class NewRenderEngineTest {
|
|||||||
.withStage(ShaderType.FRAGMENT, ID.of("shader/newTestGui/fragment.fs"))
|
.withStage(ShaderType.FRAGMENT, ID.of("shader/newTestGui/fragment.fs"))
|
||||||
.withTexture("texture", 0, 0, TextureType.TEXTURE_2D)
|
.withTexture("texture", 0, 0, TextureType.TEXTURE_2D)
|
||||||
.withUniform("Camera", 0, 0, 64)
|
.withUniform("Camera", 0, 0, 64)
|
||||||
.withFormat()
|
.withFormat(layout)
|
||||||
.attribute("in_position", 0, 3)
|
|
||||||
.attribute("in_tex", 1, 2)
|
|
||||||
.attribute("in_color", 2, 4, GraphicsDataType.UNSIGNED_BYTE, true)
|
|
||||||
.endFormat()
|
|
||||||
.build();
|
.build();
|
||||||
window.visible(true);
|
window.visible(true);
|
||||||
GraphicsCommandBuffer queue = device.createCommandBuffer(ExecutionType.IMMEDIATE);
|
GraphicsCommandBuffer queue = device.createCommandBuffer(ExecutionType.RECORDED);
|
||||||
|
|
||||||
|
queue.begin()
|
||||||
|
.pipeline(pipeline)
|
||||||
|
.mesh(mesh)
|
||||||
|
.texture(0, texture, sampler)
|
||||||
|
.uniform(0, buffer)
|
||||||
|
.drawArrays(0, 6)
|
||||||
|
.end();
|
||||||
|
|
||||||
GL11.glViewport(0, 0, window.width(), window.height());
|
GL11.glViewport(0, 0, window.width(), window.height());
|
||||||
|
|
||||||
|
GraphicsSurface surface = window.surface();
|
||||||
while(!window.shouldClose()) {
|
while(!window.shouldClose()) {
|
||||||
surface.beginFrame();
|
surface.beginFrame();
|
||||||
if(window.changed()) {
|
if(window.changed()) {
|
||||||
@@ -144,24 +135,19 @@ public class NewRenderEngineTest {
|
|||||||
new Matrix4f().ortho(0, 0, window.width() >> scale, window.height() >> scale, 1000, -1000).store(mat);
|
new Matrix4f().ortho(0, 0, window.width() >> scale, window.height() >> scale, 1000, -1000).store(mat);
|
||||||
buffer.bind().set(mat.flip().array()).unbind();
|
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();
|
|
||||||
|
|
||||||
|
device.queue().submitCommands(queue);
|
||||||
|
device.queue().submitFrame();
|
||||||
surface.finishFrame();
|
surface.finishFrame();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Thread.sleep(1);
|
Thread.sleep(10);
|
||||||
}
|
}
|
||||||
catch(Exception e) {
|
catch(Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
window.destroy();
|
||||||
|
manager.shutdown();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,12 +9,13 @@ import org.lwjgl.system.CallbackI;
|
|||||||
|
|
||||||
import speiger.src.collections.objects.lists.ObjectArrayList;
|
import speiger.src.collections.objects.lists.ObjectArrayList;
|
||||||
import speiger.src.coreengine.graphics.api.core.Graphics;
|
import speiger.src.coreengine.graphics.api.core.Graphics;
|
||||||
|
import speiger.src.coreengine.graphics.api.core.GraphicsDevice;
|
||||||
|
import speiger.src.coreengine.graphics.api.core.GraphicsSurface;
|
||||||
import speiger.src.coreengine.math.BitUtil;
|
import speiger.src.coreengine.math.BitUtil;
|
||||||
import speiger.src.coreengine.math.vector.ints.Vec4i;
|
import speiger.src.coreengine.math.vector.ints.Vec4i;
|
||||||
import speiger.src.coreengine.rendering.input.window.IWindowListener.Reason;
|
import speiger.src.coreengine.rendering.input.window.IWindowListener.Reason;
|
||||||
import speiger.src.coreengine.rendering.input.window.WindowCallback.ReloadFunction;
|
import speiger.src.coreengine.rendering.input.window.WindowCallback.ReloadFunction;
|
||||||
import speiger.src.coreengine.rendering.input.window.WindowManager.WindowBuilder;
|
import speiger.src.coreengine.rendering.input.window.WindowManager.WindowBuilder;
|
||||||
import speiger.src.coreengine.rendering.utils.GLStateTracker;
|
|
||||||
import speiger.src.coreengine.utils.collections.FlagHolder;
|
import speiger.src.coreengine.utils.collections.FlagHolder;
|
||||||
|
|
||||||
public class Window {
|
public class Window {
|
||||||
@@ -30,6 +31,8 @@ public class Window {
|
|||||||
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;
|
||||||
|
GraphicsDevice device;
|
||||||
|
GraphicsSurface surface;
|
||||||
FlagHolder flags = new FlagHolder();
|
FlagHolder flags = new FlagHolder();
|
||||||
long id;
|
long id;
|
||||||
VideoMode fullScreenMode;
|
VideoMode fullScreenMode;
|
||||||
@@ -63,8 +66,7 @@ public class Window {
|
|||||||
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);
|
||||||
if(graphics != null) graphics.setupWindowArguments();
|
graphics.setupWindowArguments();
|
||||||
else createDefaultWindowHints();
|
|
||||||
for(int i = 0,m=builder.windowHints.size();i<m;i++) {
|
for(int i = 0,m=builder.windowHints.size();i<m;i++) {
|
||||||
long value = builder.windowHints.getLong(i);
|
long value = builder.windowHints.getLong(i);
|
||||||
GLFW.glfwWindowHint(BitUtil.intKey(value), BitUtil.intValue(value));
|
GLFW.glfwWindowHint(BitUtil.intKey(value), BitUtil.intValue(value));
|
||||||
@@ -83,7 +85,7 @@ public class Window {
|
|||||||
manager.addWindow(this);
|
manager.addWindow(this);
|
||||||
createWindowListeners();
|
createWindowListeners();
|
||||||
GLFW.glfwMakeContextCurrent(id);
|
GLFW.glfwMakeContextCurrent(id);
|
||||||
if(graphics == null) capabilities = GL.createCapabilities(true);
|
// device = graphics.createDevice(this);
|
||||||
x = monitor.xOffset() + (builder.center ? (mode.width() / 2) - (width / 2) : 0);
|
x = monitor.xOffset() + (builder.center ? (mode.width() / 2) - (width / 2) : 0);
|
||||||
y = monitor.yOffset() + (builder.center ? (mode.height() / 2) - (height / 2) : 0);
|
y = monitor.yOffset() + (builder.center ? (mode.height() / 2) - (height / 2) : 0);
|
||||||
if(!fullscreen) GLFW.glfwSetWindowPos(id, x, y);
|
if(!fullscreen) GLFW.glfwSetWindowPos(id, x, y);
|
||||||
@@ -92,15 +94,6 @@ public class Window {
|
|||||||
updateViewport();
|
updateViewport();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void createDefaultWindowHints() {
|
|
||||||
GLFW.glfwDefaultWindowHints();
|
|
||||||
GLFW.glfwWindowHint(GLFW.GLFW_VISIBLE, GLFW.GLFW_FALSE);
|
|
||||||
GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_VERSION_MAJOR, 4);
|
|
||||||
GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_VERSION_MINOR, 0);
|
|
||||||
GLFW.glfwWindowHint(GLFW.GLFW_OPENGL_PROFILE, GLFW.GLFW_OPENGL_CORE_PROFILE);
|
|
||||||
GLFW.glfwWindowHint(GLFW.GLFW_OPENGL_FORWARD_COMPAT, GLFW.GLFW_TRUE);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void createWindowListeners() {
|
protected void createWindowListeners() {
|
||||||
addCallback(this::framebuffer, GLFW::glfwSetFramebufferSizeCallback);
|
addCallback(this::framebuffer, GLFW::glfwSetFramebufferSizeCallback);
|
||||||
addCallback(this::focused, GLFW::glfwSetWindowFocusCallback);
|
addCallback(this::focused, GLFW::glfwSetWindowFocusCallback);
|
||||||
@@ -149,7 +142,6 @@ public class Window {
|
|||||||
public void setupContext() {
|
public void setupContext() {
|
||||||
if(capabilities != null) GL.setCapabilities(capabilities);
|
if(capabilities != null) GL.setCapabilities(capabilities);
|
||||||
GLFW.glfwMakeContextCurrent(id);
|
GLFW.glfwMakeContextCurrent(id);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void beginFrame() {
|
public void beginFrame() {
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import speiger.src.coreengine.rendering.input.devices.InputDevice;
|
|||||||
import speiger.src.coreengine.rendering.input.window.WindowCallback.SimpleReloadFunction;
|
import speiger.src.coreengine.rendering.input.window.WindowCallback.SimpleReloadFunction;
|
||||||
|
|
||||||
public class WindowManager {
|
public class WindowManager {
|
||||||
|
Graphics graphcis;
|
||||||
Long2ObjectMap<Monitor> monitors;
|
Long2ObjectMap<Monitor> monitors;
|
||||||
Long2ObjectMap<Window> windows = new Long2ObjectConcurrentOpenHashMap<>();
|
Long2ObjectMap<Window> windows = new Long2ObjectConcurrentOpenHashMap<>();
|
||||||
Window activeWindow;
|
Window activeWindow;
|
||||||
@@ -27,7 +28,8 @@ public class WindowManager {
|
|||||||
Callback monitorTracker;
|
Callback monitorTracker;
|
||||||
List<InputDevice> devices = new ObjectArrayList<>();
|
List<InputDevice> devices = new ObjectArrayList<>();
|
||||||
|
|
||||||
public void initialize() {
|
public void initialize(Graphics graphics) {
|
||||||
|
this.graphcis = graphics;
|
||||||
monitors = Monitor.createMonitors();
|
monitors = Monitor.createMonitors();
|
||||||
addCallback(this::onMonitorChanged, GLFW::glfwSetMonitorCallback);
|
addCallback(this::onMonitorChanged, GLFW::glfwSetMonitorCallback);
|
||||||
}
|
}
|
||||||
@@ -40,7 +42,7 @@ public class WindowManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public WindowBuilder builder() { return new WindowBuilder(this); }
|
public WindowBuilder builder() { return new WindowBuilder(this); }
|
||||||
private Window create(WindowBuilder builder, Graphics graphics) { return new Window(builder, graphics); }
|
private Window create(WindowBuilder builder) { return new Window(builder, graphcis); }
|
||||||
|
|
||||||
public void addDevices(InputDevice...devices) {
|
public void addDevices(InputDevice...devices) {
|
||||||
for(InputDevice device : devices) addDevice(device);
|
for(InputDevice device : devices) addDevice(device);
|
||||||
@@ -259,8 +261,8 @@ public class WindowManager {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Window build(Graphics graphics) {
|
public Window build() {
|
||||||
return manager.create(this, graphics);
|
return manager.create(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,147 @@
|
|||||||
|
package speiger.src.coreengine.math.color;
|
||||||
|
|
||||||
|
import speiger.src.coreengine.math.MathUtils;
|
||||||
|
|
||||||
|
public enum ColorSpaces {
|
||||||
|
ARGB {
|
||||||
|
@Override
|
||||||
|
public int color(int r, int g, int b, int a) { return (a & 0xFF) << 24 | (r & 0xFF) << 16 | (g & 0xFF) << 8 | (b & 0xFF); }
|
||||||
|
@Override
|
||||||
|
public int r(int color) { return color >> 16 & 0xFF; }
|
||||||
|
@Override
|
||||||
|
public int g(int color) { return color >> 8 & 0xFF; }
|
||||||
|
@Override
|
||||||
|
public int b(int color) { return color & 0xFF; }
|
||||||
|
@Override
|
||||||
|
public int a(int color) { return color >> 24; }
|
||||||
|
@Override
|
||||||
|
public int setR(int color, int r) { return color & ~ColorUtils.R | ((r & 0xFF) << 16); }
|
||||||
|
@Override
|
||||||
|
public int setG(int color, int g) { return color & ~ColorUtils.G | ((g & 0xFF) << 8); }
|
||||||
|
@Override
|
||||||
|
public int setB(int color, int b) { return color & ~ColorUtils.B | (b & 0xFF); }
|
||||||
|
@Override
|
||||||
|
public int setA(int color, int a) { return color & ~ColorUtils.A | ((a & 0xFF) << 24); }
|
||||||
|
@Override
|
||||||
|
public int fromARGB(int color) { return color; }
|
||||||
|
@Override
|
||||||
|
public int toARGB(int color) { return color; }
|
||||||
|
},
|
||||||
|
ABGR {
|
||||||
|
@Override
|
||||||
|
public int color(int r, int g, int b, int a) { return (a & 0xFF) << 24 | (b & 0xFF) << 16 | (g & 0xFF) << 8 | (r & 0xFF); }
|
||||||
|
@Override
|
||||||
|
public int r(int color) { return color & 0xFF; }
|
||||||
|
@Override
|
||||||
|
public int g(int color) { return color >> 8 & 0xFF; }
|
||||||
|
@Override
|
||||||
|
public int b(int color) { return color >> 16 & 0xFF; }
|
||||||
|
@Override
|
||||||
|
public int a(int color) { return color >> 24 & 0xFF; }
|
||||||
|
@Override
|
||||||
|
public int setR(int color, int r) { return color & ~ColorUtils.B | (r & 0xFF); }
|
||||||
|
@Override
|
||||||
|
public int setG(int color, int g) { return color & ~ColorUtils.G | ((g & 0xFF) << 8); }
|
||||||
|
@Override
|
||||||
|
public int setB(int color, int b) { return color & ~ColorUtils.R | ((b & 0xFF) << 16); }
|
||||||
|
@Override
|
||||||
|
public int setA(int color, int a) { return color & ~ColorUtils.A | ((a & 0xFF) << 24); }
|
||||||
|
@Override
|
||||||
|
public int fromARGB(int color) { return color & 0xFF00FF00 | (color & 0xFF0000) >> 16 | (color & 0xFF) << 16; }
|
||||||
|
@Override
|
||||||
|
public int toARGB(int color) { return color & 0xFF00FF00 | (color & 0xFF0000) >> 16 | (color & 0xFF) << 16; }
|
||||||
|
};
|
||||||
|
|
||||||
|
public int color(float r, float g, float b) { return color((int)(r * 255F + 0.5F), (int)(g * 255F + 0.5F), (int)(b * 255F + 0.5F)); }
|
||||||
|
public int color(float r, float g, float b, float a) { return color((int)(r * 255F + 0.5F), (int)(g * 255F + 0.5F), (int)(b * 255F + 0.5F), (int)(a * 255F + 0.5F));}
|
||||||
|
public int color(int r, int g, int b) { return color(r, g, b, 255); }
|
||||||
|
public abstract int color(int r, int g, int b, int a);
|
||||||
|
public abstract int r(int color);
|
||||||
|
public abstract int g(int color);
|
||||||
|
public abstract int b(int color);
|
||||||
|
public abstract int a(int color);
|
||||||
|
public float rf(int color) { return r(color) / 255F; }
|
||||||
|
public float gf(int color) { return g(color) / 255F; }
|
||||||
|
public float bf(int color) { return b(color) / 255F; }
|
||||||
|
public float af(int color) { return a(color) / 255F; }
|
||||||
|
public abstract int setR(int color, int r);
|
||||||
|
public abstract int setG(int color, int g);
|
||||||
|
public abstract int setB(int color, int b);
|
||||||
|
public abstract int setA(int color, int a);
|
||||||
|
public int setRF(int color, float r) { return setR(color, (int)(r * 255F + 0.5F)); }
|
||||||
|
public int setGF(int color, float g) { return setG(color, (int)(g * 255F + 0.5F)); }
|
||||||
|
public int setBF(int color, float b) { return setB(color, (int)(b * 255F + 0.5F)); }
|
||||||
|
public int setAF(int color, float a) { return setA(color, (int)(a * 255F + 0.5F)); }
|
||||||
|
public abstract int toARGB(int color);
|
||||||
|
public abstract int fromARGB(int color);
|
||||||
|
|
||||||
|
public int darker(int color) { return darker(color, 0.7F); }
|
||||||
|
public int darker(int color, float factor) {
|
||||||
|
int r = Math.max(0, (int)(r(color) * factor));
|
||||||
|
int g = Math.max(0, (int)(g(color) * factor));
|
||||||
|
int b = Math.max(0, (int)(b(color) * factor));
|
||||||
|
return (a(color) << 24) | ((r & 0xFF) << 16) | ((g & 0xFF) << 8) | (b & 0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int brighter(int color) { return brighter(color, 0.7F); }
|
||||||
|
public int brighter(int color, float factor) {
|
||||||
|
int r = r(color);
|
||||||
|
int g = g(color);
|
||||||
|
int b = b(color);
|
||||||
|
int i = (int)(1.0 / (1.0 - factor));
|
||||||
|
if(r == 0 && g == 0 && b == 0) { return color(i & 0xFF, i & 0xFF, i & 0xFF, a(color)); }
|
||||||
|
if(r > 0 && r < i) r = i;
|
||||||
|
if(g > 0 && g < i) g = i;
|
||||||
|
if(b > 0 && b < i) b = i;
|
||||||
|
return color(Math.min(255, (int)(r / factor)), Math.min(255, (int)(g / factor)), Math.min(255, (int)(b / factor)), a(color) << 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int fromHue(float hue, float saturation, float brightness) {
|
||||||
|
if(saturation == 0) {
|
||||||
|
int result = (int)(brightness * 255F + 0.5F);
|
||||||
|
return color(result, result, result);
|
||||||
|
}
|
||||||
|
float h = (hue - MathUtils.floor(hue)) * 6F;
|
||||||
|
float f = h - MathUtils.floor(h);
|
||||||
|
float p = brightness * (1F - saturation);
|
||||||
|
float q = brightness * (1F - saturation * f);
|
||||||
|
float t = brightness * (1F - (saturation * (1F - f)));
|
||||||
|
switch((int)h) {
|
||||||
|
case 0: return color(brightness, t, p);
|
||||||
|
case 1: return color(q, brightness, p);
|
||||||
|
case 2: return color(p, brightness, t);
|
||||||
|
case 3: return color(p, q, brightness);
|
||||||
|
case 4: return color(t, p, brightness);
|
||||||
|
case 5: return color(brightness, p, q);
|
||||||
|
default: return color(0, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public float[] toHue(int color) {
|
||||||
|
int r = r(color);
|
||||||
|
int g = g(color);
|
||||||
|
int b = b(color);
|
||||||
|
int cmax = (r > g) ? r : g;
|
||||||
|
if(b > cmax) cmax = b;
|
||||||
|
int cmin = (r < g) ? r : g;
|
||||||
|
if(b < cmin) cmin = b;
|
||||||
|
float length = cmax - cmin;
|
||||||
|
|
||||||
|
float[] result = new float[3];
|
||||||
|
result[1] = cmax == 0 ? 0F : length / cmax;
|
||||||
|
result[2] = cmax * ColorUtils.DEVIDER;
|
||||||
|
float hue = 0F;
|
||||||
|
if(result[1] != 0F) {
|
||||||
|
float redc = (cmax - r) / length;
|
||||||
|
float greenc = (cmax - g) / length;
|
||||||
|
float bluec = (cmax - b) / length;
|
||||||
|
if(r == cmax) hue = bluec - greenc;
|
||||||
|
else if(g == cmax) hue = 2F + redc - bluec;
|
||||||
|
else hue = 4F + greenc - redc;
|
||||||
|
hue /= 6F;
|
||||||
|
if(hue < 0) hue += 1F;
|
||||||
|
}
|
||||||
|
result[0] = hue;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,125 @@
|
|||||||
|
package speiger.src.coreengine.math.color;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.FloatBuffer;
|
||||||
|
|
||||||
|
import speiger.src.collections.floats.lists.FloatList;
|
||||||
|
|
||||||
|
public class ColorUtils {
|
||||||
|
static final float DEVIDER = 1F / 255F;
|
||||||
|
public static final int R = 0xFF << 16;
|
||||||
|
public static final int G = 0xFF << 8;
|
||||||
|
public static final int B = 0xFF;
|
||||||
|
public static final int A = 0xFF << 24;
|
||||||
|
public static final long SIGN = 0x00000000FFFFFFFFL;
|
||||||
|
static final int ALL = 0xFFFFFFFF;
|
||||||
|
public static final int WHITE = rgb(255, 255, 255);
|
||||||
|
public static final int LIGHT_GRAY = rgb(192, 192, 192);
|
||||||
|
public static final int GRAY = rgb(128, 128, 128);
|
||||||
|
public static final int DARK_GRAY = rgb(64, 64, 64);
|
||||||
|
public static final int BLACK = rgb(0, 0, 0);
|
||||||
|
public static final int RED = rgb(255, 0, 0);
|
||||||
|
public static final int PINK = rgb(255, 175, 175);
|
||||||
|
public static final int PURPLE = rgb(106, 13, 173);
|
||||||
|
public static final int ORANGE = rgb(255, 200, 0);
|
||||||
|
public static final int YELLOW = rgb(255, 255, 0);
|
||||||
|
public static final int GREEN = rgb(0, 255, 0);
|
||||||
|
public static final int DARK_GREEN = rgb(7, 161, 0);
|
||||||
|
public static final int MAGENTA = rgb(255, 0, 255);
|
||||||
|
public static final int CYAN = rgb(0, 255, 255);
|
||||||
|
public static final int BLUE = rgb(0, 0, 255);
|
||||||
|
public static final int LIGHT_BLUE = rgb(0, 150, 255);
|
||||||
|
|
||||||
|
// Specialized Components that get reused
|
||||||
|
public static final int INVISIBLE = rgb(0, 0, 0, 0);
|
||||||
|
public static final int TEXT_DEFAULT_BACKGROUND = rgb(80, 80, 80, 144);
|
||||||
|
public static final int WINDOW_DEFAULT_BACKGROUND = rgb(64, 64, 64, 128);
|
||||||
|
public static final int POPUP_DEFAULT_BACKGROUND = rgb(85, 85, 85);
|
||||||
|
public static final int DESTRUCTION = rgb(255, 0, 0, 128);
|
||||||
|
|
||||||
|
public static byte[] toByteArray(int color, boolean alpha) {
|
||||||
|
byte[] data = new byte[alpha ? 4 : 3];
|
||||||
|
data[0] = (byte)((color >> 16) & 0xFF);
|
||||||
|
data[1] = (byte)((color >> 8) & 0xFF);
|
||||||
|
data[2] = (byte)(color & 0xFF);
|
||||||
|
if(alpha) data[3] = (byte)((color >> 24) & 0xFF);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void write(int color, boolean alpha, ByteBuffer buffer) {
|
||||||
|
buffer.put((byte)((color >> 16) & 0xFF)).put((byte)((color >> 8) & 0xFF)).put((byte)(color & 0xFF));
|
||||||
|
if(alpha) buffer.put((byte)((color >> 24) & 0xFF));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void write(int index, int color, boolean alpha, ByteBuffer buffer) {
|
||||||
|
buffer.put(index, (byte)((color >> 16) & 0xFF)).put(index + 1, (byte)((color >> 8) & 0xFF)).put(index + 2, (byte)(color & 0xFF));
|
||||||
|
if(alpha) buffer.put(index + 3, (byte)((color >> 24) & 0xFF));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void write(int color, boolean alpha, FloatBuffer buffer) {
|
||||||
|
buffer.put(((color >> 16) & 0xFF) * DEVIDER).put(((color >> 8) & 0xFF) * DEVIDER).put((color & 0xFF) * DEVIDER);
|
||||||
|
if(alpha) buffer.put(((color >> 24) & 0xFF) * DEVIDER);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void write(int index, int color, boolean alpha, FloatBuffer buffer) {
|
||||||
|
buffer.put(index, ((color >> 16) & 0xFF) * DEVIDER).put(index + 1, ((color >> 8) & 0xFF) * DEVIDER).put(index + 2, (color & 0xFF) * DEVIDER);
|
||||||
|
if(alpha) buffer.put(index + 3, ((color >> 24) & 0xFF) * DEVIDER);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void write(int color, boolean alpha, FloatList list) {
|
||||||
|
list.add(((color >> 16) & 0xFF) * DEVIDER);
|
||||||
|
list.add(((color >> 8) & 0xFF) * DEVIDER);
|
||||||
|
list.add((color & 0xFF) * DEVIDER);
|
||||||
|
if(alpha) list.add(((color >> 24) & 0xFF) * DEVIDER);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int read(ByteBuffer buffer, boolean alpha) { return alpha ? rgb(buffer.get(), buffer.get(), buffer.get()) : rgb(buffer.get(), buffer.get(), buffer.get(), buffer.get()); }
|
||||||
|
public static int read(ByteBuffer buffer, int index, boolean alpha) { return alpha ? rgb(buffer.get(index), buffer.get(index + 1), buffer.get(index + 2)) : rgb(buffer.get(index), buffer.get(index + 1), buffer.get(index + 2), buffer.get(index + 3)); }
|
||||||
|
public static int read(FloatBuffer buffer, boolean alpha) { return alpha ? rgb(buffer.get(), buffer.get(), buffer.get()) : rgb(buffer.get(), buffer.get(), buffer.get(), buffer.get()); }
|
||||||
|
public static int read(FloatBuffer buffer, int index, boolean alpha) { return alpha ? rgb(buffer.get(index), buffer.get(index + 1), buffer.get(index + 2)) : rgb(buffer.get(index), buffer.get(index + 1), buffer.get(index + 2), buffer.get(index + 3)); }
|
||||||
|
public static boolean needsDarkColor(int rgba) { return getBrightness(rgba) >= 130; }
|
||||||
|
public static int getBrightness(int rgba) { return getBrightness((rgba >> 16) & 0xFF, (rgba >> 8) & 0xFF, rgba & 0xFF); }
|
||||||
|
public static int getBrightness(int r, int g, int b) { return (int)Math.sqrt((r * r * 0.241F) + (g * g * 0.691F) + (b * b * 0.068F)); }
|
||||||
|
|
||||||
|
public static int mix(int from, int to, float factor) {
|
||||||
|
float weight0 = (1F - factor);
|
||||||
|
float weight1 = factor;
|
||||||
|
int r = (int)((((from >> 16) & 0xFF) * weight0) + (((to >> 16) & 0xFF) * weight1));
|
||||||
|
int g = (int)((((from >> 8) & 0xFF) * weight0) + (((to >> 8) & 0xFF) * weight1));
|
||||||
|
int b = (int)(((from & 0xFF) * weight0) + ((to & 0xFF) * weight1));
|
||||||
|
int a = (int)((((from >> 24) & 0xFF) * weight0) + (((to >> 24) & 0xFF) * weight1));
|
||||||
|
return ((a & 0xFF) << 24) | ((r & 0xFF) << 16) | ((g & 0xFF) << 8) | b & 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int rgb(int rgb) { return rgb | (255 << 24); }
|
||||||
|
public static int rgb(int r, int g, int b) { return A | ((r & 0xFF) << 16) | ((g & 0xFF) << 8) | (b & 0xFF); }
|
||||||
|
public static int rgb(float r, float g, float b) { return rgb((int)(r * 255F + 0.5F), (int)(g * 255F + 0.5F), (int)(b * 255F + 0.5F)); }
|
||||||
|
public static int rgb(int r, int g, int b, int a) { return ((a & 0xFF) << 24) | ((r & 0xFF) << 16) | ((g & 0xFF) << 8) | (b & 0xFF); }
|
||||||
|
public static int rgb(float r, float g, float b, float a) { return rgb((int)(r * 255F + 0.5F), (int)(g * 255F + 0.5F), (int)(b * 255F + 0.5F), (int)(b * 255F + 0.5F)); }
|
||||||
|
public static int setR(int rgba, int r) { return rgba & ~R | ((r & 0xFF) << 16); }
|
||||||
|
public static int setR(int rgba, float r) { return rgba & ~R | (((int)(r * 255F + 0.5F)) << 16); }
|
||||||
|
public static int setG(int rgba, int g) { return rgba & ~G | ((g & 0xFF) << 8); }
|
||||||
|
public static int setG(int rgba, float g) { return rgba & ~G | (((int)(g * 255F + 0.5F)) << 8); }
|
||||||
|
public static int setB(int rgba, int b) { return rgba & ~B | (b & 0xFF); }
|
||||||
|
public static int setB(int rgba, float b) { return rgba & ~B | ((int)(b * 255F + 0.5F)); }
|
||||||
|
public static int setA(int rgba, int a) { return rgba & ~A | ((a & 0xFF) << 24); }
|
||||||
|
public static int setA(int rgba, float a) { return rgba & ~A | (((int)(a * 255F + 0.5F)) << 24); }
|
||||||
|
public static int getR(int rgba) { return (rgba >> 16) & 0xFF; }
|
||||||
|
public static float getRF(int rgba) { return ((rgba >> 16) & 0xFF) * DEVIDER; }
|
||||||
|
public static int getG(int rgba) { return (rgba >> 8) & 0xFF; }
|
||||||
|
public static float getGF(int rgba) { return ((rgba >> 8) & 0xFF) * DEVIDER; }
|
||||||
|
public static int getB(int rgba) { return rgba & 0xFF; }
|
||||||
|
public static float getBF(int rgba) { return (rgba & 0xFF) * DEVIDER; }
|
||||||
|
public static int getA(int rgba) { return (rgba >> 24) & 0xFF; }
|
||||||
|
public static float getAF(int rgba) { return ((rgba >> 24) & 0xFF) * DEVIDER; }
|
||||||
|
public static int parse(String input) {
|
||||||
|
try { return Long.decode(input).intValue(); }
|
||||||
|
catch (Exception e) { return -1; }
|
||||||
|
}
|
||||||
|
public static int parse(String input, int defaultValue) {
|
||||||
|
try { return Long.decode(input).intValue(); }
|
||||||
|
catch (Exception e) { return defaultValue; }
|
||||||
|
}
|
||||||
|
public static String toHex(int rgba, boolean alpha) { return "0x"+(alpha ? Long.toHexString(1 << 32 | rgba & SIGN) : Integer.toHexString((1 << 24) | (rgba & ~A))).substring(1); }
|
||||||
|
public static String toHTML(int rgba, boolean alpha) { return "#"+(alpha ? Long.toHexString(1 << 32 | rgba & SIGN) : Integer.toHexString((1 << 24) | (rgba & ~A))).substring(1); }
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user