Fractal Softworks Forum

Please login or register.

Login with username, password and session length
Pages: [1] 2

Author Topic: [0.97a] Particle Engine (4/2/24)  (Read 32561 times)

float

  • Captain
  • ****
  • Posts: 267
    • View Profile
[0.97a] Particle Engine (4/2/24)
« on: April 10, 2023, 06:49:27 PM »

Particle Engine
 


A library that implements a stateless, instanced particle system.

What is this?
This is a utility mod and a tool. Downloading it and adding it to your game as-is does nothing. It is intended to provide a fast, relatively simple, and relatively up-to-date system for generating, allocating, and rendering particles.
[close]

Why does this exist?
Vanilla particles are CPU-bound (individual particles are simulated on the CPU), and essentially the entire game only utilizes a single CPU core. Furthermore, each particle has its own rendering loop. To keep performance reasonable, vanilla Starsector has a fairly low particle limit, after which old particles start getting replaced.

This mod uses slightly more modern (but not actually all that modern) techniques to minimize CPU strain. Most of the work, including simulation, is done on the GPU. The CPU doesn't keep track of individual particles. The particle engine is a singleton object (accessed via static methods) that performs a single draw call per particle type (every particle with the same sprite and blend mode has the same type) per frame. The result is a much more performant particle system.

The part of the vanilla particle system that is exposed and usable by modders is highly limited. As a modder, you're essentially only easily able to add a few types of particles, all via i.e. Global.getCombatEngine().addSmoothParticle. It's also possible to use a CombatEntityPluginWithParticles, though that has its own limitations -- you're forced to use a 4x4 sprite sheet, the plugin has its own state that must be initialized and updated, and you have to set the properties of each particle using a prev pointer. It also seems to be partially broken, i.e. endSizeMult doesn't actually seem to work, and particles either grow or shrink at a fixed rate.
[close]

How do I use this?
The starting point of all particle generation is the Particles class. There are essentially just three steps:
  • Create an emitter using Particles.initialize. This returns a default Emitter object.
  • Set the emitter's properties. Here you can set and randomize particles' positions, velocities, accelerations, sizes, colors, shift their colors over time, add radial motion, revolution, and so on.
  • Generate the particles using Particles.burst or Particles.stream, passing in an emitter whose properties you want to use.
There is also a javadoc bundled with the zip download that explains the function of each class and method in more detail.
[close]

What are the limitations?
Because particles are simulated on the GPU, they are also stateless (technically it's possible to store state in, e.g. an SSBO, but that's not relevant for now). This means that they don't interact with the game world -- once they're generated, their trajectories are fixed. As a result, particles won't be able to interact with ships or projectiles in either direction -- they can't affect the game state and they can't be affected by the game state. Still, because emitters are managed by the CPU, it's possible to modify emitters according to the game state, so a large variety of effects are nevertheless possible.

Performance is GPU dependent, so PCs with poor graphics cards or low VRAM might experience stuttering or drop outs with obscene numbers of particles. GPU code is also finicky, so with this early version you may run into system-specific issues or bugs.
[close]

Additional features
It's possible to read and write emitter properties from and to JSON files. To read an emitter from a JSON file, simply use Emitter.fromJSON in conjunction with Global.getSettings().loadJSON. For an example emitter JSON, see data/particleengine/sample_emitter.json.

Writing emitters to file is a bit tricker due to file IO limitations put in place by Starsector's custom class loader. The particleengine.json file in the mod's root directory contains the directory that saved emitters should be written to. By default, this is ../saved_emitters, i.e. the saved_emitters directory in the Starsector root. If this directory doesn't exist, you will have to create it manually before any write operations will work.

The JSON functionality can be a valuable tool when designing particle effects as changes to the JSON file can be effected without having to restart Starsector. In released code, it's not recommended to use writeJSON, and, if intending to use readJSON, the files should be batch-read at some initialization stage and cached for later reuse.
[close]

Custom emitters
While the default Emitter can generate a variety of effects already, it is possible to make your own emitters by implementing IEmitter, or extending the BaseIEmitter class. This is helpful in creating emitters with inter-particle dependencies, i.e. particles whose properties depend on other properties in a non-random way.

You will have to implement the initParticle function, which takes in the index of the particle in the burst or stream and outputs a ParticleData that is passed into the vertex shader. The below sample emitter replicates the vanilla muzzle flash visual, whereby particles' velocities are directed outwards and dependent on their initial distance from the emitter origin:

Muzzle flash emitter code

public class MuzzleFlashEmitter extends BaseIEmitter {

    private final Vector2f location;
    private float angle, arc, range, minLife, maxLife, minSize, maxSize, velocityScale;
    private final float[] color = new float[] {1f, 1f, 1f, 1f};
    private CombatEntityAPI anchor;

    public MuzzleFlashEmitter() {
        location = new Vector2f();
        angle = arc = range = 0f;
        minLife = maxLife = 0.5f;
        minSize = 20f;
        maxSize = 30f;
        velocityScale = 1f;
    }

    @Override
    public SpriteAPI getSprite() {
        return particleengine.Utils.getLoadedSprite("graphics/fx/particlealpha64sq.png");
    }

    public MuzzleFlashEmitter anchor(CombatEntityAPI anchor) {
        this.anchor = anchor;
        return this;
    }

    public MuzzleFlashEmitter location(Vector2f location) {
        this.location.set(location);
        return this;
    }

    public MuzzleFlashEmitter angle(float angle) {
        this.angle = angle;
        return this;
    }

    public MuzzleFlashEmitter arc(float arc) {
        this.arc = arc;
        return this;
    }

    public MuzzleFlashEmitter range(float range) {
        this.range = range;
        return this;
    }

    public MuzzleFlashEmitter life(float minLife, float maxLife) {
        this.minLife = minLife;
        this.maxLife = maxLife;
        return this;
    }

    public MuzzleFlashEmitter size(float minSize, float maxSize) {
        this.minSize = minSize;
        this.maxSize = maxSize;
        return this;
    }

    public MuzzleFlashEmitter color(float r, float g, float b, float a) {
        color[0] = r;
        color[1] = g;
        color[2] = b;
        color[3] = a;
        return this;
    }

    public MuzzleFlashEmitter velocityScale(float velocityScale) {
        this.velocityScale = velocityScale;
        return this;
    }

    @Override
    public Vector2f getLocation() {
        return location;
    }

    @Override
    protected ParticleData initParticle(int i) {
        ParticleData data = new ParticleData();

        // Life uniformly random between minLife and maxLife
        float life = randBetween(minLife, maxLife);
        data.life(life).fadeTime(0f, life);

        float theta = angle + randBetween(-arc / 2f, arc / 2f);
        float r = range * (float) Math.sqrt(Misc.random.nextFloat());
        Vector2f pt = new Vector2f(r*(float)Math.cos(theta*Misc.RAD_PER_DEG), r*(float)Math.sin(theta*Misc.RAD_PER_DEG));
        // Velocity is proportional to distance from center
        Vector2f vel = Misc.getUnitVectorAtDegreeAngle(theta);
        vel.scale(velocityScale);
        vel.scale(r);
        // Add the anchor's velocity, if it exists
        if (anchor != null) {
            Vector2f.add(anchor.getVelocity(), vel, vel);
        }
        data.offset(pt).velocity(vel);

        // Size uniformly random between minSize and maxSize
        float size = randBetween(minSize, maxSize);
        data.size(size, size);

        // Color
        data.color(color);

        return data;
    }

    public float randBetween(float a, float b) {
        return Misc.random.nextFloat() * (b - a) + a;
    }
}

[close]

[close]

Sample emitters with code
Ion explosion
Effect

[close]
Code
       
        Emitter emitter = Particles.initialize(pt, "graphics/fx/explosion1.png");
        emitter.circleOffset(30f, 50f);
        emitter.life(0.75f, 1.25f);
        emitter.fadeTime(0.1f, 0.1f, 0.3f, 0.5f);
        emitter.facing(0f, 360f);
        emitter.turnRate(-50f, 50f);
        emitter.turnAcceleration(-50f, 50f);
        emitter.circleVelocity(100f, 100f);
        emitter.radialAcceleration(-50f, -100f);
        emitter.revolutionRate(-20f, 20f);
        emitter.color(colorIn);
        emitter.randomHSVA(20f, 0.1f, 0f, 0f);
        emitter.colorShiftHSVA(0f, -0.3f, -0.3f, -0.05f);
        emitter.size(150f, 200f);
        emitter.growthRate(100f, 150f);
        emitter.growthAcceleration(-50f, -75f);
        Particles.burst(emitter, 50);

        Emitter ringEmitter = Particles.initialize(pt, "graphics/fx/custom_ring.png"); // Just a simple white ring
        ringEmitter.setAxis(proj.getFacing());
        ringEmitter.life(0.75f, 1f);
        ringEmitter.fadeTime(0.1f, 0.1f, 0.5f, 0.7f);
        ringEmitter.size(300f, 400f, 40f, 60f);
        ringEmitter.growthRate(400f, 500f);
        ringEmitter.growthAcceleration(-50f, -60f);
        ringEmitter.color(ringColorIn);
        ringEmitter.hueShift(-50f, 50f);
        ringEmitter.saturationShift(-0.2f, -0.2f);
        ringEmitter.facing(-55f, -35f);
        Particles.burst(ringEmitter, 10);
        ringEmitter.facing(35f, 55f);
        Particles.burst(ringEmitter, 10);

        Emitter arcEmitter = Particles.initialize(pt, "graphics/fx/custom_emp_arcs.png"); // Just emp_arcs.png with a darkening filter applied to border
        arcEmitter.life(0.25f, 0.3f);
        arcEmitter.fadeTime(0f, 0f, 0.15f, 0.25f);
        arcEmitter.size(350f, 350f, 300f, 300f);
        arcEmitter.growthRate(-40f, -80f);
        arcEmitter.turnRate(-10f, 10f);
        arcEmitter.facing(0f, 360f);
        arcEmitter.color(0.7f, 1f, 1f, 0.7f);
        arcEmitter.facing(0f, 360f);
        arcEmitter.alphaShift(-0.5f, -0.5f);
        Particles.stream(arcEmitter, 1, 50, 0.75f);

[close]
[close]

Simple trail
Effect

[close]
Code

        Emitter emitter = Particles.initialize(proj.getLocation(), "graphics/fx/particlealpha_textured.png");
        emitter.life(1f, 1.2f);
        emitter.fadeTime(0f, 0f, 0.5f, 0.5f);
        emitter.offset(-50f, -5f, 0f, 0f);
        emitter.velocity(-40f, -20f, 0f, 0f);
        emitter.color(1f, 0f, 1f, 0.8f);
        emitter.randomHSVA(40f, 0.2f, 0f, 0f);
        emitter.saturationShift(-1f, -1.5f);
        emitter.alphaShift(-0.6f, -0.6f);
        emitter.size(80f, 80f, 20f, 20f);
        emitter.growthRate(-50f, -50f, -8f, -8f);
        Particles.stream(emitter, 1, 100f, 5f, new Particles.StreamAction() {
            @Override
            public boolean apply(Emitter emitter) {
                // Set the emission location and axis to match the projectile's right before each burst of particles is generated.
                emitter.setLocation(proj.getLocation());
                emitter.setAxis(proj.getFacing());
                return !proj.isExpired();
            }
        });

[close]
[close]

Stress test with 1 million particles

WARNING: 55MB gif
Effect

[close]

Code
        Emitter emitter = Particles.initialize(pt);
        emitter.setSyncSize(true);
        emitter.setBlendMode(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL14.GL_FUNC_ADD);
        emitter.life(10f, 10f);
        emitter.fadeTime(0f, 0f, 0.5f, 0.5f);
        emitter.circleOffset(0f, 30f);
        emitter.colorHSVA(new float[] {0f, 0.5f, 1f, 1f});
        emitter.size(10f, 25f, 10f, 25f);
        emitter.growthRate(-10f, 10f, -10f, 10f);
        emitter.randomHSVA(360f, 1f, 0f, 0f);
        emitter.revolutionRate(-30f, 30f);
        emitter.colorShiftHSVA(-200f, 200f, -0.1f, 0.1f, -0.1f, 0.1f, -0.1f, -0.1f);
        final float[] amp = {200f};
        emitter.sinusoidalMotionX(-amp[0], amp[0], 0.25f, 0.25f, 0f, 0f);
        emitter.sinusoidalMotionY(-amp[0], amp[0], 0.25f, 0.25f, 0f, 0f);
        Particles.stream(emitter, 1000, 100000, 10f, new Particles.StreamAction() {
            @Override
            public boolean apply(Emitter emitter) {
                amp[0] += 2f;
                emitter.sinusoidalMotionX(-amp[0], amp[0], 0.25f, 0.25f, 0f, 0f);
                emitter.sinusoidalMotionY(-amp[0], amp[0], 0.25f, 0.25f, 0f, 0f);
                return true;
            }
        });

[close]

[close]
[close]

Version log
  • 0.73 (4/2/24):
    Added a toggle to disable particle effects while keeping the mod enabled.
  • 0.72 (3/3/24):
    Updated SS version to 0.97a. No changes.
  • 0.71 (12/29/23):
    Fixed default particle falloff, now is actually using square falloff and should look like particlealpha64sq.png in graphics/fx
    Made ParticleData.colorHSVA public
    No longer hard crashes if glMapBufferRange encounters an error, and will log an error message into the console
  • 0.7 (11/27/23):
    Added preInitParticles, which is called only once per burst and can be used to avoid re-computation.
    Shader now uses SpriteAPI's textureWidth and textureLength, scaling the image up when they are less than 1.
    Shader now uses SpriteAPI's centerX and centerY if they are non-negative and sticks to using the standard (0.5, 0.5) center otherwise.
    Updated dynamic anchoring to also dynamically update the emitter's axis. Note: Particles.anchorEmitter still only anchors position, not angle.
    Fixed untextured particles' alpha being capped to 0.25.
    Removed multiple extraneous getLocation calls when adding particle data to a float buffer
    Halved maximum simultaneous active dynamic emitters, down to 1024 from 2048
    Various optimizations
  • 0.62 (11/19/23):
    Fixed an issue where dynamically tracked particles could be jittery on their first frame.
    Fixed a bug where the wrong emitters were being evicted when the tracked emitter buffer was full.
    Greatly optimized the tracked emitter buffer handler. It now no longer holds on to stale emitters longer than necessary and only binds the UBO data each frame up to its maximum used index.
  • 0.61 (11/19/23):
    Hotfix a crash when doWhenFinished is null
    Fixed an issue where streams were finishing when they weren't supposed to
    Fixed an issue where streams would no longer generate more than 1 burst per frame
    Changed maxDuration in Particles.stream back to float from Float, set to negative value to never expire
  • 0.6 (11/18/23):
    Added anchorEmitter to Particles, which automates the task of setting the emitter's location to a combat entity's location each frame as well as destroying streams when the anchored-to combat entity leaves play.
    Added the ability to dynamically anchor particles to their emitters, allowing them to track and follow changes in their emitters' locations. Applied on a per-emitter basis, using enableDynamicAnchoring. Useful for particles that are meant to follow a combat entity.
    Note: emitter locations are stored in a UBO. Due to buffer size limitations, a maximum of 2048 emitters' locations can be tracked at a time. If more than 2048 emitters are being tracked, stale ones will start to be deleted. Use disableDynamicAnchoring to free an emitter's slot in the buffer.
    Added an optional doWhenFinished parameter when creating particle streams.
    Particles.stream now takes a Float for maxDuration rather than a float -- set it to null to make a stream never expire naturally.
    Particles.stream should now more accurately handle particles per burst, particles per second, and total duration.
    Optimized the particle allocation process a bit; should be less taxing on the CPU.
    Fixed an issue where createCopy was overriding the copy's sprite and blend mode settings
  • 0.5 (5/29/23):
    New classes, IEmitter and BaseIEmitter, that may be extended to generate custom particle emitters. Particles can now depend on each other's properties (prior to generation).
    Generify Particles.StreamAction. Note: this will break implementations using StreamAction, to fix, replace all StreamAction with StreamAction<Emitter>.
    Change license, relevant for v0.5+
  • 0.4 (5/5/23):
    Update Starsector version to 0.96a-RC6
    Change default out-of-bounds range from 100 to 500
    Change default particle layer to ABOVE_PARTICLES_LOWER
    Add early exit on Particles.burst if number of particles is <= 0
  • 0.3 (5/1/23):
    Emitters now support CombatEngineLayers, layer can be set via Emitter.setLayer
    Added Emitter.setInactiveBorder. Emitters outside of the screen by more than this amount of world units will ignore particle generation calls.
  • 0.2 (4/11/23):
    Add mod thread id to version file
    Remove axis from json output (it's an emitter property, not a particle property)
    Allow changing of emitter sprite and blend mode via setters
    Add setSyncSize to Emitter, which allows random particle sizes that are the same in the x and y directions (uses the x direction values)
  • 0.1: Initial release (4/10/23)
[close]
« Last Edit: April 02, 2024, 02:36:08 PM by float »
Logged

Lukas04

  • Captain
  • ****
  • Posts: 355
    • View Profile
Re: [0.95.1a] Particle Engine (4/10/23)
« Reply #1 on: April 11, 2023, 01:11:48 AM »

Seems quite interesting, being able to use Particles at reasonable quantities without immediate performance concerns would definitly be nice for some visual effects.

Does this work on just the Combat layer, or can it be used in both Combat and Campaign?

I also wonder if something like this wouldnt be better to integrate in to an existing Library aswell, this seems like something that would fit well in to the Utilities provided by MagicLib already, which already has miscellaneous utilities for in-combat graphical effects, and would probably see a quicker gain in useage through it aswell.

Otherwise looking forward on this progressing, always great to have people around that jump in to the horrifying depths of OpenGL to make it simpler for others.
« Last Edit: April 11, 2023, 01:20:19 AM by Lukas04 »
Logged
My Mods

Nia Tahl

  • Admiral
  • *****
  • Posts: 793
  • AI in disguise
    • View Profile
Re: [0.95.1a] Particle Engine (4/10/23)
« Reply #2 on: April 11, 2023, 02:42:52 AM »

This seems neat, but I'm not sure I want to integrate another lib given the rather limited use cases for high particle counts in this game.
Logged
My mods: Tahlan Shipworks - ScalarTech Solutions - Trailer Moments
It's all in the presentation

tomatopaste

  • Captain
  • ****
  • Posts: 306
    • View Profile
Re: [0.95.1a] Particle Engine (4/10/23)
« Reply #3 on: April 11, 2023, 06:43:50 AM »

cool project, can be optimised using glDrawElementsInstanced instead, also does not support CombatEngineLayers
Logged

float

  • Captain
  • ****
  • Posts: 267
    • View Profile
Re: [0.95.1a] Particle Engine (4/10/23)
« Reply #4 on: April 11, 2023, 08:14:18 AM »

does not support CombatEngineLayers

Yeah I sort of forgot that CombatEngineLayers existed since the EveryFrameCombatPlugin makes no mention of it. Looks like I’d have to use a LayeredRenderingPlugin to actually make use of the layer information, which could be feasible, but OTOH what is the use case for particles that don’t render on top of in-game objects?

can be optimised using glDrawElementsInstanced instead

Why? That’s just an additional element buffer overhead considering that particles will never share vertices (by vertex I mean the entire 38-element array containing data for each particle). If you mean to index just the quad offsets, those aren’t even passed into the GPU — they’re just a const array defined in the vertex shader.
Logged

float

  • Captain
  • ****
  • Posts: 267
    • View Profile
Re: [0.95.1a] Particle Engine (4/10/23)
« Reply #5 on: April 11, 2023, 08:42:55 AM »

Does this work on just the Combat layer, or can it be used in both Combat and Campaign?

Just the combat layer.
Logged

Nia Tahl

  • Admiral
  • *****
  • Posts: 793
  • AI in disguise
    • View Profile
Re: [0.95.1a] Particle Engine (4/10/23)
« Reply #6 on: April 12, 2023, 02:06:54 AM »

but OTOH what is the use case for particles that don’t render on top of in-game objects?

Argument can be made for background visuals or implying volumetric effects by rendering both below and above ingame objects
Logged
My mods: Tahlan Shipworks - ScalarTech Solutions - Trailer Moments
It's all in the presentation

xenoargh

  • Admiral
  • *****
  • Posts: 5078
  • naively breaking things!
    • View Profile
Re: [0.95.1a] Particle Engine (4/11/23)
« Reply #7 on: April 13, 2023, 12:40:05 AM »

Super-neat project, and I hope that it gets used. I think a lot of people here just don't really appreciate what a custom particle system can do to make things look good. I'd strongly recommend writing a few basic "basic explosions with lots of debris" examples to show additive / normal blending methods used together. Also, the examples don't show it color-shifting over time; if that's missing, you might want to consider implementing that, it's important for a lot of basic FX stuff like "fire that looks like fire".
Logged
Please check out my SS projects :)
Xeno's Mod Pack

rogerbacon

  • Commander
  • ***
  • Posts: 150
    • View Profile
Re: [0.95.1a] Particle Engine (4/11/23)
« Reply #8 on: April 13, 2023, 03:51:19 PM »

This is going to be a great addition to the game. I'm going to try it this weekend.

What's the import line look like? I'm writing my classes in Notepad++ and there is no intellisense.

Also, is it possible to atach the emitter to an object in teh game, liek a projectile, to make the particles move with it? What event did you place your code in fro the trail emitter?
« Last Edit: April 13, 2023, 06:17:01 PM by rogerbacon »
Logged

float

  • Captain
  • ****
  • Posts: 267
    • View Profile
Re: [0.95.1a] Particle Engine (4/10/23)
« Reply #9 on: May 01, 2023, 01:22:39 PM »

Argument can be made for background visuals or implying volumetric effects by rendering both below and above ingame objects

Hmm, sure. Added.

This is going to be a great addition to the game. I'm going to try it this weekend.

What's the import line look like? I'm writing my classes in Notepad++ and there is no intellisense.

Also, is it possible to atach the emitter to an object in teh game, liek a projectile, to make the particles move with it? What event did you place your code in fro the trail emitter?

Sorry, was away for a bit. It's particleengine.Emitter and particleengine.Particles, assuming you've added the jar or the source as a library. I'd highly recommend using an IDE to manage libraries, builds, etc.

You can use Particles.stream with a StreamAction to perform custom actions in between bursts of particles, including setting the emitter's location to a new value. Use an anonymous class (not lambdas, as Starsector doesn't support language levels above 7) or just create your own class that implements Particles.StreamAction.
Logged

ApolloStarsector

  • Commander
  • ***
  • Posts: 131
    • View Profile
Re: [0.95.1a] Particle Engine (5/1/23)
« Reply #10 on: May 04, 2023, 06:05:20 PM »

Brillant idea. Anything that takes pressure off the single-CPU-core limitation is of the utmost importance! If only it were possible to apply this performance enhancement to all applicable vanilla particles.
Logged

Wispborne

  • Captain
  • ****
  • Posts: 408
  • Discord: wispborne
    • View Profile
Re: [0.96a] Particle Engine (5/5/23)
« Reply #11 on: September 01, 2023, 11:08:53 AM »

Does this use the same version of OpenGL as vanilla? Are there any known additional hardware feature requirements beyond vanilla?

Some of my WIP content adds a *lot* of particles to the campaign layer and I've had to tone them down to avoid lag, so this might let me go wild. But...does it support the campaign layer out of the box, or would that require forking it?
Logged
Mod: Persean Chronicles | Mod Manager: SMOL | Tool: VRAM Estimator | Tool: Forum+Discord Mod Database | If I'm inactive for 3 months, anyone can use any of my work for anything (except selling it or its derivatives).

float

  • Captain
  • ****
  • Posts: 267
    • View Profile
Re: [0.96a] Particle Engine (5/5/23)
« Reply #12 on: September 08, 2023, 10:06:42 AM »

Does this use the same version of OpenGL as vanilla? Are there any known additional hardware feature requirements beyond vanilla?

Some of my WIP content adds a *lot* of particles to the campaign layer and I've had to tone them down to avoid lag, so this might let me go wild. But...does it support the campaign layer out of the box, or would that require forking it?

It requires a version of OpenGL that supports GLSL 3.30 (so OpenGL 3.3+), which I believe is above the vanilla Starsector standard but should be supported by any graphics card made in the past 10 years or so. I'm not actually sure what version of OpenGL vanilla requires. I think I recall seeing some GL 2.0 stuff but I could be mistaken.

I'm using CombatLayeredRenderingPlugin to render the particles as that's a clean way of supporting CombatEngineLayers, and I assume that doesn't work for the campaign layer. So there's a good chance that adding particles in the campaign layer would require some additional work. But I could be wrong -- I've never tried rendering anything in the campaign layer and don't know what that would entail.
« Last Edit: September 08, 2023, 10:13:58 AM by float »
Logged

creature

  • Captain
  • ****
  • Posts: 400
    • View Profile
Re: [0.96a] Particle Engine (11/27/23)
« Reply #13 on: December 27, 2023, 12:18:04 PM »

Hi, when you get an error like this after calling .burst(), what does it mean? Ran out of memory perhaps?

11419407 [Thread-4] ERROR com.fs.starfarer.combat.CombatMain  - java.lang.NullPointerException
java.lang.NullPointerException: null
   at particleengine.ParticleAllocator.allocateParticles(ParticleAllocator.java:174) ~[?:?]
   at particleengine.Particles.burst(Particles.java:475) ~[?:?]
   at particleengine.Particles.burst(Particles.java:438) ~[?:?]
   at data.scripts.utils.yrxp_SpellcastUtils.spawnPEHitSpray(yrxp_SpellcastUtils.java:216) ~[?:?]
   at data.scripts.ai.projectiles.yrxp_ChainmineBombletAI.explodeFX(yrxp_ChainmineBombletAI.java:210) ~[?:?]
   at data.scripts.ai.projectiles.yrxp_ChainmineBombletAI.advance(yrxp_ChainmineBombletAI.java:103) ~[?:?]
   at com.fs.starfarer.combat.entities.Missile$MissileAIWrapper.advance(Unknown Source) ~[starfarer_obf.jar:?]
   at com.fs.starfarer.combat.CombatEngine.advanceInner(Unknown Source) ~[starfarer_obf.jar:?]
   at com.fs.starfarer.combat.CombatEngine.advance(Unknown Source) ~[starfarer_obf.jar:?]
   at com.fs.starfarer.combat.CombatState.traverse(Unknown Source) ~[starfarer_obf.jar:?]
   at com.fs.state.AppDriver.begin(Unknown Source) ~[fs.common_obf.jar:?]
   at com.fs.starfarer.combat.CombatMain.main(Unknown Source) ~[starfarer_obf.jar:?]
   at com.fs.starfarer.StarfarerLauncher.o00000(Unknown Source) ~[starfarer_obf.jar:?]
   at com.fs.starfarer.StarfarerLauncher$1.run(Unknown Source) ~[starfarer_obf.jar:?]
   at java.lang.Thread.run(Thread.java:750) [?:1.8.0_392]
Logged

float

  • Captain
  • ****
  • Posts: 267
    • View Profile
Re: [0.96a] Particle Engine (11/27/23)
« Reply #14 on: December 28, 2023, 07:53:32 PM »

Hi, when you get an error like this after calling .burst(), what does it mean? Ran out of memory perhaps?

11419407 [Thread-4] ERROR com.fs.starfarer.combat.CombatMain  - java.lang.NullPointerException
java.lang.NullPointerException: null
   at particleengine.ParticleAllocator.allocateParticles(ParticleAllocator.java:174) ~[?:?]
   at particleengine.Particles.burst(Particles.java:475) ~[?:?]
   at particleengine.Particles.burst(Particles.java:438) ~[?:?]
   at data.scripts.utils.yrxp_SpellcastUtils.spawnPEHitSpray(yrxp_SpellcastUtils.java:216) ~[?:?]
   at data.scripts.ai.projectiles.yrxp_ChainmineBombletAI.explodeFX(yrxp_ChainmineBombletAI.java:210) ~[?:?]
   at data.scripts.ai.projectiles.yrxp_ChainmineBombletAI.advance(yrxp_ChainmineBombletAI.java:103) ~[?:?]
   at com.fs.starfarer.combat.entities.Missile$MissileAIWrapper.advance(Unknown Source) ~[starfarer_obf.jar:?]
   at com.fs.starfarer.combat.CombatEngine.advanceInner(Unknown Source) ~[starfarer_obf.jar:?]
   at com.fs.starfarer.combat.CombatEngine.advance(Unknown Source) ~[starfarer_obf.jar:?]
   at com.fs.starfarer.combat.CombatState.traverse(Unknown Source) ~[starfarer_obf.jar:?]
   at com.fs.state.AppDriver.begin(Unknown Source) ~[fs.common_obf.jar:?]
   at com.fs.starfarer.combat.CombatMain.main(Unknown Source) ~[starfarer_obf.jar:?]
   at com.fs.starfarer.StarfarerLauncher.o00000(Unknown Source) ~[starfarer_obf.jar:?]
   at com.fs.starfarer.StarfarerLauncher$1.run(Unknown Source) ~[starfarer_obf.jar:?]
   at java.lang.Thread.run(Thread.java:750) [?:1.8.0_392]

Looks like glMapBufferRange is returning null, which happens if it encounters an error. Out of memory is one of the possible errors.

I don't know how you encountered this issue, but if you'd like to probe into it further, you can modify ParticleAllocator.java, after line 173, test if existingBuffer is null and if so, use glGetError to get the error id. I'll also add a failsafe on my end for the next update.
Logged
Pages: [1] 2