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