- Refactor InstancedRenderRegistry to allow for 3rd party TEs to have instances
 - Add ChestInstance, incomplete but there
 - Add basic api for building vanilla formatted models
 - Better instance factory registration
This commit is contained in:
Jozufozu 2021-07-09 13:24:26 -07:00
parent c79c41c16f
commit 60d7ca6e91
16 changed files with 788 additions and 18 deletions

View file

@ -5,6 +5,8 @@ import com.jozufozu.flywheel.core.AtlasStitcher;
import com.jozufozu.flywheel.core.Contexts;
import com.jozufozu.flywheel.core.Materials;
import com.jozufozu.flywheel.vanilla.VanillaInstances;
import net.minecraftforge.eventbus.api.IEventBus;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
@ -20,5 +22,7 @@ public class FlywheelClient {
modEventBus.addListener(Contexts::flwInit);
modEventBus.addListener(Materials::flwInit);
VanillaInstances.init();
}
}

View file

@ -40,6 +40,12 @@ public abstract class InstanceManager<T> implements MaterialManager.OriginShiftL
materialManager.addListener(this);
}
protected abstract boolean canInstance(T obj);
protected abstract IInstance createRaw(T obj);
protected abstract boolean canCreateInstance(T entity);
public void tick(double cameraX, double cameraY, double cameraZ) {
tick++;
@ -100,7 +106,7 @@ public abstract class InstanceManager<T> implements MaterialManager.OriginShiftL
if (!Backend.getInstance()
.canUseInstancing()) return;
if (obj instanceof IInstanceRendered) {
if (canInstance(obj)) {
addInternal(obj);
}
}
@ -116,7 +122,7 @@ public abstract class InstanceManager<T> implements MaterialManager.OriginShiftL
if (!Backend.getInstance()
.canUseInstancing()) return;
if (obj instanceof IInstanceRendered) {
if (canInstance(obj)) {
IInstance instance = getInstance(obj, false);
if (instance != null) {
@ -143,7 +149,7 @@ public abstract class InstanceManager<T> implements MaterialManager.OriginShiftL
if (!Backend.getInstance()
.canUseInstancing()) return;
if (obj instanceof IInstanceRendered) {
if (canInstance(obj)) {
IInstance instance = getInstance(obj, false);
if (instance != null) instance.updateLight();
@ -154,7 +160,7 @@ public abstract class InstanceManager<T> implements MaterialManager.OriginShiftL
if (!Backend.getInstance()
.canUseInstancing()) return;
if (obj instanceof IInstanceRendered) {
if (canInstance(obj)) {
IInstance instance = getInstance(obj, false);
if (instance != null) removeInternal(obj, instance);
}
@ -241,8 +247,4 @@ public abstract class InstanceManager<T> implements MaterialManager.OriginShiftL
invalidate();
instancedTiles.forEach(this::add);
}
protected abstract IInstance createRaw(T obj);
protected abstract boolean canCreateInstance(T entity);
}

View file

@ -10,6 +10,8 @@ import com.jozufozu.flywheel.backend.instancing.entity.IEntityInstanceFactory;
import com.jozufozu.flywheel.backend.instancing.tile.ITileInstanceFactory;
import com.jozufozu.flywheel.backend.instancing.tile.TileEntityInstance;
import it.unimi.dsi.fastutil.objects.Object2BooleanLinkedOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2BooleanMap;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.tileentity.TileEntity;
@ -22,15 +24,36 @@ public class InstancedRenderRegistry {
return INSTANCE;
}
private final Object2BooleanMap<Object> skipRender = new Object2BooleanLinkedOpenHashMap<>();
private final Map<TileEntityType<?>, ITileInstanceFactory<?>> tiles = Maps.newHashMap();
private final Map<EntityType<?>, IEntityInstanceFactory<?>> entities = Maps.newHashMap();
public <T extends TileEntity> void register(TileEntityType<? extends T> type, ITileInstanceFactory<? super T> rendererFactory) {
this.tiles.put(type, rendererFactory);
protected InstancedRenderRegistry() {
skipRender.defaultReturnValue(false);
}
public <T extends Entity> void register(EntityType<? extends T> type, IEntityInstanceFactory<? super T> rendererFactory) {
this.entities.put(type, rendererFactory);
public <T extends TileEntity> boolean shouldSkipRender(T type) {
return _skipRender(type.getType()) || ((type instanceof IInstanceRendered) && !((IInstanceRendered) type).shouldRenderNormally());
}
public <T extends Entity> boolean shouldSkipRender(T type) {
return _skipRender(type.getType()) || ((type instanceof IInstanceRendered) && !((IInstanceRendered) type).shouldRenderNormally());
}
public <T extends TileEntity> boolean canInstance(TileEntityType<? extends T> type) {
return tiles.containsKey(type);
}
public <T extends Entity> boolean canInstance(EntityType<? extends T> type) {
return entities.containsKey(type);
}
public <T extends TileEntity> TileRegistrater<? extends T> tile(TileEntityType<? extends T> type) {
return new TileRegistrater<>(type);
}
public <T extends Entity> EntityRegistrater<? extends T> entity(EntityType<? extends T> type) {
return new EntityRegistrater<>(type);
}
@SuppressWarnings("unchecked")
@ -54,4 +77,64 @@ public class InstancedRenderRegistry {
else return factory.create(manager, tile);
}
private boolean _skipRender(Object o) {
return skipRender.getBoolean(o);
}
public class TileRegistrater<T extends TileEntity> {
private final TileEntityType<T> type;
private ITileInstanceFactory<? super T> factory;
private boolean skipRender = false;
public TileRegistrater(TileEntityType<T> type) {
this.type = type;
}
public TileRegistrater<T> factory(ITileInstanceFactory<? super T> rendererFactory) {
factory = rendererFactory;
return this;
}
public TileRegistrater<T> setSkipRender(boolean skipRender) {
this.skipRender = skipRender;
return this;
}
public InstancedRenderRegistry build() {
tiles.put(type, factory);
InstancedRenderRegistry.this.skipRender.put(type, skipRender);
return InstancedRenderRegistry.this;
}
}
public class EntityRegistrater<T extends Entity> {
private final EntityType<T> type;
private IEntityInstanceFactory<? super T> factory;
private boolean skipRender = false;
public EntityRegistrater(EntityType<T> type) {
this.type = type;
}
public EntityRegistrater<T> factory(IEntityInstanceFactory<? super T> rendererFactory) {
factory = rendererFactory;
return this;
}
public EntityRegistrater<T> setSkipRender(boolean skipRender) {
this.skipRender = skipRender;
return this;
}
public InstancedRenderRegistry build() {
entities.put(type, factory);
InstancedRenderRegistry.this.skipRender.put(type, skipRender);
return InstancedRenderRegistry.this;
}
}
}

View file

@ -7,6 +7,7 @@ import com.jozufozu.flywheel.backend.instancing.InstancedRenderRegistry;
import com.jozufozu.flywheel.backend.instancing.MaterialManager;
import net.minecraft.entity.Entity;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IBlockReader;
import net.minecraft.world.World;
@ -17,6 +18,11 @@ public class EntityInstanceManager extends InstanceManager<Entity> {
super(materialManager);
}
@Override
protected boolean canInstance(Entity obj) {
return obj != null && InstancedRenderRegistry.getInstance().canInstance(obj.getType());
}
@Override
protected IInstance createRaw(Entity obj) {
return InstancedRenderRegistry.getInstance()

View file

@ -17,6 +17,11 @@ public class TileInstanceManager extends InstanceManager<TileEntity> {
super(materialManager);
}
@Override
protected boolean canInstance(TileEntity obj) {
return obj != null && InstancedRenderRegistry.getInstance().canInstance(obj.getType());
}
@Override
protected IInstance createRaw(TileEntity obj) {
return InstancedRenderRegistry.getInstance()

View file

@ -19,7 +19,7 @@ public class OrientedData extends BasicData {
private float qX;
private float qY;
private float qZ;
private float qW;
private float qW = 1;
public OrientedData(Instancer<?> owner) {
super(owner);

View file

@ -0,0 +1,8 @@
package com.jozufozu.flywheel.core.model;
public class ModelPart {
public static PartBuilder builder(int sizeU, int sizeV) {
return new PartBuilder(sizeU, sizeV);
}
}

View file

@ -0,0 +1,199 @@
package com.jozufozu.flywheel.core.model;
import static com.jozufozu.flywheel.util.RenderMath.*;
import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer;
import com.jozufozu.flywheel.backend.model.BufferedModel;
import com.jozufozu.flywheel.backend.model.IndexedModel;
import com.jozufozu.flywheel.core.Formats;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.util.Direction;
import net.minecraft.util.math.vector.Vector3f;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.Set;
public class PartBuilder {
private float sizeU = 64.0F;
private float sizeV = 32.0F;
private TextureAtlasSprite sprite;
private final List<CuboidBuilder> cuboids = new ArrayList<>();
public PartBuilder() { }
public PartBuilder(int sizeU, int sizeV) {
this.setTextureSize(sizeU, sizeV);
}
public PartBuilder setTextureSize(int textureWidth, int textureHeight) {
this.sizeU = (float)textureWidth;
this.sizeV = (float)textureHeight;
return this;
}
public PartBuilder sprite(TextureAtlasSprite sprite) {
this.sprite = sprite;
return this;
}
public CuboidBuilder cuboid() {
return new CuboidBuilder(this);
}
public BufferedModel build() {
int vertices = 0;
for (CuboidBuilder cuboid : cuboids) {
vertices += cuboid.vertices();
}
VecBuffer buffer = VecBuffer.allocate(vertices * Formats.UNLIT_MODEL.getStride());
for (CuboidBuilder cuboid : cuboids) {
cuboid.buffer(buffer);
}
buffer.rewind();
return IndexedModel.fromSequentialQuads(Formats.UNLIT_MODEL, buffer.unwrap(), vertices);
}
private PartBuilder addCuboid(CuboidBuilder builder) {
cuboids.add(builder);
return this;
}
public static class CuboidBuilder {
TextureAtlasSprite sprite;
Set<Direction> visibleFaces = EnumSet.allOf(Direction.class);
int textureOffsetU;
int textureOffsetV;
float posX1;
float posY1;
float posZ1;
float posX2;
float posY2;
float posZ2;
final PartBuilder partBuilder;
CuboidBuilder(PartBuilder partBuilder) {
this.partBuilder = partBuilder;
this.sprite = partBuilder.sprite;
}
public CuboidBuilder textureOffset(int u, int v) {
this.textureOffsetU = u;
this.textureOffsetV = v;
return this;
}
public CuboidBuilder start(float x, float y, float z) {
this.posX1 = x;
this.posY1 = y;
this.posZ1 = z;
return this;
}
public CuboidBuilder end(float x, float y, float z) {
this.posX2 = x;
this.posY2 = y;
this.posZ2 = z;
return this;
}
public CuboidBuilder size(float x, float y, float z) {
this.posX2 = posX1 + x;
this.posY2 = posY1 + y;
this.posZ2 = posZ1 + z;
return this;
}
public CuboidBuilder sprite(TextureAtlasSprite sprite) {
this.sprite = sprite;
return this;
}
public PartBuilder endCuboid() {
return partBuilder.addCuboid(this);
}
public int vertices() {
return visibleFaces.size() * 4;
}
public void buffer(VecBuffer buffer) {
float sizeX = posX2 - posX1;
float sizeY = posY2 - posY1;
float sizeZ = posZ2 - posZ1;
Vector3f lll = new Vector3f(posX1 / 16f, posY1 / 16f, posZ1 / 16f);
Vector3f hll = new Vector3f(posX2 / 16f, posY1 / 16f, posZ1 / 16f);
Vector3f hhl = new Vector3f(posX2 / 16f, posY2 / 16f, posZ1 / 16f);
Vector3f lhl = new Vector3f(posX1 / 16f, posY2 / 16f, posZ1 / 16f);
Vector3f llh = new Vector3f(posX1 / 16f, posY1 / 16f, posZ2 / 16f);
Vector3f hlh = new Vector3f(posX2 / 16f, posY1 / 16f, posZ2 / 16f);
Vector3f hhh = new Vector3f(posX2 / 16f, posY2 / 16f, posZ2 / 16f);
Vector3f lhh = new Vector3f(posX1 / 16f, posY2 / 16f, posZ2 / 16f);
float f4 = getU((float)textureOffsetU);
float f5 = getU((float)textureOffsetU + sizeZ);
float f6 = getU((float)textureOffsetU + sizeZ + sizeX);
float f7 = getU((float)textureOffsetU + sizeZ + sizeX + sizeX);
float f8 = getU((float)textureOffsetU + sizeZ + sizeX + sizeZ);
float f9 = getU((float)textureOffsetU + sizeZ + sizeX + sizeZ + sizeX);
float f10 = getV((float)textureOffsetV);
float f11 = getV((float)textureOffsetV + sizeZ);
float f12 = getV((float)textureOffsetV + sizeZ + sizeY);
float textureWidth = partBuilder.sizeU;
float textureHeight = partBuilder.sizeV;
quad(buffer, new Vector3f[]{hlh, llh, lll, hll}, f5, f10, f6, f11, textureWidth, textureHeight, Direction.DOWN);
quad(buffer, new Vector3f[]{hhl, lhl, lhh, hhh}, f6, f11, f7, f10, textureWidth, textureHeight, Direction.UP);
quad(buffer, new Vector3f[]{lll, llh, lhh, lhl}, f4, f11, f5, f12, textureWidth, textureHeight, Direction.WEST);
quad(buffer, new Vector3f[]{hll, lll, lhl, hhl}, f5, f11, f6, f12, textureWidth, textureHeight, Direction.NORTH);
quad(buffer, new Vector3f[]{hlh, hll, hhl, hhh}, f6, f11, f8, f12, textureWidth, textureHeight, Direction.EAST);
quad(buffer, new Vector3f[]{llh, hlh, hhh, lhh}, f8, f11, f9, f12, textureWidth, textureHeight, Direction.SOUTH);
}
public void quad(VecBuffer buffer, Vector3f[] vertices, float minU, float minV, float maxU, float maxV, float texWidth, float texHeight, Direction dir) {
Vector3f normal = dir.getUnitVector();
buffer.putVec3(vertices[0].getX(), vertices[0].getY(), vertices[0].getZ()).putVec3(nb(normal.getX()), nb(normal.getY()), nb(normal.getZ())).putVec2(maxU, minV);
buffer.putVec3(vertices[1].getX(), vertices[1].getY(), vertices[1].getZ()).putVec3(nb(normal.getX()), nb(normal.getY()), nb(normal.getZ())).putVec2(minU, minV);
buffer.putVec3(vertices[2].getX(), vertices[2].getY(), vertices[2].getZ()).putVec3(nb(normal.getX()), nb(normal.getY()), nb(normal.getZ())).putVec2(minU, maxV);
buffer.putVec3(vertices[3].getX(), vertices[3].getY(), vertices[3].getZ()).putVec3(nb(normal.getX()), nb(normal.getY()), nb(normal.getZ())).putVec2(maxU, maxV);
}
public float getU(float u) {
if (sprite != null)
return sprite.getInterpolatedU(u / 4.);
else
return u;
}
public float getV(float v) {
if (sprite != null)
return sprite.getInterpolatedV(v / 4.);
else
return v;
}
}
}

View file

@ -0,0 +1,118 @@
package com.jozufozu.flywheel.core.model;
import net.minecraft.util.Direction;
import net.minecraft.util.math.vector.Vector3f;
public class Readable {
public static class ModelBox {
private final TexturedQuad[] quads;
public final float posX1;
public final float posY1;
public final float posZ1;
public final float posX2;
public final float posY2;
public final float posZ2;
public ModelBox(int texOffU, int texOffV, float posX1, float posY1, float posZ1, float sizeX, float sizeY, float sizeZ, float growX, float growY, float growZ, boolean mirror, float texWidth, float texHeight) {
this.posX1 = posX1;
this.posY1 = posY1;
this.posZ1 = posZ1;
this.posX2 = posX1 + sizeX;
this.posY2 = posY1 + sizeY;
this.posZ2 = posZ1 + sizeZ;
this.quads = new TexturedQuad[6];
float posX2 = posX1 + sizeX;
float posY2 = posY1 + sizeY;
float posZ2 = posZ1 + sizeZ;
posX1 = posX1 - growX;
posY1 = posY1 - growY;
posZ1 = posZ1 - growZ;
posX2 = posX2 + growX;
posY2 = posY2 + growY;
posZ2 = posZ2 + growZ;
if (mirror) {
float tmp = posX2;
posX2 = posX1;
posX1 = tmp;
}
PositionTextureVertex lll = new PositionTextureVertex(posX1, posY1, posZ1, 0.0F, 0.0F);
PositionTextureVertex hll = new PositionTextureVertex(posX2, posY1, posZ1, 0.0F, 8.0F);
PositionTextureVertex hhl = new PositionTextureVertex(posX2, posY2, posZ1, 8.0F, 8.0F);
PositionTextureVertex lhl = new PositionTextureVertex(posX1, posY2, posZ1, 8.0F, 0.0F);
PositionTextureVertex llh = new PositionTextureVertex(posX1, posY1, posZ2, 0.0F, 0.0F);
PositionTextureVertex hlh = new PositionTextureVertex(posX2, posY1, posZ2, 0.0F, 8.0F);
PositionTextureVertex hhh = new PositionTextureVertex(posX2, posY2, posZ2, 8.0F, 8.0F);
PositionTextureVertex lhh = new PositionTextureVertex(posX1, posY2, posZ2, 8.0F, 0.0F);
float f4 = (float)texOffU;
float f5 = (float)texOffU + sizeZ;
float f6 = (float)texOffU + sizeZ + sizeX;
float f7 = (float)texOffU + sizeZ + sizeX + sizeX;
float f8 = (float)texOffU + sizeZ + sizeX + sizeZ;
float f9 = (float)texOffU + sizeZ + sizeX + sizeZ + sizeX;
float f10 = (float)texOffV;
float f11 = (float)texOffV + sizeZ;
float f12 = (float)texOffV + sizeZ + sizeY;
this.quads[2] = new TexturedQuad(new PositionTextureVertex[]{hlh, llh, lll, hll}, f5, f10, f6, f11, texWidth, texHeight, mirror, Direction.DOWN);
this.quads[3] = new TexturedQuad(new PositionTextureVertex[]{hhl, lhl, lhh, hhh}, f6, f11, f7, f10, texWidth, texHeight, mirror, Direction.UP);
this.quads[1] = new TexturedQuad(new PositionTextureVertex[]{lll, llh, lhh, lhl}, f4, f11, f5, f12, texWidth, texHeight, mirror, Direction.WEST);
this.quads[4] = new TexturedQuad(new PositionTextureVertex[]{hll, lll, lhl, hhl}, f5, f11, f6, f12, texWidth, texHeight, mirror, Direction.NORTH);
this.quads[0] = new TexturedQuad(new PositionTextureVertex[]{hlh, hll, hhl, hhh}, f6, f11, f8, f12, texWidth, texHeight, mirror, Direction.EAST);
this.quads[5] = new TexturedQuad(new PositionTextureVertex[]{llh, hlh, hhh, lhh}, f8, f11, f9, f12, texWidth, texHeight, mirror, Direction.SOUTH);
}
}
public static class PositionTextureVertex {
public final float x;
public final float y;
public final float z;
public final float u;
public final float v;
public PositionTextureVertex(float x, float y, float z) {
this(x, y, z, 0, 0);
}
public PositionTextureVertex(float x, float y, float z, float u, float v) {
this.x = x;
this.y = y;
this.z = z;
this.u = u;
this.v = v;
}
public PositionTextureVertex setTexturePosition(float u, float v) {
return new PositionTextureVertex(x, y, z, u, v);
}
}
public static class TexturedQuad {
public final PositionTextureVertex[] vertices;
public final Vector3f normal;
public TexturedQuad(PositionTextureVertex[] vertices, float minU, float minV, float maxU, float maxV, float texWidth, float texHeight, boolean p_i225951_8_, Direction p_i225951_9_) {
this.vertices = vertices;
float w = 0.0F / texWidth;
float h = 0.0F / texHeight;
vertices[0] = vertices[0].setTexturePosition(maxU / texWidth - w, minV / texHeight + h);
vertices[1] = vertices[1].setTexturePosition(minU / texWidth + w, minV / texHeight + h);
vertices[2] = vertices[2].setTexturePosition(minU / texWidth + w, maxV / texHeight - h);
vertices[3] = vertices[3].setTexturePosition(maxU / texWidth - w, maxV / texHeight - h);
if (p_i225951_8_) {
int i = vertices.length;
for(int j = 0; j < i / 2; ++j) {
PositionTextureVertex modelrenderer$positiontexturevertex = vertices[j];
vertices[j] = vertices[i - 1 - j];
vertices[i - 1 - j] = modelrenderer$positiontexturevertex;
}
}
this.normal = p_i225951_9_.getUnitVector();
if (p_i225951_8_) {
this.normal.multiplyComponentwise(-1.0F, 1.0F, 1.0F);
}
}
}
}

View file

@ -3,6 +3,8 @@ package com.jozufozu.flywheel.mixin;
import java.util.ArrayList;
import java.util.Iterator;
import com.jozufozu.flywheel.backend.instancing.InstancedRenderRegistry;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Group;
@ -10,7 +12,6 @@ import org.spongepowered.asm.mixin.injection.Redirect;
import com.google.common.collect.Lists;
import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.instancing.IInstanceRendered;
import net.minecraft.client.renderer.WorldRenderer;
import net.minecraft.client.world.ClientWorld;
@ -28,7 +29,9 @@ public class CancelEntityRenderMixin {
.canUseInstancing()) {
ArrayList<Entity> filtered = Lists.newArrayList(entities);
filtered.removeIf(entity -> entity instanceof IInstanceRendered && !((IInstanceRendered) entity).shouldRenderNormally());
InstancedRenderRegistry r = InstancedRenderRegistry.getInstance();
filtered.removeIf(r::shouldSkipRender);
return filtered;
}
@ -42,7 +45,9 @@ public class CancelEntityRenderMixin {
.canUseInstancing()) {
ArrayList<Entity> filtered = Lists.newArrayList(classInheritanceMultiMap);
filtered.removeIf(entity -> entity instanceof IInstanceRendered && !((IInstanceRendered) entity).shouldRenderNormally());
InstancedRenderRegistry r = InstancedRenderRegistry.getInstance();
filtered.removeIf(r::shouldSkipRender);
return filtered.iterator();
}

View file

@ -2,13 +2,14 @@ package com.jozufozu.flywheel.mixin;
import java.util.List;
import com.jozufozu.flywheel.backend.instancing.InstancedRenderRegistry;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.instancing.IInstanceRendered;
import net.minecraft.client.renderer.chunk.ChunkRenderDispatcher;
import net.minecraft.tileentity.TileEntity;
@ -31,7 +32,8 @@ public class CancelTileEntityRenderMixin {
.canUseInstancing()) {
List<TileEntity> tiles = cir.getReturnValue();
tiles.removeIf(tile -> tile instanceof IInstanceRendered && !((IInstanceRendered) tile).shouldRenderNormally());
InstancedRenderRegistry r = InstancedRenderRegistry.getInstance();
tiles.removeIf(r::shouldSkipRender);
}
}
}

View file

@ -0,0 +1,8 @@
package com.jozufozu.flywheel.util;
public class RenderMath {
public static byte nb(float f) {
return (byte) (f * 127);
}
}

View file

@ -0,0 +1,97 @@
package com.jozufozu.flywheel.util.transform;
import com.google.common.collect.Lists;
import net.minecraft.util.math.vector.Quaternion;
import net.minecraft.util.math.vector.Vector3d;
import net.minecraft.util.math.vector.Vector3f;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.Stack;
import java.util.Vector;
public class QuaternionTransformStack implements TransformStack {
private final Deque<Transform> stack;
public QuaternionTransformStack() {
stack = new ArrayDeque<>();
stack.add(new Transform());
}
@Override
public TransformStack translate(double x, double y, double z) {
Transform peek = stack.peek();
double qx = peek.qx;
double qy = peek.qy;
double qz = peek.qz;
double qw = peek.qw;
peek.x += qw * x + qy * z - qz * y;
peek.y += qw * y - qx * z + qz * x;
peek.z += qw * z + qx * y - qy * x;
return this;
}
@Override
public TransformStack multiply(Quaternion quaternion) {
return this;
}
@Override
public TransformStack push() {
stack.push(stack.peek().copy());
return this;
}
@Override
public TransformStack pop() {
if (stack.size() == 1) {
stack.peek().loadIdentity();
} else {
stack.pop();
}
return this;
}
private static class Transform {
public double qx;
public double qy;
public double qz;
public double qw;
public double x;
public double y;
public double z;
public Transform() {
qw = 1.0;
}
public void loadIdentity() {
x = y = z = 0.0;
qx = qy = qz = 0.0;
qw = 1.0;
}
public Transform copy() {
Transform transform = new Transform();
transform.qx = this.qx;
transform.qy = this.qy;
transform.qz = this.qz;
transform.qw = this.qw;
transform.x = this.x;
transform.y = this.y;
transform.z = this.z;
return transform;
}
}
}

View file

@ -0,0 +1,80 @@
package com.jozufozu.flywheel.util.transform;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.vector.Quaternion;
import net.minecraft.util.math.vector.Vector3d;
import net.minecraft.util.math.vector.Vector3f;
import net.minecraft.util.math.vector.Vector3i;
public interface TransformStack {
public static final Vector3d center = new Vector3d(0.5, 0.5, 0.5);
TransformStack translate(double x, double y, double z);
TransformStack multiply(Quaternion quaternion);
TransformStack push();
TransformStack pop();
default TransformStack rotate(Direction axis, float radians) {
if (radians == 0)
return this;
return multiply(axis.getUnitVector()
.getRadialQuaternion(radians));
}
default TransformStack rotate(double angle, Direction.Axis axis) {
Vector3f vec =
axis == Direction.Axis.X ? Vector3f.POSITIVE_X : axis == Direction.Axis.Y ? Vector3f.POSITIVE_Y : Vector3f.POSITIVE_Z;
return multiply(vec, angle);
}
default TransformStack rotateX(double angle) {
return multiply(Vector3f.POSITIVE_X, angle);
}
default TransformStack rotateY(double angle) {
return multiply(Vector3f.POSITIVE_Y, angle);
}
default TransformStack rotateZ(double angle) {
return multiply(Vector3f.POSITIVE_Z, angle);
}
default TransformStack centre() {
return translate(center);
}
default TransformStack unCentre() {
return translateBack(center);
}
default TransformStack translate(Vector3i vec) {
return translate(vec.getX(), vec.getY(), vec.getZ());
}
default TransformStack translate(Vector3d vec) {
return translate(vec.x, vec.y, vec.z);
}
default TransformStack translateBack(Vector3d vec) {
return translate(-vec.x, -vec.y, -vec.z);
}
default TransformStack nudge(int id) {
long randomBits = (long) id * 31L * 493286711L;
randomBits = randomBits * randomBits * 4392167121L + randomBits * 98761L;
float xNudge = (((float) (randomBits >> 16 & 7L) + 0.5F) / 8.0F - 0.5F) * 0.004F;
float yNudge = (((float) (randomBits >> 20 & 7L) + 0.5F) / 8.0F - 0.5F) * 0.004F;
float zNudge = (((float) (randomBits >> 24 & 7L) + 0.5F) / 8.0F - 0.5F) * 0.004F;
return translate(xNudge, yNudge, zNudge);
}
default TransformStack multiply(Vector3f axis, double angle) {
if (angle == 0)
return this;
return multiply(axis.getDegreesQuaternion((float) angle));
}
}

View file

@ -0,0 +1,136 @@
package com.jozufozu.flywheel.vanilla;
import com.jozufozu.flywheel.backend.instancing.IDynamicInstance;
import com.jozufozu.flywheel.backend.instancing.MaterialManager;
import com.jozufozu.flywheel.backend.instancing.tile.TileEntityInstance;
import com.jozufozu.flywheel.backend.model.BufferedModel;
import com.jozufozu.flywheel.core.Materials;
import com.jozufozu.flywheel.core.materials.OrientedData;
import com.jozufozu.flywheel.core.model.ModelPart;
import com.jozufozu.flywheel.util.AnimationTickHolder;
import it.unimi.dsi.fastutil.floats.Float2FloatFunction;
import net.minecraft.block.Block;
import net.minecraft.block.ChestBlock;
import net.minecraft.client.renderer.Atlases;
import net.minecraft.client.renderer.model.RenderMaterial;
import net.minecraft.state.properties.ChestType;
import net.minecraft.tileentity.ChestTileEntity;
import net.minecraft.tileentity.TileEntityMerger;
import net.minecraft.util.math.vector.Quaternion;
import net.minecraft.util.math.vector.Vector3f;
import java.util.Calendar;
public class ChestInstance extends TileEntityInstance<ChestTileEntity> implements IDynamicInstance {
private final OrientedData body;
private final OrientedData lid;
private final Float2FloatFunction lidProgress;
private final RenderMaterial renderMaterial;
public ChestInstance(MaterialManager<?> materialManager, ChestTileEntity tile) {
super(materialManager, tile);
Block block = blockState.getBlock();
ChestType chestType = blockState.contains(ChestBlock.TYPE) ? blockState.get(ChestBlock.TYPE) : ChestType.SINGLE;
renderMaterial = Atlases.getChestTexture(tile, chestType, isChristmas());
body = baseInstance()
.setPosition(getInstancePosition());
lid = lidInstance()
.setPosition(getInstancePosition())
.nudge(0, 9f/16f, 0);
if (block instanceof ChestBlock) {
ChestBlock chestBlock = (ChestBlock) block;
TileEntityMerger.ICallbackWrapper<? extends ChestTileEntity> wrapper = chestBlock.getBlockEntitySource(blockState, world, getWorldPosition(), true);
this.lidProgress = wrapper.apply(ChestBlock.getAnimationProgressRetriever(tile));
} else {
lidProgress = $ -> 0f;
}
}
@Override
public void beginFrame() {
float progress = lidProgress.get(AnimationTickHolder.getPartialTicks());
progress = 1.0F - progress;
progress = 1.0F - progress * progress * progress;
float angleX = -(progress * ((float) Math.PI / 2F));
Quaternion quaternion = new Quaternion(Vector3f.POSITIVE_X, angleX, false);
lid.setRotation(quaternion)
.setPivot(0, 0, 1f / 16f);
}
@Override
public void updateLight() {
relight(getWorldPosition(), body, lid);
}
@Override
public void remove() {
body.delete();
lid.delete();
}
private OrientedData baseInstance() {
return materialManager.getMaterial(Materials.ORIENTED, renderMaterial.getAtlasId())
.get("base_" + renderMaterial.getTextureId(), this::getBaseModel)
.createInstance();
}
private OrientedData lidInstance() {
return materialManager.getMaterial(Materials.ORIENTED, renderMaterial.getAtlasId())
.get("lid_" + renderMaterial.getTextureId(), this::getLidModel)
.createInstance();
}
private BufferedModel getBaseModel() {
return ModelPart.builder(64, 64)
.sprite(renderMaterial.getSprite())
.cuboid()
.textureOffset(0, 19)
.start(1, 0, 1)
.end(15, 10, 15)
.endCuboid()
.build();
}
private BufferedModel getLidModel() {
return ModelPart.builder(64, 64)
.sprite(renderMaterial.getSprite())
.cuboid()
.textureOffset(0, 0)
.start(1, 0, 1)
.end(15, 5, 15)
.endCuboid()
.cuboid()
.start(7, -2, 15)
.size(2, 4, 1)
.endCuboid()
.build();
}
public static boolean isChristmas() {
Calendar calendar = Calendar.getInstance();
return calendar.get(Calendar.MONTH) + 1 == 12 && calendar.get(Calendar.DATE) >= 24 && calendar.get(Calendar.DATE) <= 26;
}
}

View file

@ -0,0 +1,17 @@
package com.jozufozu.flywheel.vanilla;
import com.jozufozu.flywheel.backend.instancing.InstancedRenderRegistry;
import net.minecraft.tileentity.TileEntityType;
public class VanillaInstances {
public static void init() {
InstancedRenderRegistry r = InstancedRenderRegistry.getInstance();
r.tile(TileEntityType.CHEST)
.setSkipRender(true)
.factory(ChestInstance::new)
.build();
}
}