Skip to content

Commit 74cd6e9

Browse files
committed
Minimap Rotation
1 parent d00fd81 commit 74cd6e9

File tree

6 files changed

+333
-58
lines changed

6 files changed

+333
-58
lines changed

README.md

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,29 @@ Around your player icon in the SeedMap there will be a little arrow showing you
3030
![Arrow](https://i.imgur.com/pkodE8d.png)
3131

3232
### Seed Map Minimap
33-
Run ```/sm:minimap``` to open a live SeedMap minimap in the top-left corner of the HUD (use ```/sm:minimap on``` or ```/sm:minimap off``` to explicitly control it). The overlay renders the same features you selected on the main map and tracks your current position. Adjust its placement with ```/sm:config SeedMapMinimapOffsetX``` and ```/sm:config SeedMapMinimapOffsetY```, and resize it with ```/sm:config SeedMapMinimapWidth``` / ```SeedMapMinimapHeight``` to cover other minimaps if needed.
33+
34+
- Run ``` /sm:minimap ``` to open a live SeedMap minimap in the top-left corner of the HUD.
35+
- Use ``` /sm:minimap on/off ``` or to explicitly control whether it is shown.
36+
37+
- The minimap:
38+
- Renders the same features you selected on the main map.
39+
- Tracks your current position in real time.
40+
- Ideal for overlaying over Xaeros Minimap (See Screenshots).
41+
42+
- Position & size:
43+
- Move it horizontally with ``` /sm:config SeedMapMinimapOffsetX ```
44+
- Move it vertically with ``` /sm:config SeedMapMinimapOffsetY ```
45+
- Change width with ``` /sm:config SeedMapMinimapWidth ```
46+
- Change height with ``` /sm:config SeedMapMinimapHeight ```
47+
- Can be resized to cover other minimaps if needed.
48+
49+
- Display options:
50+
- Rotate the map with the player’s facing using ``` /sm:config SeedMapMinimapRotateWithPlayer ```
51+
- Adjust zoom independently from the main map via ``` /sm:config SeedMapMinimapPixelsPerBiome ```
52+
- Scale feature icons with ``` /sm:config SeedMapMinimapIconScale ```
53+
- Fine tune the background opacity with ``` /sm:config SeedMapMinimapOpacity ``` without affecting icon readability.
54+
55+
![Map1](https://i.imgur.com/gIGgOB7.png) ![Map2](https://i.imgur.com/rNwiWhy.png)
3456

3557
### Memory Handling
3658
Added a config option ```/sm:config ClearSeedMapCachesOnClose``` to clear tiles, per‑world locations and any other relevant caches. When enabled, this prevents FPS dips from garbage collection on large caches after zooming out far and then closing the map. Opening the map again will result it in being loaded like its the first time, at smaller zoom levels this isn't a problem.

src/main/java/dev/xpple/seedmapper/config/Configs.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,30 @@ private static void setMinimapHeight(int height) {
9191
SeedMapMinimapHeight = Math.clamp(height, 64, 512);
9292
}
9393

94+
@Config
95+
public static boolean SeedMapMinimapRotateWithPlayer = false;
96+
97+
@Config(setter = @Config.Setter("setMinimapPixelsPerBiome"))
98+
public static double SeedMapMinimapPixelsPerBiome = 2.0D;
99+
100+
private static void setMinimapPixelsPerBiome(double pixelsPerBiome) {
101+
SeedMapMinimapPixelsPerBiome = Math.clamp(pixelsPerBiome, SeedMapScreen.MIN_PIXELS_PER_BIOME, SeedMapScreen.MAX_PIXELS_PER_BIOME);
102+
}
103+
104+
@Config(setter = @Config.Setter("setMinimapIconScale"))
105+
public static double SeedMapMinimapIconScale = 0.5D;
106+
107+
private static void setMinimapIconScale(double iconScale) {
108+
SeedMapMinimapIconScale = Math.clamp(iconScale, 0.25D, 4.0D);
109+
}
110+
111+
@Config(setter = @Config.Setter("setMinimapOpacity"))
112+
public static double SeedMapMinimapOpacity = 1.0D;
113+
114+
private static void setMinimapOpacity(double opacity) {
115+
SeedMapMinimapOpacity = Math.clamp(opacity, 0.00D, 1.0D);
116+
}
117+
94118
@Config(comment = "getPlayerDirectionArrowComment")
95119
public static boolean ShowPlayerDirectionArrow = true;
96120

src/main/java/dev/xpple/seedmapper/seedmap/FeatureToggleWidget.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
package dev.xpple.seedmapper.seedmap;
22

3+
import com.mojang.blaze3d.textures.GpuTextureView;
34
import dev.xpple.seedmapper.config.Configs;
5+
import net.minecraft.client.Minecraft;
46
import net.minecraft.client.gui.GuiGraphics;
57
import net.minecraft.client.gui.components.Button;
8+
import net.minecraft.client.gui.render.TextureSetup;
9+
import net.minecraft.client.gui.render.state.BlitRenderState;
10+
import net.minecraft.client.renderer.RenderPipelines;
611
import net.minecraft.network.chat.Component;
712
import net.minecraft.util.ARGB;
13+
import org.joml.Matrix3x2f;
814

915
public class FeatureToggleWidget extends Button {
1016

@@ -21,7 +27,10 @@ protected void renderWidget(GuiGraphics guiGraphics, int mouseX, int mouseY, flo
2127
if (!Configs.ToggledFeatures.contains(this.feature)) {
2228
colour = ARGB.color(255 >> 1, 255, 255, 255);
2329
}
24-
SeedMapScreen.FeatureWidget.drawFeatureIcon(guiGraphics, this.feature.getDefaultTexture(), this.getX(), this.getY(), colour);
30+
MapFeature.Texture texture = this.feature.getDefaultTexture();
31+
GpuTextureView gpuTextureView = Minecraft.getInstance().getTextureManager().getTexture(texture.resourceLocation()).getTextureView();
32+
BlitRenderState renderState = new BlitRenderState(RenderPipelines.GUI_TEXTURED, TextureSetup.singleTexture(gpuTextureView), new Matrix3x2f(guiGraphics.pose()), this.getX(), this.getY(), this.getX() + this.getWidth(), this.getY() + this.getHeight(), 0, 1, 0, 1, colour, guiGraphics.scissorStack.peek());
33+
guiGraphics.guiRenderState.submitBlitToCurrentLayer(renderState);
2534
}
2635

2736
public Component getTooltip() {

src/main/java/dev/xpple/seedmapper/seedmap/SeedMapMinimapManager.java

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package dev.xpple.seedmapper.seedmap;
22

3-
import dev.xpple.seedmapper.config.Configs;
43
import net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback;
54
import net.minecraft.client.DeltaTracker;
65
import net.minecraft.client.Minecraft;
@@ -70,19 +69,9 @@ private void render(GuiGraphics guiGraphics, DeltaTracker deltaTracker) {
7069
return;
7170
}
7271

73-
int minimapWidth = Math.max(64, Configs.SeedMapMinimapWidth);
74-
int minimapHeight = Math.max(64, Configs.SeedMapMinimapHeight);
75-
this.minimapScreen.initForOverlay(minecraft, minimapWidth, minimapHeight);
7672
this.minimapScreen.focusOn(player.blockPosition());
7773

78-
int offsetX = Configs.SeedMapMinimapOffsetX;
79-
int offsetY = Configs.SeedMapMinimapOffsetY;
80-
int translateX = offsetX - SeedMapScreen.horizontalPadding();
81-
int translateY = offsetY - SeedMapScreen.verticalPadding();
8274
float partialTick = deltaTracker.getGameTimeDeltaPartialTick(false);
83-
guiGraphics.pose().pushMatrix();
84-
guiGraphics.pose().translate((float) translateX, (float) translateY);
85-
this.minimapScreen.renderToHud(guiGraphics, partialTick);
86-
guiGraphics.pose().popMatrix();
75+
this.minimapScreen.renderToHud(guiGraphics, player, partialTick);
8776
}
8877
}

src/main/java/dev/xpple/seedmapper/seedmap/SeedMapMinimapScreen.java

Lines changed: 158 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
package dev.xpple.seedmapper.seedmap;
22

3+
import dev.xpple.seedmapper.config.Configs;
34
import dev.xpple.seedmapper.util.QuartPos2;
45
import dev.xpple.seedmapper.util.QuartPos2f;
56
import net.minecraft.client.Minecraft;
67
import net.minecraft.client.gui.GuiGraphics;
8+
import net.minecraft.client.player.LocalPlayer;
79
import net.minecraft.core.BlockPos;
10+
import net.minecraft.util.Mth;
11+
import net.minecraft.world.phys.Vec3;
812

913
public class SeedMapMinimapScreen extends SeedMapScreen {
1014

@@ -26,8 +30,127 @@ public void initForOverlay(Minecraft minecraft, int width, int height) {
2630
this.cachedHeight = height;
2731
}
2832

29-
public void renderToHud(GuiGraphics guiGraphics, float partialTick) {
33+
public void renderToHud(GuiGraphics guiGraphics, LocalPlayer player, float partialTick) {
34+
this.refreshPixelsPerBiome();
35+
boolean rotateWithPlayer = Configs.SeedMapMinimapRotateWithPlayer;
36+
int configuredWidth = Math.max(64, Configs.SeedMapMinimapWidth);
37+
int configuredHeight = Math.max(64, Configs.SeedMapMinimapHeight);
38+
int contentWidth = Math.max(32, configuredWidth - 2 * horizontalPadding());
39+
int contentHeight = Math.max(32, configuredHeight - 2 * verticalPadding());
40+
int renderContentWidth = contentWidth;
41+
int renderContentHeight = contentHeight;
42+
if (rotateWithPlayer) {
43+
int diagonal = Mth.ceil(Math.sqrt(contentWidth * contentWidth + contentHeight * contentHeight));
44+
renderContentWidth = diagonal;
45+
renderContentHeight = diagonal;
46+
}
47+
int renderWidth = renderContentWidth + 2 * horizontalPadding();
48+
int renderHeight = renderContentHeight + 2 * verticalPadding();
49+
this.initForOverlay(Minecraft.getInstance(), renderWidth, renderHeight);
50+
51+
int offsetX = Configs.SeedMapMinimapOffsetX;
52+
int offsetY = Configs.SeedMapMinimapOffsetY;
53+
double extraWidth = renderContentWidth - contentWidth;
54+
double extraHeight = renderContentHeight - contentHeight;
55+
double translateX = offsetX - horizontalPadding() - extraWidth / 2.0;
56+
double translateY = offsetY - verticalPadding() - extraHeight / 2.0;
57+
double centerX = offsetX + contentWidth / 2.0;
58+
double centerY = offsetY + contentHeight / 2.0;
59+
float rotationRadians = rotateWithPlayer ? -this.getMapRotation(player, partialTick) : 0.0F;
60+
61+
guiGraphics.enableScissor(offsetX, offsetY, offsetX + contentWidth, offsetY + contentHeight);
62+
63+
this.setFeatureIconRenderingEnabled(false);
64+
this.setMarkerRenderingEnabled(false);
65+
this.setPlayerIconRenderingEnabled(!rotateWithPlayer);
66+
67+
var pose = guiGraphics.pose();
68+
pose.pushMatrix();
69+
if (rotateWithPlayer) {
70+
pose.translate((float) centerX, (float) centerY);
71+
pose.rotate(rotationRadians);
72+
pose.translate((float) -centerX, (float) -centerY);
73+
}
74+
pose.translate((float) translateX, (float) translateY);
3075
this.renderSeedMap(guiGraphics, Integer.MIN_VALUE, Integer.MIN_VALUE, partialTick);
76+
pose.popMatrix();
77+
78+
this.setFeatureIconRenderingEnabled(true);
79+
this.setMarkerRenderingEnabled(true);
80+
this.setPlayerIconRenderingEnabled(true);
81+
82+
this.renderMinimapIcons(guiGraphics, translateX, translateY, centerX, centerY, rotationRadians);
83+
if (rotateWithPlayer) {
84+
this.drawCenterCross(guiGraphics, centerX, centerY);
85+
}
86+
87+
guiGraphics.disableScissor();
88+
}
89+
90+
private float getMapRotation(LocalPlayer player, float partialTick) {
91+
Vec3 look = player.getViewVector(partialTick);
92+
double dirX = look.x;
93+
double dirZ = look.z;
94+
double len = Math.hypot(dirX, dirZ);
95+
if (len < 1.0E-4D) {
96+
return 0.0F;
97+
}
98+
double normX = dirX / len;
99+
double normZ = dirZ / len;
100+
return (float) Math.atan2(normX, -normZ);
101+
}
102+
103+
private void renderMinimapIcons(GuiGraphics guiGraphics, double translateX, double translateY, double centerX, double centerY, float rotationRadians) {
104+
double cos = Math.cos(rotationRadians);
105+
double sin = Math.sin(rotationRadians);
106+
double iconScale = Configs.SeedMapMinimapIconScale;
107+
for (FeatureWidget widget : this.featureWidgets) {
108+
MapFeature.Texture texture = widget.texture();
109+
int scaledWidth = (int) Math.max(1, Math.round(texture.width() * iconScale));
110+
int scaledHeight = (int) Math.max(1, Math.round(texture.height() * iconScale));
111+
double baseCenterX = widget.drawX() + texture.width() / 2.0;
112+
double baseCenterY = widget.drawY() + texture.height() / 2.0;
113+
double shiftedX = baseCenterX + translateX;
114+
double shiftedY = baseCenterY + translateY;
115+
double dx = shiftedX - centerX;
116+
double dy = shiftedY - centerY;
117+
double rotatedX = centerX + dx * cos - dy * sin;
118+
double rotatedY = centerY + dx * sin + dy * cos;
119+
int drawX = (int) Math.round(rotatedX - scaledWidth / 2.0);
120+
int drawY = (int) Math.round(rotatedY - scaledHeight / 2.0);
121+
this.drawFeatureIcon(guiGraphics, texture, drawX, drawY, scaledWidth, scaledHeight, 0xFF_FFFFFF);
122+
}
123+
124+
FeatureWidget marker = this.getMarkerWidget();
125+
if (marker != null && marker.withinBounds()) {
126+
this.renderSingleIcon(guiGraphics, marker, translateX, translateY, centerX, centerY, cos, sin, 0xFF_FFFFFF, iconScale);
127+
}
128+
}
129+
130+
private void renderSingleIcon(GuiGraphics guiGraphics, FeatureWidget widget, double translateX, double translateY, double centerX, double centerY, double cos, double sin, int colour, double iconScale) {
131+
MapFeature.Texture texture = widget.texture();
132+
int scaledWidth = (int) Math.max(1, Math.round(texture.width() * iconScale));
133+
int scaledHeight = (int) Math.max(1, Math.round(texture.height() * iconScale));
134+
double baseCenterX = widget.drawX() + texture.width() / 2.0;
135+
double baseCenterY = widget.drawY() + texture.height() / 2.0;
136+
double shiftedX = baseCenterX + translateX;
137+
double shiftedY = baseCenterY + translateY;
138+
double dx = shiftedX - centerX;
139+
double dy = shiftedY - centerY;
140+
double rotatedX = centerX + dx * cos - dy * sin;
141+
double rotatedY = centerY + dx * sin + dy * cos;
142+
int drawX = (int) Math.round(rotatedX - scaledWidth / 2.0);
143+
int drawY = (int) Math.round(rotatedY - scaledHeight / 2.0);
144+
this.drawFeatureIcon(guiGraphics, texture, drawX, drawY, scaledWidth, scaledHeight, colour);
145+
}
146+
147+
private void drawCenterCross(GuiGraphics guiGraphics, double centerX, double centerY) {
148+
int cx = (int) Math.round(centerX);
149+
int cy = (int) Math.round(centerY);
150+
int crossHalf = 3;
151+
int color = 0xFF_FFFFFF;
152+
guiGraphics.fill(cx - crossHalf, cy, cx + crossHalf + 1, cy + 1, color);
153+
guiGraphics.fill(cx, cy - crossHalf, cx + 1, cy + crossHalf + 1, color);
31154
}
32155

33156
public void focusOn(BlockPos pos) {
@@ -39,6 +162,40 @@ public boolean isInitialized() {
39162
return this.initialized;
40163
}
41164

165+
@Override
166+
protected void applyDefaultZoom() {
167+
this.setPixelsPerBiome(this.readPixelsPerBiomeFromConfig());
168+
}
169+
170+
private void refreshPixelsPerBiome() {
171+
double configured = this.readPixelsPerBiomeFromConfig();
172+
if (Math.abs(configured - this.getPixelsPerBiome()) > 1.0E-4D) {
173+
this.setPixelsPerBiome(configured);
174+
}
175+
}
176+
177+
@Override
178+
protected double readPixelsPerBiomeFromConfig() {
179+
return Configs.SeedMapMinimapPixelsPerBiome;
180+
}
181+
182+
@Override
183+
protected void writePixelsPerBiomeToConfig(double pixelsPerBiome) {
184+
Configs.SeedMapMinimapPixelsPerBiome = pixelsPerBiome;
185+
}
186+
187+
@Override
188+
protected boolean shouldRenderChestLootWidget() {
189+
return false;
190+
}
191+
192+
@Override
193+
protected int getMapBackgroundTint() {
194+
float opacity = (float) Mth.clamp(Configs.SeedMapMinimapOpacity, 0.0D, 1.0D);
195+
int alpha = (int) Math.round(opacity * 255.0F);
196+
return (alpha << 24) | 0x00_FFFFFF;
197+
}
198+
42199
@Override
43200
protected boolean showCoordinateOverlay() {
44201
return false;

0 commit comments

Comments
 (0)