У меня проблемы с реализацией алгоритма Джона Аманатида в моем проекте, подобном 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), но это также оставило меня с иногда точным, а иногда и очень неточным радиопередачей.Я не знаю, использую ли я неправильный подход к лучевому вещанию вообще, или это внутренняя неточность.
Спасибо за любую помощь!