vector.cross = vector.cross or function(a, b) return { x = a.y * b.z - a.z * b.y, y = a.z * b.x - a.x * b.z, z = a.x * b.y - a.y * b.x } end local function project(player) local camera_z = player:get_look_dir() local camera_x = minetest.yaw_to_dir(player:get_look_horizontal() + math.pi / 2) local camera_y = vector.cross(camera_x, camera_z) for theta = 0, math.pi * 2 - 0.1, 0.1 do local cv = {x = math.cos(theta) * 2, y = math.sin(theta) * 2, z = 2} local wv = player:get_pos() wv.y = wv.y + player:get_properties().eye_height wv = vector.add(wv, vector.multiply(camera_x, cv.x)) wv = vector.add(wv, vector.multiply(camera_y, cv.y)) wv = vector.add(wv, vector.multiply(camera_z, cv.z)) minetest.add_particle({ pos = wv, size = 0.25, texture = "solid_generated_color.png" }) end end local function tick() for _, v in pairs(minetest.get_connected_players()) do project(v) end minetest.after(0.25, tick) end tick()