Skip to content

Commit

Permalink
Fixed Immersive Wires
Browse files Browse the repository at this point in the history
Resolves #152 #89 #84 #71
  • Loading branch information
Asek3 committed Jul 19, 2022
1 parent 7be82a5 commit 241a7b9
Show file tree
Hide file tree
Showing 6 changed files with 272 additions and 3 deletions.
2 changes: 2 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ dependencies {
forge "net.minecraftforge:forge:${minecraft_version}-${forge_version}"

modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
compileOnly files("libs/Flywheel-Forge-1.18-0.6.0.53.jar")
compileOnly files("libs/ImmersiveEngineering-1.18.2-8.0.2-149.jar")
}

processResources {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
package me.jellysquid.mods.sodium.client;

import me.jellysquid.mods.sodium.client.compat.immersive.ImmersiveConnectionRenderer;
import me.jellysquid.mods.sodium.client.gui.SodiumGameOptions;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.AddReloadListenerEvent;
import net.minecraftforge.fml.IExtensionPoint;
import net.minecraftforge.fml.ModList;
import net.minecraftforge.fml.ModLoadingContext;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
import net.minecraftforge.fml.loading.FMLLoader;
import net.minecraftforge.network.NetworkConstants;

import java.io.IOException;
Expand All @@ -25,9 +29,11 @@ public class SodiumClientMod {

public static boolean flywheelLoaded = false;
public static boolean oculusLoaded = false;
public static boolean immersiveLoaded = FMLLoader.getLoadingModList().getModFileById("immersiveengineering") != null;

public SodiumClientMod() {
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::setup);
MinecraftForge.EVENT_BUS.addListener(this::registerReloadListener);
MOD_VERSION = ModList.get().getModContainerById(MODID).get().getModInfo().getVersion().toString();

ModLoadingContext.get().registerExtensionPoint(IExtensionPoint.DisplayTest.class, () -> new IExtensionPoint.DisplayTest(() -> NetworkConstants.IGNORESERVERONLY, (a, b) -> true));
Expand All @@ -38,6 +44,11 @@ public void setup(final FMLClientSetupEvent event) {
flywheelLoaded = ModList.get().isLoaded("flywheel");
oculusLoaded = ModList.get().isLoaded("oculus");
}

public void registerReloadListener(AddReloadListenerEvent ev) {
if(immersiveLoaded)
ev.addListener(new ImmersiveConnectionRenderer());
}

public static SodiumGameOptions options() {
if (CONFIG == null) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
package me.jellysquid.mods.sodium.client.compat.immersive;

import blusunrize.immersiveengineering.api.utils.ResettableLazy;
import blusunrize.immersiveengineering.api.wires.Connection;
import blusunrize.immersiveengineering.api.wires.ConnectionPoint;
import blusunrize.immersiveengineering.api.wires.GlobalWireNetwork;
import blusunrize.immersiveengineering.api.wires.WireCollisionData;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import me.jellysquid.mods.sodium.client.model.quad.properties.ModelQuadFacing;
import me.jellysquid.mods.sodium.client.model.quad.properties.ModelQuadWinding;
import me.jellysquid.mods.sodium.client.render.chunk.compile.ChunkBuildBuffers;
import me.jellysquid.mods.sodium.client.render.chunk.compile.buffers.ChunkModelBuilder;
import me.jellysquid.mods.sodium.client.render.chunk.format.ModelVertexSink;
import me.jellysquid.mods.sodium.client.util.color.ColorARGB;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.RenderLayer;
import net.minecraft.client.render.WorldRenderer;
import net.minecraft.client.texture.Sprite;
import net.minecraft.resource.ResourceManager;
import net.minecraft.resource.SynchronousResourceReloader;
import net.minecraft.screen.PlayerScreenHandler;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.ChunkSectionPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.math.Vec3i;
import net.minecraft.world.BlockRenderView;

import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

public class ImmersiveConnectionRenderer implements SynchronousResourceReloader {
private static final LoadingCache<SegmentsKey, List<RenderedSegment>> SEGMENT_CACHE = CacheBuilder.newBuilder()
.expireAfterAccess(120, TimeUnit.SECONDS)
.build(CacheLoader.from(ImmersiveConnectionRenderer::renderSegmentForCache));
private static final ResettableLazy<Sprite> WIRE_TEXTURE = new ResettableLazy<>(
() -> MinecraftClient.getInstance().getBakedModelManager()
.getAtlas(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE)
.getSprite(rl("block/wire"))
);

public static Identifier rl(String path)
{
return new Identifier("immersiveengineering", path);
}

@Override
public void reload(@Nonnull ResourceManager pResourceManager) {
WIRE_TEXTURE.reset();
resetCache();
}

public static void resetCache() {
SEGMENT_CACHE.invalidateAll();
}

public static void renderConnectionsInSection(
ChunkBuildBuffers buffers, BlockRenderView region, ChunkSectionPos section
) {
GlobalWireNetwork globalNet = GlobalWireNetwork.getNetwork(MinecraftClient.getInstance().world);
List<WireCollisionData.ConnectionSegments> connectionParts = globalNet.getCollisionData().getWiresIn(section);
if (connectionParts == null || connectionParts.isEmpty()) {
return;
}
RenderLayer renderType = RenderLayer.getSolid();
ChunkModelBuilder builder = buffers.get(renderType);
int originX = section.getMinX();
int originY = section.getMinY();
int originZ = section.getMinZ();
for (WireCollisionData.ConnectionSegments connection : connectionParts) {
ConnectionPoint connectionOrigin = connection.connection().getEndA();
renderSegments(
builder, connection, region,
connectionOrigin.getX() - originX,
connectionOrigin.getY() - originY,
connectionOrigin.getZ() - originZ
);
}
}

public static void renderSegments(
ChunkModelBuilder out, WireCollisionData.ConnectionSegments toRender,
BlockRenderView level, int offX, int offY, int offZ
) {
Connection connection = toRender.connection();
int colorRGB = connection.type.getColour(connection);
int colorBGR = ColorARGB.toABGR(colorRGB);
double radius = connection.type.getRenderDiameter() / 2;
var vertices = out.getVertexSink();
List<RenderedSegment> segments = SEGMENT_CACHE.getUnchecked(new SegmentsKey(
radius, colorBGR, connection.getCatenaryData(),
toRender.firstPointToRender(), toRender.lastPointToRender()
));
vertices.ensureCapacity(2 * 4 * segments.size());
int lastLight = 0;
for (int startPoint = 0; startPoint < segments.size(); ++startPoint) {
var renderedSegment = segments.get(startPoint);
if (startPoint == 0) {
lastLight = getLight(connection, renderedSegment.offsetStart, level);
}
int nextLight = getLight(connection, renderedSegment.offsetEnd, level);
renderedSegment.render(lastLight, nextLight, offX, offY, offZ, out);
lastLight = nextLight;
}
vertices.flush();
}

private static List<RenderedSegment> renderSegmentForCache(SegmentsKey key) {
List<RenderedSegment> segments = new ArrayList<>(key.endIndex() - key.beginIndex());
for (int i = key.beginIndex(); i < key.endIndex(); ++i) {
segments.add(renderSegmentForCache(key, i));
}
return segments;
}

private static RenderedSegment renderSegmentForCache(SegmentsKey key, int startIndex) {
Connection.CatenaryData catenaryData = key.catenaryShape();
Vec3d start = key.catenaryShape().getRenderPoint(startIndex);
Vec3d end = key.catenaryShape().getRenderPoint(startIndex + 1);
Vec3d horNormal;
if (key.catenaryShape().isVertical()) {
horNormal = new Vec3d(1, 0, 0);
} else {
horNormal = new Vec3d(-catenaryData.delta().z, 0, catenaryData.delta().x).normalize();
}
Vec3d verticalNormal = start.subtract(end).crossProduct(horNormal).normalize();
Vec3d horRadius = horNormal.multiply(key.radius());
Vec3d verticalRadius = verticalNormal.multiply(-key.radius());
return new RenderedSegment(
renderQuad(start, end, horRadius, key.color()),
renderQuad(start, end, verticalRadius, key.color()),
new Vec3i(start.x, start.y, start.z),
new Vec3i(end.x, end.y, end.z)
);
}

private static int getLight(Connection connection, Vec3i point, BlockRenderView level) {
return WorldRenderer.getLightmapCoordinates(level, connection.getEndA().position().add(point));
}

private static Quad renderQuad(Vec3d start, Vec3d end, Vec3d radius, int color) {
Sprite texture = WIRE_TEXTURE.get();
return new Quad(
vertex(start.add(radius), texture.getMinU(), texture.getMinV(), color, true),
vertex(end.add(radius), texture.getMaxU(), texture.getMinV(), color, false),
vertex(end.subtract(radius), texture.getMaxU(), texture.getMaxV(), color, false),
vertex(start.subtract(radius), texture.getMinU(), texture.getMaxV(), color, true)
);
}

private static Vertex vertex(Vec3d point, double u, double v, int color, boolean lightForStart) {
return new Vertex(
(float) point.x, (float) point.y, (float) point.z, (float) u, (float) v, color, lightForStart
);
}

private record SegmentsKey(
double radius, int color, Connection.CatenaryData catenaryShape, int beginIndex, int endIndex
) {
}

private record Vertex(
float posX, float posY, float posZ,
float texU, float texV,
int color,
boolean lightForStart
) {
void write(
ModelVertexSink vertexSink, int offX, int offY, int offZ, int lightStart, int lightEnd, int chunkId
) {
vertexSink.writeVertex(
offX + posX, offY + posY, offZ + posZ,
color, texU, texV, lightForStart ? lightStart : lightEnd, chunkId
);
}
}

private record Quad(Vertex v0, Vertex v1, Vertex v2, Vertex v3) {
void write(
ChunkModelBuilder out, int offX, int offY, int offZ, int lightStart, int lightEnd
) {
var vertexSink = out.getVertexSink();
int quadStart = vertexSink.getVertexCount();
v0.write(vertexSink, offX, offY, offZ, lightStart, lightEnd, out.getChunkId());
v1.write(vertexSink, offX, offY, offZ, lightStart, lightEnd, out.getChunkId());
v2.write(vertexSink, offX, offY, offZ, lightStart, lightEnd, out.getChunkId());
v3.write(vertexSink, offX, offY, offZ, lightStart, lightEnd, out.getChunkId());
var indexBuffer = out.getIndexBufferBuilder(ModelQuadFacing.UNASSIGNED);
indexBuffer.add(quadStart, ModelQuadWinding.CLOCKWISE);
indexBuffer.add(quadStart, ModelQuadWinding.COUNTERCLOCKWISE);
}
}

private record RenderedSegment(Quad quadA, Quad quadB, Vec3i offsetStart, Vec3i offsetEnd) {
public void render(
int lightStart, int lightEnd, int offX, int offY, int offZ, ChunkModelBuilder out
) {
quadA.write(out, offX, offY, offZ, lightStart, lightEnd);
quadB.write(out, offX, offY, offZ, lightStart, lightEnd);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package me.jellysquid.mods.sodium.client.compat.immersive;

import blusunrize.immersiveengineering.api.wires.GlobalWireNetwork;
import blusunrize.immersiveengineering.api.wires.WireCollisionData.ConnectionSegments;
import me.jellysquid.mods.sodium.client.render.chunk.RenderSection;
import me.jellysquid.mods.sodium.client.render.chunk.tasks.ChunkRenderBuildTask;
import me.jellysquid.mods.sodium.client.render.chunk.tasks.ChunkRenderRebuildTask;
import me.jellysquid.mods.sodium.client.world.cloned.ChunkRenderContext;
import me.jellysquid.mods.sodium.client.world.cloned.ClonedChunkSection;
import me.jellysquid.mods.sodium.client.world.cloned.ClonedChunkSectionCache;
import net.minecraft.client.MinecraftClient;
import net.minecraft.util.math.BlockBox;
import net.minecraft.util.math.ChunkSectionPos;

import java.util.Arrays;
import java.util.List;

public class ImmersiveEmptyChunkChecker {
public static boolean hasWires(ChunkSectionPos origin) {
GlobalWireNetwork globalNet = GlobalWireNetwork.getNetwork(MinecraftClient.getInstance().world);
List<ConnectionSegments> wiresInSection = globalNet.getCollisionData().getWiresIn(origin);
return wiresInSection != null && !wiresInSection.isEmpty();
}

public static ChunkRenderBuildTask makeEmptyRebuildTask(
ClonedChunkSectionCache sectionCache, ChunkSectionPos origin, RenderSection render, int frame
) {
var sections = new ClonedChunkSection[64];
// TODO rethink this mess, and possibly call release?
var centerSection = sectionCache.acquire(origin.getSectionX(), origin.getSectionY(), origin.getSectionZ());
Arrays.fill(sections, centerSection);
var sectionBB = new BlockBox(
origin.getMinX() - 2, origin.getMinY() - 2, origin.getMinZ() - 2,
origin.getMaxX() + 2, origin.getMaxY() + 2, origin.getMaxZ() + 2
);
var context = new ChunkRenderContext(origin, sections, sectionBB);
return new ChunkRenderRebuildTask(render, context, frame);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import it.unimi.dsi.fastutil.objects.ObjectList;
import me.jellysquid.mods.sodium.client.SodiumClientMod;
import me.jellysquid.mods.sodium.client.compat.immersive.ImmersiveEmptyChunkChecker;
import me.jellysquid.mods.sodium.client.gl.device.CommandList;
import me.jellysquid.mods.sodium.client.gl.device.RenderDevice;
import me.jellysquid.mods.sodium.client.render.SodiumWorldRenderer;
Expand Down Expand Up @@ -255,7 +256,11 @@ private boolean loadSection(int x, int y, int z) {
ChunkSection section = chunk.getSectionArray()[this.world.sectionCoordToIndex(y)];

if (section.isEmpty()) {
render.setData(ChunkRenderData.EMPTY);
if(!SodiumClientMod.immersiveLoaded) {
render.setData(ChunkRenderData.EMPTY);
}else if(!ImmersiveEmptyChunkChecker.hasWires(ChunkSectionPos.from(x, y, z))) {
render.setData(ChunkRenderData.EMPTY);
}
} else {
render.markForUpdate(ChunkUpdateType.INITIAL_BUILD);
}
Expand Down Expand Up @@ -381,7 +386,9 @@ public ChunkRenderBuildTask createRebuildTask(RenderSection render) {
int frame = this.currentFrame;

if (context == null) {
return new ChunkRenderEmptyBuildTask(render, frame);
return SodiumClientMod.immersiveLoaded ? ImmersiveEmptyChunkChecker.makeEmptyRebuildTask(
sectionCache, render.getChunkPos(), render, currentFrame
) : new ChunkRenderEmptyBuildTask(render, frame);
}

return new ChunkRenderRebuildTask(render, context, frame);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import me.jellysquid.mods.sodium.client.SodiumClientMod;
import me.jellysquid.mods.sodium.client.compat.FlywheelCompat;
import me.jellysquid.mods.sodium.client.compat.immersive.ImmersiveConnectionRenderer;
import me.jellysquid.mods.sodium.client.gl.compile.ChunkBuildContext;
import me.jellysquid.mods.sodium.client.render.chunk.RenderSection;
import me.jellysquid.mods.sodium.client.render.chunk.compile.ChunkBuildBuffers;
Expand Down Expand Up @@ -35,7 +36,6 @@

import java.util.EnumMap;
import java.util.Map;
import java.util.Objects;

/**
* Rebuilds all the meshes of a chunk for each given render pass with non-occluded blocks. The result is then uploaded
Expand Down Expand Up @@ -172,6 +172,11 @@ public ChunkBuildResult performBuild(ChunkBuildContext buildContext, Cancellatio
ForgeHooksClient.setRenderType(null);

Map<BlockRenderPass, ChunkMeshData> meshes = new EnumMap<>(BlockRenderPass.class);

if(SodiumClientMod.immersiveLoaded)
ImmersiveConnectionRenderer.renderConnectionsInSection(
buildContext.buffers, buildContext.cache.getWorldSlice(), render.getChunkPos()
);

for (BlockRenderPass pass : BlockRenderPass.VALUES) {
ChunkMeshData mesh = buffers.createMesh(pass);
Expand Down

0 comments on commit 241a7b9

Please sign in to comment.