Очень неточная позиция для радиопередачи в центре экрана в воксельной игре - PullRequest
0 голосов
/ 08 марта 2019

У меня проблемы с реализацией алгоритма Джона Аманатида в моем проекте, подобном Minecraft.Кажется, алгоритм работает должным образом, но луч как-то не направлен в правильном направлении или из правильного положения.Я беру углы поворота (сохраненные в трехмерном векторе) и преобразую их в компоненты dx, dy и dz вектора курса.Блоки, которые фактически удалены лучом, очень близки (в большинстве случаев) к фактическому положению куба.Я несколько раз переделывал алгоритм с разными стратегиями и даже конвертировал положение и вращение камеры в двойные, но это ничего не меняет.

Это формат моего кода: -Класс EventHandler обрабатывает ввод мыши каждый кадр и отправляет запрос dig в класс RayMaster.Этот класс создает объект Ray в отдельном потоке и зацикливается до тех пор, пока не будет достигнут блок или пока не будет достигнут максимальный радиус (8).В случае попадания лучевой мастер отправляет событие обратно в обработчик событий, который сообщает миру об удалении этого блока.С этим базовым объяснением, вот код:

public class EventHandler {

    public static void proccessInput () {
        if (Mouse.pressed(GLFW.GLFW_MOUSE_BUTTON_LEFT)) {
            RayMaster.castRay(Camera.getPosition(), Camera.getRotation(), EventType.DIG_EVENT);
        }
    }

    public static void dispatchEvent (Event e, SubChunk chunk) {
        Vectori location;
        switch (e.getType()) {
        case DIG_EVENT:
            location = e.getLocation();

            chunk.setCube(location.x(), location.y(), location.z(), BlockLookup.AIR);
            ChunkScape.addToUpdateQueue(chunk);
            break;
        }
    }
}

Метод castRay() в классе RayMaster:

public static void castRay (Vector3 position, Vector3 rotation, EventType onHitEvent) {
    Thread raycast = new Thread(()-> {
        Ray ray = new Ray(position, rotation);

        Vectori rayPos = ray.step();

        int chunkX = STUMath.fastfloor(rayPos.x()/16d)*16;
        int chunkY = STUMath.fastfloor(rayPos.y()/16d)*16;
        int chunkZ = STUMath.fastfloor(rayPos.z()/16d)*16;

        SubChunk currentChunk = null;
        for (Chunk chunk : ChunkScape.getChunks()) {
            for (SubChunk subChunk : chunk.getSubChunks()) {
                if (subChunk.getAbsX() == chunkX && subChunk.getAbsY() == chunkY && subChunk.getAbsZ() == chunkZ) {
                    currentChunk = subChunk;
                }
            }
        }

        while (rayPos != null) {
            int nextChunkX = STUMath.fastfloor(rayPos.x()/16d)*16;
            int nextChunkY = STUMath.fastfloor(rayPos.y()/16d)*16;
            int nextChunkZ = STUMath.fastfloor(rayPos.z()/16d)*16;

            if (nextChunkX != chunkX || nextChunkY != chunkY || nextChunkZ != chunkZ) {
                currentChunk = null;
                for (Chunk chunk : ChunkScape.getChunks()) {
                    for (SubChunk subChunk : chunk.getSubChunks()) {
                        if (subChunk.getAbsX() == nextChunkX && subChunk.getAbsY() == nextChunkY && subChunk.getAbsZ() == nextChunkZ) {
                            currentChunk = subChunk;
                        }
                    }
                }
            }

            chunkX = nextChunkX;
            chunkY = nextChunkY;
            chunkZ = nextChunkZ;

            if (currentChunk == null) return;

            int locX =STUMath.wrap(rayPos.x(), 0, SubChunk.CHUNK_SIZE);
            int locY =STUMath.wrap(rayPos.y(), 0, SubChunk.CHUNK_SIZE);
            int locZ =STUMath.wrap(rayPos.z(), 0, SubChunk.CHUNK_SIZE);

            rayPos = ray.step();

            if (currentChunk.getCube(locX, locY, locZ).getID() == BlockData.AIR_ID) continue;

            EventHandler.dispatchEvent(new Event(onHitEvent, new Vectori(locX, locY, locZ)), currentChunk);
            return;
        }
    });

    raycast.start();
}

Это класс Ray:

public class Ray {

    public static final float STEP_SIZE = 0.01f;
    public static final int MAX_DIST = 8;

    private int x, y, z;
    private int stepX, stepY, stepZ;
    private float tMaxX, tMaxY, tMaxZ;
    private float tDeltaX, tDeltaY, tDeltaZ;
    private Face faceEntered;

    public Ray (Vector3 position, Vector3 rotation) {
        x = STUMath.fastfloor(position.x());
        y = STUMath.fastfloor(position.y());
        z = STUMath.fastfloor(position.z());

        float cosX = (float)Math.cos(Math.PI/180 * rotation.x());
        float dx = (float)-Math.sin(Math.PI/180 * rotation.y())*cosX;
        float dz = (float)Math.cos(Math.PI/180 * rotation.y())*cosX;
        float dy = (float)-Math.sin(Math.PI/180 * rotation.x());

        stepX = dx > 0? 1 : dx < 0? -1 : 0;
        stepY = dy > 0? 1 : dy < 0? -1 : 0;
        stepZ = dz > 0? 1 : dz < 0? -1 : 0;

        tMaxX = findBound(position.x(), dx);
        tMaxY = findBound(position.y(), dy);
        tMaxZ = findBound(position.z(), dz);

        tDeltaX = stepX/dx;
        tDeltaY = stepY/dy;
        tDeltaZ = stepZ/dz;
    }

    public Vectori step () {
        if (stepX == 0 && stepY == 0 && stepZ == 0) return null;

        if (tMaxX < tMaxY) {
            if (tMaxX < tMaxZ) {
                if (tMaxX > MAX_DIST) return null;

                x += stepX;
                tMaxX += tDeltaX;

                faceEntered = stepX > 0? Face.LEFT : Face.RIGHT;
            } else {
                if (tMaxZ > MAX_DIST) return null;

                z += stepZ;
                tMaxZ += tDeltaZ;

                faceEntered = stepZ > 0? Face.FRONT : Face.BACK;
            }
        } else {
            if (tMaxY < tMaxZ) {
                if (tMaxY > MAX_DIST) return null;
                y += stepY;
                tMaxY += tDeltaY;

                faceEntered = stepY > 0? Face.DOWN : Face.UP;
            } else {
                if (tMaxZ > MAX_DIST) return null;

                z += stepZ;
                tMaxZ += tDeltaZ;

                faceEntered = stepZ > 0? Face.FRONT : Face.BACK;
            }
        }

        return new Vectori(x, y, z);
    }

    public Face getFaceEntered () {
        return faceEntered;
    }

    private float findBound (float s, float ds) {
        return s<0? findBound(-s, -ds) : (1-s%1)/ds;
    }
}

Мой код ротации в классе Camera:

rotation.setI(0, rotation.x() + (float)(Mouse.getDY() * ROT_SENSITIVITY)); // sets the x-component of rotation
rotation.setI(1, rotation.y() + (float)(Mouse.getDX() * ROT_SENSITIVITY)); // sets the y-component of rotation

Библиотека joul - это созданный мной пакет, в котором есть несколько полезных утилит.Класс Vector3 является эквивалентом объекта Vector3f в библиотеке java open math.Математические функции в STUMath довольно понятны, поэтому я не испытываю необходимости объяснять их прямо сейчас, но если я что-то сделал неясно, пожалуйста, дайте мне знать.

РЕДАКТИРОВАТЬ: Еслиэто полезно для всех, я пытался использовать технику Хопсона от его недельного испытания, чтобы не победить.Это дает то же самое: слегка отклоненный луч.Когда люди используют алгоритм выбора moue, они должны отменить матрицу проекции.Может ли это быть причиной?Мне пока не очень удобно работать с матрицами (так что будьте осторожны со мной: P), но был бы признателен за ввод.

РЕДАКТИРОВАТЬ: Это трудно отладить.Я действительно не знаю, как легко рисовать линии, но я уверен, что положение и поворот камеры отбрасывают начальные значения.

РЕДАКТИРОВАТЬ: В недавней версии, где я переписал код в Java-12 Я снова попытался реализовать этот алгоритм.Я попытался не проецировать координаты центра экрана (0, 0, 1, -1), но это также оставило меня с иногда точным, а иногда и очень неточным радиопередачей.Я не знаю, использую ли я неправильный подход к лучевому вещанию вообще, или это внутренняя неточность.

Спасибо за любую помощь!

...