// based on this source https://github.com/camthesaxman/cubecraft/blob/master/source/field.c#L78 // and fixes from this https://gamedev.stackexchange.com/questions/47362/cast-ray-to-select-block-in-voxel-game // todo: instead of all parkour fixes just do one mod and if its on block boundary just add 0.00001f to it lol public static bool RaycastVoxel(World world, Vector3 origin, Vector3 dir, out RaycastVoxelHit hit) { if (dir == Vector3.zero) { Debug.LogError("RaycastVoxel dir is zero!"); hit.bpos = Vector3i.zero; hit.dir = Dir.none; return(false); } // make sure origin never exactly lines up to block boundary // this is kinda filth but seems to work more reliably const float ff = 0.001f; if (origin.x % Chunk.BLOCK_SIZE == 0) { origin.x += ff; } if (origin.y % Chunk.BLOCK_SIZE == 0) { origin.y += ff; } if (origin.z % Chunk.BLOCK_SIZE == 0) { origin.z += ff; } Vector3i pos = WorldUtils.GetBlockPos(origin); // check if inside a block already if (world.GetBlock(pos.x, pos.y, pos.z).ColliderSolid()) { hit.bpos = pos; hit.dir = Dir.none; return(true); } #if _DEBUG lastOrigin = origin; lastDir = dir; posAlong.Clear(); posAlong.Add(pos); #endif // how far we are from origin in blocks Vector3i radius = Vector3i.zero; // direction to increment when stepping Vector3i step = Vector3i.zero; // ray distance it takes to equal one block unit in each direction (this one doesnt change in loop) Vector3 tDelta = Vector3.positiveInfinity; // ray distance it takes to move to next block boundary in each direction (this changes) Vector3 tMax = Vector3.positiveInfinity; if (dir.x > 0.0f) { step.x = 1; tDelta.x = Chunk.BLOCK_SIZE / dir.x; tMax.x = (Mathf.Ceil(origin.x * Chunk.BPU) / Chunk.BPU - origin.x) / dir.x; //tMax.x = (ceil(origin.x * Chunk.BPU) / Chunk.BPU - origin.x) / dir.x; } else if (dir.x < 0.0f) { step.x = -1; tDelta.x = -Chunk.BLOCK_SIZE / dir.x; tMax.x = -(origin.x - Mathf.Floor(origin.x * Chunk.BPU) / Chunk.BPU) / dir.x; //bool atBoundary = Mathf.Round(origin.x * Chunk.BPU) == origin.x * Chunk.BPU; //bool atBoundary = Mth.Mod(origin.x, Chunk.BPU) == 0.0f; //tMax.x = atBoundary ? 0 : -(origin.x - Mathf.Floor(origin.x * Chunk.BPU) / Chunk.BPU) / dir.x; } if (dir.y > 0.0f) { step.y = 1; tDelta.y = Chunk.BLOCK_SIZE / dir.y; tMax.y = (Mathf.Ceil(origin.y * Chunk.BPU) / Chunk.BPU - origin.y) / dir.y; //tMax.y = (ceil(origin.y * Chunk.BPU) / Chunk.BPU - origin.y) / dir.y; } else if (dir.y < 0.0f) { step.y = -1; tDelta.y = -Chunk.BLOCK_SIZE / dir.y; tMax.y = -(origin.y - Mathf.Floor(origin.y * Chunk.BPU) / Chunk.BPU) / dir.y; //bool atBoundary = Mathf.Round(origin.y * Chunk.BPU) == origin.y * Chunk.BPU; //bool atBoundary = Mth.Mod(origin.y, Chunk.BPU) == 0.0f; //tMax.y = atBoundary ? 0 : -(origin.y - Mathf.Floor(origin.y * Chunk.BPU) / Chunk.BPU) / dir.y; } if (dir.z > 0.0f) { step.z = 1; tDelta.z = Chunk.BLOCK_SIZE / dir.z; tMax.z = (Mathf.Ceil(origin.z * Chunk.BPU) / Chunk.BPU - origin.z) / dir.z; //tMax.z = (ceil(origin.z * Chunk.BPU) / Chunk.BPU - origin.z) / dir.z; } else if (dir.z < 0.0f) { step.z = -1; tDelta.z = -Chunk.BLOCK_SIZE / dir.z; tMax.z = -(origin.z - Mathf.Floor(origin.z * Chunk.BPU) / Chunk.BPU) / dir.z; //bool atBoundary = Mathf.Round(origin.z * Chunk.BPU) == origin.z * Chunk.BPU; //bool atBoundary = Mth.Mod(origin.z, Chunk.BPU) == 0.0f; //tMax.z = atBoundary ? 0 : -(origin.z - Mathf.Floor(origin.z * Chunk.BPU) / Chunk.BPU) / dir.z; } Assert.IsTrue(tDelta.x != 0 || tDelta.y != 0 || tDelta.z != 0); while (radius.x * radius.x + radius.y * radius.y + radius.z * radius.z < SELECT_RADIUS * SELECT_RADIUS) { if (tMax.x < tMax.y) { if (tMax.x < tMax.z) { // increment x tMax.x += tDelta.x; pos.x += step.x; radius.x++; hit.dir = step.x > 0 ? Dir.west : Dir.east; } else { // increment z tMax.z += tDelta.z; pos.z += step.z; radius.z++; hit.dir = step.z > 0 ? Dir.south : Dir.north; } } else { if (tMax.y < tMax.z) { // increment y tMax.y += tDelta.y; pos.y += step.y; radius.y++; hit.dir = step.y > 0 ? Dir.down : Dir.up; } else // duplicated from above // increment z { tMax.z += tDelta.z; pos.z += step.z; radius.z++; hit.dir = step.z > 0 ? Dir.south : Dir.north; } } #if _DEBUG posAlong.Add(pos); #endif // if hit a solid block then return successfully! if (world.GetBlock(pos.x, pos.y, pos.z).ColliderSolid()) { hit.bpos = pos; return(true); } } hit.bpos = Vector3i.zero; hit.dir = Dir.none; return(false); }
void Update() { // scroll to select blocks (create mesh as you switch) float scroll = Input.GetAxis("Mouse ScrollWheel"); bool changed = true; if (scroll > 0.0f) { blockIndex--; } else if (scroll < 0.0f) // scroll down will progress list forward { blockIndex++; } else { changed = false; } blockIndex = Mth.Mod(blockIndex, blocks.Length); if (changed) { MeshBuilder.GetBlockMesh(blocks[blockIndex], blockMeshFilter); } // show square around block aiming at // todo: have option to show 2x2 for hammering! //bool mainRaycast = Physics.Raycast(transform.position, transform.forward, out hit, 1000); drawer.Clear(); RaycastVoxelHit vhit; bool success = BlonkPhysics.RaycastVoxel(world, transform.position, transform.forward, out vhit); if (success) { // move cube towards camera direction a little so it looks better when intersecting with other blocks Vector3 center = (vhit.bpos.ToVector3() + Vector3.one * placementSize * 0.5f) / Chunk.BPU; drawer.AddBounds(new Bounds(center - transform.forward * 0.01f, Vector3.one * placementSize / Chunk.BPU), Color.white); } // left click delete if (Input.GetMouseButtonDown(0) && success) { lastPos = transform.position; lastHit = vhit; //todo: add some better directional intuitions on where to place edit shape for (int y = 0; y < placementSize; ++y) { for (int z = 0; z < placementSize; ++z) { for (int x = 0; x < placementSize; ++x) { world.SetBlock(vhit.bpos + new Vector3i(x, y, z), Blocks.AIR); } } } } // right click place if (Input.GetMouseButtonDown(1) && success) { lastPos = transform.position; lastHit = vhit; for (int y = 0; y < placementSize; ++y) { for (int z = 0; z < placementSize; ++z) { for (int x = 0; x < placementSize; ++x) { world.SetBlock(vhit.bpos + Dirs.GetNormal(vhit.dir) + new Vector3i(x, y, z), blocks[blockIndex]); } } } } if (Input.GetKeyDown(KeyCode.UpArrow)) { placementSize++; } if (Input.GetKeyDown(KeyCode.DownArrow)) { placementSize--; } if (placementSize < 1) { placementSize = 1; } if (Input.GetKeyDown(KeyCode.F1)) { flyCam.ToggleFlyMode(); } if (Input.GetKeyDown(KeyCode.F2)) { drawChunkBorders = !drawChunkBorders; } if (Input.GetKeyDown(KeyCode.F3)) { ToggleDebugDay(); } if (Input.GetKeyDown(KeyCode.F4)) { LoadChunks.drawDebug = !LoadChunks.drawDebug; } if (Input.GetKeyDown(KeyCode.F5)) { LoadChunks.updateChunks = !LoadChunks.updateChunks; } if (drawChunkBorders) { DrawChunkBorders(); } }