mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2025-01-13 15:56:07 +01:00
Arrays and alignment
- Allow array elements in layouts with lengths in range [1, 256] - Align each layout element to its primitive size instead of always aligning to 4 bytes; this is the same as C struct alignment - Rename package lib.visual.components -> component - Add more methods to layout API interfaces to allow retrieving information such as padding size and alignment
This commit is contained in:
parent
09cb15e2ed
commit
2f7fce29c3
24 changed files with 350 additions and 218 deletions
|
@ -0,0 +1,12 @@
|
|||
package com.jozufozu.flywheel.api.layout;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.Range;
|
||||
|
||||
@ApiStatus.NonExtendable
|
||||
public non-sealed interface ArrayElementType extends ElementType {
|
||||
ElementType innerType();
|
||||
|
||||
@Range(from = 1, to = 256)
|
||||
int length();
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
package com.jozufozu.flywheel.api.layout;
|
||||
|
||||
public sealed interface ElementType permits ScalarElementType, VectorElementType, MatrixElementType {
|
||||
public sealed interface ElementType permits ScalarElementType, VectorElementType, MatrixElementType, ArrayElementType {
|
||||
int byteSize();
|
||||
|
||||
int byteAlignment();
|
||||
}
|
||||
|
|
|
@ -17,12 +17,18 @@ public interface Layout {
|
|||
|
||||
int byteSize();
|
||||
|
||||
int byteAlignment();
|
||||
|
||||
@ApiStatus.NonExtendable
|
||||
interface Element {
|
||||
String name();
|
||||
|
||||
ElementType type();
|
||||
|
||||
int offset();
|
||||
int byteOffset();
|
||||
|
||||
int paddedByteSize();
|
||||
|
||||
int paddingByteSize();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,14 @@ public interface LayoutBuilder {
|
|||
|
||||
LayoutBuilder matrix(String name, FloatRepr repr, @Range(from = 2, to = 4) int size);
|
||||
|
||||
LayoutBuilder scalarArray(String name, ValueRepr repr, @Range(from = 1, to = 256) int length);
|
||||
|
||||
LayoutBuilder vectorArray(String name, ValueRepr repr, @Range(from = 2, to = 4) int size, @Range(from = 1, to = 256) int length);
|
||||
|
||||
LayoutBuilder matrixArray(String name, FloatRepr repr, @Range(from = 2, to = 4) int rows, @Range(from = 2, to = 4) int columns, @Range(from = 1, to = 256) int length);
|
||||
|
||||
LayoutBuilder matrixArray(String name, FloatRepr repr, @Range(from = 2, to = 4) int size, @Range(from = 1, to = 256) int length);
|
||||
|
||||
Layout build();
|
||||
|
||||
static LayoutBuilder create() {
|
||||
|
|
|
@ -7,7 +7,6 @@ import com.jozufozu.flywheel.api.layout.FloatRepr;
|
|||
import com.jozufozu.flywheel.api.layout.Layout;
|
||||
import com.jozufozu.flywheel.api.layout.LayoutBuilder;
|
||||
import com.jozufozu.flywheel.api.vertex.VertexView;
|
||||
import com.jozufozu.flywheel.backend.engine.LayoutAttributes;
|
||||
import com.jozufozu.flywheel.backend.gl.array.VertexAttribute;
|
||||
import com.jozufozu.flywheel.lib.vertex.FullVertexView;
|
||||
|
||||
|
@ -24,7 +23,6 @@ public final class InternalVertex {
|
|||
.build();
|
||||
|
||||
public static final List<VertexAttribute> ATTRIBUTES = LayoutAttributes.attributes(LAYOUT);
|
||||
public static final int ATTRIBUTE_COUNT = ATTRIBUTES.size();
|
||||
public static final int STRIDE = LAYOUT.byteSize();
|
||||
|
||||
public static final ResourceLocation LAYOUT_SHADER = Flywheel.rl("internal/vertex_input.vert");
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
package com.jozufozu.flywheel.backend.engine;
|
||||
package com.jozufozu.flywheel.backend;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.jozufozu.flywheel.api.layout.ArrayElementType;
|
||||
import com.jozufozu.flywheel.api.layout.ElementType;
|
||||
import com.jozufozu.flywheel.api.layout.FloatRepr;
|
||||
import com.jozufozu.flywheel.api.layout.IntegerRepr;
|
||||
import com.jozufozu.flywheel.api.layout.Layout;
|
||||
|
@ -25,28 +27,23 @@ public class LayoutAttributes {
|
|||
List<VertexAttribute> out = new ArrayList<>();
|
||||
|
||||
for (Layout.Element element : layout.elements()) {
|
||||
var type = element.type();
|
||||
|
||||
if (type instanceof ScalarElementType scalar) {
|
||||
vector(out, scalar.repr(), 1);
|
||||
} else if (type instanceof VectorElementType vector) {
|
||||
vector(out, vector.repr(), vector.size());
|
||||
} else if (type instanceof MatrixElementType matrix) {
|
||||
matrix(out, matrix);
|
||||
}
|
||||
element(out, element.type());
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
private static void matrix(List<VertexAttribute> out, MatrixElementType matrix) {
|
||||
int size = matrix.columns();
|
||||
var repr = matrix.repr();
|
||||
var glType = toGlType(repr);
|
||||
boolean normalized = normalized(repr);
|
||||
|
||||
for (int i = 0; i < matrix.rows(); i++) {
|
||||
out.add(new VertexAttribute.Float(glType, size, normalized));
|
||||
private static void element(List<VertexAttribute> out, ElementType type) {
|
||||
if (type instanceof ScalarElementType scalar) {
|
||||
vector(out, scalar.repr(), 1);
|
||||
} else if (type instanceof VectorElementType vector) {
|
||||
vector(out, vector.repr(), vector.size());
|
||||
} else if (type instanceof MatrixElementType matrix) {
|
||||
matrix(out, matrix);
|
||||
} else if (type instanceof ArrayElementType array) {
|
||||
array(out, array);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unknown type " + type);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -56,7 +53,29 @@ public class LayoutAttributes {
|
|||
} else if (repr instanceof UnsignedIntegerRepr integer) {
|
||||
out.add(new VertexAttribute.Int(toGlType(integer), size));
|
||||
} else if (repr instanceof FloatRepr floatRepr) {
|
||||
out.add(new VertexAttribute.Float(toGlType(floatRepr), size, normalized(floatRepr)));
|
||||
out.add(new VertexAttribute.Float(toGlType(floatRepr), size, isNormalized(floatRepr)));
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unknown repr " + repr);
|
||||
}
|
||||
}
|
||||
|
||||
private static void matrix(List<VertexAttribute> out, MatrixElementType matrix) {
|
||||
int size = matrix.columns();
|
||||
var repr = matrix.repr();
|
||||
var glType = toGlType(repr);
|
||||
boolean normalized = isNormalized(repr);
|
||||
|
||||
for (int i = 0; i < matrix.rows(); i++) {
|
||||
out.add(new VertexAttribute.Float(glType, size, normalized));
|
||||
}
|
||||
}
|
||||
|
||||
private static void array(List<VertexAttribute> out, ArrayElementType array) {
|
||||
ElementType innerType = array.innerType();
|
||||
int length = array.length();
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
element(out, innerType);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -88,7 +107,7 @@ public class LayoutAttributes {
|
|||
};
|
||||
}
|
||||
|
||||
private static boolean normalized(FloatRepr repr) {
|
||||
private static boolean isNormalized(FloatRepr repr) {
|
||||
return switch (repr) {
|
||||
case NORMALIZED_BYTE, NORMALIZED_UNSIGNED_BYTE, NORMALIZED_SHORT, NORMALIZED_UNSIGNED_SHORT, NORMALIZED_INT, NORMALIZED_UNSIGNED_INT ->
|
||||
true;
|
|
@ -28,7 +28,7 @@ public class BufferTextureInstanceComponent extends InstanceAssemblerComponent {
|
|||
protected void generateUnpacking(GlslBuilder builder) {
|
||||
var fnBody = new GlslBlock();
|
||||
|
||||
var texels = MoreMath.ceilingDiv(layout.byteSize(), 16);
|
||||
int texels = MoreMath.ceilingDiv(layout.byteSize(), 16);
|
||||
|
||||
fnBody.add(GlslStmt.raw("int base = " + UNPACK_ARG + " * " + texels + ";"));
|
||||
|
||||
|
@ -38,11 +38,8 @@ public class BufferTextureInstanceComponent extends InstanceAssemblerComponent {
|
|||
}
|
||||
|
||||
var unpackArgs = new ArrayList<GlslExpr>();
|
||||
int uintOffset = 0;
|
||||
for (Layout.Element element : layout.elements()) {
|
||||
unpackArgs.add(unpackElement(element, uintOffset));
|
||||
// Element byte size is always a multiple of 4
|
||||
uintOffset += element.type().byteSize() / 4;
|
||||
unpackArgs.add(unpackElement(element));
|
||||
}
|
||||
|
||||
fnBody.ret(GlslExpr.call(STRUCT_NAME, unpackArgs));
|
||||
|
|
|
@ -9,6 +9,8 @@ import java.util.List;
|
|||
import java.util.function.Function;
|
||||
|
||||
import com.jozufozu.flywheel.api.instance.InstanceType;
|
||||
import com.jozufozu.flywheel.api.layout.ArrayElementType;
|
||||
import com.jozufozu.flywheel.api.layout.ElementType;
|
||||
import com.jozufozu.flywheel.api.layout.FloatRepr;
|
||||
import com.jozufozu.flywheel.api.layout.IntegerRepr;
|
||||
import com.jozufozu.flywheel.api.layout.Layout;
|
||||
|
@ -20,6 +22,7 @@ import com.jozufozu.flywheel.api.layout.VectorElementType;
|
|||
import com.jozufozu.flywheel.backend.glsl.SourceComponent;
|
||||
import com.jozufozu.flywheel.backend.glsl.generate.GlslBuilder;
|
||||
import com.jozufozu.flywheel.backend.glsl.generate.GlslExpr;
|
||||
import com.jozufozu.flywheel.impl.layout.LayoutInterpreter;
|
||||
|
||||
public abstract class InstanceAssemblerComponent implements SourceComponent {
|
||||
protected static final String STRUCT_NAME = "FlwInstance";
|
||||
|
@ -95,164 +98,146 @@ public abstract class InstanceAssemblerComponent implements SourceComponent {
|
|||
|
||||
protected abstract GlslExpr access(int uintOffset);
|
||||
|
||||
protected GlslExpr unpackElement(Layout.Element element, int uintOffset) {
|
||||
var type = element.type();
|
||||
protected GlslExpr unpackElement(Layout.Element element) {
|
||||
return unpackElement(element.type(), element.byteOffset());
|
||||
}
|
||||
|
||||
private GlslExpr unpackElement(ElementType type, int byteOffset) {
|
||||
if (type instanceof ScalarElementType scalar) {
|
||||
return unpackScalar(scalar, uintOffset);
|
||||
return unpackScalar(scalar, byteOffset);
|
||||
} else if (type instanceof VectorElementType vector) {
|
||||
return unpackVector(vector, uintOffset);
|
||||
return unpackVector(vector, byteOffset);
|
||||
} else if (type instanceof MatrixElementType matrix) {
|
||||
return unpackMatrix(matrix, uintOffset);
|
||||
return unpackMatrix(matrix, byteOffset);
|
||||
} else if (type instanceof ArrayElementType array) {
|
||||
return unpackArray(array, byteOffset);
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Unknown type " + type);
|
||||
}
|
||||
|
||||
private GlslExpr unpackScalar(ScalarElementType type, int uintOffset) {
|
||||
private GlslExpr unpackScalar(ScalarElementType type, int byteOffset) {
|
||||
var repr = type.repr();
|
||||
Function<GlslExpr, GlslExpr> unpackingFunc;
|
||||
|
||||
if (repr instanceof IntegerRepr intRepr) {
|
||||
unpackingFunc = INT_UNPACKING_FUNCS.get(intRepr);
|
||||
} else if (repr instanceof UnsignedIntegerRepr uintRepr) {
|
||||
unpackingFunc = UINT_UNPACKING_FUNCS.get(uintRepr);
|
||||
} else if (repr instanceof FloatRepr floatRepr) {
|
||||
unpackingFunc = FLOAT_UNPACKING_FUNCS.get(floatRepr);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unknown repr " + repr);
|
||||
}
|
||||
|
||||
if (isByteBacked(repr)) {
|
||||
return unpackByteBackedScalar(uintOffset, unpackingFunc);
|
||||
} else if (isShortBacked(repr)) {
|
||||
return unpackShortBackedScalar(uintOffset, unpackingFunc);
|
||||
} else {
|
||||
return unpackIntBackedScalar(uintOffset, unpackingFunc);
|
||||
}
|
||||
Function<GlslExpr, GlslExpr> unpackingFunc = getUnpackingFunc(repr);
|
||||
return unpackScalar(byteOffset, repr.byteSize(), unpackingFunc);
|
||||
}
|
||||
|
||||
private GlslExpr unpackVector(VectorElementType type, int uintOffset) {
|
||||
private GlslExpr unpackVector(VectorElementType type, int byteOffset) {
|
||||
var repr = type.repr();
|
||||
int size = type.size();
|
||||
Function<GlslExpr, GlslExpr> unpackingFunc;
|
||||
String outType;
|
||||
|
||||
if (repr instanceof IntegerRepr intRepr) {
|
||||
unpackingFunc = INT_UNPACKING_FUNCS.get(intRepr);
|
||||
outType = "ivec" + size;
|
||||
} else if (repr instanceof UnsignedIntegerRepr uintRepr) {
|
||||
unpackingFunc = UINT_UNPACKING_FUNCS.get(uintRepr);
|
||||
outType = "uvec" + size;
|
||||
} else if (repr instanceof FloatRepr floatRepr) {
|
||||
unpackingFunc = FLOAT_UNPACKING_FUNCS.get(floatRepr);
|
||||
outType = "vec" + size;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unknown repr " + repr);
|
||||
}
|
||||
|
||||
if (isByteBacked(repr)) {
|
||||
return unpackByteBackedVector(outType, size, uintOffset, unpackingFunc);
|
||||
} else if (isShortBacked(repr)) {
|
||||
return unpackShortBackedVector(outType, size, uintOffset, unpackingFunc);
|
||||
} else {
|
||||
return unpackIntBackedVector(outType, size, uintOffset, unpackingFunc);
|
||||
}
|
||||
Function<GlslExpr, GlslExpr> unpackingFunc = getUnpackingFunc(repr);
|
||||
String outType = LayoutInterpreter.vectorTypeName(type);
|
||||
return unpackVector(outType, size, byteOffset, repr.byteSize(), unpackingFunc);
|
||||
}
|
||||
|
||||
private GlslExpr unpackMatrix(MatrixElementType type, int uintOffset) {
|
||||
private GlslExpr unpackMatrix(MatrixElementType type, int byteOffset) {
|
||||
var repr = type.repr();
|
||||
int rows = type.rows();
|
||||
int columns = type.columns();
|
||||
Function<GlslExpr, GlslExpr> unpackingFunc = FLOAT_UNPACKING_FUNCS.get(repr);
|
||||
String outType = "mat" + columns + "x" + rows;
|
||||
String outType = LayoutInterpreter.matrixTypeName(type);
|
||||
int size = rows * columns;
|
||||
return unpackVector(outType, size, byteOffset, repr.byteSize(), unpackingFunc);
|
||||
}
|
||||
|
||||
if (isByteBacked(repr)) {
|
||||
return unpackByteBackedVector(outType, size, uintOffset, unpackingFunc);
|
||||
} else if (isShortBacked(repr)) {
|
||||
return unpackShortBackedVector(outType, size, uintOffset, unpackingFunc);
|
||||
private GlslExpr unpackArray(ArrayElementType type, int byteOffset) {
|
||||
ElementType innerType = type.innerType();
|
||||
int innerByteSize = innerType.byteSize();
|
||||
int length = type.length();
|
||||
String outType = LayoutInterpreter.arrayTypeName(type);
|
||||
|
||||
List<GlslExpr> args = new ArrayList<>();
|
||||
for (int i = 0; i < length; i++) {
|
||||
args.add(unpackElement(innerType, byteOffset + i * innerByteSize));
|
||||
}
|
||||
return GlslExpr.call(outType, args);
|
||||
}
|
||||
|
||||
private GlslExpr unpackScalar(int byteOffset, int byteSize, Function<GlslExpr, GlslExpr> unpackingFunc) {
|
||||
int offset = byteOffset / byteSize;
|
||||
|
||||
if (byteSize == Byte.BYTES) {
|
||||
return unpackByteBackedScalar(offset, unpackingFunc);
|
||||
} else if (byteSize == Short.BYTES) {
|
||||
return unpackShortBackedScalar(offset, unpackingFunc);
|
||||
} else {
|
||||
return unpackIntBackedVector(outType, size, uintOffset, unpackingFunc);
|
||||
return unpackIntBackedScalar(offset, unpackingFunc);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isByteBacked(ValueRepr repr) {
|
||||
return repr.byteSize() == Byte.BYTES;
|
||||
}
|
||||
|
||||
private boolean isShortBacked(ValueRepr repr) {
|
||||
return repr.byteSize() == Short.BYTES;
|
||||
}
|
||||
|
||||
private GlslExpr unpackByteBackedScalar(int uintOffset, Function<GlslExpr, GlslExpr> perElement) {
|
||||
GlslExpr e;
|
||||
private GlslExpr unpackByteBackedScalar(int byteOffset, Function<GlslExpr, GlslExpr> unpackingFunc) {
|
||||
int bitPos = (byteOffset % 4) * 8;
|
||||
if (BIG_ENDIAN) {
|
||||
e = access(uintOffset)
|
||||
.rsh(24)
|
||||
.and(0xFF);
|
||||
} else {
|
||||
e = access(uintOffset)
|
||||
.and(0xFF);
|
||||
bitPos = 24 - bitPos;
|
||||
}
|
||||
return perElement.apply(e);
|
||||
int wordOffset = byteOffset / 4;
|
||||
GlslExpr prepared = access(wordOffset)
|
||||
.rsh(bitPos)
|
||||
.and(0xFF);
|
||||
return unpackingFunc.apply(prepared);
|
||||
}
|
||||
|
||||
private GlslExpr unpackShortBackedScalar(int uintOffset, Function<GlslExpr, GlslExpr> perElement) {
|
||||
GlslExpr e;
|
||||
private GlslExpr unpackShortBackedScalar(int shortOffset, Function<GlslExpr, GlslExpr> unpackingFunc) {
|
||||
int bitPos = (shortOffset % 2) * 16;
|
||||
if (BIG_ENDIAN) {
|
||||
e = access(uintOffset)
|
||||
.rsh(16)
|
||||
.and(0xFFFF);
|
||||
bitPos = 16 - bitPos;
|
||||
}
|
||||
int wordOffset = shortOffset / 2;
|
||||
GlslExpr prepared = access(wordOffset)
|
||||
.rsh(bitPos)
|
||||
.and(0xFFFF);
|
||||
return unpackingFunc.apply(prepared);
|
||||
}
|
||||
|
||||
private GlslExpr unpackIntBackedScalar(int intOffset, Function<GlslExpr, GlslExpr> unpackingFunc) {
|
||||
return unpackingFunc.apply(access(intOffset));
|
||||
}
|
||||
|
||||
private GlslExpr unpackVector(String outType, int size, int byteOffset, int byteSize, Function<GlslExpr, GlslExpr> unpackingFunc) {
|
||||
int offset = byteOffset / byteSize;
|
||||
|
||||
if (byteSize == Byte.BYTES) {
|
||||
return unpackByteBackedVector(outType, size, offset, unpackingFunc);
|
||||
} else if (byteSize == Short.BYTES) {
|
||||
return unpackShortBackedVector(outType, size, offset, unpackingFunc);
|
||||
} else {
|
||||
e = access(uintOffset)
|
||||
.and(0xFFFF);
|
||||
return unpackIntBackedVector(outType, size, offset, unpackingFunc);
|
||||
}
|
||||
return perElement.apply(e);
|
||||
}
|
||||
|
||||
private GlslExpr unpackIntBackedScalar(int uintOffset, Function<GlslExpr, GlslExpr> perElement) {
|
||||
return perElement.apply(access(uintOffset));
|
||||
}
|
||||
|
||||
private GlslExpr unpackByteBackedVector(String outType, int size, int uintOffset, Function<GlslExpr, GlslExpr> perElement) {
|
||||
private GlslExpr unpackByteBackedVector(String outType, int size, int byteOffset, Function<GlslExpr, GlslExpr> unpackingFunc) {
|
||||
List<GlslExpr> args = new ArrayList<>();
|
||||
for (int i = 0; i < size; i++) {
|
||||
// Vectors cannot contain more than 4 elements, but matrix unpacking treats the matrix as a long vector, which for mat4x4 would be the equivalent of a vec16.
|
||||
int bitPos = (i % 4) * 8;
|
||||
if (BIG_ENDIAN) {
|
||||
bitPos = 24 - bitPos;
|
||||
}
|
||||
int wordOffset = i / 4;
|
||||
var element = access(uintOffset + wordOffset)
|
||||
.rsh(bitPos)
|
||||
.and(0xFF);
|
||||
args.add(perElement.apply(element));
|
||||
args.add(unpackByteBackedScalar(byteOffset + i, unpackingFunc));
|
||||
}
|
||||
return GlslExpr.call(outType, args);
|
||||
}
|
||||
|
||||
private GlslExpr unpackShortBackedVector(String outType, int size, int uintOffset, Function<GlslExpr, GlslExpr> perElement) {
|
||||
private GlslExpr unpackShortBackedVector(String outType, int size, int shortOffset, Function<GlslExpr, GlslExpr> unpackingFunc) {
|
||||
List<GlslExpr> args = new ArrayList<>();
|
||||
for (int i = 0; i < size; i++) {
|
||||
int bitPos = (i % 2) * 16;
|
||||
if (BIG_ENDIAN) {
|
||||
bitPos = 16 - bitPos;
|
||||
}
|
||||
int wordOffset = i / 2;
|
||||
var element = access(uintOffset + wordOffset)
|
||||
.rsh(bitPos)
|
||||
.and(0xFFFF);
|
||||
args.add(perElement.apply(element));
|
||||
args.add(unpackShortBackedScalar(shortOffset + i, unpackingFunc));
|
||||
}
|
||||
return GlslExpr.call(outType, args);
|
||||
}
|
||||
|
||||
private GlslExpr unpackIntBackedVector(String outType, int size, int uintOffset, Function<GlslExpr, GlslExpr> perElement) {
|
||||
private GlslExpr unpackIntBackedVector(String outType, int size, int intOffset, Function<GlslExpr, GlslExpr> unpackingFunc) {
|
||||
List<GlslExpr> args = new ArrayList<>();
|
||||
for (int i = 0; i < size; i++) {
|
||||
args.add(perElement.apply(access(uintOffset + i)));
|
||||
args.add(unpackIntBackedScalar(intOffset + i, unpackingFunc));
|
||||
}
|
||||
return GlslExpr.call(outType, args);
|
||||
}
|
||||
|
||||
private static Function<GlslExpr, GlslExpr> getUnpackingFunc(ValueRepr repr) {
|
||||
if (repr instanceof IntegerRepr intRepr) {
|
||||
return INT_UNPACKING_FUNCS.get(intRepr);
|
||||
} else if (repr instanceof UnsignedIntegerRepr uintRepr) {
|
||||
return UINT_UNPACKING_FUNCS.get(uintRepr);
|
||||
} else if (repr instanceof FloatRepr floatRepr) {
|
||||
return FLOAT_UNPACKING_FUNCS.get(floatRepr);
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Unknown repr " + repr);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,17 +5,10 @@ import java.util.Collections;
|
|||
|
||||
import com.jozufozu.flywheel.Flywheel;
|
||||
import com.jozufozu.flywheel.api.instance.InstanceType;
|
||||
import com.jozufozu.flywheel.api.layout.ElementType;
|
||||
import com.jozufozu.flywheel.api.layout.FloatRepr;
|
||||
import com.jozufozu.flywheel.api.layout.IntegerRepr;
|
||||
import com.jozufozu.flywheel.api.layout.Layout;
|
||||
import com.jozufozu.flywheel.api.layout.MatrixElementType;
|
||||
import com.jozufozu.flywheel.api.layout.ScalarElementType;
|
||||
import com.jozufozu.flywheel.api.layout.UnsignedIntegerRepr;
|
||||
import com.jozufozu.flywheel.api.layout.ValueRepr;
|
||||
import com.jozufozu.flywheel.api.layout.VectorElementType;
|
||||
import com.jozufozu.flywheel.backend.glsl.SourceComponent;
|
||||
import com.jozufozu.flywheel.backend.glsl.generate.GlslBuilder;
|
||||
import com.jozufozu.flywheel.impl.layout.LayoutInterpreter;
|
||||
|
||||
public class InstanceStructComponent implements SourceComponent {
|
||||
private static final String STRUCT_NAME = "FlwInstance";
|
||||
|
@ -43,48 +36,10 @@ public class InstanceStructComponent implements SourceComponent {
|
|||
var instance = builder.struct();
|
||||
instance.setName(STRUCT_NAME);
|
||||
for (var element : layout.elements()) {
|
||||
instance.addField(typeName(element.type()), element.name());
|
||||
instance.addField(LayoutInterpreter.typeName(element.type()), element.name());
|
||||
}
|
||||
|
||||
builder.blankLine();
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private static String typeName(ElementType type) {
|
||||
if (type instanceof ScalarElementType scalar) {
|
||||
return scalarTypeName(scalar.repr());
|
||||
} else if (type instanceof VectorElementType vector) {
|
||||
return vectorTypeName(vector.repr(), vector.size());
|
||||
} else if (type instanceof MatrixElementType matrix) {
|
||||
return matrixTypeName(matrix);
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Unknown type " + type);
|
||||
}
|
||||
|
||||
private static String scalarTypeName(ValueRepr repr) {
|
||||
if (repr instanceof IntegerRepr) {
|
||||
return "int";
|
||||
} else if (repr instanceof UnsignedIntegerRepr) {
|
||||
return "uint";
|
||||
} else if (repr instanceof FloatRepr) {
|
||||
return "float";
|
||||
}
|
||||
throw new IllegalArgumentException("Unknown repr " + repr);
|
||||
}
|
||||
|
||||
private static String vectorTypeName(ValueRepr repr, int size) {
|
||||
if (repr instanceof IntegerRepr) {
|
||||
return "ivec" + size;
|
||||
} else if (repr instanceof UnsignedIntegerRepr) {
|
||||
return "uvec" + size;
|
||||
} else if (repr instanceof FloatRepr) {
|
||||
return "vec" + size;
|
||||
}
|
||||
throw new IllegalArgumentException("Unknown repr " + repr);
|
||||
}
|
||||
|
||||
private static String matrixTypeName(MatrixElementType matrix) {
|
||||
return "mat" + matrix.columns() + "x" + matrix.rows();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import com.jozufozu.flywheel.backend.glsl.generate.GlslBlock;
|
|||
import com.jozufozu.flywheel.backend.glsl.generate.GlslBuilder;
|
||||
import com.jozufozu.flywheel.backend.glsl.generate.GlslExpr;
|
||||
import com.jozufozu.flywheel.backend.glsl.generate.GlslStmt;
|
||||
import com.jozufozu.flywheel.lib.math.MoreMath;
|
||||
|
||||
public class SsboInstanceComponent extends InstanceAssemblerComponent {
|
||||
public SsboInstanceComponent(InstanceType<?> type) {
|
||||
|
@ -26,7 +27,7 @@ public class SsboInstanceComponent extends InstanceAssemblerComponent {
|
|||
protected void generateUnpacking(GlslBuilder builder) {
|
||||
var fnBody = new GlslBlock();
|
||||
|
||||
var uintCount = layout.byteSize() / 4;
|
||||
int uintCount = MoreMath.ceilingDiv(layout.byteSize(), 4);
|
||||
|
||||
fnBody.add(GlslStmt.raw("uint base = " + UNPACK_ARG + " * " + uintCount + "u;"));
|
||||
|
||||
|
@ -36,11 +37,8 @@ public class SsboInstanceComponent extends InstanceAssemblerComponent {
|
|||
}
|
||||
|
||||
var unpackArgs = new ArrayList<GlslExpr>();
|
||||
int uintOffset = 0;
|
||||
for (Layout.Element element : layout.elements()) {
|
||||
unpackArgs.add(unpackElement(element, uintOffset));
|
||||
// Element byte size is always a multiple of 4
|
||||
uintOffset += element.type().byteSize() / 4;
|
||||
unpackArgs.add(unpackElement(element));
|
||||
}
|
||||
|
||||
fnBody.ret(GlslExpr.call(STRUCT_NAME, unpackArgs));
|
||||
|
|
|
@ -30,6 +30,7 @@ import com.jozufozu.flywheel.backend.engine.uniform.Uniforms;
|
|||
import com.jozufozu.flywheel.backend.gl.Driver;
|
||||
import com.jozufozu.flywheel.backend.gl.GlCompat;
|
||||
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
|
||||
import com.jozufozu.flywheel.lib.math.MoreMath;
|
||||
|
||||
public class IndirectCullingGroup<I extends Instance> {
|
||||
private static final Comparator<IndirectDraw> DRAW_COMPARATOR = Comparator.comparing(IndirectDraw::stage)
|
||||
|
@ -58,8 +59,8 @@ public class IndirectCullingGroup<I extends Instance> {
|
|||
IndirectCullingGroup(InstanceType<I> instanceType, Environment environment, IndirectPrograms programs) {
|
||||
this.instanceType = instanceType;
|
||||
this.environment = environment;
|
||||
instanceStride = instanceType.layout()
|
||||
.byteSize();
|
||||
instanceStride = MoreMath.align4(instanceType.layout()
|
||||
.byteSize());
|
||||
buffers = new IndirectBuffers(instanceStride);
|
||||
|
||||
this.programs = programs;
|
||||
|
|
|
@ -12,6 +12,7 @@ import com.jozufozu.flywheel.api.instance.InstanceWriter;
|
|||
import com.jozufozu.flywheel.api.model.Model;
|
||||
import com.jozufozu.flywheel.backend.engine.AbstractInstancer;
|
||||
import com.jozufozu.flywheel.backend.engine.embed.Environment;
|
||||
import com.jozufozu.flywheel.lib.math.MoreMath;
|
||||
|
||||
public class IndirectInstancer<I extends Instance> extends AbstractInstancer<I> {
|
||||
private final long instanceStride;
|
||||
|
@ -27,8 +28,8 @@ public class IndirectInstancer<I extends Instance> extends AbstractInstancer<I>
|
|||
|
||||
public IndirectInstancer(InstanceType<I> type, Environment environment, Model model) {
|
||||
super(type, environment);
|
||||
instanceStride = type.layout()
|
||||
.byteSize();
|
||||
instanceStride = MoreMath.align4(type.layout()
|
||||
.byteSize());
|
||||
writer = this.type.writer();
|
||||
boundingSphere = model.boundingSphere();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
package com.jozufozu.flywheel.impl.layout;
|
||||
|
||||
import org.jetbrains.annotations.Range;
|
||||
|
||||
import com.jozufozu.flywheel.api.layout.ArrayElementType;
|
||||
import com.jozufozu.flywheel.api.layout.ElementType;
|
||||
|
||||
record ArrayElementTypeImpl(ElementType innerType, @Range(from = 1, to = 256) int length, int byteSize, int byteAlignment) implements ArrayElementType {
|
||||
static ArrayElementTypeImpl create(ElementType innerType, @Range(from = 1, to = 256) int length) {
|
||||
if (length < 1 || length > 256) {
|
||||
throw new IllegalArgumentException("Array element length must be in range [1, 256]!");
|
||||
}
|
||||
|
||||
return new ArrayElementTypeImpl(innerType, length, innerType.byteSize() * length, innerType.byteAlignment());
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@ import java.util.List;
|
|||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.Range;
|
||||
|
||||
import com.jozufozu.flywheel.api.layout.ElementType;
|
||||
|
@ -14,6 +15,7 @@ import com.jozufozu.flywheel.api.layout.Layout.Element;
|
|||
import com.jozufozu.flywheel.api.layout.LayoutBuilder;
|
||||
import com.jozufozu.flywheel.api.layout.ValueRepr;
|
||||
import com.jozufozu.flywheel.impl.layout.LayoutImpl.ElementImpl;
|
||||
import com.jozufozu.flywheel.lib.math.MoreMath;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||
|
@ -102,7 +104,9 @@ public class LayoutBuilderImpl implements LayoutBuilder {
|
|||
);
|
||||
|
||||
private final List<Element> elements = new ArrayList<>();
|
||||
private int offset = 0;
|
||||
@Nullable
|
||||
private UnpaddedElement lastElement;
|
||||
private int maxByteAlignment;
|
||||
|
||||
@Override
|
||||
public LayoutBuilder scalar(String name, ValueRepr repr) {
|
||||
|
@ -124,15 +128,57 @@ public class LayoutBuilderImpl implements LayoutBuilder {
|
|||
return matrix(name, repr, size, size);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LayoutBuilder scalarArray(String name, ValueRepr repr, @Range(from = 1, to = 256) int length) {
|
||||
return element(name, ArrayElementTypeImpl.create(ScalarElementTypeImpl.create(repr), length));
|
||||
}
|
||||
|
||||
@Override
|
||||
public LayoutBuilder vectorArray(String name, ValueRepr repr, @Range(from = 2, to = 4) int size, @Range(from = 1, to = 256) int length) {
|
||||
return element(name, ArrayElementTypeImpl.create(VectorElementTypeImpl.create(repr, size), length));
|
||||
}
|
||||
|
||||
@Override
|
||||
public LayoutBuilder matrixArray(String name, FloatRepr repr, @Range(from = 2, to = 4) int rows, @Range(from = 2, to = 4) int columns, @Range(from = 1, to = 256) int length) {
|
||||
return element(name, ArrayElementTypeImpl.create(MatrixElementTypeImpl.create(repr, rows, columns), length));
|
||||
}
|
||||
|
||||
@Override
|
||||
public LayoutBuilder matrixArray(String name, FloatRepr repr, @Range(from = 2, to = 4) int size, @Range(from = 1, to = 256) int length) {
|
||||
return matrixArray(name, repr, size, size, length);
|
||||
}
|
||||
|
||||
private LayoutBuilder element(String name, ElementType type) {
|
||||
elements.add(new ElementImpl(name, type, offset));
|
||||
// type.byteSize() is guaranteed to be 4-aligned.
|
||||
offset += type.byteSize();
|
||||
UnpaddedElement newElement;
|
||||
|
||||
if (lastElement != null) {
|
||||
int byteOffset = padLastElement(type.byteAlignment());
|
||||
newElement = new UnpaddedElement(name, type, byteOffset);
|
||||
} else {
|
||||
newElement = new UnpaddedElement(name, type, 0);
|
||||
}
|
||||
|
||||
lastElement = newElement;
|
||||
maxByteAlignment = Math.max(lastElement.type.byteAlignment(), maxByteAlignment);
|
||||
return this;
|
||||
}
|
||||
|
||||
private int padLastElement(int byteAlignment) {
|
||||
int nextByte = lastElement.byteOffset + lastElement.type.byteSize();
|
||||
int byteOffset = MoreMath.alignPot(nextByte, byteAlignment);
|
||||
elements.add(lastElement.complete(byteOffset - nextByte));
|
||||
return byteOffset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Layout build() {
|
||||
int byteSize;
|
||||
if (lastElement != null) {
|
||||
byteSize = padLastElement(maxByteAlignment);
|
||||
} else {
|
||||
byteSize = 0;
|
||||
}
|
||||
|
||||
Object2IntMap<String> name2IndexMap = new Object2IntOpenHashMap<>();
|
||||
name2IndexMap.defaultReturnValue(-1);
|
||||
|
||||
|
@ -172,7 +218,17 @@ public class LayoutBuilderImpl implements LayoutBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
return new LayoutImpl(List.copyOf(elements), offset);
|
||||
List<Element> elements = List.copyOf(this.elements);
|
||||
int maxByteAlignment = this.maxByteAlignment;
|
||||
clear();
|
||||
|
||||
return new LayoutImpl(elements, byteSize, maxByteAlignment);
|
||||
}
|
||||
|
||||
private void clear() {
|
||||
elements.clear();
|
||||
lastElement = null;
|
||||
maxByteAlignment = 0;
|
||||
}
|
||||
|
||||
private static boolean isValidNameCharacter(char c) {
|
||||
|
@ -182,4 +238,10 @@ public class LayoutBuilderImpl implements LayoutBuilder {
|
|||
private static boolean isLetter(char c) {
|
||||
return c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z';
|
||||
}
|
||||
|
||||
private static record UnpaddedElement(String name, ElementType type, int byteOffset) {
|
||||
private ElementImpl complete(int paddingByteSize) {
|
||||
return new ElementImpl(name, type, byteOffset, type.byteSize() + paddingByteSize, paddingByteSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,8 +17,9 @@ final class LayoutImpl implements Layout {
|
|||
@Unmodifiable
|
||||
private final Map<String, Element> map;
|
||||
private final int byteSize;
|
||||
private final int byteAlignment;
|
||||
|
||||
LayoutImpl(@Unmodifiable List<Element> elements, int byteSize) {
|
||||
LayoutImpl(@Unmodifiable List<Element> elements, int byteSize, int byteAlignment) {
|
||||
this.elements = elements;
|
||||
|
||||
Object2ObjectOpenHashMap<String, Element> map = new Object2ObjectOpenHashMap<>();
|
||||
|
@ -29,6 +30,7 @@ final class LayoutImpl implements Layout {
|
|||
|
||||
this.map = Collections.unmodifiableMap(map);
|
||||
this.byteSize = byteSize;
|
||||
this.byteAlignment = byteAlignment;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -48,6 +50,11 @@ final class LayoutImpl implements Layout {
|
|||
return byteSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int byteAlignment() {
|
||||
return byteAlignment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
|
@ -71,6 +78,6 @@ final class LayoutImpl implements Layout {
|
|||
return elements.equals(other.elements);
|
||||
}
|
||||
|
||||
record ElementImpl(String name, ElementType type, int offset) implements Element {
|
||||
record ElementImpl(String name, ElementType type, int byteOffset, int paddedByteSize, int paddingByteSize) implements Element {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
package com.jozufozu.flywheel.impl.layout;
|
||||
|
||||
import com.jozufozu.flywheel.api.layout.ArrayElementType;
|
||||
import com.jozufozu.flywheel.api.layout.ElementType;
|
||||
import com.jozufozu.flywheel.api.layout.FloatRepr;
|
||||
import com.jozufozu.flywheel.api.layout.IntegerRepr;
|
||||
import com.jozufozu.flywheel.api.layout.MatrixElementType;
|
||||
import com.jozufozu.flywheel.api.layout.ScalarElementType;
|
||||
import com.jozufozu.flywheel.api.layout.UnsignedIntegerRepr;
|
||||
import com.jozufozu.flywheel.api.layout.ValueRepr;
|
||||
import com.jozufozu.flywheel.api.layout.VectorElementType;
|
||||
|
||||
public class LayoutInterpreter {
|
||||
public static String typeName(ElementType type) {
|
||||
if (type instanceof ScalarElementType scalar) {
|
||||
return scalarTypeName(scalar);
|
||||
} else if (type instanceof VectorElementType vector) {
|
||||
return vectorTypeName(vector);
|
||||
} else if (type instanceof MatrixElementType matrix) {
|
||||
return matrixTypeName(matrix);
|
||||
} else if (type instanceof ArrayElementType array) {
|
||||
return arrayTypeName(array);
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Unknown type " + type);
|
||||
}
|
||||
|
||||
public static String scalarTypeName(ScalarElementType scalar) {
|
||||
ValueRepr repr = scalar.repr();
|
||||
|
||||
if (repr instanceof IntegerRepr) {
|
||||
return "int";
|
||||
} else if (repr instanceof UnsignedIntegerRepr) {
|
||||
return "uint";
|
||||
} else if (repr instanceof FloatRepr) {
|
||||
return "float";
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Unknown repr " + repr);
|
||||
}
|
||||
|
||||
public static String vectorTypeName(VectorElementType vector) {
|
||||
ValueRepr repr = vector.repr();
|
||||
int size = vector.size();
|
||||
|
||||
if (repr instanceof IntegerRepr) {
|
||||
return "ivec" + size;
|
||||
} else if (repr instanceof UnsignedIntegerRepr) {
|
||||
return "uvec" + size;
|
||||
} else if (repr instanceof FloatRepr) {
|
||||
return "vec" + size;
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Unknown repr " + repr);
|
||||
}
|
||||
|
||||
public static String matrixTypeName(MatrixElementType matrix) {
|
||||
return "mat" + matrix.columns() + "x" + matrix.rows();
|
||||
}
|
||||
|
||||
// This will not produce correct types for multidimensional arrays (the lengths will be in the opposite order),
|
||||
// but the API does not allow creating multidimensional arrays anyway because they are not core until GLSL 430.
|
||||
public static String arrayTypeName(ArrayElementType array) {
|
||||
return typeName(array.innerType()) + "[" + array.length() + "]";
|
||||
}
|
||||
}
|
|
@ -4,10 +4,9 @@ import org.jetbrains.annotations.Range;
|
|||
|
||||
import com.jozufozu.flywheel.api.layout.FloatRepr;
|
||||
import com.jozufozu.flywheel.api.layout.MatrixElementType;
|
||||
import com.jozufozu.flywheel.lib.math.MoreMath;
|
||||
|
||||
record MatrixElementTypeImpl(FloatRepr repr, @Range(from = 2, to = 4) int rows, @Range(from = 2, to = 4) int columns,
|
||||
int byteSize) implements MatrixElementType {
|
||||
int byteSize, int byteAlignment) implements MatrixElementType {
|
||||
static MatrixElementTypeImpl create(FloatRepr repr, @Range(from = 2, to = 4) int rows, @Range(from = 2, to = 4) int columns) {
|
||||
if (rows < 2 || rows > 4) {
|
||||
throw new IllegalArgumentException("Matrix element row count must be in range [2, 4]!");
|
||||
|
@ -15,7 +14,7 @@ record MatrixElementTypeImpl(FloatRepr repr, @Range(from = 2, to = 4) int rows,
|
|||
if (columns < 2 || columns > 4) {
|
||||
throw new IllegalArgumentException("Matrix element column count must be in range [2, 4]!");
|
||||
}
|
||||
int byteSize = MoreMath.align4(repr.byteSize() * rows * columns);
|
||||
return new MatrixElementTypeImpl(repr, rows, columns, byteSize);
|
||||
|
||||
return new MatrixElementTypeImpl(repr, rows, columns, repr.byteSize() * rows * columns, repr.byteSize());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,10 +2,9 @@ package com.jozufozu.flywheel.impl.layout;
|
|||
|
||||
import com.jozufozu.flywheel.api.layout.ScalarElementType;
|
||||
import com.jozufozu.flywheel.api.layout.ValueRepr;
|
||||
import com.jozufozu.flywheel.lib.math.MoreMath;
|
||||
|
||||
record ScalarElementTypeImpl(ValueRepr repr, int byteSize) implements ScalarElementType {
|
||||
record ScalarElementTypeImpl(ValueRepr repr, int byteSize, int byteAlignment) implements ScalarElementType {
|
||||
static ScalarElementTypeImpl create(ValueRepr repr) {
|
||||
return new ScalarElementTypeImpl(repr, MoreMath.align4(repr.byteSize()));
|
||||
return new ScalarElementTypeImpl(repr, repr.byteSize(), repr.byteSize());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,17 +4,14 @@ import org.jetbrains.annotations.Range;
|
|||
|
||||
import com.jozufozu.flywheel.api.layout.ValueRepr;
|
||||
import com.jozufozu.flywheel.api.layout.VectorElementType;
|
||||
import com.jozufozu.flywheel.lib.math.MoreMath;
|
||||
|
||||
record VectorElementTypeImpl(ValueRepr repr, @Range(from = 2, to = 4) int size,
|
||||
int byteSize) implements VectorElementType {
|
||||
|
||||
int byteSize, int byteAlignment) implements VectorElementType {
|
||||
static VectorElementTypeImpl create(ValueRepr repr, @Range(from = 2, to = 4) int size) {
|
||||
if (size < 2 || size > 4) {
|
||||
throw new IllegalArgumentException("Vector element size must be in range [2, 4]!");
|
||||
}
|
||||
|
||||
int byteSize = MoreMath.align4(repr.byteSize() * size);
|
||||
return new VectorElementTypeImpl(repr, size, byteSize);
|
||||
return new VectorElementTypeImpl(repr, size, repr.byteSize() * size, repr.byteSize());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,10 @@ public final class MoreMath {
|
|||
return (size + 3) & ~3;
|
||||
}
|
||||
|
||||
public static int alignPot(int size, int to) {
|
||||
return (size + (to - 1)) & ~(to - 1);
|
||||
}
|
||||
|
||||
public static int ceilingDiv(int numerator, int denominator) {
|
||||
return (numerator + denominator - 1) / denominator;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package com.jozufozu.flywheel.lib.visual.components;
|
||||
package com.jozufozu.flywheel.lib.visual.component;
|
||||
|
||||
import org.joml.Vector4f;
|
||||
import org.joml.Vector4fc;
|
|
@ -1,4 +1,4 @@
|
|||
package com.jozufozu.flywheel.lib.visual.components;
|
||||
package com.jozufozu.flywheel.lib.visual.component;
|
||||
|
||||
import org.joml.Quaternionf;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.jozufozu.flywheel.lib.visual.components;
|
||||
package com.jozufozu.flywheel.lib.visual.component;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.joml.Vector4f;
|
|
@ -14,9 +14,9 @@ import com.jozufozu.flywheel.lib.model.part.ModelPartConverter;
|
|||
import com.jozufozu.flywheel.lib.visual.SimpleDynamicVisual;
|
||||
import com.jozufozu.flywheel.lib.visual.SimpleEntityVisual;
|
||||
import com.jozufozu.flywheel.lib.visual.SimpleTickableVisual;
|
||||
import com.jozufozu.flywheel.lib.visual.components.FireComponent;
|
||||
import com.jozufozu.flywheel.lib.visual.components.HitboxComponent;
|
||||
import com.jozufozu.flywheel.lib.visual.components.ShadowComponent;
|
||||
import com.jozufozu.flywheel.lib.visual.component.FireComponent;
|
||||
import com.jozufozu.flywheel.lib.visual.component.HitboxComponent;
|
||||
import com.jozufozu.flywheel.lib.visual.component.ShadowComponent;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.math.Axis;
|
||||
|
||||
|
|
Loading…
Reference in a new issue