Ява Брезенхем линия к точкам на окружности - PullRequest
0 голосов
/ 20 марта 2019

Я пытаюсь написать движок молнии для 2D плитки. У меня есть карта [] [] и карта освещения [] [] на карте, как и в большинстве игр. ничего особенного. Из светлой точки я рисую круг в диапазоне света и пытаюсь нарисовать линии Брезенхема в точках на круге. по какой-то причине мой алгоритм Брезенхэма не идеален, я добавил его bresenham error. почему-то линия Брезенхема пропускает некоторые диагональные координаты (например, из точки 20,20 пропущены 13,14 и 14,13.) Я думаю, что вычисление ошибок неверно. Я использую set, чтобы избежать дублирования в массивах. Алгоритм круга работает отлично, и в массивах я получил правильные значения. Еще одна проблема на рисунке: в некоторых случаях горизонтальные и иногда вертикальные линии тоже неправильны. Я думаю, что это тоже ошибка Брезенхэма. Я пытаюсь отладить debugging, это показывает ошибку Bresenham. Белые кубы - это точки на окружности, красные цвета добавляются в карту освещения с помощью линии Брезенхамса, есть холм, который блокирует свет (он работает отлично), и моя миникарта включена. Я добавил еще несколько изображений: enter image description hereenter image description here

import java.awt.geom.Point2D;
import java.util.HashSet;

public class PointLight {

Handler handler;
public float x,y;
private int plx,ply;
public int radius,intensity;
public static Point2D.Double pnt;
public static HashSet<Point2D> circpoints;
public static HashSet<Point2D> bline;
public PointLight(float x,float y,int radius,int intensity,Handler handler) {
    super();
    this.x=x;
    this.y=y;
    this.radius=radius;
    this.intensity=intensity;
    this.handler=handler;
    circpoints = new HashSet<Point2D>();
    bline = new HashSet<Point2D>();
    pnt=new Point2D.Double();
}

public void tick() {
    //visibility for rendering process (1000 is for range, to avoid sudden light emitting)
    if (x > Game.camera.getX()-1000 && x < Game.camera.getX()+Game.Windoww+1000 && y > Game.camera.getY()-1000 && y < Game.camera.getY()+Game.Windowh+1000) {
        if (!Game.visiblepointlight.contains(this)) Game.visiblepointlight.add(this);
    } else {
        if (Game.visiblepointlight.contains(this)) Game.visiblepointlight.remove(this);
    }

    //reset visible lightmap
    for (int i=(int)((Game.camera.getX()-1000)/Game.tilesize);i < (int)((Game.camera.getX()+1000+Game.Windoww)/Game.tilesize);i++) {
        for (int j=(int)((Game.camera.getY()-1000)/Game.tilesize);j < (int)((Game.camera.getY()+1000+Game.Windowh)/Game.tilesize);j++) {
            if (i > 0 && i < Game.tiles && j > 0 && j < Game.tiles) Game.lightMap[i][j]=0;
        }
    }

    //set lights
    for (PointLight pli : Game.visiblepointlight) {
        bline.clear();
        circpoints.clear();
        Lightcalculate(pli);
    }

}

public void Lightcalculate(PointLight pl) {

    plx=(int)(pl.x)/Game.tilesize;
    ply=(int)(pl.y)/Game.tilesize;

    PointsOnCircumference(plx,ply,pl.radius);
    for (Point2D circp : circpoints) {
        bresenhamsLine(plx,ply,(int)circp.getX(),(int)circp.getY());

        for (Point2D plotl :bline) {
            double mult = Game.AmbientLight+(pl.intensity / PointDistance(plx,ply,plotl.getX(),plotl.getY()))/(Game.tilesize*2);
            //working but no brightness addition:
            //if (mult >1) mult=1;
            //Game.lightMap[(int) plotl.getX()][(int) plotl.getY()] = mult;
            Game.lightMap[(int) plotl.getX()][(int) plotl.getY()] += mult;
            if (Game.lightMap[(int) plotl.getX()][(int) plotl.getY()] > 1) Game.lightMap[(int) plotl.getX()][(int) plotl.getY()]= 1;
        }

    }
}

private double PointDistance(double x1, double y1, double x2, double y2) {       
    return Math.sqrt((y2 - y1) * (y2 - y1) + (x2 - x1) * (x2 - x1));
}

private void PointsOnCircumference(int x0, int y0, int radius)
{
    int x = radius-1;
    int y = 0;
    int dx = 1;
    int dy = 1;
    int err = dx - (radius << 1);

    while (x >= y) {
        pnt.setLocation(x0 + x, y0 + y);
        circpoints.add((Point2D) pnt.clone());
        pnt.setLocation(x0 + y, y0 + x);
        circpoints.add((Point2D) pnt.clone());
        pnt.setLocation(x0 - y, y0 + x);
        circpoints.add((Point2D) pnt.clone());
        pnt.setLocation(x0 - x, y0 + y);
        circpoints.add((Point2D) pnt.clone());
        pnt.setLocation(x0 - x, y0 - y);
        circpoints.add((Point2D) pnt.clone());
        pnt.setLocation(x0 - y, y0 - x);
        circpoints.add((Point2D) pnt.clone());
        pnt.setLocation(x0 + y, y0 - x);
        circpoints.add((Point2D) pnt.clone());
        pnt.setLocation(x0 + x, y0 - y);
        circpoints.add((Point2D) pnt.clone());

        if (err <= 0) {
            y++;
            err += dy;
            dy += 2;
        }

        if (err > 0) {
            x--;
            dx += 2;
            err += dx - (radius << 1);
        }
    }
}

private void bresenhamsLine(double x1,double y1,double x2,double y2) {
    double dx = Math.abs(x2 - x1);
    double dy = Math.abs(y2 - y1);

    double sx = (x1 < x2 ? 1 : -1);
    double sy = (y1 < y2 ? 1 : -1);

    double error = dx - dy;

    double x = x1, y = y1;

    while(1==1)
    {
        if ((int)x < 0 || (int)y < 0 ) break;
        if ((int)x >= Game.tiles || (int)y >= Game.tiles ) break;
        pnt.setLocation(x,y);
        bline.add((Point2D) pnt.clone());
        if (Game.map[(int)x][(int)y] >= 0.8) break; //light blocker

        if(x==x2 && y==y2) { break; }

        double e2 = 2 * error;

        if(e2 >= -dy) { error-= dy; x+= sx; }
        if(e2 <= dx) { error+= dx; y+= sy; }
    }
}
}

1 Ответ

0 голосов
/ 26 марта 2019

Я понял, как генерировать тени-источники света другим способом. Это проще и работает на 100%. Вот код для точечного светильника молнии:

import java.awt.Point;
import java.util.HashSet;
import java.util.Random;

public class PointLight {

Handler handler;
public int x,y;
private int plx,ply;
public int radius,intensity;
public static Point pnt;
public static HashSet<Point> circpoints;
public static HashSet<Point> bline;
public GameObject BoundToObject;

public PointLight(int x,int y,int radius,int intensity,Handler handler) {
    super();
    this.x=x;
    this.y=y;
    this.radius=radius;
    this.intensity=intensity;
    this.handler=handler;
    circpoints = new HashSet<Point>();
    bline = new HashSet<Point>();
    pnt=new Point();
}

public PointLight(GameObject GamObj,int radius,int intensity,Handler handler) {
    super();
    this.BoundToObject=GamObj;
    this.x=(int) GamObj.getX();
    this.y=(int) GamObj.getY();
    this.radius=radius;
    this.intensity=intensity;
    this.handler=handler;
    circpoints = new HashSet<Point>();
    bline = new HashSet<Point>();
    pnt=new Point();
}

public void tick() {
    Random rnd;

    rnd=new Random();
    x+=rnd.nextDouble()*4;
    y+=rnd.nextDouble()*4;

    if (BoundToObject != null) {
        x=(int) BoundToObject.getX();
        y=(int) BoundToObject.getY();
    }

    //visibility for rendering process (200 is for range, to avoid sudden light emitting)
    if (x > (int)Game.camera.getX()-200 && x < (int)Game.camera.getX()+Game.Windoww+200 && y > (int)Game.camera.getY()-200 && y < (int)Game.camera.getY()+Game.Windowh+200) {
        if (!Game.visiblepointlight.contains(this)) Game.visiblepointlight.add(this);
    } else {
        if (Game.visiblepointlight.contains(this)) Game.visiblepointlight.remove(this);
    }

    //reset visible lightmap
    for (int i=(int)((Game.camera.getX()-200)/Game.tilesize);i < (int)((Game.camera.getX()+200+Game.Windoww)/Game.tilesize);i++) {
        for (int j=(int)((Game.camera.getY()-200)/Game.tilesize);j < (int)((Game.camera.getY()+200+Game.Windowh)/Game.tilesize);j++) {
            if (i > 0 && i < Game.tiles && j > 0 && j < Game.tiles) Game.lightMap[i][j]=Game.AmbientLight;
        }
    }

    //set lights
    for (PointLight pli : Game.visiblepointlight) {
        bline.clear();
        circpoints.clear();
        Lightcalculate(pli);
    }

}

public void Lightcalculate(PointLight pl) {

    plx=pl.x/Game.tilesize;
    ply=pl.y/Game.tilesize;

    RayCast(plx,ply,pl.radius);
        for (Point bln :bline) {
            double mult = (pl.intensity / PointDistance(plx,ply,bln.x,bln.y))/(Game.tilesize*3);
            if (Game.lightMap[bln.x][bln.y] < mult) Game.lightMap[bln.x][bln.y] =     mult;
            if (Game.lightMap[bln.x][bln.y] > 1) Game.lightMap[bln.x][bln.y]= 1;
        }
}

private void RayCast(int xcord,int ycord,int range) {

    double angle = 0;
    int rays=range*10+1;

    for (int i=0; i<rays; i++) {
        angle += 2 * Math.PI / rays;
        for (int j=0;j<range;j++) {
            double xx = xcord+Math.sin(angle)*j;
            double yy = ycord+Math.cos(angle)*j;
            if ((int) xx <= 0 || (int) yy <= 0 || (int) xx >= Game.tiles-1 || (int) yy >= Game.tiles-1 ) continue;
            if (Game.map[(int) xx][(int)yy] >= 0.8) break;
            Point dir = new Point((int) Math.round(xx),(int)Math.round(yy));
            bline.add(dir);
        }
    }
}

private double PointDistance(int x1, int y1, int x2, int y2) {       
    return Math.sqrt((y2 - y1) * (y2 - y1) + (x2 - x1) * (x2 - x1));
}
}
...