Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
0afb9e7
Started Particles
Mark7625 Feb 23, 2026
d656508
optimizations
Mark7625 Feb 23, 2026
7b51cd7
Simple Scene Pass System
Mark7625 Feb 24, 2026
b774222
Removed getLastParticleDrawn from zone render
Mark7625 Feb 24, 2026
7a9c9dc
Cleaned up
Mark7625 Feb 24, 2026
ee7269c
Add Test Command
Mark7625 Feb 24, 2026
38bfba6
Improved startup logging
Mark7625 Feb 24, 2026
05b427f
Moved Spawning logic to on scene load
Mark7625 Feb 24, 2026
38d1c2e
optimizations
Mark7625 Feb 24, 2026
45cbb3c
SoA particle data and Batch/spatial culling
Mark7625 Feb 24, 2026
e1acd00
Pooling
Mark7625 Feb 24, 2026
df702dd
Make loadSceneParticles use the job system
Mark7625 Feb 24, 2026
9be56e4
Improved How Particles Look and speed and added float precision
Mark7625 Feb 24, 2026
f70151c
,
Mark7625 Feb 24, 2026
f6c3d3a
Add Basic support for attaching to objects fully
Mark7625 Feb 25, 2026
271a8fa
Some Custom Particles
Mark7625 Feb 25, 2026
75aefb9
Update ParticleManager.java
Mark7625 Feb 25, 2026
ad501f3
Sync
Mark7625 Feb 25, 2026
f8474ba
Merge branch 'master' into particles
Mark7625 Feb 25, 2026
033889e
Delete 117HD.log
Mark7625 Feb 25, 2026
4d4870f
Added some imports
Mark7625 Feb 25, 2026
152732e
Fix particle texture conflicts by batching draws per texture
Mark7625 Feb 25, 2026
6bc279f
Temp Shadow Fix
Mark7625 Feb 25, 2026
a831949
Gpu Particle Optimizations
Mark7625 Feb 25, 2026
daa6e5f
Add some optimizations to update
Mark7625 Feb 25, 2026
584ea15
Fixed Offsets
Mark7625 Feb 26, 2026
f83c905
Very basic flipbook support
Mark7625 Feb 26, 2026
54fd110
Another Debug
Mark7625 Feb 26, 2026
8c2a4b5
Particle Definition Cleanup
Mark7625 Feb 26, 2026
408513f
New Debug Tools
Mark7625 Feb 26, 2026
8c6e0f1
optimizations
Mark7625 Feb 27, 2026
03fcd28
Use Lombok
Mark7625 Feb 27, 2026
8b3fbef
Scene ambient light
Mark7625 Feb 28, 2026
e825737
Three instance buffers
Mark7625 Feb 28, 2026
21487a9
improved Debugging
Mark7625 Feb 28, 2026
44d9fe3
Basic Weather
Mark7625 Mar 1, 2026
1e7635a
Merge branch 'master' into particles
Mark7625 Mar 1, 2026
a0cdc4b
added a option to always spawn top of world
Mark7625 Mar 2, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ plugins {
}

repositories {
mavenLocal()
maven {
url = 'https://repo.runelite.net'
}
Expand Down
73 changes: 70 additions & 3 deletions src/main/java/rs117/hd/HdPlugin.java
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,11 @@
import net.runelite.client.plugins.PluginDescriptor;
import net.runelite.client.plugins.PluginManager;
import net.runelite.client.plugins.entityhider.EntityHiderPlugin;
import net.runelite.client.ui.ClientToolbar;
import net.runelite.client.ui.ClientUI;
import net.runelite.client.ui.NavigationButton;
import net.runelite.client.ui.components.colorpicker.ColorPickerManager;
import net.runelite.client.util.ImageUtil;
import net.runelite.client.util.LinkBrowser;
import net.runelite.client.util.OSType;
import net.runelite.rlawt.AWTContext;
Expand Down Expand Up @@ -110,6 +114,9 @@
import rs117.hd.scene.ModelOverrideManager;
import rs117.hd.scene.ProceduralGenerator;
import rs117.hd.scene.SceneContext;
import rs117.hd.scene.particles.ParticleManager;
import rs117.hd.scene.particles.debug.ParticleGizmoOverlay;
import rs117.hd.scene.particles.debug.ParticleSidebarPanel;
import rs117.hd.scene.TextureManager;
import rs117.hd.scene.TileOverrideManager;
import rs117.hd.scene.WaterTypeManager;
Expand Down Expand Up @@ -165,6 +172,7 @@ public class HdPlugin extends Plugin {
public static final int TEXTURE_UNIT_SHADOW_MAP = GL_TEXTURE0 + TEXTURE_UNIT_COUNT++;
public static final int TEXTURE_UNIT_TILE_HEIGHT_MAP = GL_TEXTURE0 + TEXTURE_UNIT_COUNT++;
public static final int TEXTURE_UNIT_TILED_LIGHTING_MAP = GL_TEXTURE0 + TEXTURE_UNIT_COUNT++;
public static final int TEXTURE_UNIT_PARTICLE = GL_TEXTURE0 + TEXTURE_UNIT_COUNT++;

public static int MAX_IMAGE_UNITS;
public static int IMAGE_UNIT_COUNT = 0;
Expand Down Expand Up @@ -224,14 +232,15 @@ public class HdPlugin extends Plugin {
TextureManager.class,
TileOverrideManager.class,
WaterTypeManager.class,
SceneManager.class
SceneManager.class,
ParticleManager.class
);

@Getter
private Gson gson;

@Inject
private Client client;
public Client client;

@Inject
private ClientUI clientUI;
Expand Down Expand Up @@ -287,6 +296,20 @@ public class HdPlugin extends Plugin {
@Inject
private DeveloperTools developerTools;

@Inject
private ClientToolbar clientToolbar;

@Getter
private ParticleSidebarPanel particleSidebarPanel;

@Inject
private ParticleGizmoOverlay particleGizmoOverlay;

@Inject
private ColorPickerManager colorPickerManager;

private NavigationButton particleNavButton;

@Inject
private FrameTimer frameTimer;

Expand All @@ -296,6 +319,10 @@ public class HdPlugin extends Plugin {
@Inject
private SceneManager sceneManager;

@Getter
@Inject
private ParticleManager particleManager;

@Inject
private JobSystem jobSystem;

Expand Down Expand Up @@ -420,6 +447,7 @@ public class HdPlugin extends Plugin {
public ShadingMode configShadingMode;
public ColorFilter configColorFilter = ColorFilter.NONE;
public ColorFilter configColorFilterPrevious;
public boolean configParticleAmbientLight;

public boolean useLowMemoryMode;
public boolean enableDetailedTimers;
Expand Down Expand Up @@ -690,11 +718,14 @@ protected void startUp() {
tileOverrideManager.startUp();
modelOverrideManager.startUp();
lightManager.startUp();
particleManager.startUp();
environmentManager.startUp();
fishingSpotReplacer.startUp();
gammaCalibrationOverlay.initialize();
npcDisplacementCache.initialize();

isActive = true;
updateCachedConfigs();
hasLoggedIn = client.getGameState().getState() > GameState.LOGGING_IN.getState();
redrawPreviousFrame = false;
skipScene = null;
Expand All @@ -706,6 +737,20 @@ protected void startUp() {
checkGLErrors();

clientThread.invokeLater(this::displayUpdateMessage);

SwingUtilities.invokeLater(() -> {
particleSidebarPanel = new ParticleSidebarPanel(this, particleManager, clientThread, client, colorPickerManager, particleGizmoOverlay);
if (particleNavButton == null) {
BufferedImage icon = ImageUtil.loadImageResource(HdPlugin.class, "icon.png");
particleNavButton = NavigationButton.builder()
.tooltip("117 HD Particles")
.icon(icon)
.panel(particleSidebarPanel)
.build();
clientToolbar.addNavigation(particleNavButton);
}
});

} catch (Throwable err) {
log.error("Error while starting 117 HD", err);
stopPlugin();
Expand Down Expand Up @@ -753,10 +798,17 @@ protected void shutDown() {
}

developerTools.deactivate();
tileOverrideManager.shutDown();
particleGizmoOverlay.setActive(false);
SwingUtilities.invokeLater(() -> {
if (particleNavButton != null) {
clientToolbar.removeNavigation(particleNavButton);
particleNavButton = null;
}
});
groundMaterialManager.shutDown();
modelOverrideManager.shutDown();
lightManager.shutDown();
particleManager.shutDown();
environmentManager.shutDown();
fishingSpotReplacer.shutDown();
areaManager.shutDown();
Expand Down Expand Up @@ -818,6 +870,18 @@ public SceneContext getSceneContext() {
return renderer == null ? null : renderer.getSceneContext();
}

/** Open the particle sidebar panel to the Particles tab and select the given particle definition. */
public void openParticleConfig(String particleId) {
SwingUtilities.invokeLater(() -> {
if (particleNavButton != null) {
clientToolbar.openPanel(particleNavButton);
}
if (particleSidebarPanel != null && particleId != null) {
particleSidebarPanel.openToParticleConfig(particleId);
}
});
}

public void toggleFreezeFrame() {
clientThread.invoke(() -> {
enableFreezeFrame = !enableFreezeFrame;
Expand Down Expand Up @@ -869,6 +933,7 @@ public ShaderIncludes getShaderIncludes() {
.define("UI_SCALING_MODE", config.uiScalingMode())
.define("COLOR_BLINDNESS", config.colorBlindness())
.define("APPLY_COLOR_FILTER", configColorFilter != ColorFilter.NONE)
.define("GLOBAL_PARTICLE_AMBIENT_LIGHT", config.particleAmbientLight())
.define("MATERIAL_COUNT", MaterialManager.MATERIALS.length)
.define("WATER_TYPE_COUNT", waterTypeManager.uboWaterTypes.getCount())
.define("DYNAMIC_LIGHTS", configDynamicLights != DynamicLights.NONE)
Expand Down Expand Up @@ -1634,6 +1699,7 @@ private void updateCachedConfigs() {
configHideVanillaWaterEffects = config.hideVanillaWaterEffects();
configSeasonalTheme = config.seasonalTheme();
configSeasonalHemisphere = config.seasonalHemisphere();
configParticleAmbientLight = config.particleAmbientLight();

var newColorFilter = config.colorFilter();
if (newColorFilter != configColorFilter) {
Expand Down Expand Up @@ -1779,6 +1845,7 @@ public void processPendingConfigChanges() {
case KEY_WIREFRAME:
case KEY_SHADOW_FILTERING:
case KEY_WINDOWS_HDR_CORRECTION:
case KEY_PARTICLE_AMBIENT_LIGHT:
recompilePrograms = true;
break;
case KEY_ANTI_ALIASING_MODE:
Expand Down
28 changes: 25 additions & 3 deletions src/main/java/rs117/hd/HdPluginConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -785,12 +785,34 @@ default boolean characterDisplacement() {
)
default boolean hideVanillaWaterEffects() { return true; }

/*====== Particles settings ======*/

@ConfigSection(
name = "Particles",
description = "Particle effect settings",
position = 4,
closedByDefault = true
)
String particlesSettings = "particlesSettings";

String KEY_PARTICLE_AMBIENT_LIGHT = "particleAmbientLight";
@ConfigItem(
keyName = KEY_PARTICLE_AMBIENT_LIGHT,
name = "Scene ambient light",
description = "Apply scene ambient lighting to particles so they match the area's light and color.",
section = particlesSettings,
position = 0
)
default boolean particleAmbientLight() {
return true;
}

/*====== Miscellaneous settings ======*/

@ConfigSection(
name = "Miscellaneous",
description = "Miscellaneous settings",
position = 4,
position = 5,
closedByDefault = true
)
String miscellaneousSettings = "miscellaneousSettings";
Expand Down Expand Up @@ -944,7 +966,7 @@ default boolean windowsHdrCorrection() {
@ConfigSection(
name = "Legacy",
description = "Legacy options. If you dislike a change, you might find an option to change it back here.",
position = 5,
position = 6,
closedByDefault = true
)
String legacySettings = "legacySettings";
Expand Down Expand Up @@ -1097,7 +1119,7 @@ default boolean legacyTzHaarReskin() {
@ConfigSection(
name = "Experimental",
description = "Experimental features - if you're experiencing issues you should consider disabling these.",
position = 6,
position = 7,
closedByDefault = true
)
String experimentalSettings = "experimentalSettings";
Expand Down
30 changes: 30 additions & 0 deletions src/main/java/rs117/hd/opengl/shader/ParticleShaderProgram.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright (c) 2025, Hooder <ahooder@protonmail.com>
* All rights reserved.
*/
package rs117.hd.opengl.shader;

import java.io.IOException;
import org.lwjgl.opengl.GL33C;
import rs117.hd.opengl.shader.ShaderException;
import rs117.hd.opengl.shader.ShaderIncludes;

public class ParticleShaderProgram extends ShaderProgram {
private ShaderProgram.UniformTexture uParticleTexture;

public ParticleShaderProgram() {
super(t -> t
.add(GL33C.GL_VERTEX_SHADER, "particle_vert.glsl")
.add(GL33C.GL_FRAGMENT_SHADER, "particle_frag.glsl"));
}

@Override
protected void initialize() {
uParticleTexture = addUniformTexture("uParticleTexture");
}

public void setParticleTextureUnit(int textureUnit) {
if (uParticleTexture != null && isValid())
uParticleTexture.set(textureUnit);
}
}
45 changes: 41 additions & 4 deletions src/main/java/rs117/hd/overlays/FrameTimerOverlay.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
import net.runelite.client.ui.overlay.components.LineComponent;
import net.runelite.client.ui.overlay.components.TitleComponent;
import rs117.hd.HdPlugin;
import rs117.hd.renderer.zone.pass.impl.ParticlePass;
import rs117.hd.scene.particles.ParticleManager;
import rs117.hd.renderer.zone.SceneManager;
import rs117.hd.renderer.zone.WorldViewContext;
import rs117.hd.renderer.zone.ZoneRenderer;
Expand Down Expand Up @@ -50,6 +52,12 @@ public class FrameTimerOverlay extends OverlayPanel implements FrameTimer.Listen
@Inject
private SceneManager sceneManager;

@Inject
private ParticleManager particleManager;

@Inject
private ParticlePass particlePass;

private final ArrayDeque<FrameTimings> frames = new ArrayDeque<>();
private final long[] timings = new long[Timer.TIMERS.length];
private float cpuLoad;
Expand Down Expand Up @@ -168,10 +176,12 @@ public Dimension render(Graphics2D g) {

if (plugin.getSceneContext() != null) {
var sceneContext = plugin.getSceneContext();
children.add(LineComponent.builder()
.left("Lights:")
.right(format("%d/%d", sceneContext.numVisibleLights, sceneContext.lights.size()))
.build());

children.add(LineComponent.builder()
.left("Lights:")
.right(String.format("%d/%d", sceneContext.numVisibleLights, sceneContext.lights.size()))
.build());

}

if (plugin.renderer instanceof ZoneRenderer) {
Expand Down Expand Up @@ -206,6 +216,33 @@ public Dimension render(Graphics2D g) {
.build());
}

children.add(LineComponent.builder()
.leftFont(boldFont)
.left("Particle stats:")
.build());
int totalEmitters = particleManager.getSceneEmitters().size();
children.add(LineComponent.builder()
.left("Emitters Updating:")
.right(String.format("%d/%d", particleManager.getLastEmittersUpdating(), totalEmitters))
.build());
children.add(LineComponent.builder()
.left("Emitters (culled):")
.right(String.valueOf(particleManager.getLastEmittersCulled()))
.build());
if (plugin.renderer instanceof ZoneRenderer) {
int drawn = particlePass.getLastParticleDrawn();
int totalOnPlane = particlePass.getLastParticleTotalOnPlane();
int culled = particlePass.getLastParticleCulledDistance() + particlePass.getLastParticleCulledFrustum();
children.add(LineComponent.builder()
.left("Particles (drawn):")
.right(String.format("%d/%d", drawn, totalOnPlane))
.build());
children.add(LineComponent.builder()
.left("Particles (culled):")
.right(String.valueOf(culled))
.build());
}

children.add(LineComponent.builder()
.leftFont(boldFont)
.left("Streaming Stats:")
Expand Down
31 changes: 31 additions & 0 deletions src/main/java/rs117/hd/overlays/TileInfoOverlay.java
Original file line number Diff line number Diff line change
Expand Up @@ -1002,6 +1002,37 @@ public Polygon getCanvasTilePoly(@Nonnull Client client, SceneContext ctx, Tile
return getCanvasTilePoly(client, ctx, l.getX(), l.getY(), tile.getPlane());
}

/**
* Draws a filled rectangle for a world AABB. Uses same projection as tile outlines (localToCanvas).
* Call for weather area debug overlay.
*/
public void drawFilledWorldAabb(Graphics2D g, SceneContext ctx, AABB worldAabb, int plane, Color fillColor) {
if (ctx == null || ctx.sceneBase == null || !ctx.intersects(worldAabb))
return;
int x1 = (worldAabb.minX - ctx.sceneBase[0]) * LOCAL_TILE_SIZE;
int y1 = (worldAabb.minY - ctx.sceneBase[1]) * LOCAL_TILE_SIZE;
int x2 = (worldAabb.maxX + 1 - ctx.sceneBase[0]) * LOCAL_TILE_SIZE;
int y2 = (worldAabb.maxY + 1 - ctx.sceneBase[1]) * LOCAL_TILE_SIZE;
int[][] corners = {{x1, y1}, {x2, y1}, {x2, y2}, {x1, y2}};
int[] polyX = new int[4];
int[] polyY = new int[4];
int valid = 0;
for (int i = 0; i < 4; i++) {
int z = getHeight(ctx, corners[i][0], corners[i][1], plane);
float[] p = localToCanvas(client, corners[i][0], corners[i][1], z);
if (p != null) {
polyX[valid] = round(p[0]);
polyY[valid] = round(p[1]);
valid++;
}
}
if (valid >= 3) {
setAntiAliasing(g, true);
g.setColor(fillColor);
g.fillPolygon(polyX, polyY, valid);
}
}

public Polygon getCanvasTilePoly(@Nonnull Client client, SceneContext ctx, int... sceneXYplane) {
final int wx = sceneXYplane[0] * LOCAL_TILE_SIZE;
final int sy = sceneXYplane[1] * LOCAL_TILE_SIZE;
Expand Down
Loading