|null four corners (top left, top * right, bottom right, bottom left): 3D x y z, u v */ public static function plane(Camera3d $cam, float $speed, float $tw, float $th, float $scrollX = 0.0): ?array { $depth = $cam->distance * (1 / $speed - 1); $a = deg2rad($cam->yaw * self::TURN); $normal = [sin($a), 0.0, cos($a)]; // towards the camera $side = [cos($a), 0.0, -sin($a)]; // the plane's own x axis [$tx, $ty3] = $cam->target; $c = [$tx - $normal[0] * $depth, $ty3, -$normal[2] * $depth]; $e = $cam->eye; $k = $cam->zoom * $speed; $m = 6; $out = []; // the layer's rows are hung on the point the middle of the view sees: a camera that looks down a little does // not lift the mountains up into the sky $mid = null; foreach ([[Camera3d::VIEW_W / 2, Camera3d::VIEW_H / 2], [-$m, -$m], [Camera3d::VIEW_W + $m, -$m], [Camera3d::VIEW_W + $m, Camera3d::VIEW_H + $m], [-$m, Camera3d::VIEW_H + $m]] as [$vx, $vy]) { $d = $cam->ray($vx, $vy); $dn = $d[0] * $normal[0] + $d[2] * $normal[2]; if ($dn >= -1e-6) { return null; } $t = (($c[0] - $e[0]) * $normal[0] + ($c[2] - $e[2]) * $normal[2]) / $dn; $p = [$e[0] + $t * $d[0], $e[1] + $t * $d[1], $e[2] + $t * $d[2]]; // where this point is drawn by the 2D view at 0 degrees, then which pixel of the layer is there $local = ($p[0] - $c[0]) * $side[0] + ($p[2] - $c[2]) * $side[2]; $sx = Camera3d::VIEW_W / 2 + $local * $k; if ($mid === null) { $mid = $p[1]; continue; } $sy = Camera3d::VIEW_H / 2 + ($mid - $p[1]) * $k; $out[] = [$p[0], $p[1], $p[2], ($sx + $scrollX) / $tw, $sy / $th]; } return $out; } }