1 package com.irtimaled.bbor.client;
3 import com.irtimaled.bbor.client.renderers.*;
4 import com.irtimaled.bbor.common.BoundingBoxCache;
5 import com.irtimaled.bbor.common.BoundingBoxType;
6 import com.irtimaled.bbor.common.Dimensions;
7 import com.irtimaled.bbor.common.MathHelper;
8 import com.irtimaled.bbor.common.models.*;
9 import com.irtimaled.bbor.config.ConfigManager;
10 import net.minecraft.client.Minecraft;
11 import org.lwjgl.opengl.GL11;
15 public class ClientRenderer {
16 private static final int CHUNK_SIZE = 16;
17 private static final Map<Class<? extends BoundingBox>, Renderer> boundingBoxRendererMap = new HashMap<>();
19 private final GetCache getCache;
21 private Set<BoundingBox> spawnChunkBoundingBoxes = new HashSet<>();
23 ClientRenderer(GetCache getCache) {
24 this.getCache = getCache;
25 boundingBoxRendererMap.put(BoundingBoxVillage.class, new VillageRenderer());
26 boundingBoxRendererMap.put(BoundingBoxSlimeChunk.class, new SlimeChunkRenderer());
27 boundingBoxRendererMap.put(BoundingBoxWorldSpawn.class, new WorldSpawnRenderer());
28 boundingBoxRendererMap.put(BoundingBoxStructure.class, new StructureRenderer());
29 boundingBoxRendererMap.put(BoundingBoxMobSpawner.class, new MobSpawnerRenderer());
32 private boolean isWithinRenderDistance(BoundingBox boundingBox) {
33 Coords minCoords = boundingBox.getMinCoords();
34 Coords maxCoords = boundingBox.getMaxCoords();
35 int renderDistanceBlocks = getRenderDistanceChunks() * CHUNK_SIZE;
36 int minX = MathHelper.floor(PlayerCoords.getX() - renderDistanceBlocks);
37 int maxX = MathHelper.floor(PlayerCoords.getX() + renderDistanceBlocks);
38 int minZ = MathHelper.floor(PlayerCoords.getZ() - renderDistanceBlocks);
39 int maxZ = MathHelper.floor(PlayerCoords.getZ() + renderDistanceBlocks);
41 return maxCoords.getX() >= minX &&
42 maxCoords.getZ() >= minZ &&
43 minCoords.getX() <= maxX &&
44 minCoords.getZ() <= maxZ;
47 public void render(int dimensionId, Boolean outerBoxesOnly) {
48 Map<BoundingBox, Set<BoundingBox>> boundingBoxes = getBoundingBoxes(dimensionId);
50 GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
51 GL11.glLineWidth(2.0f);
52 GL11.glDisable(GL11.GL_TEXTURE_2D);
53 GL11.glDisable(GL11.GL_CULL_FACE);
55 if (ConfigManager.alwaysVisible.get()) {
56 GL11.glClear(GL11.GL_DEPTH_BUFFER_BIT);
58 for (Map.Entry<BoundingBox, Set<BoundingBox>> entry : boundingBoxes.entrySet()) {
59 BoundingBox key = entry.getKey();
60 if (!key.shouldRender()) continue;
62 Renderer renderer = boundingBoxRendererMap.get(key.getClass());
63 if (renderer == null) continue;
65 if (!outerBoxesOnly) {
66 Set<BoundingBox> children = entry.getValue();
67 if (children != null) {
68 children.forEach(renderer::render);
75 GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_FILL);
76 GL11.glEnable(GL11.GL_CULL_FACE);
77 GL11.glEnable(GL11.GL_TEXTURE_2D);
80 private Map<BoundingBox, Set<BoundingBox>> getBoundingBoxes(int dimensionId) {
81 Map<BoundingBox, Set<BoundingBox>> boundingBoxes = new HashMap<>();
82 if (dimensionId == Dimensions.OVERWORLD) {
83 if (BoundingBoxType.SlimeChunks.shouldRender()) {
84 addSlimeChunks(boundingBoxes);
87 for (BoundingBox boundingBox : spawnChunkBoundingBoxes) {
88 if (boundingBox.shouldRender() && isWithinRenderDistance(boundingBox)) {
89 boundingBoxes.put(boundingBox, null);
94 BoundingBoxCache cache = getCache.apply(dimensionId);
96 for (Map.Entry<BoundingBox, Set<BoundingBox>> entry : cache.getBoundingBoxes().entrySet()) {
97 BoundingBox key = entry.getKey();
98 if (key.shouldRender() && isWithinRenderDistance(key)) {
99 boundingBoxes.put(key, entry.getValue());
103 return boundingBoxes;
106 private void addSlimeChunks(Map<BoundingBox, Set<BoundingBox>> boundingBoxes) {
107 int renderDistanceChunks = getRenderDistanceChunks();
108 int playerChunkX = MathHelper.floor(PlayerCoords.getX() / 16.0D);
109 int playerChunkZ = MathHelper.floor(PlayerCoords.getZ() / 16.0D);
110 for (int chunkX = playerChunkX - renderDistanceChunks; chunkX <= playerChunkX + renderDistanceChunks; ++chunkX) {
111 for (int chunkZ = playerChunkZ - renderDistanceChunks; chunkZ <= playerChunkZ + renderDistanceChunks; ++chunkZ) {
112 if (isSlimeChunk(chunkX, chunkZ)) {
113 int chunkXStart = chunkX << 4;
114 int chunkZStart = chunkZ << 4;
115 Coords minCoords = new Coords(chunkXStart, 1, chunkZStart);
116 Coords maxCoords = new Coords(chunkXStart + 15, 38, chunkZStart + 15);
117 boundingBoxes.put(BoundingBoxSlimeChunk.from(minCoords, maxCoords), null);
123 private int getRenderDistanceChunks() {
124 return Minecraft.getInstance().gameSettings.renderDistanceChunks;
127 private boolean isSlimeChunk(int chunkX, int chunkZ) {
128 Random r = new Random(seed +
129 (long) (chunkX * chunkX * 4987142) +
130 (long) (chunkX * 5947611) +
131 (long) (chunkZ * chunkZ) * 4392871L +
132 (long) (chunkZ * 389711) ^ 987234911L);
133 return r.nextInt(10) == 0;
136 void setWorldData(long seed, int spawnX, int spawnZ) {
138 spawnChunkBoundingBoxes = getSpawnChunkBoundingBoxes(spawnX, spawnZ);
141 private Set<BoundingBox> getSpawnChunkBoundingBoxes(int spawnX, int spawnZ) {
142 Set<BoundingBox> boundingBoxes = new HashSet<>();
143 boundingBoxes.add(getWorldSpawnBoundingBox(spawnX, spawnZ));
144 boundingBoxes.add(buildSpawnChunksBoundingBox(spawnX, spawnZ, 12, BoundingBoxType.SpawnChunks));
145 boundingBoxes.add(buildSpawnChunksBoundingBox(spawnX, spawnZ, 16, BoundingBoxType.LazySpawnChunks));
146 return boundingBoxes;
149 private BoundingBox getWorldSpawnBoundingBox(int spawnX, int spawnZ) {
150 Coords minCoords = new Coords(spawnX - 10, 0, spawnZ - 10);
151 Coords maxCoords = new Coords(spawnX + 10, 0, spawnZ + 10);
153 return BoundingBoxWorldSpawn.from(minCoords, maxCoords, BoundingBoxType.WorldSpawn);
156 private BoundingBox buildSpawnChunksBoundingBox(int spawnX, int spawnZ, int size, BoundingBoxType type) {
157 double midOffset = CHUNK_SIZE * (size / 2.0);
158 double midX = Math.round((float) (spawnX / (double) CHUNK_SIZE)) * (double) CHUNK_SIZE;
159 double midZ = Math.round((float) (spawnZ / (double) CHUNK_SIZE)) * (double) CHUNK_SIZE;
160 Coords minCoords = new Coords(midX - midOffset, 0, midZ - midOffset);
161 if (spawnX / (double) CHUNK_SIZE % 0.5D == 0.0D && spawnZ / (double) CHUNK_SIZE % 0.5D == 0.0D) {
162 midX += (double) CHUNK_SIZE;
163 midZ += (double) CHUNK_SIZE;
165 Coords maxCoords = new Coords(midX + midOffset, 0, midZ + midOffset);
166 return BoundingBoxWorldSpawn.from(minCoords, maxCoords, type);