12 struct Point3D {
float x{},
y{},
z{}; };
14 float dot(
const std::array<float, 3>&
a,
15 const std::array<float, 3>&
b)
noexcept
16 {
return a[0] *
b[0] +
a[1] *
b[1] +
a[2] *
b[2]; }
18 std::array<float, 3>
subtract(
const std::array<float, 3>&
a,
19 const std::array<float, 3>&
b)
noexcept
20 {
return {
a[0] -
b[0],
a[1] -
b[1],
a[2] -
b[2]}; }
22 std::array<float, 3> scale(
const std::array<float, 3>&
a,
float value)
noexcept
28 std::array<float, 3>
normalize(
const std::array<float, 3>&
value)
noexcept
34 std::array<float, 3>
cross(
const std::array<float, 3>&
a,
35 const std::array<float, 3>&
b)
noexcept
38 a[1] *
b[2] -
a[2] *
b[1],
39 a[2] *
b[0] -
a[0] *
b[2],
40 a[0] *
b[1] -
a[1] *
b[0]};
43 Point3D transformPoint(Point3D point,
44 const sequence::Matrix3D&
matrix)
noexcept
47 const float x =
m[0] * point.x +
m[4] * point.y +
m[8] * point.z +
m[12];
48 const float y =
m[1] * point.x +
m[5] * point.y +
m[9] * point.z +
m[13];
49 const float z =
m[2] * point.x +
m[6] * point.y +
m[10] * point.z +
m[14];
50 const float w =
m[3] * point.x +
m[7] * point.y +
m[11] * point.z +
m[15];
51 if (
w == 0.0F)
return {};
52 return {
x /
w,
y /
w,
z /
w};
55 std::optional<std::int32_t>
coord(
float value)
noexcept
57 if (!std::isfinite(
value))
return std::nullopt;
58 constexpr auto low =
static_cast<float>(std::numeric_limits<std::int32_t>::min());
59 constexpr auto high =
static_cast<float>(std::numeric_limits<std::int32_t>::max());
61 return static_cast<std::int32_t
>(
value);
64 sequence::Matrix3D
makeView(
const World3DCamera& camera)
noexcept
66 const auto forward =
normalize(camera.forward);
67 const auto projected = scale(forward,
dot(camera.up, forward));
71 result.values[0] =
right[0]; result.values[1] = up[0]; result.values[2] = forward[0];
72 result.values[4] =
right[1]; result.values[5] = up[1]; result.values[6] = forward[1];
73 result.values[8] =
right[2]; result.values[9] = up[2]; result.values[10] = forward[2];
74 result.values[12] = -
dot(camera.location,
right);
75 result.values[13] = -
dot(camera.location, up);
76 result.values[14] = -
dot(camera.location, forward);
80 sequence::Matrix3D
makeProjection(
const World3DCamera& camera)
noexcept
82 sequence::Matrix3D result{};
83 const float cotangent = std::cos(camera.fieldOfView * 0.5F) /
84 std::sin(camera.fieldOfView * 0.5F);
87 result.values[10] = camera.farPlane / (camera.farPlane - camera.nearPlane);
88 result.values[11] = 1.0F;
89 result.values[14] = -result.values[10] * camera.nearPlane;
93 sequence::Matrix3D
makeViewport(World3DRect viewport)
noexcept
96 const auto width = viewport.right - viewport.left;
97 const auto height = viewport.bottom - viewport.top;
99 result.values[0] =
static_cast<float>(
width / 2);
100 result.values[5] =
aspect *
static_cast<float>((viewport.top - viewport.bottom) / 2);
101 result.values[12] =
static_cast<float>(viewport.left +
width / 2);
102 result.values[13] =
static_cast<float>(viewport.top +
height / 2);
107 {
return std::isfinite(
value[0]) && std::isfinite(
value[1]) && std::isfinite(
value[2]); }
110 std::expected<World3DProjectionState, World3DProjectionError>
113 if (viewport.
empty())
137 result.camera = camera;
140 result.rasterProjection = result.projection;
145 static_cast<float>(viewport.
bottom - viewport.
top);
160 return result.
empty() ? std::nullopt : std::optional<World3DRect>{result};
169 std::array<Point3D, 8>
corners{};
170 for (std::size_t
i = 0;
i <
corners.size(); ++
i)
173 (
i & 4U) ? bounds.maximum[0] : bounds.minimum[0],
174 (
i & 2U) ? bounds.maximum[1] : bounds.minimum[1],
175 (
i & 1U) ? bounds.maximum[2] : bounds.minimum[2]};
179 std::vector<Point3D> visible;
181 for (
const auto& point :
corners)
182 if (point.z >= projection.camera.nearPlane)
183 visible.push_back(point);
184 if (visible.empty())
return std::nullopt;
186 if (visible.size() <
corners.size())
188 for (std::size_t
i = 0;
i <
corners.size(); ++
i)
204 projection.camera.nearPlane});
209 std::vector<Point3D> screen;
210 screen.reserve(visible.size());
211 for (
const auto& point : visible)
212 screen.push_back(transformPoint(point, projection.cameraToScreen));
213 if (screen.empty())
return std::nullopt;
219 for (
const auto& point : screen)
223 if (!
x || !
y)
return std::nullopt;
std::atomic< float > left
std::atomic< float > right
std::optional< World3DRect > world3DMeshScreenRect(const data::MeshBounds &bounds, const sequence::Matrix3D &worldTransform, const World3DProjectionState &projection) noexcept
std::optional< World3DRect > intersectWorld3DRect(World3DRect left, World3DRect right) noexcept
std::expected< World3DProjectionState, World3DProjectionError > makeWorld3DProjectionState(World3DRect viewport, World3DCamera camera)
Matrix3D identity3D() noexcept
Matrix2D multiply(const Matrix2D &left, const Matrix2D &right) noexcept
std::array< float, 3 > location
std::array< float, 3 > forward
std::array< float, 3 > up
bool empty() const noexcept