mirror of
https://github.com/Sollace/Unicopia.git
synced 2024-11-23 21:38:00 +01:00
Add portal rendering (wip)
This commit is contained in:
parent
fce8623f44
commit
8785a3f5aa
8 changed files with 336 additions and 14 deletions
|
@ -17,18 +17,27 @@ public class RenderUtil {
|
|||
new Vertex(new Vector3f(1, 1, 0), 0, 0),
|
||||
new Vertex(new Vector3f(1, 0, 0), 0, 1)
|
||||
};
|
||||
public static final Vertex[] FRAME_BUFFER_VERTICES = new Vertex[] {
|
||||
new Vertex(new Vector3f(0, 1, 0), 0, 0),
|
||||
new Vertex(new Vector3f(1, 1, 0), 1, 0),
|
||||
new Vertex(new Vector3f(1, 0, 0), 1, 1),
|
||||
new Vertex(new Vector3f(0, 0, 0), 0, 1)
|
||||
};
|
||||
|
||||
public static void renderFace(MatrixStack matrices, Tessellator te, BufferBuilder buffer, float r, float g, float b, float a, int light) {
|
||||
renderFace(matrices, te, buffer, r, g, b, a, light, 1, 1);
|
||||
}
|
||||
|
||||
public static void renderFace(MatrixStack matrices, Tessellator te, BufferBuilder buffer, float r, float g, float b, float a, int light, float uScale, float vScale) {
|
||||
buffer.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR_LIGHT);
|
||||
for (Vertex vertex : UNIT_FACE) {
|
||||
Vector4f position = vertex.position(matrices);
|
||||
buffer.vertex(position.x, position.y, position.z).texture(vertex.u(), vertex.v()).color(r, g, b, a).light(light).next();
|
||||
buffer.vertex(position.x, position.y, position.z).texture(vertex.u() * uScale, vertex.v() * vScale).color(r, g, b, a).light(light).next();
|
||||
}
|
||||
te.draw();
|
||||
}
|
||||
|
||||
public record Vertex(Vector3f position, float u, float v) {
|
||||
|
||||
public Vector4f position(MatrixStack matrices) {
|
||||
matrices.peek().getPositionMatrix().transform(TEMP_VECTOR.set(position, 1));
|
||||
return TEMP_VECTOR;
|
||||
|
|
|
@ -20,7 +20,7 @@ public class SphereModel extends BakedModel {
|
|||
compileVertices(azimuthRange, zenithIncrement, azimuthIncrement, this::addVertex);
|
||||
}
|
||||
|
||||
private static void compileVertices(double azimuthRange, double zenithIncrement, double azimuthIncrement, Consumer<Vector4f> collector) {
|
||||
static void compileVertices(double azimuthRange, double zenithIncrement, double azimuthIncrement, Consumer<Vector4f> collector) {
|
||||
Vector4f vector = new Vector4f();
|
||||
for (double zenith = 0; zenith < DrawableUtil.PI; zenith += zenithIncrement) {
|
||||
for (double azimuth = 0; azimuth < azimuthRange; azimuth += azimuthIncrement) {
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
package com.minelittlepony.unicopia.client.render.model;
|
||||
|
||||
import org.joml.Vector4f;
|
||||
|
||||
import com.minelittlepony.unicopia.client.gui.DrawableUtil;
|
||||
import com.minelittlepony.unicopia.client.render.RenderUtil;
|
||||
|
||||
import net.minecraft.client.render.VertexConsumer;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
|
||||
public class TexturedSphereModel extends BakedModel {
|
||||
public static final TexturedSphereModel DISK = new TexturedSphereModel(40, 2, DrawableUtil.PI);
|
||||
|
||||
public TexturedSphereModel(double rings, double sectors, double azimuthRange) {
|
||||
double zenithIncrement = DrawableUtil.PI / rings;
|
||||
double azimuthIncrement = DrawableUtil.TAU / sectors;
|
||||
SphereModel.compileVertices(azimuthRange, zenithIncrement, azimuthIncrement, this::addVertex);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addVertex(Vector4f vertex) {
|
||||
addVertex(vertex.x, vertex.y, vertex.z, vertex.x, vertex.z);
|
||||
}
|
||||
|
||||
public final void render(MatrixStack matrices, VertexConsumer buffer, float scale, float r, float g, float b, float a, float uScale, float vScale) {
|
||||
scale = Math.abs(scale);
|
||||
if (scale < 0.001F) {
|
||||
return;
|
||||
}
|
||||
|
||||
matrices.push();
|
||||
matrices.scale(scale, scale, scale);
|
||||
uScale *= 0.5F;
|
||||
vScale *= 0.5F;
|
||||
for (RenderUtil.Vertex vertex : vertices) {
|
||||
Vector4f pos = vertex.position(matrices);
|
||||
buffer.vertex(pos.x, pos.y, pos.z).texture((vertex.u() + 1) * uScale, (vertex.v() + 1) * vScale).color(r, g, b, a).next();
|
||||
}
|
||||
matrices.pop();
|
||||
}
|
||||
}
|
|
@ -27,6 +27,8 @@ public class PlacedSpellRenderer extends SpellRenderer<PlaceableSpell> {
|
|||
|
||||
@Override
|
||||
public void render(MatrixStack matrices, VertexConsumerProvider vertices, PlaceableSpell spell, Caster<?> caster, int light, float limbAngle, float limbDistance, float tickDelta, float animationProgress, float headYaw, float headPitch) {
|
||||
|
||||
|
||||
if (!(caster.asEntity() instanceof CastSpellEntity castSpell)) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -1,17 +1,57 @@
|
|||
package com.minelittlepony.unicopia.client.render.spell;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.joml.Matrix3f;
|
||||
import org.joml.Matrix4f;
|
||||
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.minelittlepony.common.util.Color;
|
||||
import com.minelittlepony.unicopia.ability.magic.Caster;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.effect.PortalSpell;
|
||||
import com.minelittlepony.unicopia.client.render.RenderLayers;
|
||||
import com.minelittlepony.unicopia.client.render.model.SphereModel;
|
||||
import com.minelittlepony.unicopia.client.render.model.TexturedSphereModel;
|
||||
import com.minelittlepony.unicopia.entity.EntityReference;
|
||||
import com.minelittlepony.unicopia.entity.mob.UEntities;
|
||||
import com.minelittlepony.unicopia.mixin.client.MixinMinecraftClient;
|
||||
import com.mojang.blaze3d.platform.GlConst;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import com.mojang.blaze3d.systems.VertexSorter;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.gl.SimpleFramebuffer;
|
||||
import net.minecraft.client.option.Perspective;
|
||||
import net.minecraft.client.render.BackgroundRenderer;
|
||||
import net.minecraft.client.render.BufferBuilder;
|
||||
import net.minecraft.client.render.Camera;
|
||||
import net.minecraft.client.render.GameRenderer;
|
||||
import net.minecraft.client.render.Tessellator;
|
||||
import net.minecraft.client.render.VertexConsumer;
|
||||
import net.minecraft.client.render.VertexConsumerProvider;
|
||||
import net.minecraft.client.render.VertexFormat;
|
||||
import net.minecraft.client.render.VertexFormats;
|
||||
import net.minecraft.client.util.Window;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.screen.PlayerScreenHandler;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.util.math.RotationAxis;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
|
||||
public class PortalSpellRenderer extends SpellRenderer<PortalSpell> {
|
||||
private static final LoadingCache<UUID, PortalFrameBuffer> FRAME_BUFFERS = CacheBuilder.newBuilder()
|
||||
.expireAfterAccess(10, TimeUnit.SECONDS)
|
||||
.<UUID, PortalFrameBuffer>removalListener(n -> n.getValue().close())
|
||||
.build(CacheLoader.from(uuid -> new PortalFrameBuffer()));
|
||||
|
||||
@Override
|
||||
public boolean shouldRenderEffectPass(int pass) {
|
||||
|
@ -32,20 +72,228 @@ public class PortalSpellRenderer extends SpellRenderer<PortalSpell> {
|
|||
float green = Color.g(color);
|
||||
float blue = Color.b(color);
|
||||
|
||||
VertexConsumer buffer = vertices.getBuffer(RenderLayers.getEndGateway());
|
||||
|
||||
double thickness = 0.1;
|
||||
VertexConsumer buff = vertices.getBuffer(RenderLayers.getEndGateway());
|
||||
|
||||
matrices.push();
|
||||
matrices.translate(0, thickness, 0);
|
||||
SphereModel.DISK.render(matrices, buffer, light, 0, 2.5F, red, green, blue, 1);
|
||||
matrices.translate(0, 0.02, 0);
|
||||
SphereModel.DISK.render(matrices, buff, light, 0, 2F, red, green, blue, 1);
|
||||
matrices.pop();
|
||||
|
||||
matrices.push();
|
||||
matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(180));
|
||||
matrices.translate(0, thickness, 0);
|
||||
SphereModel.DISK.render(matrices, buffer, light, 0, 2.5F, red, green, blue, 1);
|
||||
if (caster.asEntity().distanceTo(client.cameraEntity) > 50) {
|
||||
return; // don't bother rendering if too far away
|
||||
}
|
||||
|
||||
matrices.pop();
|
||||
spell.getTarget().ifPresent(target -> {
|
||||
try {
|
||||
float grown = Math.min(caster.asEntity().age, 20) / 20F;
|
||||
matrices.push();
|
||||
matrices.translate(0, 0.01, 0);
|
||||
matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(-spell.getYaw()));
|
||||
matrices.scale(grown, 1, grown);
|
||||
PortalFrameBuffer buffer = FRAME_BUFFERS.get(target.uuid());
|
||||
buffer.build(spell, caster, target);
|
||||
buffer.draw(matrices);
|
||||
matrices.pop();
|
||||
} catch (ExecutionException e) { }
|
||||
});
|
||||
}
|
||||
|
||||
static class PortalFrameBuffer implements AutoCloseable {
|
||||
@Nullable
|
||||
private SimpleFramebuffer framebuffer;
|
||||
private boolean closed;
|
||||
|
||||
private final MinecraftClient client = MinecraftClient.getInstance();
|
||||
|
||||
private boolean pendingDraw;
|
||||
|
||||
private static int recursionCount;
|
||||
|
||||
public void draw(MatrixStack matrices) {
|
||||
if (closed || framebuffer == null) {
|
||||
return;
|
||||
}
|
||||
float uScale = (float)framebuffer.viewportWidth / (float)framebuffer.textureWidth;
|
||||
float vScale = (float)framebuffer.viewportHeight / (float)framebuffer.textureHeight;
|
||||
|
||||
Tessellator tessellator = RenderSystem.renderThreadTesselator();
|
||||
BufferBuilder buffer = tessellator.getBuffer();
|
||||
|
||||
RenderSystem.enableDepthTest();
|
||||
RenderSystem.disableCull();
|
||||
RenderSystem.setShaderTexture(0, framebuffer.getColorAttachment());
|
||||
RenderSystem.setShader(GameRenderer::getPositionTexColorProgram);
|
||||
|
||||
Matrix4f textureMatrix = matrices.peek().getPositionMatrix();
|
||||
|
||||
RenderSystem.setTextureMatrix(textureMatrix);
|
||||
|
||||
buffer.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR);
|
||||
TexturedSphereModel.DISK.render(matrices, buffer, 2F, 1, 1, 1, 1, uScale, vScale);
|
||||
tessellator.draw();
|
||||
client.getTextureManager().bindTexture(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE);
|
||||
RenderSystem.enableCull();
|
||||
RenderSystem.resetTextureMatrix();
|
||||
}
|
||||
|
||||
public void build(PortalSpell spell, Caster<?> caster, EntityReference.EntityValues<Entity> target) {
|
||||
|
||||
if (System.currentTimeMillis() % 100 < 50) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (pendingDraw && recursionCount > 0) {
|
||||
innerBuild(spell, caster, target);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pendingDraw) {
|
||||
return;
|
||||
}
|
||||
pendingDraw = true;
|
||||
if (recursionCount > 0) {
|
||||
innerBuild(spell, caster, target);
|
||||
} else {
|
||||
((MixinMinecraftClient)client).getRenderTaskQueue().add(() -> innerBuild(spell, caster, target));
|
||||
}
|
||||
}
|
||||
|
||||
private void innerBuild(PortalSpell spell, Caster<?> caster, EntityReference.EntityValues<Entity> target) {
|
||||
synchronized (client) {
|
||||
pendingDraw = false;
|
||||
|
||||
if (recursionCount > 2) {
|
||||
return;
|
||||
}
|
||||
recursionCount++;
|
||||
|
||||
try {
|
||||
if (closed || client.interactionManager == null) {
|
||||
close();
|
||||
return;
|
||||
}
|
||||
|
||||
var fov = client.options.getFov();
|
||||
int originalFov = fov.getValue();
|
||||
fov.setValue(110);
|
||||
|
||||
Vec3d offset = new Vec3d(0, -1.2, -0.2);
|
||||
float yaw = spell.getYawDifference();// spell.getYawDifference();
|
||||
|
||||
offset = offset.rotateY(yaw * MathHelper.RADIANS_PER_DEGREE);
|
||||
|
||||
Entity cameraEntity = UEntities.CAST_SPELL.create(caster.asWorld());
|
||||
cameraEntity.setPosition(target.pos().add(offset));
|
||||
cameraEntity.setPitch(spell.getTargetPitch());
|
||||
cameraEntity.setYaw(yaw);
|
||||
|
||||
drawWorld(cameraEntity, 400, 400);
|
||||
|
||||
fov.setValue(originalFov);
|
||||
} finally {
|
||||
recursionCount--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
private void drawWorld(Entity cameraEntity, int width, int height) {
|
||||
Entity oldCameraEntity = client.cameraEntity;
|
||||
client.cameraEntity = cameraEntity;
|
||||
|
||||
Window window = client.getWindow();
|
||||
|
||||
Perspective perspective = client.options.getPerspective();
|
||||
client.options.setPerspective(Perspective.FIRST_PERSON);
|
||||
|
||||
int i = window.getFramebufferWidth();
|
||||
int j = window.getFramebufferHeight();
|
||||
|
||||
width = i;
|
||||
height = j;
|
||||
|
||||
client.getFramebuffer().endWrite();
|
||||
|
||||
if (framebuffer == null) {
|
||||
framebuffer = new SimpleFramebuffer(width, height, true, MinecraftClient.IS_SYSTEM_MAC);
|
||||
}
|
||||
|
||||
window.setFramebufferWidth(width);
|
||||
window.setFramebufferHeight(height);
|
||||
|
||||
MatrixStack view = RenderSystem.getModelViewStack();
|
||||
view.push();
|
||||
view.loadIdentity();
|
||||
RenderSystem.applyModelViewMatrix();
|
||||
Matrix4f proj = RenderSystem.getProjectionMatrix();
|
||||
Matrix3f invView = RenderSystem.getInverseViewRotationMatrix();
|
||||
|
||||
int fbo = client.getFramebuffer().fbo;
|
||||
client.getFramebuffer().fbo = framebuffer.fbo;
|
||||
|
||||
framebuffer.setClearColor(0, 0, 0, 0);
|
||||
framebuffer.clear(MinecraftClient.IS_SYSTEM_MAC);
|
||||
|
||||
RenderSystem.clear(GlConst.GL_DEPTH_BUFFER_BIT | GlConst.GL_COLOR_BUFFER_BIT, MinecraftClient.IS_SYSTEM_MAC);
|
||||
framebuffer.beginWrite(true);
|
||||
BackgroundRenderer.clearFog();
|
||||
|
||||
Camera camera = client.gameRenderer.getCamera();
|
||||
|
||||
ObjectArrayList<?> chunkInfos = ((WorldRendererDuck)client.worldRenderer).unicopia_getChunkInfos();
|
||||
List<Object> backup = new ArrayList<>(chunkInfos);
|
||||
|
||||
client.gameRenderer.setRenderHand(false);
|
||||
MatrixStack matrices = new MatrixStack();
|
||||
|
||||
matrices.scale((float)width / height, 1, 1);
|
||||
|
||||
client.gameRenderer.renderWorld(1, 0, matrices);
|
||||
if (recursionCount <= 1) {
|
||||
client.gameRenderer.setRenderHand(true);
|
||||
}
|
||||
framebuffer.endWrite();
|
||||
|
||||
chunkInfos.clear();
|
||||
chunkInfos.addAll((List)backup);
|
||||
|
||||
view.pop();
|
||||
RenderSystem.applyModelViewMatrix();
|
||||
|
||||
client.getFramebuffer().fbo = fbo;
|
||||
window.setFramebufferWidth(i);
|
||||
window.setFramebufferHeight(j);
|
||||
|
||||
client.options.setPerspective(perspective);
|
||||
client.cameraEntity = oldCameraEntity;
|
||||
|
||||
RenderSystem.setProjectionMatrix(proj, VertexSorter.BY_Z);
|
||||
RenderSystem.setInverseViewRotationMatrix(invView);
|
||||
|
||||
if (recursionCount <= 1) {
|
||||
camera.update(client.world,
|
||||
client.getCameraEntity() == null ? client.player : client.getCameraEntity(),
|
||||
perspective.isFirstPerson(),
|
||||
perspective.isFrontView(),
|
||||
1
|
||||
);
|
||||
}
|
||||
|
||||
client.getFramebuffer().beginWrite(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
closed = true;
|
||||
if (framebuffer != null) {
|
||||
SimpleFramebuffer fb = framebuffer;
|
||||
framebuffer = null;
|
||||
fb.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public interface WorldRendererDuck {
|
||||
ObjectArrayList<?> unicopia_getChunkInfos();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
package com.minelittlepony.unicopia.mixin.client;
|
||||
|
||||
import java.util.Queue;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
|
||||
@Mixin(MinecraftClient.class)
|
||||
public interface MixinMinecraftClient {
|
||||
@Accessor("renderTaskQueue")
|
||||
Queue<Runnable> getRenderTaskQueue();
|
||||
}
|
|
@ -15,7 +15,10 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
|||
|
||||
import com.minelittlepony.unicopia.client.ClientBlockDestructionManager;
|
||||
import com.minelittlepony.unicopia.client.UnicopiaClient;
|
||||
import com.minelittlepony.unicopia.client.render.spell.PortalSpellRenderer;
|
||||
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import net.minecraft.client.render.BlockBreakingInfo;
|
||||
import net.minecraft.client.render.Camera;
|
||||
import net.minecraft.client.render.WorldRenderer;
|
||||
|
@ -25,7 +28,7 @@ import net.minecraft.resource.SynchronousResourceReloader;
|
|||
import net.minecraft.util.math.RotationAxis;
|
||||
|
||||
@Mixin(value = WorldRenderer.class, priority = 1001)
|
||||
abstract class MixinWorldRenderer implements SynchronousResourceReloader, AutoCloseable, ClientBlockDestructionManager.Source {
|
||||
abstract class MixinWorldRenderer implements SynchronousResourceReloader, AutoCloseable, ClientBlockDestructionManager.Source, PortalSpellRenderer.WorldRendererDuck {
|
||||
|
||||
private final ClientBlockDestructionManager destructions = new ClientBlockDestructionManager();
|
||||
|
||||
|
@ -41,6 +44,10 @@ abstract class MixinWorldRenderer implements SynchronousResourceReloader, AutoCl
|
|||
return destructions;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Accessor("chunkInfos")
|
||||
public abstract ObjectArrayList<?> unicopia_getChunkInfos();
|
||||
|
||||
@Override
|
||||
@Accessor("ticks")
|
||||
public abstract int getTicks();
|
||||
|
|
|
@ -73,6 +73,7 @@
|
|||
"client.MixinItemStack",
|
||||
"client.MixinKeyboardInput",
|
||||
"client.MixinLivingEntityRenderer",
|
||||
"client.MixinMinecraftClient",
|
||||
"client.MixinModelPart",
|
||||
"client.MixinMouse",
|
||||
"client.MixinPlayerEntityRenderer",
|
||||
|
|
Loading…
Reference in a new issue