Skip to content

Commit c7ab5fe

Browse files
committed
Optimised SpawnRadiusHack rendering
1 parent 27671a5 commit c7ab5fe

2 files changed

Lines changed: 192 additions & 14 deletions

File tree

src/main/java/net/wurstclient/hacks/spawnradius/SpawnRadiusHack.java

Lines changed: 148 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,28 @@
77
*/
88
package net.wurstclient.hacks.spawnradius;
99

10+
import com.mojang.blaze3d.PrimitiveTopology;
11+
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
1012
import com.mojang.blaze3d.vertex.PoseStack;
13+
import com.mojang.blaze3d.vertex.VertexConsumer;
1114
import java.util.ArrayList;
1215
import java.util.List;
1316
import java.lang.reflect.Method;
17+
import net.minecraft.client.renderer.rendertype.RenderType;
1418
import net.minecraft.core.BlockPos;
1519
import net.minecraft.world.level.BaseSpawner;
1620
import net.minecraft.world.level.block.entity.SpawnerBlockEntity;
1721
import net.minecraft.world.phys.AABB;
1822
import net.minecraft.world.phys.Vec3;
1923
import net.wurstclient.Category;
24+
import net.wurstclient.WurstRenderLayers;
2025
import net.wurstclient.hack.Hack;
2126
import net.wurstclient.events.RenderListener;
2227
import net.wurstclient.events.UpdateListener;
2328
import net.wurstclient.nicewurst.NiceWurstModule;
2429
import net.wurstclient.util.BlockUtils;
30+
import net.wurstclient.util.EasyVertexBuffer;
31+
import net.wurstclient.util.RegionPos;
2532
import net.wurstclient.util.RenderUtils;
2633
import net.wurstclient.util.chunk.ChunkUtils;
2734

@@ -55,6 +62,13 @@ public final class SpawnRadiusHack extends Hack
5562

5663
private final List<SpawnerInfo> spawners = new ArrayList<>();
5764

65+
private EasyVertexBuffer buffer;
66+
private RegionPos bufferRegion = new RegionPos(0, 0);
67+
private long bufferSignature;
68+
private RegionPos pendingRegion = new RegionPos(0, 0);
69+
private long pendingSignature;
70+
private boolean dirty = true;
71+
5872
public SpawnRadiusHack()
5973
{
6074
super("SpawnRadius");
@@ -74,6 +88,8 @@ protected void onDisable()
7488
EVENTS.remove(UpdateListener.class, this);
7589
EVENTS.remove(RenderListener.class, this);
7690
spawners.clear();
91+
closeBuffer();
92+
dirty = true;
7793
}
7894

7995
@Override
@@ -98,6 +114,15 @@ public void onUpdate()
98114
spawners
99115
.add(new SpawnerInfo(pos.immutable(), radius, detected));
100116
});
117+
118+
RegionPos region = RegionPos.of(MC.player.blockPosition());
119+
long signature = signature(spawners, region);
120+
if(signature != bufferSignature)
121+
{
122+
pendingRegion = region;
123+
pendingSignature = signature;
124+
dirty = true;
125+
}
101126
}
102127

103128
@Override
@@ -106,37 +131,146 @@ public void onRender(PoseStack matrices, float partialTicks)
106131
if(MC.level == null || spawners.isEmpty())
107132
return;
108133

134+
if(NiceWurstModule.isActive())
135+
{
136+
renderCulled(matrices);
137+
return;
138+
}
139+
140+
if(dirty || buffer == null)
141+
rebuild();
142+
143+
if(buffer == null)
144+
return;
145+
146+
RenderType layer = WurstRenderLayers.getLines(false);
147+
matrices.pushPose();
148+
RenderUtils.applyRegionalRenderOffset(matrices, bufferRegion);
149+
buffer.draw(matrices, layer, 1, 1, 1, 1);
150+
matrices.popPose();
151+
}
152+
153+
private void renderCulled(PoseStack matrices)
154+
{
155+
List<AABB> boxes = new ArrayList<>();
109156
for(SpawnerInfo info : spawners)
110157
{
111158
Vec3 center = Vec3.atCenterOf(info.pos()).add(0, HEIGHT_OFFSET, 0);
112159
if(!NiceWurstModule.shouldRenderTarget(center))
113160
continue;
114161

115-
drawCircle(matrices, center, info.radius(), info.detected());
116-
// Draw a simple white ESP box on the spawner block itself
162+
int color = info.detected() ? COLOR_DETECTED : COLOR_IDLE;
163+
int segments = Math.max(32, info.radius() * 6);
164+
RenderUtils.drawCircle(matrices, center, info.radius(), segments,
165+
color, false);
166+
117167
AABB box = BlockUtils.getBoundingBox(info.pos());
118168
if(box != null)
119-
RenderUtils.drawOutlinedBoxes(matrices,
120-
java.util.Collections.singletonList(box), SPAWNER_BOX_COLOR,
121-
false);
169+
boxes.add(box);
122170
}
171+
172+
if(!boxes.isEmpty())
173+
RenderUtils.drawOutlinedBoxes(matrices, boxes, SPAWNER_BOX_COLOR,
174+
false);
175+
}
176+
177+
private void rebuild()
178+
{
179+
closeBuffer();
180+
bufferRegion = pendingRegion;
181+
bufferSignature = pendingSignature;
182+
dirty = false;
183+
184+
if(spawners.isEmpty())
185+
return;
186+
187+
List<SpawnerInfo> snapshot = new ArrayList<>(spawners);
188+
RegionPos region = bufferRegion;
189+
buffer = EasyVertexBuffer.createAndUpload(PrimitiveTopology.LINES,
190+
DefaultVertexFormat.POSITION_COLOR_NORMAL_LINE_WIDTH, b -> {
191+
for(SpawnerInfo info : snapshot)
192+
buildSpawner(b, info, region);
193+
});
123194
}
124195

125-
private static void drawCircle(PoseStack matrices, Vec3 center, int radius,
126-
boolean detected)
196+
private static void buildSpawner(VertexConsumer b, SpawnerInfo info,
197+
RegionPos region)
127198
{
128-
int color = detected ? COLOR_DETECTED : COLOR_IDLE;
199+
int radius = info.radius();
200+
double cx = info.pos().getX() + 0.5 - region.x();
201+
double cy = info.pos().getY() + 0.5 + HEIGHT_OFFSET;
202+
double cz = info.pos().getZ() + 0.5 - region.z();
203+
int color = info.detected() ? COLOR_DETECTED : COLOR_IDLE;
204+
129205
int segments = Math.max(32, radius * 6);
130206
double step = (Math.PI * 2) / segments;
131-
Vec3 prev = center.add(radius, 0, 0);
132-
207+
float py = (float)cy;
208+
float px = (float)(cx + radius);
209+
float pz = (float)cz;
133210
for(int i = 1; i <= segments; i++)
134211
{
135212
double angle = i * step;
136-
Vec3 next = center.add(Math.cos(angle) * radius, 0,
137-
Math.sin(angle) * radius);
138-
RenderUtils.drawLine(matrices, prev, next, color, false);
139-
prev = next;
213+
float nx = (float)(cx + Math.cos(angle) * radius);
214+
float nz = (float)(cz + Math.sin(angle) * radius);
215+
RenderUtils.drawLine(b, px, py, pz, nx, py, nz, color);
216+
px = nx;
217+
pz = nz;
218+
}
219+
220+
float x1 = info.pos().getX() - region.x();
221+
float x2 = x1 + 1;
222+
float y1 = info.pos().getY();
223+
float y2 = y1 + 1;
224+
float z1 = info.pos().getZ() - region.z();
225+
float z2 = z1 + 1;
226+
addBoxOutline(b, x1, y1, z1, x2, y2, z2, SPAWNER_BOX_COLOR);
227+
}
228+
229+
private static void addBoxOutline(VertexConsumer b, float x1, float y1,
230+
float z1, float x2, float y2, float z2, int color)
231+
{
232+
RenderUtils.drawLine(b, x1, y1, z1, x2, y1, z1, color);
233+
RenderUtils.drawLine(b, x2, y1, z1, x2, y1, z2, color);
234+
RenderUtils.drawLine(b, x2, y1, z2, x1, y1, z2, color);
235+
RenderUtils.drawLine(b, x1, y1, z2, x1, y1, z1, color);
236+
RenderUtils.drawLine(b, x1, y2, z1, x2, y2, z1, color);
237+
RenderUtils.drawLine(b, x2, y2, z1, x2, y2, z2, color);
238+
RenderUtils.drawLine(b, x2, y2, z2, x1, y2, z2, color);
239+
RenderUtils.drawLine(b, x1, y2, z2, x1, y2, z1, color);
240+
RenderUtils.drawLine(b, x1, y1, z1, x1, y2, z1, color);
241+
RenderUtils.drawLine(b, x2, y1, z1, x2, y2, z1, color);
242+
RenderUtils.drawLine(b, x2, y1, z2, x2, y2, z2, color);
243+
RenderUtils.drawLine(b, x1, y1, z2, x1, y2, z2, color);
244+
}
245+
246+
/*
247+
* Cheap order-independent hash-combine,
248+
* fingerprints current spawner set to cheaply detect changes since last GPU upload
249+
*/
250+
private static long signature(List<SpawnerInfo> list, RegionPos region)
251+
{
252+
long set = 0;
253+
for(SpawnerInfo info : list)
254+
{
255+
long h = info.pos().asLong() * 1099511628211L; // FNV prime
256+
h ^= info.radius() * 31L;
257+
h ^= info.detected() ? 0x9E3779B97F4A7C15L : 0L; // golden-ratio constant
258+
set ^= h; // XOR accumulate
259+
}
260+
261+
long sig = region.x();
262+
sig = sig * 31 + region.z();
263+
sig = sig * 31 + list.size();
264+
sig = sig * 31 + set;
265+
return sig;
266+
}
267+
268+
private void closeBuffer()
269+
{
270+
if(buffer != null)
271+
{
272+
buffer.close();
273+
buffer = null;
140274
}
141275
}
142276

src/main/java/net/wurstclient/util/RenderUtils.java

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -661,6 +661,50 @@ public static void drawLine(VertexConsumer buffer, float x1, float y1,
661661
.setLineWidth(DEFAULT_LINE_WIDTH);
662662
}
663663

664+
public static void drawCircle(PoseStack matrices, Vec3 center,
665+
double radius, int segments, int color, boolean depthTest)
666+
{
667+
if(segments < 3 || radius <= 0)
668+
return;
669+
670+
depthTest = NiceWurstModule.enforceDepthTest(depthTest);
671+
Vec3 c = center.add(getCameraPos().reverse());
672+
double step = (Math.PI * 2) / segments;
673+
674+
GlobalEspManager globalEsp = GlobalEspManager.getInstance();
675+
if(globalEsp.shouldTakeOverRenderCalls())
676+
{
677+
Vec3 prev = c.add(radius, 0, 0);
678+
for(int i = 1; i <= segments; i++)
679+
{
680+
double angle = i * step;
681+
Vec3 next = c.add(Math.cos(angle) * radius, 0,
682+
Math.sin(angle) * radius);
683+
globalEsp.submitLine(matrices, prev, next, color, depthTest,
684+
DEFAULT_LINE_WIDTH);
685+
prev = next;
686+
}
687+
688+
return;
689+
}
690+
691+
WurstBufferSource vcp = getVCP();
692+
RenderType layer = WurstRenderLayers.getLines(depthTest);
693+
VertexConsumer buffer = vcp.getBuffer(layer);
694+
695+
Vec3 prev = c.add(radius, 0, 0);
696+
for(int i = 1; i <= segments; i++)
697+
{
698+
double angle = i * step;
699+
Vec3 next =
700+
c.add(Math.cos(angle) * radius, 0, Math.sin(angle) * radius);
701+
drawLine(matrices, buffer, prev, next, color, DEFAULT_LINE_WIDTH);
702+
prev = next;
703+
}
704+
705+
vcp.endBatch(layer);
706+
}
707+
664708
public static void drawCurvedLine(PoseStack matrices, List<Vec3> points,
665709
int color, boolean depthTest)
666710
{

0 commit comments

Comments
 (0)