More work on the engine

This commit is contained in:
2026-07-15 00:12:35 +02:00
parent 641f19dd67
commit b43d76bbc2
23 changed files with 562 additions and 270 deletions
-6
View File
@@ -18,12 +18,6 @@
<attribute name="gradle_used_by_scope" value="graphics"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="bin/graphics" path="src/graphics/resources">
<attributes>
<attribute name="gradle_scope" value="graphics"/>
<attribute name="gradle_used_by_scope" value="graphics"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="bin/math" path="src/math/java">
<attributes>
<attribute name="gradle_scope" value="math"/>
@@ -11,6 +11,7 @@ import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import speiger.src.coreengine.assets.impl.parsers.Buffers;
import speiger.src.coreengine.assets.impl.parsers.Checksums;
import speiger.src.coreengine.assets.impl.parsers.Closables;
import speiger.src.coreengine.assets.impl.parsers.Serialized;
@@ -26,5 +27,8 @@ public interface IAsset {
public default JsonObject jsonObj() throws IOException { return parse(Serialized.JSON_OBJ); }
public default JsonArray jsonArray() throws IOException { return parse(Serialized.JSON_ARRAY); }
public default String crc() throws IOException { return parse(Checksums.CRC); }
public default String sha256() throws IOException { return parse(Checksums.SHA_256); }
public <T> T parse(IAssetParser<T> parser) throws IOException;
}
@@ -5,12 +5,15 @@ import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Supplier;
import org.jspecify.annotations.Nullable;
import speiger.src.collections.ints.functions.consumer.IntObjectConsumer;
import speiger.src.collections.objects.collections.ObjectIterable;
import speiger.src.collections.objects.collections.ObjectIterator;
import speiger.src.collections.objects.functions.consumer.ObjectObjectConsumer;
import speiger.src.collections.objects.lists.ObjectArrayList;
import speiger.src.collections.objects.lists.ObjectList;
import speiger.src.coreengine.assets.impl.parsers.Checksums;
public record MultiAsset(ObjectList<IAsset> assets) implements ObjectIterable<IAsset> {
@@ -26,6 +29,9 @@ public record MultiAsset(ObjectList<IAsset> assets) implements ObjectIterable<IA
public IAsset get(int index) { return assets.get(index); }
@Override
public ObjectIterator<IAsset> iterator() { return assets.iterator(); }
public String crc() { return map(Checksums.CRC, () -> "").reduce(new StringBuilder(), StringBuilder::append).toString(); }
public String sha256() { return map(Checksums.SHA_256, () -> "").reduce(new StringBuilder(), StringBuilder::append).toString(); }
public <T> ObjectIterable<T> map(IAssetParser<T> mapper, Supplier<T> defaultValue) {
return assets.map(E -> {
@@ -51,4 +57,19 @@ public record MultiAsset(ObjectList<IAsset> assets) implements ObjectIterable<IA
public void forEachIndexed(IntObjectConsumer<IAsset> action) {
assets.forEachIndexed(action);
}
@Nullable
public static MultiAsset combineNonNull(IAssetProvider provider, ID...ids) {
ObjectList<IAsset> all = new ObjectArrayList<>();
for(ID id : ids) {
IAsset entry = provider.get(id);
if(entry == null) return null;
all.add(entry);
}
return new MultiAsset(all);
}
public static MultiAsset combine(IAssetProvider provider, ID...ids) {
return new MultiAsset(ObjectArrayList.wrap(ids).map(provider::get).filter(T -> T != null).pourAsList());
}
}
@@ -0,0 +1,41 @@
package speiger.src.coreengine.assets.impl.parsers;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import java.util.Map;
import java.util.zip.CRC32C;
import speiger.src.coreengine.assets.api.IAssetParser;
public class Checksums {
public static final IAssetParser<String> CRC = new CRC();
public static final IAssetParser<String> SHA_256 = new SHA256();
public static class CRC implements IAssetParser<String> {
@Override
public String parse(Path path) throws IOException {
Map<String, Object> files = Files.readAttributes(path, "crc");
if(files.containsKey("crc")) return Long.toHexString(((Long)files.get("crc")) & 0xFFFFFFFFL);
CRC32C crc = new CRC32C();
crc.update(Files.readAllBytes(path));
return Long.toHexString(crc.getValue());
}
}
public static class SHA256 implements IAssetParser<String> {
@Override
public String parse(Path path) throws IOException {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
digest.update(Files.readAllBytes(path));
return Base64.getEncoder().encodeToString(digest.digest());
}
catch(NoSuchAlgorithmException e) { e.printStackTrace(); }
return null;
}
}
}
@@ -0,0 +1,26 @@
package speiger.src.coreengine.graphics.api.core;
import speiger.src.coreengine.graphics.api.buffer.VertexBuffer;
import speiger.src.coreengine.graphics.api.mesh.Mesh;
import speiger.src.coreengine.graphics.api.sampler.Sampler;
import speiger.src.coreengine.graphics.api.shader.ShaderPipeline;
import speiger.src.coreengine.graphics.api.texture.Texture;
import speiger.src.coreengine.graphics.api.utils.GraphicsResource;
public interface GraphicsCommandBuffer extends GraphicsResource {
GraphicsCommandBuffer begin();
GraphicsCommandBuffer setScissors(int x, int y, int width, int height);
GraphicsCommandBuffer pipeline(ShaderPipeline pipeline);
GraphicsCommandBuffer mesh(Mesh mesh);
GraphicsCommandBuffer texture(int set, int binding, Texture texture, Sampler sampler);
GraphicsCommandBuffer uniform(int set, int binding, VertexBuffer buffer);
GraphicsCommandBuffer drawIndexed(int start, int count);
GraphicsCommandBuffer pushScissors(int x, int y, int width, int height);
GraphicsCommandBuffer popScissors();
GraphicsCommandBuffer end();
int commandCount();
}
@@ -1,6 +1,5 @@
package speiger.src.coreengine.graphics.api.core;
import speiger.src.coreengine.graphics.api.shader.RenderPass;
import speiger.src.coreengine.graphics.api.target.RenderTarget;
import speiger.src.coreengine.graphics.api.texture.Texture;
import speiger.src.coreengine.graphics.api.utils.PushableResource;
@@ -20,7 +19,8 @@ public interface GraphicsCommandQueue {
public void writeToTexture(Texture target, int x, int y, int width, int height, long source);
public void readFromTexture(Texture source, int x, int y, int width, int height, long target);
public RenderPass createRenderPass(RenderTarget target, DrawArea area);
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) {
@@ -3,18 +3,22 @@ package speiger.src.coreengine.graphics.api.core;
import speiger.src.coreengine.graphics.api.buffer.VertexBuffer;
import speiger.src.coreengine.graphics.api.buffer.states.BufferState;
import speiger.src.coreengine.graphics.api.buffer.states.BufferType;
import speiger.src.coreengine.graphics.api.mesh.Mesh;
import speiger.src.coreengine.graphics.api.sampler.Sampler;
import speiger.src.coreengine.graphics.api.sampler.SamplerSettings;
import speiger.src.coreengine.graphics.api.texture.Texture;
import speiger.src.coreengine.graphics.api.texture.TextureSettings;
import speiger.src.coreengine.graphics.api.utils.ExecutionType;
import speiger.src.coreengine.rendering.input.window.Window;
public interface GraphicsDevice {
public Window getWindow();
public GraphicsSurface createSurface();
public GraphicsCommandBuffer createBuffer(ExecutionType type);
public GraphicsCommandQueue getQueue();
public VertexBuffer createBuffer(BufferType type, BufferState state);
public VertexBuffer createBuffer(BufferType type, BufferState state, int allocatedBytes);
public Texture createTexture(TextureSettings data, int width, int height);
public Sampler createSampler(SamplerSettings settings);
public Mesh createMesh(Mesh.Builder builder, boolean autocreate);
}
@@ -0,0 +1,77 @@
package speiger.src.coreengine.graphics.api.mesh;
import speiger.src.collections.ints.maps.interfaces.Int2ObjectMap;
import speiger.src.coreengine.graphics.api.buffer.VertexBuffer;
import speiger.src.coreengine.graphics.api.buffer.states.IndeciesType;
import speiger.src.coreengine.graphics.api.utils.GraphicsResource;
import speiger.src.coreengine.graphics.api.vertex.VertexLayout;
public abstract class Mesh implements GraphicsResource {
protected final Int2ObjectMap<LayoutInfo> layouts;
protected Int2ObjectMap<VertexBuffer> buffers;
protected VertexBuffer indeciesBuffer;
protected IndeciesType indeciesType;
public Mesh(Int2ObjectMap<LayoutInfo> layouts, Int2ObjectMap<VertexBuffer> buffers, VertexBuffer indeciesBuffer) {
this.layouts = layouts;
this.buffers = buffers;
this.indeciesBuffer = indeciesBuffer;
}
protected abstract void closeWithBuffers();
public Int2ObjectMap<LayoutInfo> layouts() {
return layouts;
}
public VertexBuffer indeciesBuffer() {
return indeciesBuffer;
}
public IndeciesType indeciesType() {
return indeciesType;
}
public VertexBuffer buffer(int binding) {
return buffers.get(binding);
}
public Mesh indexBuffer(VertexBuffer indexBuffer, IndeciesType indeciesType) {
this.indeciesBuffer = indexBuffer;
this.indeciesType = indeciesType;
return this;
}
public Mesh buffer(int binding, VertexBuffer buffer) {
buffers.put(binding, buffer);
return this;
}
public static Builder builder() {
return new Builder();
}
public static class Builder {
Int2ObjectMap<LayoutInfo> layouts = Int2ObjectMap.builder().linkedMap();
boolean autoGenerate;
private Builder() {}
public Builder layout(VertexLayout layout) {
return layout(layouts.size(), layout);
}
public Builder layout(int index, VertexLayout layout) {
return layout(index, layout, 0);
}
public Builder layout(int index, VertexLayout layout, int instanceCount) {
layouts.put(index, new LayoutInfo(layout, instanceCount));
return this;
}
}
public record LayoutInfo(VertexLayout layout, int instanceCount) {
}
}
@@ -1,32 +1,12 @@
package speiger.src.coreengine.graphics.api.shader;
import java.util.Objects;
import speiger.src.coreengine.graphics.api.shader.states.GraphicsDataType;
public record BufferAttribute(int index, int size, GraphicsDataType type, boolean normal, int instancedAmount) {
public BufferAttribute(int index, int size) {
this(index, size, GraphicsDataType.FLOAT, false, 0);
}
public BufferAttribute(int index, int size, GraphicsDataType type) {
this(index, size, type, false, 0);
}
public BufferAttribute(int index, int size, GraphicsDataType type, boolean normal) {
this(index, size, type, normal, 0);
}
public BufferAttribute(int index, int size, GraphicsDataType type, int instancedAmount) {
this(index, size, type, false, instancedAmount);
}
public static BufferAttribute[] array(int startIndex, int width, int size, GraphicsDataType type) { return array(startIndex, width, size, type, false, 0); }
public static BufferAttribute[] array(int startIndex, int width, int size, GraphicsDataType type, boolean normal) { return array(startIndex, width, size, type, normal, 0); }
public static BufferAttribute[] array(int startIndex, int width, int size, GraphicsDataType type, int instanceAmount) { return array(startIndex, width, size, type, false, instanceAmount); }
public static BufferAttribute[] array(int startIndex, int width, int size, GraphicsDataType type, boolean normal, int instancedAmount) {
BufferAttribute[] attributes = new BufferAttribute[width];
for(int i = 0;i < width;i++) {
attributes[i] = new BufferAttribute(startIndex + i, size, type, normal, instancedAmount);
}
return attributes;
public record BufferAttribute(String name, int index, int size, GraphicsDataType type, boolean normal) {
public BufferAttribute {
Objects.requireNonNull(name, "Attributes requires name");
Objects.requireNonNull(type, "GraphicsDataType required");
}
}
@@ -0,0 +1,14 @@
package speiger.src.coreengine.graphics.api.shader;
import java.util.Objects;
import speiger.src.coreengine.graphics.api.shader.states.DrawMode;
import speiger.src.coreengine.graphics.api.shader.states.PolygonMode;
public record RasterizerState(DrawMode mode, PolygonMode fillMode, boolean cullBack) {
public static final RasterizerState DEFAULT = new RasterizerState(DrawMode.TRIANGLES, PolygonMode.FILL, true);
public RasterizerState {
Objects.requireNonNull(mode);
Objects.requireNonNull(fillMode);
}
}
@@ -5,120 +5,103 @@ import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import speiger.src.collections.objects.lists.ImmutableObjectList;
import speiger.src.collections.objects.lists.ObjectArrayList;
import speiger.src.collections.objects.lists.ObjectList;
import speiger.src.collections.objects.sets.ObjectOpenHashSet;
import speiger.src.collections.objects.sets.ObjectSet;
import speiger.src.coreengine.assets.AssetLocation;
import speiger.src.coreengine.assets.api.ID;
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.DrawMode;
import speiger.src.coreengine.graphics.api.shader.states.GraphicsDataType;
import speiger.src.coreengine.graphics.api.shader.states.PolygonMode;
import speiger.src.coreengine.graphics.api.shader.states.ShaderType;
import speiger.src.coreengine.graphics.api.texture.states.TextureType;
public record ShaderPipeline(AssetLocation id, Map<ShaderType, AssetLocation> shaders, List<List<BufferAttribute>> attributes, List<String> attributeNames, Set<String> uniforms, Set<String> samplers, DrawMode mode, PolygonMode fillMode, boolean cullBack, ColorTarget colorTarget, DepthTarget depthTarget) {
public record ShaderPipeline(ID id, Map<ShaderType, ID> shaders, List<VertexBinding> attributes, List<UniformBinding> uniforms, List<TextureBinding> textures, RasterizerState rasterizer, ColorTarget colorTarget, DepthTarget depthTarget) {
public ShaderPipeline {
Objects.requireNonNull(id);
if(shaders.isEmpty()) throw new IllegalStateException("Shaders isn't allowed to be empty");
Objects.requireNonNull(uniforms);
Objects.requireNonNull(samplers);
Objects.requireNonNull(mode);
Objects.requireNonNull(fillMode);
Objects.requireNonNull(textures);
Objects.requireNonNull(rasterizer);
Objects.requireNonNull(colorTarget);
Objects.requireNonNull(depthTarget);
}
public static Builder builder(AssetLocation id) {
public static Builder builder(ID id) {
return new Builder(id);
}
public Builder copy(AssetLocation id) {
public Builder copy(ID id) {
return new Builder(id, this);
}
public static class Builder {
AssetLocation id;
EnumMap<ShaderType, AssetLocation> shaders = new EnumMap<>(ShaderType.class);
ObjectList<String> attributeNames = new ObjectArrayList<>();
@SuppressWarnings("unchecked")
ObjectList<List<BufferAttribute>> attributes = ObjectArrayList.wrap(new List[8]);
ObjectSet<String> uniforms = new ObjectOpenHashSet<>();
ObjectSet<String> samplers = new ObjectOpenHashSet<>();
DrawMode drawMode;
PolygonMode fillMode = PolygonMode.FILL;
boolean cullBack = true;
ID id;
EnumMap<ShaderType, ID> shaders = new EnumMap<>(ShaderType.class);
ObjectList<VertexBinding> attributes = new ObjectArrayList<>();
ObjectList<UniformBinding> uniforms = new ObjectArrayList<>();
ObjectList<TextureBinding> textures = new ObjectArrayList<>();
RasterizerState rasterizer = RasterizerState.DEFAULT;
ColorTarget colorTarget = ColorTarget.DEFAULT;
DepthTarget depthTarget = DepthTarget.DEFAULT;
private Builder(AssetLocation id) {
private Builder(ID id) {
this.id = Objects.requireNonNull(id);
}
private Builder(AssetLocation id, ShaderPipeline line) {
private Builder(ID id, ShaderPipeline line) {
this(id);
shaders.putAll(line.shaders());
attributeNames.addAll(line.attributeNames());
for(int i = 0,m=line.attributes().size();i<m;i++) {
attributes.set(i, line.attributes().get(i));
}
attributes.addAll(line.attributes());
uniforms.addAll(line.uniforms());
samplers.addAll(line.samplers());
drawMode = line.mode();
fillMode = line.fillMode();
cullBack = line.cullBack();
textures.addAll(line.textures());
rasterizer = line.rasterizer();
colorTarget = line.colorTarget();
depthTarget = line.depthTarget();
}
public Builder withShader(ShaderType type, AssetLocation location) {
public Builder withStage(ShaderType type, ID location) {
shaders.put(Objects.requireNonNull(type, "Shouldn't be null"), Objects.requireNonNull(location, "Resource shouldn't be null"));
return this;
}
public Builder withUniform(String uniform) {
uniforms.add(Objects.requireNonNull(uniform));
public Builder withUniform(String name, int binding, int set, int bytes) {
uniforms.add(new UniformBinding(name, binding, set, bytes));
return this;
}
public Builder withSampler(String sampler) {
samplers.add(Objects.requireNonNull(sampler));
public Builder withTexture(String name, int binding, int set, TextureType type) {
textures.add(new TextureBinding(name, binding, set, type));
return this;
}
public Builder withDrawMode(DrawMode mode) {
drawMode = Objects.requireNonNull(mode);
public Builder withRasterizer(DrawMode draw, PolygonMode polygon, boolean cullBack) {
rasterizer = new RasterizerState(draw, polygon, cullBack);
return this;
}
public Builder withFillMode(PolygonMode mode) {
this.fillMode = Objects.requireNonNull(mode);
return this;
public AttributeBuilder withFormat() {
return withFormat(attributes.size());
}
public Builder withBackCulling(boolean cull) {
this.cullBack = cull;
return this;
public AttributeBuilder withInstanceFormat() {
return withFormat(attributes.size(), 1);
}
public Builder withAttributeNames(String... names) {
this.attributeNames.clear();
this.attributeNames.addAll(names);
return this;
public AttributeBuilder withInstanceFormat(int instanceCount) {
return withFormat(attributes.size(), instanceCount);
}
public Builder withBufferFormat(int index, BufferAttribute...attributes) {
if(index < 0 || index >= 8) throw new IndexOutOfBoundsException(index);
this.attributes.set(index, new ImmutableObjectList<>(attributes));
return this;
public AttributeBuilder withFormat(int bindingIndex) {
return withFormat(bindingIndex, 0);
}
public Builder clearBufferFormat(int index) {
if(index < 0 || index >= 8) throw new IndexOutOfBoundsException(index);
attributes.set(index, null);
return this;
public AttributeBuilder withFormat(int bindingIndex, int instanceCount) {
return new AttributeBuilder(this, bindingIndex, instanceCount);
}
public Builder withColorTarget(ColorTarget target) {
@@ -133,7 +116,52 @@ public record ShaderPipeline(AssetLocation id, Map<ShaderType, AssetLocation> sh
public ShaderPipeline build() {
if(shaders.isEmpty()) throw new IllegalStateException("Shaders must be provided in a Shader Pipeline");
return new ShaderPipeline(id, Collections.unmodifiableMap(shaders), attributes.unmodifiable(), attributeNames.unmodifiable(), uniforms.unmodifiable(), samplers.unmodifiable(), Objects.requireNonNull(drawMode, "Draw Mode has to be defined"), fillMode, cullBack, colorTarget, depthTarget);
return new ShaderPipeline(id, Collections.unmodifiableMap(shaders), attributes.unmodifiable(), uniforms.unmodifiable(), textures.unmodifiable(), Objects.requireNonNull(rasterizer, "A Rasterizer should be defined"), colorTarget, depthTarget);
}
}
public static class AttributeBuilder {
final Builder owner;
final List<BufferAttribute> attributes = new ObjectArrayList<>();
final int bindingIndex;
final int instanceCount;
int stride;
private AttributeBuilder(Builder owner, int bindingIndex, int instanceCount) {
this.owner = owner;
this.bindingIndex = bindingIndex;
this.instanceCount = instanceCount;
}
public AttributeBuilder attribute(String name, int index, int size) {
return attribute(name, index, size, GraphicsDataType.FLOAT, false);
}
public AttributeBuilder attribute(String name, int index, int size, GraphicsDataType type) {
return attribute(name, index, size, type, false);
}
public AttributeBuilder attribute(String name, int index, int size, GraphicsDataType type, boolean normalized) {
attributes.add(new BufferAttribute(name, index, size, type, normalized));
stride += type.size(size);
return this;
}
public AttributeBuilder array(String baseName, int startIndex, int width, int size, GraphicsDataType type) {
return array(baseName, startIndex, width, size, type, false);
}
public AttributeBuilder array(String baseName, int startIndex, int width, int size, GraphicsDataType type, boolean normalized) {
Objects.requireNonNull(baseName);
for(int i = 0;i<width;i++) {
attribute(baseName+" ["+i+"]", startIndex + i, size, type, normalized);
}
return this;
}
public Builder endFormat() {
owner.attributes.add(new VertexBinding(bindingIndex, stride, instanceCount, new ImmutableObjectList<>(attributes)));
return owner;
}
}
}
@@ -0,0 +1,7 @@
package speiger.src.coreengine.graphics.api.shader;
import java.util.List;
public record VertexBinding(int bindingIndex, int stide, int instanceOffset, List<BufferAttribute> attributes) {
}
@@ -0,0 +1,21 @@
package speiger.src.coreengine.graphics.api.shader.states;
import java.util.Objects;
import speiger.src.coreengine.graphics.api.texture.states.TextureType;
public class Bindings {
public record UniformBinding(String name, int binding, int set, int bytes) {
public UniformBinding {
Objects.requireNonNull(name, "A name has to be provided");
}
}
public record TextureBinding(String name, int binding, int set, TextureType type) {
public TextureBinding {
Objects.requireNonNull(name, "A name has to be provided");
Objects.requireNonNull(type, "A TextureType has to be provided");
}
}
}
@@ -2,49 +2,50 @@ package speiger.src.coreengine.graphics.api.shader.states;
public enum GraphicsDataType {
BYTE("byte", 1, PrimitiveType.BYTE),
UNSIGNED_BYTE("u_byte", 1, PrimitiveType.BYTE, true, false),
UNSIGNED_BYTE("u_byte", 1, PrimitiveType.BYTE, true),
SHORT("short", 2, PrimitiveType.SHORT),
UNSIGNED_SHORT("u_short", 2, PrimitiveType.SHORT, true, false),
UNSIGNED_SHORT("u_short", 2, PrimitiveType.SHORT, true),
INT("int", 4, PrimitiveType.INT),
UNSIGNED_INT("u_int", 4, PrimitiveType.INT, true, false),
UNSIGNED_INT("u_int", 4, PrimitiveType.INT, true),
FLOAT("float", 4, PrimitiveType.FLOAT),
DOUBLE("double", 8, PrimitiveType.DOUBLE),
//CompressedTypes
UNSIGNED_3_3_2("u_byte_3_3_2", 1, PrimitiveType.BYTE),
UNSIGNED_2_3_3_REV("u_byte_2_3_3_rev", 1, PrimitiveType.BYTE),
UNSIGNED_5_6_5("u_short_5_6_5", 2, PrimitiveType.SHORT),
UNSIGNED_5_6_5_REV("u_short_5_6_5_rev", 2, PrimitiveType.SHORT),
UNSIGNED_4_4_4_4("u_short_4_4_4_4", 2, PrimitiveType.SHORT),
UNSIGNED_4_4_4_4_REV("u_short_4_4_4_4_rev", 2, PrimitiveType.SHORT),
UNSIGNED_5_5_5_1("u_short_5_5_5_1", 2, PrimitiveType.SHORT),
UNSIGNED_1_5_5_5_REV("u_short_1_5_5_5_rev", 2, PrimitiveType.SHORT),
UNSIGNED_8_8_8_8("u_int_8_8_8_8", 4, PrimitiveType.INT),
UNSIGNED_8_8_8_8_REV("u_int_8_8_8_8_rev", 4, PrimitiveType.INT),
UNSIGNED_10_10_10_2("u_int_10_10_10_2", 4, PrimitiveType.INT),
UNSIGNED_2_10_10_10_REV("u_int_2_10_10_10_rev", 4, PrimitiveType.INT),
UNSIGNED_3_3_2("u_byte_3_3_2", 1, PrimitiveType.BYTE, false, true),
UNSIGNED_2_3_3_REV("u_byte_2_3_3_rev", 1, PrimitiveType.BYTE, false, true),
UNSIGNED_5_6_5("u_short_5_6_5", 2, PrimitiveType.SHORT, false, true),
UNSIGNED_5_6_5_REV("u_short_5_6_5_rev", 2, PrimitiveType.SHORT, false, true),
UNSIGNED_4_4_4_4("u_short_4_4_4_4", 2, PrimitiveType.SHORT, false, true),
UNSIGNED_4_4_4_4_REV("u_short_4_4_4_4_rev", 2, PrimitiveType.SHORT, false, true),
UNSIGNED_5_5_5_1("u_short_5_5_5_1", 2, PrimitiveType.SHORT, false, true),
UNSIGNED_1_5_5_5_REV("u_short_1_5_5_5_rev", 2, PrimitiveType.SHORT, false, true),
UNSIGNED_8_8_8_8("u_int_8_8_8_8", 4, PrimitiveType.INT, false, true),
UNSIGNED_8_8_8_8_REV("u_int_8_8_8_8_rev", 4, PrimitiveType.INT, false, true),
UNSIGNED_10_10_10_2("u_int_10_10_10_2", 4, PrimitiveType.INT, false, true),
UNSIGNED_2_10_10_10_REV("u_int_2_10_10_10_rev", 4, PrimitiveType.INT, false, true),
UNSIGNED_INT_10F_11F_11F_REV("u_int_10_11_11_rev", 4, PrimitiveType.INT),
UNSIGNED_INT_5_9_9_9_REV("u_int_5_9_9_9_rev", 4, PrimitiveType.INT);
UNSIGNED_INT_10F_11F_11F_REV("u_int_10_11_11_rev", 4, PrimitiveType.INT, false, true),
UNSIGNED_INT_5_9_9_9_REV("u_int_5_9_9_9_rev", 4, PrimitiveType.INT, false, true);
String name;
int byteSize;
PrimitiveType type;
boolean supportsIBO;
boolean isCompound;
boolean compound;
private GraphicsDataType(String name, int byteSize, PrimitiveType type, boolean supportsIBO, boolean isCompound) {
private GraphicsDataType(String name, int byteSize, PrimitiveType type, boolean supportsIBO, boolean compound) {
this.name = name;
this.byteSize = byteSize;
this.type = type;
this.supportsIBO = supportsIBO;
this.isCompound = isCompound;
this.compound = compound;
}
private GraphicsDataType(String name, int byteSize, PrimitiveType type, boolean isCompound) {
this(name, byteSize, type, false, isCompound);
private GraphicsDataType(String name, int byteSize, PrimitiveType type, boolean supportsIBO) {
this(name, byteSize, type, supportsIBO, false);
}
private GraphicsDataType(String name, int byteSize, PrimitiveType type) {
this(name, byteSize, type, false, false);
}
@@ -61,16 +62,16 @@ public enum GraphicsDataType {
return type;
}
public boolean isCompound() {
return compound;
}
public boolean supportsIBO() {
return supportsIBO;
}
public boolean isCompound() {
return isCompound;
}
public int size(int attributes) {
return isCompound ? byteSize : attributes * byteSize;
return compound ? byteSize : attributes * byteSize;
}
}
@@ -0,0 +1,6 @@
package speiger.src.coreengine.graphics.api.utils;
public enum ExecutionType {
IMMEDIATE,
RECORDED;
}
@@ -1,12 +0,0 @@
package speiger.src.coreengine.graphics.api.vertex;
public record VertexElement(int size, Usage usage)
{
public static enum Usage {
POS,
UV,
COLOR,
NORMAL,
CUSTOM;
}
}
@@ -1,78 +0,0 @@
package speiger.src.coreengine.graphics.api.vertex;
import java.util.EnumSet;
import java.util.Iterator;
import java.util.Set;
import speiger.src.collections.ints.lists.IntArrayList;
import speiger.src.collections.ints.lists.IntList;
import speiger.src.collections.objects.lists.ObjectArrayList;
import speiger.src.collections.objects.lists.ObjectList;
import speiger.src.collections.objects.maps.impl.hash.Object2ObjectLinkedOpenHashMap;
import speiger.src.collections.objects.maps.interfaces.Object2ObjectMap;
import speiger.src.collections.objects.maps.interfaces.Object2ObjectMap.Entry;
import speiger.src.collections.objects.utils.ObjectIterators;
import speiger.src.coreengine.graphics.api.vertex.VertexElement.Usage;
public class VertexFormat implements Iterable<VertexElement> {
Object2ObjectMap<String, VertexElement> mappedElements = new Object2ObjectLinkedOpenHashMap<>();
ObjectList<VertexElement> elements = new ObjectArrayList<>();
Set<Usage> types = EnumSet.noneOf(Usage.class);
IntList offsets = new IntArrayList();
int totalOffset;
private VertexFormat() {}
private VertexFormat(VertexFormat vertexList) {
elements.addAll(vertexList.elements);
offsets.addAll(vertexList.offsets);
totalOffset = vertexList.totalOffset;
}
public static Builder builder() {
return new Builder();
}
public static Builder builder(VertexFormat format) {
return new Builder(format);
}
private void add(String name, VertexElement element) {
if(mappedElements.put(name, element) != null) throw new IllegalStateException("Duplicated Names are not allowed");
elements.add(element);
offsets.add(totalOffset);
totalOffset += element.size();
types.add(element.usage());
}
public int totalOffset() { return totalOffset; }
public int size() { return elements.size(); }
public VertexElement get(int index) { return elements.get(index); }
public int offset(int index) { return offsets.getInt(index); }
public Iterable<Entry<String, VertexElement>> entrySet() { return mappedElements.object2ObjectEntrySet().unmodifiable(); }
@Override
public Iterator<VertexElement> iterator() { return ObjectIterators.unmodifiable(elements.iterator()); }
public boolean hasType(Usage type) { return types.contains(type); }
public boolean hasName(String name) { return mappedElements.containsKey(name); }
public static class Builder {
VertexFormat format;
public Builder() {
format = new VertexFormat();
}
public Builder(VertexFormat prev) {
format = new VertexFormat(prev);
}
public Builder add(String name, VertexElement element) {
format.add(name, element);
return this;
}
public VertexFormat build() {
return format;
}
}
}
@@ -0,0 +1,90 @@
package speiger.src.coreengine.graphics.api.vertex;
import java.util.Objects;
import speiger.src.collections.ints.lists.IntArrayList;
import speiger.src.collections.ints.lists.IntList;
import speiger.src.collections.objects.collections.ObjectIterable;
import speiger.src.collections.objects.collections.ObjectIterator;
import speiger.src.collections.objects.lists.ObjectArrayList;
import speiger.src.collections.objects.lists.ObjectList;
import speiger.src.collections.objects.maps.interfaces.Object2ObjectMap;
import speiger.src.collections.objects.utils.ObjectIterators;
import speiger.src.coreengine.graphics.api.shader.states.GraphicsDataType;
import speiger.src.coreengine.graphics.api.vertex.VertexLayout.Element;
public class VertexLayout implements ObjectIterable<Element> {
Object2ObjectMap<String, Element> mapped = Object2ObjectMap.builder().linkedMap();
ObjectList<Element> elements = new ObjectArrayList<>();
IntList offsets = new IntArrayList();
int stride;
int bytes;
private VertexLayout() {}
public static Builder builder() {
return new Builder(new VertexLayout());
}
public int stride() { return stride; }
public int size() { return elements.size(); }
public int bytes() { return bytes; }
public int offset(int index) { return offsets.getInt(index); }
public Element get(int index) { return elements.get(index); }
public boolean contains(String key) { return mapped.containsKey(key); }
public ObjectList<Element> elements() { return elements.unmodifiable(); }
@Override
public ObjectIterator<Element> iterator() { return ObjectIterators.unmodifiable(elements.iterator()); }
private void add(Element element) {
if(mapped.put(element.name(), element) != null) throw new IllegalStateException("Duplicated Names are not allowed");
elements.add(element);
offsets.add(stride);
int size = element.size();
stride += size;
bytes += element.type().size(size);
}
public static class Builder {
VertexLayout format;
private Builder(VertexLayout format) {
this.format = format;
}
public Builder element(String name, int index, int size, Usage usage) {
return element(name, index, size, usage, GraphicsDataType.FLOAT, false);
}
public Builder element(String name, int index, int size, Usage usage, GraphicsDataType type) {
return element(name, index, size, usage, type, false);
}
public Builder element(String name, int index, int size, Usage usage, GraphicsDataType type, boolean normalized) {
format.add(new Element(name, index, size, usage, type, normalized));
return this;
}
public Builder array(String baseName, int startIndex, int width, int size, Usage usage, GraphicsDataType type) {
return array(baseName, startIndex, width, size, usage, type, false);
}
public Builder array(String baseName, int startIndex, int width, int size, Usage usage, GraphicsDataType type, boolean normalized) {
Objects.requireNonNull(baseName);
for(int i = 0;i<width;i++) {
element(baseName+" ["+i+"]", startIndex + i, size, usage, type, normalized);
}
return this;
}
public VertexLayout build() {
return format;
}
}
public record Element(String name, int index, int size, Usage usage, GraphicsDataType type, boolean normalized) {}
public enum Usage {
POS, UV, COLOR, NORMAL, MATRIX, CUSTOM;
}
}
@@ -8,8 +8,8 @@ import org.lwjgl.opengl.GL30;
import org.lwjgl.opengl.GL45;
import speiger.src.coreengine.assets.AssetLocation;
import speiger.src.coreengine.graphics.api.core.GraphicsCommandBuffer;
import speiger.src.coreengine.graphics.api.core.GraphicsCommandQueue;
import speiger.src.coreengine.graphics.api.shader.RenderPass;
import speiger.src.coreengine.graphics.api.target.RenderTarget;
import speiger.src.coreengine.graphics.api.target.ScreenTarget;
import speiger.src.coreengine.graphics.api.target.TextureTarget;
@@ -22,18 +22,17 @@ import speiger.src.coreengine.math.vector.floats.Vec4f;
import speiger.src.coreengine.rendering.input.window.Window;
public class GLCommandQueue implements GraphicsCommandQueue {
private static final int HAS_COLOR = 1;
private static final int HAS_DEPTH = 2;
private static final int HAS_BOTH = 3;
GLGraphicsDevice device;
FrameBufferCache fboCache = new FrameBufferCache();
GLRenderPass activePass;
RenderTarget renderTarget = ScreenTarget.INSTANCE;
ScreenBuffer targetFBO;
Vec4f clearColor = Vec4f.mutable(0F, 0F, 0F, 1F);
double clearDepth = 0D;
public GLCommandQueue(GLGraphicsDevice device) {
this.device = device;
this.targetFBO = new ScreenBuffer(0, device.getWindow().width(), device.getWindow().height());
}
@Override
@@ -68,23 +67,26 @@ public class GLCommandQueue implements GraphicsCommandQueue {
private RenderTarget setRenderTarget(RenderTarget target) {
if(this.renderTarget.equals(target)) return target;
this.renderTarget = target;
switch(target) {
targetFBO = switch(target) {
case ScreenTarget _ -> {
GL30.glBindFramebuffer(GL30.GL_DRAW_FRAMEBUFFER, 0);
Window window = device.getWindow();
GL11.glViewport(0, 0, window.width(), window.height());
break;
yield new ScreenBuffer(0, window.width(), window.height());
}
case TextureTarget(AssetLocation id, Texture color, Texture depth) -> {
int fbo = fboCache.getOrCreateFBO(id);
if(color != null) GL45.glNamedFramebufferTexture(fbo, GL30.GL_COLOR_ATTACHMENT0, ((GLTexture)color).id(), 0);
if(depth != null) GL45.glNamedFramebufferTexture(fbo, GL30.GL_DEPTH_ATTACHMENT, ((GLTexture)depth).id(), 0);
GL30.glBindFramebuffer(GL30.GL_DRAW_FRAMEBUFFER, fbo);
if(color != null) GL11.glViewport(0, 0, color.width(), color.height());
else if(depth != null) GL11.glViewport(0, 0, depth.width(), depth.height());
break;
if(color != null) {
GL45.glNamedFramebufferTexture(fbo, GL30.GL_COLOR_ATTACHMENT0, ((GLTexture)color).id(), 0);
yield new ScreenBuffer(fbo, color.width(), color.height());
}
if(depth != null) {
GL45.glNamedFramebufferTexture(fbo, GL30.GL_DEPTH_ATTACHMENT, ((GLTexture)depth).id(), 0);
yield new ScreenBuffer(fbo, depth.width(), depth.height());
}
throw new IllegalArgumentException("No Color or Depth Provided");
}
}
};
GL30.glBindFramebuffer(GL30.GL_DRAW_FRAMEBUFFER, targetFBO.fbo());
GL11.glViewport(0, 0, targetFBO.width(), targetFBO.height());
return target;
}
@@ -112,50 +114,6 @@ public class GLCommandQueue implements GraphicsCommandQueue {
public void readFromTexture(Texture source, int x, int y, int width, int height, long target) {
}
@Override
public RenderPass createRenderPass(RenderTarget target, DrawArea area) {
int width = 0;
int height = 0;
int hasState = 0;
int frameBuffer = switch(target) {
case ScreenTarget _ -> {
Window window = device.getWindow();
width = window.width();
height = window.height();
hasState = HAS_BOTH;
yield 0;
}
case TextureTarget(AssetLocation id, Texture color, Texture depth) -> {
int fbo = fboCache.getOrCreateFBO(id);
if(color != null) {
hasState |= HAS_COLOR;
GL45.glNamedFramebufferTexture(fbo, GL30.GL_COLOR_ATTACHMENT0, ((GLTexture)color).id(), 0);
}
if(depth != null) {
hasState |= HAS_DEPTH;
GL45.glNamedFramebufferTexture(fbo, GL30.GL_DEPTH_ATTACHMENT, ((GLTexture)depth).id(), 0);
}
if(color != null) {
width = color.width();
height = color.height();
}
else if(depth != null) {
width = depth.width();
height = depth.height();
}
yield fbo;
}
};
GL30.glBindFramebuffer(GL30.GL_DRAW_FRAMEBUFFER, frameBuffer);
GL11.glViewport(0, 0, width, height);
boolean scissors = area != null && !area.fills(width, height);
if(scissors) {
GL11.glEnable(GL11.GL_SCISSOR_TEST);
GL11.glScissor(area.x(), area.y(), area.width(), area.height());
}
return (activePass = new GLRenderPass(this, area, scissors, (hasState & HAS_COLOR) != 0, (hasState & HAS_DEPTH) != 0, frameBuffer));
}
private record PushableObject<T>(T original, T applying, Supplier<T> current, Consumer<T> apply) implements PushableResource {
@Override
public void close() {
@@ -164,4 +122,11 @@ public class GLCommandQueue implements GraphicsCommandQueue {
}
}
}
private record ScreenBuffer(int fbo, int width, int height) {}
@Override
public void submitCommands(GraphicsCommandBuffer buffer) {
}
}
@@ -9,14 +9,17 @@ import org.lwjgl.opengl.GL33;
import org.lwjgl.opengl.GL43;
import org.lwjgl.opengl.GL45;
import speiger.src.coreengine.assets.base.IAssetProvider;
import speiger.src.coreengine.assets.api.IAssetProvider;
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.GraphicsCommandBuffer;
import speiger.src.coreengine.graphics.api.core.GraphicsDevice;
import speiger.src.coreengine.graphics.api.mesh.Mesh;
import speiger.src.coreengine.graphics.api.sampler.SamplerSettings;
import speiger.src.coreengine.graphics.api.shader.ShaderPipeline;
import speiger.src.coreengine.graphics.api.texture.TextureSettings;
import speiger.src.coreengine.graphics.api.texture.states.SwizzleMask;
import speiger.src.coreengine.graphics.api.utils.ExecutionType;
import speiger.src.coreengine.graphics.opengl.buffer.GLVertexBuffer;
import speiger.src.coreengine.graphics.opengl.sampler.GLSampler;
import speiger.src.coreengine.graphics.opengl.shader.ShaderInstance;
@@ -44,6 +47,11 @@ public class GLGraphicsDevice implements GraphicsDevice {
return new GLSurface(owner);
}
@Override
public GraphicsCommandBuffer createBuffer(ExecutionType type) {
return null;
}
@Override
public GLCommandQueue getQueue() {
return queue;
@@ -85,9 +93,12 @@ public class GLGraphicsDevice implements GraphicsDevice {
return new GLSampler(Objects.requireNonNull(settings));
}
@Override
public Mesh createMesh(Mesh.Builder builder, boolean autocreate) {
return null;
}
public ShaderInstance createShader(ShaderPipeline line, IAssetProvider provider) {
return null;
}
}
@@ -0,0 +1,64 @@
package speiger.src.coreengine.graphics.opengl.mesh;
import org.lwjgl.opengl.GL45;
import speiger.src.collections.ints.maps.interfaces.Int2ObjectMap;
import speiger.src.coreengine.graphics.api.buffer.VertexBuffer;
import speiger.src.coreengine.graphics.api.mesh.Mesh;
import speiger.src.coreengine.graphics.api.vertex.VertexLayout;
import speiger.src.coreengine.graphics.api.vertex.VertexLayout.Element;
import speiger.src.coreengine.graphics.opengl.buffer.GLVertexBuffer;
import speiger.src.coreengine.graphics.opengl.utils.GLUtils;
public class GLMesh extends Mesh {
int vao;
boolean managed;
public GLMesh(Int2ObjectMap<LayoutInfo> layouts, Int2ObjectMap<VertexBuffer> buffers, VertexBuffer indeciesBuffer, boolean managed) {
super(layouts, buffers, indeciesBuffer);
this.managed = managed;
vao = GL45.glCreateVertexArrays();
}
@Override
public boolean isRemoved() { return vao == 0; }
@Override
public Mesh buffer(int binding, VertexBuffer buffer) {
super.buffer(binding, buffer);
bind(binding);
return this;
}
private void bind(int binding) {
LayoutInfo info = layouts.get(binding);
VertexLayout layout = info.layout();
GLVertexBuffer buffer = (GLVertexBuffer)buffers.get(binding);
GL45.glVertexArrayVertexBuffer(vao, binding, buffer.id(), 0, layout.bytes());
if(info.instanceCount() > 0) GL45.glVertexArrayBindingDivisor(vao, binding, info.instanceCount());
int index = 0;
for(Element element : layout) {
GL45.glVertexArrayAttribFormat(vao, element.index(), element.size(), GLUtils.toGL(element.type()), element.normalized(), layout.offset(index++));
GL45.glVertexArrayAttribBinding(vao, element.index(), binding);
}
}
@Override
public void remove() {
if(vao == 0) return;
GL45.glDeleteVertexArrays(vao);
vao = 0;
if(!managed) return;
if(indeciesBuffer != null) indeciesBuffer.remove();
buffers.values().forEach(VertexBuffer::close);
}
@Override
protected void closeWithBuffers() {
boolean wasManaged = managed;
managed = true;
close();
managed = wasManaged;
}
}
@@ -18,6 +18,7 @@ import speiger.src.coreengine.graphics.api.buffer.states.BufferState;
import speiger.src.coreengine.graphics.api.buffer.states.BufferType;
import speiger.src.coreengine.graphics.api.sampler.states.BorderMode;
import speiger.src.coreengine.graphics.api.sampler.states.SampleMode;
import speiger.src.coreengine.graphics.api.shader.states.GraphicsDataType;
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.TextureType;
@@ -126,4 +127,31 @@ public class GLUtils {
case NOTEQUAL -> GL11.GL_NOTEQUAL;
};
}
public static int toGL(GraphicsDataType type) {
return switch(type) {
case BYTE -> GL11.GL_BYTE;
case UNSIGNED_BYTE -> GL11.GL_UNSIGNED_BYTE;
case SHORT -> GL11.GL_SHORT;
case UNSIGNED_SHORT -> GL11.GL_UNSIGNED_SHORT;
case INT -> GL11.GL_INT;
case UNSIGNED_INT -> GL11.GL_UNSIGNED_INT;
case FLOAT -> GL11.GL_FLOAT;
case DOUBLE -> GL11.GL_DOUBLE;
case UNSIGNED_3_3_2 -> GL12.GL_UNSIGNED_BYTE_3_3_2;
case UNSIGNED_2_3_3_REV -> GL12.GL_UNSIGNED_BYTE_2_3_3_REV;
case UNSIGNED_5_6_5 -> GL12.GL_UNSIGNED_SHORT_5_6_5;
case UNSIGNED_5_6_5_REV -> GL12.GL_UNSIGNED_SHORT_5_6_5_REV;
case UNSIGNED_4_4_4_4 -> GL12.GL_UNSIGNED_SHORT_4_4_4_4;
case UNSIGNED_4_4_4_4_REV -> GL12.GL_UNSIGNED_SHORT_4_4_4_4_REV;
case UNSIGNED_5_5_5_1 -> GL12.GL_UNSIGNED_SHORT_5_5_5_1;
case UNSIGNED_1_5_5_5_REV -> GL12.GL_UNSIGNED_SHORT_1_5_5_5_REV;
case UNSIGNED_8_8_8_8 -> GL12.GL_UNSIGNED_INT_8_8_8_8;
case UNSIGNED_8_8_8_8_REV -> GL12.GL_UNSIGNED_INT_8_8_8_8_REV;
case UNSIGNED_10_10_10_2 -> GL12.GL_UNSIGNED_INT_10_10_10_2;
case UNSIGNED_2_10_10_10_REV -> GL12.GL_UNSIGNED_INT_2_10_10_10_REV;
case UNSIGNED_INT_10F_11F_11F_REV -> GL30.GL_UNSIGNED_INT_10F_11F_11F_REV;
case UNSIGNED_INT_5_9_9_9_REV -> GL30.GL_UNSIGNED_INT_5_9_9_9_REV;
};
}
}