Next Milestone reached.

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