mirror of
https://github.com/Sollace/Unicopia.git
synced 2024-12-17 23:48:00 +01:00
Improve structure rendering performance and change lighting
This commit is contained in:
parent
3b0a64326e
commit
e48b7bd244
4 changed files with 131 additions and 141 deletions
|
@ -0,0 +1,45 @@
|
|||
package com.minelittlepony.unicopia.block.state;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
class ExpandableList<T> {
|
||||
private final Supplier<T> defaultValue;
|
||||
private Object[] values;
|
||||
private int size;
|
||||
|
||||
public ExpandableList(int initialCapacity, Supplier<T> defaultValue) {
|
||||
this.defaultValue = defaultValue;
|
||||
values = new Object[Math.max(0, initialCapacity)];
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return size;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public T get(int index) {
|
||||
T t = (T)values[index];
|
||||
if (t == null) {
|
||||
values[index] = t = defaultValue.get();
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
public T getOrExpand(int index) {
|
||||
resize(index);
|
||||
return get(index);
|
||||
}
|
||||
|
||||
public void set(int index, T value) {
|
||||
resize(index);
|
||||
values[index] = value;
|
||||
}
|
||||
|
||||
private void resize(int index) {
|
||||
if (index >= values.length) {
|
||||
values = Arrays.copyOf(values, Math.max(index + 1, (values.length + 1) * 2));
|
||||
}
|
||||
size = Math.max(index + 1, size);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
package com.minelittlepony.unicopia.block.state;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.Blocks;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
|
||||
public record Schematic(int dx, int dy, int dz, Entry[] states) {
|
||||
public static Schematic fromPacket(PacketByteBuf buffer) {
|
||||
Builder builder = new Builder();
|
||||
buffer.readCollection(ArrayList::new, buf -> {
|
||||
byte op = buf.readByte();
|
||||
return switch (op) {
|
||||
case 1 -> builder.set(buf.readInt(), buf.readInt(), buf.readInt(), Block.getStateFromRawId(buf.readInt()));
|
||||
case 2 -> builder.fill(buf.readInt(), buf.readInt(), buf.readInt(), buf.readInt(), buf.readInt(), buf.readInt(), Block.getStateFromRawId(buf.readInt()));
|
||||
default -> builder;
|
||||
};
|
||||
});
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
public int volume() {
|
||||
return states.length;
|
||||
}
|
||||
|
||||
public static final class Builder {
|
||||
private static final BlockState AIR = Blocks.AIR.getDefaultState();
|
||||
|
||||
private int dx = -1;
|
||||
private int dy = -1;
|
||||
private int dz = -1;
|
||||
private final ExpandableList<ExpandableList<ExpandableList<BlockState>>> layers = new ExpandableList<>(0, () -> new ExpandableList<>(dz, () -> new ExpandableList<>(dx, () -> AIR)));
|
||||
|
||||
public Builder set(int x, int y, int z, BlockState state) {
|
||||
dx = Math.max(dx, x);
|
||||
dy = Math.max(dy, y);
|
||||
dz = Math.max(dz, z);
|
||||
layers.getOrExpand(y).getOrExpand(z).set(x, state);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder fill(int minX, int minY, int minZ, int maxX, int maxY, int maxZ, BlockState state) {
|
||||
dx = Math.max(dx, maxX);
|
||||
dy = Math.max(dy, maxY);
|
||||
dz = Math.max(dz, maxZ);
|
||||
for (int x = minX; x <= maxX; x++)
|
||||
for (int y = minY; y <= maxY; y++)
|
||||
for (int z = minZ; z <= maxZ; z++)
|
||||
set(x, y, z, state);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Schematic build() {
|
||||
List<Entry> states = new LinkedList<>();
|
||||
for (int y = 0; y <= dy && y < layers.size(); y++)
|
||||
for (int z = 0; z <= dz && z < layers.get(y).size(); z++)
|
||||
for (int x = 0; x <= dx && x < layers.get(y).get(z).size(); x++) {
|
||||
BlockState state = layers.get(y).get(z).get(x);
|
||||
if (!state.isAir()) {
|
||||
states.add(new Entry(x, y, z, state));
|
||||
}
|
||||
}
|
||||
|
||||
return new Schematic(dx, dy, dz, states.toArray(Entry[]::new));
|
||||
}
|
||||
}
|
||||
|
||||
public record Entry(int x, int y, int z, BlockState state) {}
|
||||
}
|
|
@ -10,6 +10,7 @@ import com.minelittlepony.common.client.gui.dimension.Bounds;
|
|||
import com.minelittlepony.unicopia.ability.magic.spell.crafting.IngredientWithSpell;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
|
||||
import com.minelittlepony.unicopia.block.state.Schematic;
|
||||
import com.minelittlepony.unicopia.client.gui.spellbook.SpellbookChapterList.Drawable;
|
||||
import com.minelittlepony.unicopia.container.SpellbookChapterLoader.Flow;
|
||||
import net.minecraft.client.gui.DrawContext;
|
||||
|
@ -59,7 +60,7 @@ public interface PageElement extends Drawable {
|
|||
default -> throw new IllegalArgumentException("Unexpected value: " + t);
|
||||
};
|
||||
}));
|
||||
case 5 -> new Structure.Builder().fromBuffer(buffer).build();
|
||||
case 5 -> new Structure(Bounds.empty(), Schematic.fromPacket(buffer));
|
||||
default -> throw new IllegalArgumentException("Unexpected value: " + type);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,15 +1,8 @@
|
|||
package com.minelittlepony.unicopia.client.gui.spellbook.element;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import com.minelittlepony.common.client.gui.IViewRoot;
|
||||
import com.minelittlepony.common.client.gui.dimension.Bounds;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.Blocks;
|
||||
import com.minelittlepony.unicopia.block.state.Schematic;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.gui.DrawContext;
|
||||
import net.minecraft.client.render.DiffuseLighting;
|
||||
|
@ -17,48 +10,13 @@ import net.minecraft.client.render.LightmapTextureManager;
|
|||
import net.minecraft.client.render.OverlayTexture;
|
||||
import net.minecraft.client.render.VertexConsumerProvider.Immediate;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.state.property.Properties;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.util.math.RotationAxis;
|
||||
|
||||
public record Structure(Bounds bounds, List<List<List<BlockState>>> states) implements PageElement {
|
||||
static final BlockState DIAMOND = Blocks.DIAMOND_BLOCK.getDefaultState();
|
||||
static final BlockState AIR = Blocks.AIR.getDefaultState();
|
||||
static final BlockState OBS = Blocks.OBSIDIAN.getDefaultState();
|
||||
static final BlockState SOU = Blocks.SOUL_SAND.getDefaultState();
|
||||
public static final Structure CRYSTAL_HEART_ALTAR = new Structure.Builder()
|
||||
.fill(0, 0, 0, 2, 0, 2, DIAMOND)
|
||||
.set(1, 1, 1, DIAMOND)
|
||||
.set(1, 2, 1, Blocks.END_ROD.getDefaultState().with(Properties.FACING, Direction.UP))
|
||||
.set(1, 4, 1, Blocks.END_ROD.getDefaultState().with(Properties.FACING, Direction.DOWN))
|
||||
.set(1, 5, 1, DIAMOND)
|
||||
.fill(0, 6, 0, 2, 6, 2, DIAMOND)
|
||||
.build();
|
||||
public static final Structure ALTAR_STRUCTURE = new Structure.Builder()
|
||||
.fill(0, 0, 0, 8, 0, 8, SOU)
|
||||
.fill(3, 1, 3, 5, 1, 5, OBS)
|
||||
.set(4, 1, 4, SOU)
|
||||
.set(4, 1, 6, Blocks.LODESTONE.getDefaultState())
|
||||
.fill(0, 1, 2, 0, 4, 2, OBS).fill(0, 1, 6, 0, 4, 6, OBS)
|
||||
.fill(2, 1, 0, 2, 4, 0, OBS).fill(6, 1, 0, 6, 4, 0, OBS)
|
||||
.fill(8, 1, 2, 8, 4, 2, OBS).fill(8, 1, 6, 8, 4, 6, OBS)
|
||||
.fill(2, 1, 8, 2, 4, 8, OBS).fill(6, 1, 8, 6, 4, 8, OBS)
|
||||
.build();
|
||||
|
||||
public record Structure(Bounds bounds, Schematic schematic) implements PageElement {
|
||||
@Override
|
||||
public void draw(DrawContext context, int mouseX, int mouseY, IViewRoot container) {
|
||||
int height = states.size();
|
||||
if (height == 0) {
|
||||
return;
|
||||
}
|
||||
int depth = states.get(0).size();
|
||||
if (depth == 0) {
|
||||
return;
|
||||
}
|
||||
int width = states.get(0).get(0).size();
|
||||
if (width == 0) {
|
||||
if (schematic.volume() == 0) {
|
||||
return;
|
||||
}
|
||||
MatrixStack matrices = context.getMatrices();
|
||||
|
@ -72,110 +30,24 @@ public record Structure(Bounds bounds, List<List<List<BlockState>>> states) impl
|
|||
if (container != null) {
|
||||
matrices.translate(container.getBounds().width / 2, container.getBounds().height / 2, 100);
|
||||
float minDimensions = Math.min(container.getBounds().width, container.getBounds().height) - 30;
|
||||
int minSize = Math.max(height, Math.max(width, depth)) * 16;
|
||||
int minSize = (Math.max(schematic.dx(), Math.max(schematic.dy(), schematic.dz())) + 1) * 16;
|
||||
float scale = minDimensions / minSize;
|
||||
matrices.scale(scale, scale, 1);
|
||||
}
|
||||
matrices.scale(-16, -16, -16);
|
||||
matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(-20 + MathHelper.sin(tickDelta / 10F) * 2));
|
||||
matrices.scale(16, -16, 16);
|
||||
matrices.peek().getNormalMatrix().scale(1, -1, 1);
|
||||
matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(20 + MathHelper.sin(tickDelta / 10F) * 2));
|
||||
matrices.peek().getPositionMatrix().rotate(RotationAxis.POSITIVE_Y.rotationDegrees(age));
|
||||
DiffuseLighting.enableForLevel(matrices.peek().getPositionMatrix());
|
||||
matrices.translate((-schematic.dx() - 1) / 2F, (-schematic.dy() - 1) / 2F, (-schematic.dz() - 1) / 2F);
|
||||
DiffuseLighting.disableGuiDepthLighting();
|
||||
|
||||
matrices.translate(-width / 2F, -height / 2F, -depth / 2F);
|
||||
|
||||
for (int y = 0; y < height; y++)
|
||||
for (int x = 0; x < width; x++)
|
||||
for (int z = 0; z < depth; z++) {
|
||||
BlockState state = states.get(y).get(z).get(x);
|
||||
for (var entry : schematic.states()) {
|
||||
matrices.push();
|
||||
matrices.translate(x, y, z);
|
||||
client.getBlockRenderManager().renderBlockAsEntity(state, matrices, immediate, LightmapTextureManager.MAX_LIGHT_COORDINATE, OverlayTexture.DEFAULT_UV);
|
||||
matrices.translate(entry.x(), entry.y(), entry.z());
|
||||
client.getBlockRenderManager().renderBlockAsEntity(entry.state(), matrices, immediate, LightmapTextureManager.MAX_LIGHT_COORDINATE, OverlayTexture.DEFAULT_UV);
|
||||
matrices.pop();
|
||||
}
|
||||
|
||||
matrices.pop();
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
private int dx = -1;
|
||||
private int dy = -1;
|
||||
private int dz = -1;
|
||||
private final List<List<List<BlockState>>> layers = new ArrayList<>();
|
||||
|
||||
private void resize(int x, int y, int z) {
|
||||
int ddx = Math.max(dx, x);
|
||||
int ddy = Math.max(dy, y);
|
||||
int ddz = Math.max(dz, z);
|
||||
|
||||
if (ddx <= dx && ddy <= dy && ddz <= dz) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ddy > dy) {
|
||||
dy = ddy;
|
||||
while (layers.size() <= ddy) {
|
||||
layers.add(createFilledList(ddz, () -> createFilledList(ddx, () -> AIR)));
|
||||
}
|
||||
}
|
||||
if (ddz > dz || ddx > dx) {
|
||||
layers.forEach(layer -> {
|
||||
if (ddz > dz) {
|
||||
while (layer.size() <= ddz) {
|
||||
layer.add(createFilledList(ddx, () -> AIR));
|
||||
}
|
||||
}
|
||||
if (ddx > dx) {
|
||||
layer.forEach(row -> {
|
||||
while (row.size() <= ddx) {
|
||||
row.add(AIR);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
dz = ddz;
|
||||
dx = ddx;
|
||||
}
|
||||
}
|
||||
|
||||
private <T> List<T> createFilledList(int length, Supplier<T> contentSupplier) {
|
||||
List<T> list = new ArrayList<>();
|
||||
while (list.size() <= length) {
|
||||
list.add(contentSupplier.get());
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public Builder set(int x, int y, int z, BlockState state) {
|
||||
resize(x, y, z);
|
||||
layers.get(y).get(z).set(x, state);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder fill(int minX, int minY, int minZ, int maxX, int maxY, int maxZ, BlockState state) {
|
||||
resize(maxX, maxY, maxZ);
|
||||
for (int x = minX; x <= maxX; x++)
|
||||
for (int y = minY; y <= maxY; y++)
|
||||
for (int z = minZ; z <= maxZ; z++)
|
||||
layers.get(y).get(z).set(x, state);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder fromBuffer(PacketByteBuf buffer) {
|
||||
|
||||
buffer.readCollection(ArrayList::new, buf -> {
|
||||
byte op = buf.readByte();
|
||||
return switch (op) {
|
||||
case 1 -> set(buf.readInt(), buf.readInt(), buf.readInt(), Block.getStateFromRawId(buf.readInt()));
|
||||
case 2 -> fill(buf.readInt(), buf.readInt(), buf.readInt(), buf.readInt(), buf.readInt(), buf.readInt(), Block.getStateFromRawId(buf.readInt()));
|
||||
default -> this;
|
||||
};
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Structure build() {
|
||||
return new Structure(Bounds.empty(), layers);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue