Detailed Particle Behavoir Reference

Discussion in 'Resources' started by RingOfStorms, Jan 7, 2015.

Thread Status:
Not open for further replies.
  1. Offline

    RingOfStorms

    Hello guys, I'm making this post as a resource for people who have made particle libraries, or just want to learn about some of the more nitty-gritty details of how the client handles particles sent from the server. This is a very dense read and I attempted to make useful titles so you can easily skip through to the section you're interested in.

    Do note, I may or may not update this as minecraft is updates, but do check back or ask when a new update comes out.

    For the length of this tutorial I will be refering to the different parts of the particle packet. I'm following the names that are shown here on the protocol wiki. The only different is that I will be using the word speed instead of particle data.

    To start out with, I'll be going over the first step that the client does when it reads a new particle packet. After that I will be listing out the specifics of every single particle and how they react to the two different packet handling methods I describe below.

    Under each section I've included a spoiler that includes the NMS code that is relevant to the explanation, this will help some people understand my explanations a bit better. I deobfuscated some parts of the code snippets, but a lot will still be obfuscated.

    Particle Packet Handling on the client

    The very first check that occurs checks if the particle count is zero, or not. These two outcomes produce different effects and spawn the particles slightly different depending on the particle in question.

    When the particle count is equal to zero:
    Three new x/y/z variables are created by multiplying the offsetX/Y/Z by the particle's speed. These newly created x/y/z will be passed in instead of the offsets that were originally in the packet.

    In a lot of the cases when the zero particle count is used then the newly created x/y/z represent the direction of the particle, however it is not quite the same for every single particle.
    Client Code :: count zero (open)

    Code:java
    1.  
    2. double var2 = (double)(packetIn.particleSpeedData() * packetIn.offsetX());
    3. double var4 = (double)(packetIn.particleSpeedData() * packetIn.offsetY());
    4. double var6 = (double)(packetIn.particleSpeedData() * packetIn.offsetZ());
    5.  
    6. try {
    7. this.clientWorldController.spawnParticle(packetIn.particleType(), packetIn.ignoreRenderDistance(), packetIn.positionX(), packetIn.positionY(), packetIn.positionZ(), var2, var4, var6, packetIn.dataArray());
    8. }catch (Throwable var17){
    9. logger.warn("Could not spawn particle effect " + packetIn.particleType());
    10. }
    11.  



    When the particle count is not equal to zero:
    A particle is spawned Z amount of times where Z is the particle count.
    On each iteration six new variables are created.

    The first three of these variables are based on the offsetX/Y/Z variables from the packet, these are the radius from the center in each axis that allows you to spawn several particles within a circular region. These bounds are then calculated using Random.nextGaussian() multiplied by each offset part. These three new x/y/z parts are then added to the original x/y/z location that was given, thus producing an effect where the number of particles are spawned inside of a circular area.

    The next three variables created are the randomized motion. These are calculated by multiplying Random.nextGaussian by the particle's speed. Note: some particles have hardcoded motion and will ignore the speed inside of the packet.
    Client Code :: count > zero (open)

    Code:java
    1.  
    2. for (int var18 = 0; var18 < packetIn.particleCount(); ++var18) {
    3. double var3 = this.avRandomizer.nextGaussian() * (double)packetIn.offsetX();
    4. double var5 = this.avRandomizer.nextGaussian() * (double)packetIn.offsetY();
    5. double var7 = this.avRandomizer.nextGaussian() * (double)packetIn.offsetZ();
    6. double var9 = this.avRandomizer.nextGaussian() * (double)packetIn.particleSpeedData();
    7. double var11 = this.avRandomizer.nextGaussian() * (double)packetIn.particleSpeedData();
    8. double var13 = this.avRandomizer.nextGaussian() * (double)packetIn.particleSpeedData();
    9.  
    10. try {
    11. this.clientWorldController.spawnParticle(packetIn.particleType(), packetIn.ignoreRenderDistance(), packetIn.positionX() + var3, packetIn.positionY() + var5, packetIn.positionZ() + var7, var9, var11, var13, packetIn.dataArray());
    12. }catch (Throwable var16){
    13. logger.warn("Could not spawn particle effect " + packetIn.particleType());
    14. return;
    15. }
    16. }
    17.  



    Creation of the Particle Object on the client

    Before we get down to the details on each particle, I want to explain what happens between the last step of handling the packet, and the actual creation of the particle.

    All of the parameters that were generated in one of the previous two handling methods are passed into the RenderGlobal instance where it does a few more things before actually making a particle.

    If the particle ignores render distance (whether that is it's default nature like explosion, or you set it as true in the packet) it skips all particle settings and goes straight to spawning it in.

    However, if ignore render distance is false, then it checks the particle settings. From my understanding there are three integers that represent the particle setting in the client:
    All = 0
    Decreased = 1
    Minimal = 2

    No matter what setting, if the particle is a distance greater than 16, the particle will not be created. If it is under or equal 16 then it will continue to the particle settings check...

    When the client has the particles set to ...
    All: The particle is created
    Decreased: 33% chance that the particle is not created
    Minimal: Particle is not created

    Client Code :: distance and settings check (open)

    Code:java
    1.  
    2. int var16 = this.mc.gameSettings.particleSetting;
    3.  
    4. if (var16 == 1 && this.theWorld.rand.nextInt(3) == 0) {
    5. var16 = 2;
    6. }
    7.  
    8. double var17 = this.mc.func_175606_aa().posX - posX;
    9. double var19 = this.mc.func_175606_aa().posY - posY;
    10. double var21 = this.mc.func_175606_aa().posZ - posZ;
    11.  
    12. if (ignoreRenderDist) {
    13. return this.mc.effectRenderer.func_178927_a(id, posX, posY, posZ, extraX, extraY, extraZ, dataArray);
    14. }else{
    15. double var23 = 16.0D;
    16. return var17 * var17 + var19 * var19 + var21 * var21 > 256.0D ? null : (var16 > 1 ? null : this.mc.effectRenderer.func_178927_a(id, posX, posY, posZ, extraX, extraY, extraZ, dataArray));
    17. }
    18.  



    Displaying of the created particle

    In the last section the particle was sent to be created, at this point the client gets the matching particle creation factory based on the particle type id that was passed in the first parameter. At this point particle's behavior will differ between some of the particles, although a large portion of them will be similar.

    Here is the most updated list from the last time this post was edited of the particles in the client.
    Gist
    Show Spoiler

    Code:java
    1.  
    2. /*
    3. (String name of the particle, id of particle, ignores render distance by default)
    4. */
    5.  
    6. EXPLOSION_NORMAL("explode", 0, true),
    7. EXPLOSION_LARGE("largeexplode", 1, true),
    8. EXPLOSION_HUGE("hugeexplosion", 2, true),
    9. FIREWORKS_SPARK("fireworksSpark", 3, false),
    10. WATER_BUBBLE("bubble", 4, false),
    11. WATER_SPLASH("splash", 5, false),
    12. WATER_WAKE("wake", 6, false),
    13. SUSPENDED("suspended", 7, false),
    14. SUSPENDED_DEPTH("depthsuspend", 8, false),
    15. CRIT("crit", 9, false),
    16. CRIT_MAGIC("magicCrit", 10, false),
    17. SMOKE_NORMAL("smoke", 11, false),
    18. SMOKE_LARGE("largesmoke", 12, false),
    19. SPELL("spell", 13, false),
    20. SPELL_INSTANT("instantSpell", 14, false),
    21. SPELL_MOB("mobSpell", 15, false),
    22. SPELL_MOB_AMBIENT("mobSpellAmbient", 16, false),
    23. SPELL_WITCH("witchMagic", 17, false),
    24. DRIP_WATER("dripWater", 18, false),
    25. DRIP_LAVA("dripLava", 19, false),
    26. VILLAGER_ANGRY("angryVillager", 20, false),
    27. VILLAGER_HAPPY("happyVillager", 21, false),
    28. TOWN_AURA("townaura", 22, false),
    29. NOTE("note", 23, false),
    30. PORTAL("portal", 24, false),
    31. ENCHANTMENT_TABLE("enchantmenttable", 25, false),
    32. FLAME("flame", 26, false),
    33. LAVA("lava", 27, false),
    34. FOOTSTEP("footstep", 28, false),
    35. CLOUD("cloud", 29, false),
    36. REDSTONE("reddust", 30, false),
    37. SNOWBALL("snowballpoof", 31, false),
    38. SNOW_SHOVEL("snowshovel", 32, false),
    39. SLIME("slime", 33, false),
    40. HEART("heart", 34, false),
    41. BARRIER("barrier", 35, false),
    42. ITEM_CRACK("iconcrack_", 36, false, 2),
    43. BLOCK_CRACK("blockcrack_", 37, false, 1),
    44. BLOCK_DUST("blockdust_", 38, false, 1),
    45. WATER_DROP("droplet", 39, false),
    46. ITEM_TAKE("take", 40, false),
    47. MOB_APPEARANCE("mobappearance", 41, true);
    48.  



    Below this is every single particle's details and behavior, along with how each part of the particle packet effects the particle's behavior.

    Each particle will contain a spoiler with what parameters from the packet effect it's behavoir. They will follow this general format:
    Parameters that are carried in from packet (open)

    Always:
    Position X, Y, and Z

    When count is 0:
    blabla

    When count is > 0:
    blabla


    NOTE: All R/G/B values are a float value between 0 and 1. They are then later multiplied by 255 when the particle is rendered, so treat all R/G/B in the particle reference as the percentage of R/G/B of the final RGB color.

    Particle Reference

    Barrier
    Parameters that are carried in from packet (open)

    Always:
    Position X, Y, and Z

    Town Aura
    Parameters that are carried in from packet (open)

    Always:
    Position X, Y, and Z

    When count is 0:
    Motion X, Y, and Z

    Happy Villager
    Parameters that are carried in from packet (open)

    Always:
    Position X, Y, and Z

    When count is 0:
    Motion X, Y, and Z

    Block Dust
    Parameters that are carried in from packet (open)

    Always:
    Position X, Y, and Z
    Block Id (Index 0 of data array) [required]

    When count is 0:
    Motion X, Y, and Z

    Icon Crack
    Parameters that are carried in from packet (open)

    Always:
    Position X, Y, and Z
    Item Id (Index 0 of data array) [required]
    Item Durability (Index 1 of data array) [optional|default:0]

    When count is 0:
    Motion X, Y, and Z

    Slime
    Parameters that are carried in from packet (open)

    Always:
    Position X, Y, and Z

    Snowball Poof
    Parameters that are carried in from packet (open)

    Always:
    Position X, Y, and Z

    Bubble
    Parameters that are carried in from packet (open)

    Always:
    Position X, Y, and Z

    When count is 0:
    Motion X, Y, and Z

    Cloud
    Parameters that are carried in from packet (open)

    Always:
    Position X, Y, and Z

    When count is 0:
    Motion X, Y, and Z

    Crit
    Parameters that are carried in from packet (open)

    Always:
    Position X, Y, and Z

    When count is 0:
    Motion X, Y, and Z

    Magic Crit
    Parameters that are carried in from packet (open)

    Always:
    Position X, Y, and Z

    When count is 0:
    Motion X, Y, and Z

    Smoke Large
    Parameters that are carried in from packet (open)

    Always:
    Position X, Y, and Z

    When count is 0:
    Motion X, Y, and Z

    Block Crack
    Parameters that are carried in from packet (open)

    Always:
    Position X, Y, and Z
    Block Id (Index 0 of data array) [required]

    When count is 0:
    Motion X, Y, and Z

    Drip Lava
    Parameters that are carried in from packet (open)

    Always:
    Position X, Y, and Z

    Drip Water
    Parameters that are carried in from packet (open)

    Always:
    Position X, Y, and Z

    Enchantment Table
    Parameters that are carried in from packet (open)

    Always:
    Position X, Y, and Z

    When count is 0:
    Motion X, Y, and Z
    - Note: The particle's position is shifted in the x/y/z of the vector given which gives the particle its inward movement.

    Explode
    Parameters that are carried in from packet (open)

    Always:
    Position X, Y, and Z

    When count is 0:
    Motion X, Y, and Z

    Fireworks Spark
    Parameters that are carried in from packet (open)

    Always:
    Position X, Y, and Z

    When count is 0:
    Motion X, Y, and Z

    Wake
    Parameters that are carried in from packet (open)

    Always:
    Position X, Y, and Z

    When count is 0:
    Motion X, Y, and Z

    Flame
    Parameters that are carried in from packet (open)

    Always:
    Position X, Y, and Z

    When count is 0:
    Motion X, Y, and Z

    Footstep
    Parameters that are carried in from packet (open)

    Always:
    Position X, Y, and Z

    Heart
    Parameters that are carried in from packet (open)

    Always:
    Position X, Y, and Z

    Huge Explosion
    Parameters that are carried in from packet (open)

    Always:
    Position X, Y, and Z

    Large Explode
    Parameters that are carried in from packet (open)

    Always:
    Position X, Y, and Z

    Lava
    Parameters that are carried in from packet (open)

    Always:
    Position X, Y, and Z

    Note
    Parameters that are carried in from packet (open)

    Always:
    Position X, Y, and Z

    When count is 0:
    Color, however the note color is fairly limited/weird, it only uses Offset X and discards Y and Z offsets
    The RGB is calculated as follows:
    R = MathHelper.sin(((float)offsetX+ 0.0F) * (float)Math.PI * 2.0F) * 0.65F + 0.35F;
    G = MathHelper.sin(((float)offsetX+ 0.33333334F) * (float)Math.PI * 2.0F) * 0.65F + 0.35F;
    B = MathHelper.sin(((float)offsetX+ 0.6666667F) * (float)Math.PI * 2.0F) * 0.65F + 0.35F;

    I'm not sure if this is something mojang made up or if it is some standard color format into RGB, if you research it and find out comment below so I can add details here!

    Portal
    Parameters that are carried in from packet (open)

    Always:
    Position X, Y, and Z

    When count is 0:
    Motion X, Y, and Z
    - Note: The particle's position is shifted in the x/y/z of the vector given which gives the particle its inward motion.

    Droplet (Water Drop)
    Parameters that are carried in from packet (open)

    Always:
    Position X, Y, and Z

    Red Dust
    Parameters that are carried in from packet (open)

    Always:
    Position X, Y, and Z

    When count is 0:
    RGB color of red dust
    When the offsetX is equal to 0, it will be set to 1
    the rgb does get slightly randomized, so I'll include the calculations for them
    flaot var12 = (float)Math.random() * 0.4F + 0.6F;
    R = ((float)(Math.random() * 0.20000000298023224D) + 0.8F) * offsetX * var12;
    G = ((float)(Math.random() * 0.20000000298023224D) + 0.8F) * OffsetY * var12;
    B = ((float)(Math.random() * 0.20000000298023224D) + 0.8F) * OffsetZ * var12;

    Smoke
    Parameters that are carried in from packet (open)

    Always:
    Position X, Y, and Z

    When count is 0:
    Motion X, Y, and Z

    Snow Shovel
    Parameters that are carried in from packet (open)

    Always:
    Position X, Y, and Z

    When count is 0:
    Motion X, Y, and Z

    Splash
    Parameters that are carried in from packet (open)

    Always:
    Position X, Y, and Z

    When count is 0:
    Motion X and Z
    Splash motion in the Y direction is locked, and there are some requirements to set the X and the Z motion
    For the motion to work the following conditions must be met:
    -offsetY must equal 0
    -offsetX and offsetZ must not equal 0

    If those two conditions are met then the motion int the X and Z axis are set to the corresponding offsetX/Z and the motion in the Y direction is set to 0.1.

    Suspend
    Parameters that are carried in from packet (open)

    Always:
    Position X, Y, and Z

    Mob Appearance
    Parameters that are carried in from packet (open)

    Always:
    Position X, Y, and Z [specific location doesn't seem to matter on this particle effect]

    Spell
    Parameters that are carried in from packet (open)

    Always:
    Position X, Y, and Z

    When count is 0:
    Motion Y
    motion in the X and Z directions are limited, but there are some weird special things you can do
    OffsetY is used for motion Y, while motion X and Z are random between -.5 to .5

    However, if the following conditions are met, the X and Z motions are multiplied by aprox .1, making it basically no movement:
    - offsetX must equal 0
    - offsetZ must equal 0

    If either of them do not equal 0, then that X/Z motion damper is not applied.

    Mob Spell Ambient
    Parameters that are carried in from packet (open)

    Always:
    Position X, Y, and Z

    When count is 0:
    The offsetX/Y/Z will do the same thing as explained in the Spell particle above, but they also set color.

    R/G/B is set to the offsetX/Y/Z accordingly. Because the offset values are used in two places, you can not have a special color and have the X/Z dampening at the same time. Similarly, if you have a large green value, the particle will have a larger y velocity.

    Instant Spell
    Parameters that are carried in from packet (open)

    Always:
    Position X, Y, and Z

    When count is 0:
    The offsetX/Y/Z will do the same thing as explained in the Spell particle above.

    Mob Spell
    Parameters that are carried in from packet (open)

    Always:
    Position X, Y, and Z

    When count is 0:
    The offsetX/Y/Z will do the same thing as explained in the Spell particle above, but they also set color.

    R/G/B is set to the offsetX/Y/Z accordingly. Because the offset values are used in two places, you can not have a special color and have the X/Z dampening at the same time. Similarly, if you have a large green value, the particle will have a larger y velocity.

    Witch Magic
    Parameters that are carried in from packet (open)

    Always:
    Position X, Y, and Z

    When count is 0:
    The offsetX/Y/Z will do the same thing as explained in the Spell particle above.


    That's it for now.

    I have not been able to fully test every single one of these myself yet, so if there are any mistakes or things left out, please do tell me below in a reply.
     
    macmc, GrandmaJam, NathanWolf and 9 others like this.
  2. Offline

    Cybermaxke

    You can also resize the LargeExplode particle ;)
    Instead of the offsetX you will need to the size calculated with the formula: (not tested)
    offsetX = (-size * 2f) + 2f;
    The value scales between 0 and infinite

    And about the note particle, for the color is the actual note. Like you would use them for the note blocks, so they will scale between 0 and 24:
    offsetX = note / 24f;
     
  3. Offline

    NathanWolf

    What a great resource, thank you for putting this together!

    So, if I'm reading this correctly- of the 3 particle types that are colorizeable, there isn't one that can really do *all* colors and also stay in place?

    Meaning, for the spell particles - you can't use motion dampening without also affecting the color range (the slower the particles, the darker they'll be?)

    And for the red dust particle, they kind of stay in place no matter what- but you can't make colors without any red component? (I wonder how low you can make R before it decides 0? Can I pass 0.0000001?)

    Well anyway this is just great, I've been wishing for colored particles for so long, I had no idea we already had them!
     
  4. Offline

    ChipDev

    Yeah, sadly!
    *But, changing the amount to '1' allows offsets actually.
     
  5. Offline

    RingOfStorms

    Reddust stays still and can be any color.
     
    NathanWolf and ChipDev like this.
  6. Offline

    Skionz

    @RingOfStorms I read this a few days ago and this is a wonderful thread to reference. I love particles and this will be very useful.
     
    ChipDev likes this.
Thread Status:
Not open for further replies.

Share This Page