Random utilities from a messy workspace

- More documentation/clean up some old docs
 - isFirstLoad check on GatherContextEvent
 - Instancers no longer crash on empty model
 - setIdentity on MatrixTransformStack
 - more utilities for TransformStack
 - ModelData "nullification"
This commit is contained in:
Jozufozu 2021-08-13 11:23:09 -07:00
parent 4817ce3fe1
commit 11f55d1d56
11 changed files with 296 additions and 33 deletions

View file

@ -44,6 +44,8 @@ public class Loader implements ISelectiveResourceReloadListener {
private final Backend backend; private final Backend backend;
private boolean shouldCrash; private boolean shouldCrash;
private boolean firstLoad = true;
public Loader(Backend backend) { public Loader(Backend backend) {
this.backend = backend; this.backend = backend;
@ -72,7 +74,7 @@ public class Loader implements ISelectiveResourceReloadListener {
Resolver.INSTANCE.invalidate(); Resolver.INSTANCE.invalidate();
ModLoader.get() ModLoader.get()
.postEvent(new GatherContextEvent(backend)); .postEvent(new GatherContextEvent(backend, firstLoad));
ShaderSources sources = new ShaderSources(manager); ShaderSources sources = new ShaderSources(manager);
@ -97,6 +99,8 @@ public class Loader implements ISelectiveResourceReloadListener {
CrumblingRenderer.reset(); CrumblingRenderer.reset();
} }
} }
firstLoad = false;
} }
} }

View file

@ -4,6 +4,7 @@ import java.io.BufferedReader;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.FileReader; import java.io.FileReader;
import java.io.IOException;
import java.util.Optional; import java.util.Optional;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
@ -55,13 +56,18 @@ public class OptifineHandler {
public static void refresh() { public static void refresh() {
if (optifine == null) return; if (optifine == null) return;
boolean shadersOff = areShadersDisabledInOptifineConfigFile();
handler = new OptifineHandler(!shadersOff);
}
private static boolean areShadersDisabledInOptifineConfigFile() {
File dir = Minecraft.getInstance().gameDirectory; File dir = Minecraft.getInstance().gameDirectory;
File shaderOptions = new File(dir, "optionsshaders.txt"); File shaderOptions = new File(dir, "optionsshaders.txt");
boolean shadersOff = true; boolean shadersOff = true;
try { try (BufferedReader reader = new BufferedReader(new FileReader(shaderOptions))) {
BufferedReader reader = new BufferedReader(new FileReader(shaderOptions));
shadersOff = reader.lines() shadersOff = reader.lines()
.anyMatch(it -> { .anyMatch(it -> {
@ -73,11 +79,10 @@ public class OptifineHandler {
} }
return false; return false;
}); });
} catch (FileNotFoundException e) { } catch (IOException e) {
Backend.log.info("No shader config found."); Backend.log.info("No shader config found.");
} }
return shadersOff;
handler = new OptifineHandler(!shadersOff);
} }
public boolean isUsingShaders() { public boolean isUsingShaders() {

View file

@ -18,8 +18,9 @@ import net.minecraftforge.event.TickEvent;
/** /**
* A manager class for a single world where instancing is supported. * A manager class for a single world where instancing is supported.
* <br> * <p>
* The material manager is shared between the different instance managers. * The material manager is shared between the different instance managers.
* </p>
*/ */
public class InstanceWorld { public class InstanceWorld {
protected final MaterialManager<WorldProgram> materialManager; protected final MaterialManager<WorldProgram> materialManager;
@ -63,11 +64,12 @@ public class InstanceWorld {
} }
/** /**
* Get ready to render a frame: * Get ready to render a frame.
* <br> * <p>
* Check and shift the origin coordinate. * Check and shift the origin coordinate.
* <br> * <br>
* Call {@link IDynamicInstance#beginFrame()} on all instances in this world. * Call {@link IDynamicInstance#beginFrame()} on all instances in this world.
* </p>
*/ */
public void beginFrame(BeginFrameEvent event) { public void beginFrame(BeginFrameEvent event) {
materialManager.checkAndShiftOrigin(event.getInfo()); materialManager.checkAndShiftOrigin(event.getInfo());
@ -78,8 +80,9 @@ public class InstanceWorld {
/** /**
* Tick the renderers after the game has ticked: * Tick the renderers after the game has ticked:
* <br> * <p>
* Call {@link ITickableInstance#tick()} on all instances in this world. * Call {@link ITickableInstance#tick()} on all instances in this world.
* </p>
*/ */
public void tick() { public void tick() {
Minecraft mc = Minecraft.getInstance(); Minecraft mc = Minecraft.getInstance();

View file

@ -28,13 +28,17 @@ public class InstancedRenderDispatcher {
private static final WorldAttached<InstanceWorld> instanceWorlds = new WorldAttached<>($ -> new InstanceWorld()); private static final WorldAttached<InstanceWorld> instanceWorlds = new WorldAttached<>($ -> new InstanceWorld());
/** /**
* Call this when you want to invoke * Call this when you want to manually run {@link IInstance#update()}.
* @param te * @param te The tile whose instance you want to update.
*/ */
public static void enqueueUpdate(TileEntity te) { public static void enqueueUpdate(TileEntity te) {
getTiles(te.getLevel()).queueUpdate(te); getTiles(te.getLevel()).queueUpdate(te);
} }
/**
* Call this when you want to manually run {@link IInstance#update()}.
* @param entity The entity whose instance you want to update.
*/
public static void enqueueUpdate(Entity entity) { public static void enqueueUpdate(Entity entity) {
getEntities(entity.level).queueUpdate(entity); getEntities(entity.level).queueUpdate(entity);
} }
@ -66,7 +70,10 @@ public class InstancedRenderDispatcher {
@SubscribeEvent @SubscribeEvent
public static void onBeginFrame(BeginFrameEvent event) { public static void onBeginFrame(BeginFrameEvent event) {
instanceWorlds.get(event.getWorld()).beginFrame(event); if (Backend.isGameActive()) {
instanceWorlds.get(event.getWorld())
.beginFrame(event);
}
} }
@SubscribeEvent @SubscribeEvent

View file

@ -37,10 +37,10 @@ import com.jozufozu.flywheel.util.AttribUtil;
public class Instancer<D extends InstanceData> { public class Instancer<D extends InstanceData> {
protected final Supplier<IModel> gen; protected final Supplier<IModel> gen;
protected IBufferedModel model;
protected final VertexFormat instanceFormat; protected final VertexFormat instanceFormat;
protected final IInstanceFactory<D> factory; protected final IInstanceFactory<D> factory;
protected IBufferedModel model;
protected GlVertexArray vao; protected GlVertexArray vao;
protected GlBuffer instanceVBO; protected GlBuffer instanceVBO;
protected int glBufferSize = -1; protected int glBufferSize = -1;
@ -81,7 +81,7 @@ public class Instancer<D extends InstanceData> {
public void render() { public void render() {
if (!isInitialized()) init(); if (!isInitialized()) init();
if (deleted) return; if (invalid()) return;
vao.bind(); vao.bind();
renderSetup(); renderSetup();
@ -91,13 +91,19 @@ public class Instancer<D extends InstanceData> {
vao.unbind(); vao.unbind();
} }
private boolean invalid() {
return deleted || model == null;
}
private void init() { private void init() {
model = new IndexedModel(gen.get());
initialized = true; initialized = true;
IModel iModel = gen.get();
if (model.getVertexCount() <= 0) if (iModel.empty()) {
throw new IllegalArgumentException("Refusing to instance a model with no vertices."); return;
}
model = new IndexedModel(iModel);
vao = new GlVertexArray(); vao = new GlVertexArray();
instanceVBO = new GlBuffer(GlBufferType.ARRAY_BUFFER); instanceVBO = new GlBuffer(GlBufferType.ARRAY_BUFFER);
@ -133,16 +139,14 @@ public class Instancer<D extends InstanceData> {
* Free acquired resources. All other Instancer methods are undefined behavior after calling delete. * Free acquired resources. All other Instancer methods are undefined behavior after calling delete.
*/ */
public void delete() { public void delete() {
if (deleted) return; if (invalid()) return;
deleted = true; deleted = true;
if (isInitialized()) { model.delete();
model.delete();
instanceVBO.delete(); instanceVBO.delete();
vao.delete(); vao.delete();
}
} }
private D _add(D instanceData) { private D _add(D instanceData) {

View file

@ -17,6 +17,7 @@ import com.jozufozu.flywheel.core.materials.OrientedData;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.tileentity.TileEntity; import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.vector.Vector3d; import net.minecraft.util.math.vector.Vector3d;
import net.minecraft.util.math.vector.Vector3f; import net.minecraft.util.math.vector.Vector3f;
import net.minecraft.util.math.vector.Vector3i; import net.minecraft.util.math.vector.Vector3i;
@ -89,8 +90,7 @@ public abstract class EntityInstance<E extends Entity> implements IInstance {
* {@link TileInstanceManager}s are allowed to arbitrarily adjust the origin, and * {@link TileInstanceManager}s are allowed to arbitrarily adjust the origin, and
* shift the world matrix provided as a shader uniform accordingly. * shift the world matrix provided as a shader uniform accordingly.
* *
* @return The {@link BlockPos position} of the {@link Entity} this instance * @return The position this instance should be rendered at to appear in the correct location.
* represents should be rendered at to appear in the correct location.
*/ */
public Vector3f getInstancePosition() { public Vector3f getInstancePosition() {
Vector3d pos = entity.position(); Vector3d pos = entity.position();
@ -98,6 +98,23 @@ public abstract class EntityInstance<E extends Entity> implements IInstance {
return new Vector3f((float) (pos.x - origin.getX()), (float) (pos.y - origin.getY()), (float) (pos.z - origin.getZ())); return new Vector3f((float) (pos.x - origin.getX()), (float) (pos.y - origin.getY()), (float) (pos.z - origin.getZ()));
} }
/**
* In order to accommodate for floating point precision errors at high coordinates,
* {@link TileInstanceManager}s are allowed to arbitrarily adjust the origin, and
* shift the world matrix provided as a shader uniform accordingly.
*
* @return The position this instance should be rendered at to appear in the correct location.
*/
public Vector3f getInstancePosition(float partialTicks) {
Vector3d pos = entity.position();
Vector3i origin = materialManager.getOriginCoordinate();
return new Vector3f(
(float) (MathHelper.lerp(partialTicks, entity.xOld, pos.x) - origin.getX()),
(float) (MathHelper.lerp(partialTicks, entity.yOld, pos.y) - origin.getY()),
(float) (MathHelper.lerp(partialTicks, entity.zOld, pos.z) - origin.getZ())
);
}
@Override @Override
public BlockPos getWorldPosition() { public BlockPos getWorldPosition() {
return entity.blockPosition(); return entity.blockPosition();

View file

@ -20,6 +20,19 @@ public class ModelData extends BasicData {
return this; return this;
} }
/**
* Sets the transform matrices to be all zeros.
*
* <p>
* This will allow the gpu to quickly discard all geometry for this instance, effectively "turning it off".
* </p>
*/
public ModelData setEmptyTransform() {
matrices = empty;
markDirty();
return this;
}
@Override @Override
public void write(MappedBuffer buf) { public void write(MappedBuffer buf) {
super.write(buf); super.write(buf);

View file

@ -0,0 +1,139 @@
package com.jozufozu.flywheel.core.model;
import static com.jozufozu.flywheel.util.RenderMath.nb;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import org.lwjgl.system.MemoryStack;
import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat;
import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer;
import com.jozufozu.flywheel.core.Formats;
import com.jozufozu.flywheel.util.VirtualEmptyModelData;
import com.mojang.blaze3d.matrix.MatrixStack;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.ItemRenderer;
import net.minecraft.client.renderer.color.ItemColors;
import net.minecraft.client.renderer.model.BakedQuad;
import net.minecraft.client.renderer.model.IBakedModel;
import net.minecraft.client.renderer.model.ItemCameraTransforms;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Direction;
import net.minecraft.util.math.vector.Vector3f;
import net.minecraft.util.math.vector.Vector3i;
import net.minecraftforge.client.ForgeHooksClient;
public class BakedModelModel implements IModel {
// DOWN, UP, NORTH, SOUTH, WEST, EAST, null
private static final Direction[] dirs;
static {
Direction[] directions = Direction.values();
dirs = Arrays.copyOf(directions, directions.length + 1);
}
public final IBakedModel model;
private final int numQuads;
public BakedModelModel(IBakedModel model) {
this.model = model;
Random random = new Random();
int numQuads = 0;
for (Direction dir : dirs) {
random.setSeed(42);
List<BakedQuad> quads = model.getQuads(null, dir, random, VirtualEmptyModelData.INSTANCE);
numQuads += quads.size();
}
this.numQuads = numQuads;
}
@Override
public void buffer(VecBuffer buffer) {
Minecraft mc = Minecraft.getInstance();
ItemColors itemColors = mc.getItemColors();
Random random = new Random();
for (Direction dir : dirs) {
random.setSeed(42);
List<BakedQuad> quads = model.getQuads(null, dir, random, VirtualEmptyModelData.INSTANCE);
for (BakedQuad bakedQuad : quads) {
// int i = -1;
// if (!itemStack.isEmpty() && bakedQuad.isTinted()) {
// i = itemColors.getColor(itemStack, bakedQuad.getTintIndex());
// }
//
// byte red = (byte)(i >> 16 & 255);
// byte green = (byte)(i >> 8 & 255);
// byte blue = (byte)(i & 255);
int[] aint = bakedQuad.getVertices();
Vector3i faceNormal = bakedQuad.getDirection().getNormal();
Vector3f normal = new Vector3f((float)faceNormal.getX(), (float)faceNormal.getY(), (float)faceNormal.getZ());
int intSize = DefaultVertexFormats.BLOCK.getIntegerSize();
int vertexCount = aint.length / intSize;
try (MemoryStack memorystack = MemoryStack.stackPush()) {
ByteBuffer bytebuffer = memorystack.malloc(DefaultVertexFormats.BLOCK.getVertexSize());
IntBuffer intbuffer = bytebuffer.asIntBuffer();
for(int j = 0; j < vertexCount; ++j) {
((Buffer)intbuffer).clear();
intbuffer.put(aint, j * 8, 8);
float f = bytebuffer.getFloat(0);
float f1 = bytebuffer.getFloat(4);
float f2 = bytebuffer.getFloat(8);
// float cr;
// float cg;
// float cb;
// float ca;
// {
// float r = (float)(bytebuffer.get(12) & 255) / 255.0F;
// float g = (float)(bytebuffer.get(13) & 255) / 255.0F;
// float b = (float)(bytebuffer.get(14) & 255) / 255.0F;
// float a = (float)(bytebuffer.get(15) & 255) / 255.0F;
// cr = r * red;
// cg = g * green;
// cb = b * blue;
// ca = a;
// }
float u = bytebuffer.getFloat(16);
float v = bytebuffer.getFloat(20);
buffer.putVec3(f, f1, f2);
buffer.putVec3(nb(normal.x()), nb(normal.y()), nb(normal.z()));
buffer.putVec2(u, v);
}
}
}
}
}
@Override
public int vertexCount() {
return numQuads * 4;
}
@Override
public VertexFormat format() {
return Formats.UNLIT_MODEL;
}
}

View file

@ -8,12 +8,21 @@ import net.minecraftforge.fml.event.lifecycle.IModBusEvent;
public class GatherContextEvent extends Event implements IModBusEvent { public class GatherContextEvent extends Event implements IModBusEvent {
private final Backend backend; private final Backend backend;
private final boolean firstLoad;
public GatherContextEvent(Backend backend) { public GatherContextEvent(Backend backend, boolean firstLoad) {
this.backend = backend; this.backend = backend;
this.firstLoad = firstLoad;
} }
public Backend getBackend() { public Backend getBackend() {
return backend; return backend;
} }
/**
* @return true iff it is the first time the event is fired.
*/
public boolean isFirstLoad() {
return firstLoad;
}
} }

View file

@ -24,6 +24,22 @@ public class MatrixTransformStack implements TransformStack {
return internal; return internal;
} }
public MatrixTransformStack setIdentity() {
if (internal.clear()) {
MatrixStack.Entry last = internal.last();
last.normal()
.setIdentity();
last.pose()
.setIdentity();
} else {
internal.popPose();
internal.pushPose();
}
return this;
}
@Override @Override
public TransformStack translate(double x, double y, double z) { public TransformStack translate(double x, double y, double z) {
internal.translate(x, y, z); internal.translate(x, y, z);

View file

@ -44,6 +44,18 @@ public interface TransformStack {
return multiply(Vector3f.ZP, angle); return multiply(Vector3f.ZP, angle);
} }
default TransformStack rotateXRadians(double angle) {
return multiplyRadians(Vector3f.XP, angle);
}
default TransformStack rotateYRadians(double angle) {
return multiplyRadians(Vector3f.YP, angle);
}
default TransformStack rotateZRadians(double angle) {
return multiplyRadians(Vector3f.ZP, angle);
}
default TransformStack centre() { default TransformStack centre() {
return translateAll(0.5); return translateAll(0.5);
} }
@ -76,6 +88,10 @@ public interface TransformStack {
return translate(vec.x, vec.y, vec.z); return translate(vec.x, vec.y, vec.z);
} }
default TransformStack translate(Vector3f vec) {
return translate(vec.x(), vec.y(), vec.z());
}
default TransformStack translateBack(Vector3d vec) { default TransformStack translateBack(Vector3d vec) {
return translate(-vec.x, -vec.y, -vec.z); return translate(-vec.x, -vec.y, -vec.z);
} }
@ -94,4 +110,34 @@ public interface TransformStack {
return this; return this;
return multiply(axis.rotationDegrees((float) angle)); return multiply(axis.rotationDegrees((float) angle));
} }
default TransformStack multiplyRadians(Vector3f axis, double angle) {
if (angle == 0)
return this;
return multiply(axis.rotation((float) angle));
}
default TransformStack rotateToFace(Direction facing) {
switch (facing) {
case SOUTH:
multiply(Vector3f.YP.rotationDegrees(180));
break;
case WEST:
multiply(Vector3f.YP.rotationDegrees(90));
break;
case NORTH:
multiply(Vector3f.YP.rotationDegrees(0));
break;
case EAST:
multiply(Vector3f.YP.rotationDegrees(270));
break;
case UP:
multiply(Vector3f.XP.rotationDegrees(90));
break;
case DOWN:
multiply(Vector3f.XN.rotationDegrees(90));
break;
}
return this;
}
} }