Advanced Particle Algorithm

Discussion in 'Plugin Development' started by ski23, Jan 11, 2016.

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

    ski23

    Overview:
    I had been working on this particle effect a while ago. It sort of works and I just left it at the time but, I would like to get this fully working so I can utilize parts of this particle effect to create more complicated particle effects.

    What its suppose to do:
    The particle effect should act like a laser but instead of sending a line of particles, it should send a circle of particles that is perpendicular to player.getDirection(). After hours and hours of math theory, I was able to determine that to do this, I needed to create a circle about the x axis. Then, I needed to convert the coordinates from traditional cortesian coordinates to polar coordinates. This was needed so that I could move all of the points in the circle to generate the circle perpendicular to the player direction. Then, it would go off in that direction like a lazer.

    What it is doing.
    The particle effect works perfectly while the player direction vector lies along the xz plane. Inside of this plane, it shoots out the circle perpendicular to the player's direction. However, as the player increases or decreases the y component of his or her direction, the circle begins to get distorted. If a player creates the effect looking straight down, it generates a perfect figure 8. Looking straight up, the method does nothing. This is most likely due to the restriction of the domain on one of the trigonometric functions and should be easily solveable at the end. If a player looks almost straight up, it also generates a figure 8. In between parallel to the xz plane and the y axis, the shape is somewhere between a figure 8 and a circle.
    There is no stack trace.
    I realize this is a fairly complicated issue. Any help would be appreciated. @ChipDev or @Skionz have any advice?
    Here is my code:

    Code:
    public void lazerBeamCircle(Player _player, String _particleType, double interval)
        {
            MyLogger.info("In lazerBeamCircle in Particles");
            Vector playerDirection = _player.getLocation().getDirection();
            final int increment = 16;
            final double radius = 1;
            if (LazerCircleTaskID.get(_player.getName()) == null)
            {
                LazerCircleTaskID.put(_player.getName(), 0);
            }
            else if ((LazerCircleTaskID.get(_player.getName()) != 0))
            {
                Bukkit.getServer().getScheduler().cancelTask((LazerCircleTaskID.get(_player.getName())));
            }
             MyLogger.info("Right before the task");
            LazerCircleTaskID.put(_player.getName(), Bukkit.getServer().getScheduler().scheduleSyncRepeatingTask(JavaPlugin.getProvidingPlugin(MinigameDriver.class), new Runnable()
                {
                    @Override
                    public void run()
                    {
                        //MyLogger.info("In the task after run");
                        taskLazerBeamCircle _lazerVarsCircle = new taskLazerBeamCircle();
                        _lazerVarsCircle.Loc = new Location[increment];
                        if (!PlayerLazerCircleBeam.containsKey(LazerCircleTaskID.get(_player.getName())))
                        {
                            double    changeTheta = Math.acos(playerDirection.getX() / (Math.sin(Math.acos((playerDirection.getY())))));
                            //if (changeTheta < 0 )
                            //{
                            //    changeTheta += 2 * Math.PI;
                            //}
                            if (playerDirection.getZ() < 0)
                            {
                                changeTheta = ( 2 * Math.PI) - changeTheta;
                            }
                            double changePhi = Math.acos(playerDirection.getY()) - ((Math.PI)/2);
                      
                            double ro;
                            double theta;
                            double phi;
                            double thetaNew;
                            double phiNew;
                      
                            for (int i = 1 ; i <= increment; i++)
                            {
                                MyLogger.info("In the for calculating coords i = " + i);
                                MyLogger.info("inverse sin test " + Math.asin(Math.sqrt(2) / 2));
                                Location locThrowAway = new Location(_player.getWorld(), 1, 0, 0);
                                //finding the coords of the circle on the x axis;
                                locThrowAway.setY(radius * Math.sin(((double) i / increment) * (2 * Math.PI)));
                                locThrowAway.setZ(radius * Math.cos(((double) i / increment) * (2 * Math.PI)));
                                MyLogger.info("after Finding the coords of the circle on the x axis" + locThrowAway.getX() + "  " + locThrowAway.getY() + "  " + locThrowAway.getZ());
                          
                                //finding the coords in spherical coords
                                ro = Math.sqrt((locThrowAway.getX() * locThrowAway.getX()) +
                                        (locThrowAway.getY() * locThrowAway.getY()) +(locThrowAway.getZ() * locThrowAway.getZ()));
                          
                                phi = Math.acos((locThrowAway.getY()) / ro);
                                theta = Math.asin(locThrowAway.getZ() / (ro * Math.sin(phi)));
                                if (theta < 0)
                                {
                                    theta += 2 * Math.PI;
                                }
                          
                                //adding the arcs to the spherical coords
                                thetaNew = (theta + changeTheta);
                                phiNew = (phi + changePhi);
                                //thetaNew = theta;
                                //phiNew = phi;
                          
                          
                                //converting back to normal coords
                                //changeTheta is used as it is theta as well
                                locThrowAway.setX(Math.sin(phiNew) * Math.cos(thetaNew) * ro);
                                locThrowAway.setY(Math.cos(phiNew) * ro);
                                locThrowAway.setZ(Math.sin(phiNew) * Math.sin(thetaNew) * ro);
                                //locThrowAway.setX(locThrowAway.getX());
                                //locThrowAway.setY(locThrowAway.getY());
                                //locThrowAway.setZ(locThrowAway.getZ());
                                locThrowAway.setX(locThrowAway.getX() + _player.getLocation().getX());
                                locThrowAway.setY(locThrowAway.getY() + _player.getLocation().getY() + 2);
                                locThrowAway.setZ(locThrowAway.getZ() + _player.getLocation().getZ());
                                //MyLogger.info("after converting back to normal coords" + locThrowAway.getX() + "  " + locThrowAway.getY() + "  " + locThrowAway.getZ());
                                _lazerVarsCircle.Loc[i-1] = locThrowAway;
                                MyLogger.info("X , Y , Z after calculations " + locThrowAway.getX() + "  " + locThrowAway.getY() + "  " + locThrowAway.getZ());
                            }
                            PlayerLazerCircleBeam.put(LazerCircleTaskID.get(_player.getName()), _lazerVarsCircle);
                        }
                        else
                        {
                            _lazerVarsCircle = PlayerLazerCircleBeam.get(LazerCircleTaskID.get(_player.getName()));
                            for (int i = 0; i < _lazerVarsCircle.Loc.length; i++)
                            {
                                _lazerVarsCircle.Loc[i].setX(_lazerVarsCircle.Loc[i].getX() + (playerDirection.getX() / interval));
                                _lazerVarsCircle.Loc[i].setY(_lazerVarsCircle.Loc[i].getY() + (playerDirection.getY() / interval));
                                _lazerVarsCircle.Loc[i].setZ(_lazerVarsCircle.Loc[i].getZ() + (playerDirection.getZ() / interval));
                                if (_lazerVarsCircle.Loc[i].getBlock().getType() != Material.AIR)
                                {
                                    _lazerVarsCircle.Loc[i].getWorld().createExplosion(_lazerVarsCircle.Loc[i], 5);
                                    Bukkit.getServer().getScheduler().cancelTask((LazerCircleTaskID.get(_player.getName())));
                                }
                            }
                            PlayerLazerCircleBeam.put(LazerCircleTaskID.get(_player.getName()), _lazerVarsCircle);
                        }
                        for (int j = 0; j < increment; j++)
                        {
                        //    MyLogger.info("sending packets to display particles");
                        //    MyLogger.info("X , Y , Z particle locations " + _lazerVarsCircle.Loc[j].getX() + _lazerVarsCircle.Loc[j].getY() + _lazerVarsCircle.Loc[j].getZ());
                            PacketPlayOutWorldParticles _packet = new PacketPlayOutWorldParticles(EnumParticle.valueOf(_particleType), true,
                                    (float) (_lazerVarsCircle.Loc[j].getX()), (float) (_lazerVarsCircle.Loc[j].getY()), (float) (_lazerVarsCircle.Loc[j].getZ()),(float) 0,(float) 0,(float) 0,(float) 0, 1);
                                for(Player _online : Bukkit.getOnlinePlayers())
                                {
                                    ((CraftPlayer)_online).getHandle().playerConnection.sendPacket(_packet);
                                }
                        }
                      //  MyLogger.info("about to exit run");
                    }
                },0L, 2L));
      
        }
    Pictures:
    Images (open)

    The particles circle doesn't look 100% there because I'm on a laptop with a bad connection at the moment. Normally it is a perfect circle. But, the problem can still be seen


    Thanks,
    Skier
     
    Last edited: Jan 12, 2016
  2. Offline

    567legodude

    @ski23 Could you post photos of what is happening. And maybe a drawing of the desired effect?
     
  3. Offline

    ski23

    Last edited: Jan 11, 2016
  4. Offline

    87pen

    I see your photos are not loading the reason for this is because when adding the bb code for an image after the link in the img bb code put a .jpg and it will display your photo and not this photo not found picture -> [​IMG]
     
  5. Offline

    mcdorli

    Why not just have a cartesian coordinate and generate a circle around that point?
    Code:
    x = cos(angle)*r + middleX
    y = sin(angle)*r + middleY
    
    Then just add the players direction (probably divide it with something, so it goes slower) to the middle location, and voilá, You're done.
     
  6. Offline

    ski23

    @mcdorli That won't work. Originally I do generate a cartesian circle but, it isnt as simple as just dividing it by something to move it. You might not understand that I am not simply moving the circle but tilting it so that it is perpendicular to the vector player direction. I'm not sure what you mean "so it goes slower. @87pen Yea, I saw the photos wont working so I just added the links.

    @Zombie_Striker Any help?

    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: Jan 12, 2016
  7. Offline

    Zombie_Striker

    @ski23
    From what I can tell, you are not taking into account the player direction when forming the circle. You need to check which way the player is looking, and then apply the formula correctly to whichever applies in that situation (You would need to change ".setZ" to ".setX" if the player turns East/West)
     
  8. Offline

    ski23

    @Zombie_Striker When I create the circle I do not take that into account. I generate the circle about the x axis. Then, I move it to be in the correct position relative to the player direction using the ro and phi values in the part of the code commented with: adding the arcs to the spherical coords. This gives a good visual representation of what I am doing with ro and phi:
    http://hyperphysics.phy-astr.gsu.edu/hbase/sphc.html
    Basically, I convert the player direction into ro and phi which are the angles down from the z (y in minecraft) and away from the x axis. I then add these angles to the ro and phi angles of the points in the circle once they are in polar form.
    Edit: Even as it is now, the circle always generates in the right position relative to the player direction but, the circle itself gets distorted. I know the original circle I create about the x axis is not distorted from testing. It's when I move it that it gets distorted. So I am pretty sure that the problem lies within here:
    Code:
                            double    changeTheta = Math.acos(playerDirection.getX() / (Math.sin(Math.acos((playerDirection.getY())))));
                            //if (changeTheta < 0 )
                            //{
                            //    changeTheta += 2 * Math.PI;
                            //}
                            if (playerDirection.getZ() < 0)
                            {
                                changeTheta = ( 2 * Math.PI) - changeTheta;
                            }
                            double changePhi = Math.acos(playerDirection.getY()) - ((Math.PI)/2);
    or Here:
    Code:
                                //adding the arcs to the spherical coords
                                thetaNew = (theta + changeTheta);
                                phiNew = (phi + changePhi);
     
  9. Offline

    567legodude

    @ski23 I'm a bit of a mathy guy, I will look into this, do some tests, and get back to you with my results.
     
  10. Offline

    ChipDev

    n
    Nope, actually. Sorry! I also don't exactly know what you mean, as in 90 degrees from the player? I have also tried to make this before (Not just using a x=sin(t), z=cos(t) etc. but have failed.
    Sorry!
     
    ski23 likes this.
  11. Offline

    ski23

    @ChipDev I'll try to get it working and explain it when I have it. I'm currently starting on a very big api that will include this most likely. While the math behind this is complex, the power that it will give is astronomical. I think this could be a big help to devs working with particle effects. Any help that I can get is still appreciated.
     
    ChipDev likes this.
  12. Offline

    ChipDev

    I'll be happy to see the result, but I'll help when I can :)
     
  13. Offline

    567legodude

    @ski23 Hey, I got it working. Looks great.
    You just have to use the parametric equation for a circle in 3D space:
    P(a) = C + r*cos(a)*u + r*sin(a)*n
    Where u and n are orthonormal vectors (meaning perpendicular) that go through the circle.
    Pics (open)

    Pitch: 0
    Yaw: 0
    [​IMG]
    Pitch: 45
    Yaw: 0
    [​IMG]
    Pitch: 45
    Yaw: 45
    [​IMG]
    Code (open)
    You will probably have to modify this to what you want.
    But it works perfectly fine.
    Code:
    public void drawCircle(Location center, double radius, double pitch, double yaw) {
        yaw = Math.toRadians(yaw);
        pitch = Math.toRadians(pitch);
        Vector right = new Vector(Math.cos(yaw + (Math.PI/2.0)), 0, Math.sin(yaw + (Math.PI/2.0)));
        Vector up = new Vector(Math.cos(yaw), Math.sin(pitch), Math.sin(yaw));
        Location temp = center.clone();
        double a = 0;
        for (int i = 0; i < 360; i = i + 2) {
            a = Math.toRadians(i);
            temp.setX(center.getX() + (radius * Math.cos(a) * right.getX()) + (radius * Math.sin(a) * up.getX()));
            temp.setY(center.getY() + (radius * Math.cos(a) * right.getY()) + (radius * Math.sin(a) * up.getY()));
            temp.setZ(center.getZ() + (radius * Math.cos(a) * right.getZ()) + (radius * Math.sin(a) * up.getZ()));
            // send particle at location temp
        }
    }
     
    Last edited: Jan 12, 2016
    ChipDev, ski23 and Juancomaster1998 like this.
  14. Offline

    mcdorli

    Congratz, now, that piece of glass changed to a sacrifical altar.
     
  15. Offline

    ski23

    @567legodude That sounds great! I'll try it as soon as I get the chance!

    Do you happen to know how to fix what I have? For example I think I would need what I have if I wanted to put a square somewhere tilted in 3d or some other shape.

    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Juancomaster1998 likes this.
  16. What's that particle at the middle of the glass? maybe you should start your index at 1 and stop it at 360 instead of starting it at 0 and stoping at 359
     
  17. Offline

    567legodude

    @Juancomaster1998 The black particle marks the center point, I draw that separately from the circle.
     
  18. @567legodude
    i = 0;
    a = Math.toRadians(i) = 0;
    temp.setX(center.getX() + 0 + 0);
    temp.setY(center.getY() + 0 + 0);
    temp.setZ(center.getZ() + 0 + 0);

    temp == center
     
  19. Offline

    567legodude

    @Juancomaster1998 That is not correct.
    radius = 2
    pitch = 0
    yaw = 0
    i = 0
    a = i -> radians = 0
    setX(centerX + 0 + 0)
    setY(centerY + 0 + 0)
    setZ(centerZ + 2 + 0)
    temp != center

    i = 0 makes:
    [​IMG]
     
  20. If you debuged I suppose it works, but why that + radius on z?

    setZ(center.getZ() + (radius * Math.cos(a) * right.getZ()) + (radius * Math.sin(a) * up.getZ()));
    setZ(centerZ + (2 * 0 * x) + (2 * 0 * y);
    setZ(centerZ + 0 + 0);
    Every value multiplied by 0 = 0
     
  21. Offline

    567legodude

    @Juancomaster1998 But every value is not multiplied by 0.
    Vector right = (cos(90), 0, sin(90))
    right = (0, 0, 1)

    setZ(centerZ + (r * cos(0) * rZ) + (r * sin(0) * uZ))
    setZ(centerZ + (2 * 1 * 1) + (2 * 0 * 0)
    setZ(centerZ + 2 + 0)

    Don't forget cos(0) is 1. And right.getZ is also 1 in this case.
     
  22. Offline

    ChipDev

    AMAZING.
     
  23. Offline

    ski23

    After hours of testing and debugging, it still doesn't seem to work. I can''t decipher how the pitch and yaw are playing into the circle. When I look different directions, the circle is tilted in different ways but doesn't seem to be in any way connected to where I am looking. Also when I look straight up, the circle is distorted into an oval.
    http://imgur.com/1KCSKjh
    Edit:
    I would still prefer to get the original algorithm working though as I would like to be able to tilt more than just circles.
    @567legodude Which is resource pack is that?
     
  24. Offline

    567legodude

    @ski23 It's not really a texture pack it's just 64x.
    The pitch is the up/down angle, it will tilt the circle.
    The yaw is the left/right angle, it will spin the circle.
    I can't tell what algorithm you are trying to use. But if you can give me the formula, or tell me what formula to search for, I can probably figure it out.
     
  25. Offline

    ski23

    @567legodude So, first I get the direction the player is looking and I convert the point at the end of the vector from the cartesian coordinate system to 3 dimentional polar coordinates. This can be done using these formulas to go back and forth between the cartesian and polar systems:https://en.wikipedia.org/wiki/Spherical_coordinate_system
    I create a circle around the x axis in cartesian coordinates. I do this with a center of 1,0,0. The center of this circle can be represented as (1,90,0) 1 is ro or the distance from the origin, 90 is the angle down from the z axis or phi (y in minecraft), 0 is the angle around the x axis or theta (its on the axis so 0). Then, I take all of the points that I generated in the cartesian coordinate system and convert them to spherical coordinates(3d polar coordinates). Earlier on, I get change in theta and change in phi between the player's direction vector and the center of the circle. This tells me the angles that I need to move the points to get them to the desired location. Then, I add changeTheta and ChangePhi to the phi and theta of all of the points in the circle. Now, I should have the final circle in spherical coordinates. Next, I convert the spherical coordinates back to cartesian coordinates with the player's location representing the origin. Then I simply need to send the packets.
     
  26. Offline

    ski23

    bump.
     
  27. Offline

    ski23

    bump.
     
  28. Offline

    567legodude

    @ski23 So after spending hours researching 3D rotation. I have finally done it. (I realized what I was doing wrong.)
    I solved the problem by using rotation matrices.
    So my old method just drew a circle at an angle (and sometimes it was distorted). But I have made a new method which can transform any shape.
    Just remember that since the x, y, and z rotations are separate, they may be slightly off at 45 degree rotations.
    EDIT: Actually you can use a roll value to compensate for the yaw, so forget what I said about it being distorted at 45 angles.
    New Code (open)
    it works pretty darn good (see video)
    Code:
    // Point is simply a class that holds x, y, and z values.
    // The list of points should be relative to the center; i.e. a point of (0, 1, 0) will be 1 block above the center you specify.
    private List<Location> transformPoints(Location center, List<Point> points, double yaw, double pitch, double roll, double scale) {
            // Convert to radians
        yaw = Math.toRadians(yaw);
        pitch = Math.toRadians(pitch);
        roll = Math.toRadians(roll);
        List<Location> list = new ArrayList<>();
            // Store the values so we don't have to calculate them again for every single point.
        double cp = Math.cos(pitch);
        double sp = Math.sin(pitch);
        double cy = Math.cos(yaw);
        double sy = Math.sin(yaw);
        double cr = Math.cos(roll);
        double sr = Math.sin(roll);
        double x, bx, y, by, z, bz;
        for (Point point : points) {
            x = point.getX();
            bx = x;
            y = point.getY();
            by = y;
            z = point.getZ();
            bz = z;
            x = ((x*cy-bz*sy)*cr+by*sr)*scale;
            y = ((y*cp+bz*sp)*cr-bx*sr)*scale;
            z = ((z*cp-by*sp)*cy+bx*sy)*scale;
            list.add(new Location(center.getWorld(), (center.getX()+x), (center.getY()+y), (center.getZ()+z)));
        }
        return list;
            // list contains all the points where particles should be
    }
    And here it is being used:
    Video (open)
     
    Last edited: Jan 17, 2016
  29. Offline

    ski23

    Does this allow you to rotate it up and down as well? That was the original problem.
     
Thread Status:
Not open for further replies.

Share This Page