3#include <RendererCore/Decals/DecalComponent.h>
4#include <RendererCore/Lights/DirectionalLightComponent.h>
5#include <RendererCore/Lights/Implementation/ReflectionProbeData.h>
6#include <RendererCore/Lights/PointLightComponent.h>
7#include <RendererCore/Lights/SpotLightComponent.h>
8#include <RendererFoundation/Shader/ShaderUtils.h>
10#include <RendererCore/../../../Data/Base/Shaders/Common/LightData.h>
11PL_DEFINE_AS_POD_TYPE(plPerLightData);
12PL_DEFINE_AS_POD_TYPE(plPerDecalData);
13PL_DEFINE_AS_POD_TYPE(plPerReflectionProbeData);
14PL_DEFINE_AS_POD_TYPE(plPerClusterData);
16#include <Core/Graphics/Camera.h>
17#include <Foundation/Math/Float16.h>
18#include <Foundation/SimdMath/SimdConversion.h>
19#include <Foundation/SimdMath/SimdVec4i.h>
20#include <Foundation/Utilities/GraphicsUtils.h>
25 static float s_fMinLightDistance = 5.0f;
26 static float s_fMaxLightDistance = 500.0f;
28 static float s_fDepthSliceScale = (NUM_CLUSTERS_Z - 1) / (plMath::Log2(s_fMaxLightDistance) - plMath::Log2(s_fMinLightDistance));
29 static float s_fDepthSliceBias = -s_fDepthSliceScale * plMath::Log2(s_fMinLightDistance) + 1.0f;
31 PL_ALWAYS_INLINE
float GetDepthFromSliceIndex(plUInt32 uiSliceIndex)
33 return plMath::Pow(2.0f, (uiSliceIndex - s_fDepthSliceBias + 1.0f) / s_fDepthSliceScale);
36 PL_ALWAYS_INLINE plUInt32 GetSliceIndexFromDepth(
float fLinearDepth)
38 return plMath::Clamp((plInt32)(plMath::Log2(fLinearDepth) * s_fDepthSliceScale + s_fDepthSliceBias), 0, NUM_CLUSTERS_Z - 1);
41 PL_ALWAYS_INLINE plUInt32 GetClusterIndexFromCoord(plUInt32 x, plUInt32 y, plUInt32 z)
43 return z * NUM_CLUSTERS_XY + y * NUM_CLUSTERS_X + x;
47 PL_FORCE_INLINE
void GetClusterCornerPoints(
48 const plCamera& camera,
float fZf,
float fZn,
float fTanLeft,
float fTanRight,
float fTanBottom,
float fTanTop, plInt32 x, plInt32 y, plInt32 z,
plVec3* out_pCorners)
55 const float fStartXf = fZf * fTanLeft;
56 const float fStartYf = fZf * fTanBottom;
57 const float fEndXf = fZf * fTanRight;
58 const float fEndYf = fZf * fTanTop;
60 float fStepXf = (fEndXf - fStartXf) / NUM_CLUSTERS_X;
61 float fStepYf = (fEndYf - fStartYf) / NUM_CLUSTERS_Y;
63 float fXf = fStartXf + x * fStepXf;
64 float fYf = fStartYf + y * fStepYf;
66 out_pCorners[0] = pos + dirForward * fZf + dirRight * fXf - dirUp * fYf;
67 out_pCorners[1] = out_pCorners[0] + dirRight * fStepXf;
68 out_pCorners[2] = out_pCorners[0] - dirUp * fStepYf;
69 out_pCorners[3] = out_pCorners[2] + dirRight * fStepXf;
71 const float fStartXn = fZn * fTanLeft;
72 const float fStartYn = fZn * fTanBottom;
73 const float fEndXn = fZn * fTanRight;
74 const float fEndYn = fZn * fTanTop;
76 float fStepXn = (fEndXn - fStartXn) / NUM_CLUSTERS_X;
77 float fStepYn = (fEndYn - fStartYn) / NUM_CLUSTERS_Y;
78 float fXn = fStartXn + x * fStepXn;
79 float fYn = fStartYn + y * fStepYn;
81 out_pCorners[4] = pos + dirForward * fZn + dirRight * fXn - dirUp * fYn;
82 out_pCorners[5] = out_pCorners[4] + dirRight * fStepXn;
83 out_pCorners[6] = out_pCorners[4] - dirUp * fStepYn;
84 out_pCorners[7] = out_pCorners[6] + dirRight * fStepXn;
89 PL_PROFILE_SCOPE(
"FillClusterBoundingSpheres");
92 if (camera.IsOrthographic())
105 plGraphicsUtils::ExtractPerspectiveMatrixFieldOfView(mProj, fFovLeft, fFovRight, fFovBottom, fFovTop);
112 float fStepXf = (fTanRight - fTanLeft) / NUM_CLUSTERS_X;
113 float fStepYf = (fTanTop - fTanBottom) / NUM_CLUSTERS_Y;
115 stepScale =
plSimdVec4f(fStepXf, fStepYf, fStepXf, fStepYf);
116 tanLBLB =
plSimdVec4f(fTanLeft, fTanBottom, fTanLeft, fTanBottom);
128 for (plInt32 z = 0; z < NUM_CLUSTERS_Z; z++)
139 for (plInt32 y = 0; y < NUM_CLUSTERS_Y; y++)
141 for (plInt32 x = 0; x < NUM_CLUSTERS_X; x++)
144 plSimdVec4f xfyf = startLBLB + (xyxy).CompMul(steps);
146 cc[0] = depthF + dirRight * xfyf.x() - dirUp * xfyf.y();
147 cc[1] = cc[0] + dirRight * steps.x();
148 cc[2] = cc[0] - dirUp * steps.y();
149 cc[3] = cc[2] + dirRight * steps.x();
151 cc[4] = depthN + dirRight * xfyf.z() - dirUp * xfyf.w();
152 cc[5] = cc[4] + dirRight * steps.z();
153 cc[6] = cc[4] - dirUp * steps.w();
154 cc[7] = cc[6] + dirRight * steps.z();
164 PL_ALWAYS_INLINE
void FillLightData(plPerLightData& ref_perLightData,
const plLightRenderData* pLightRenderData, plUInt8 uiType)
169 lightColor.a = uiType;
171 ref_perLightData.colorAndType = *
reinterpret_cast<plUInt32*
>(&lightColor.r);
172 ref_perLightData.intensity = pLightRenderData->m_fIntensity;
173 ref_perLightData.specularMultiplier = pLightRenderData->m_fSpecularMultiplier;
174 ref_perLightData.width = pLightRenderData->m_fWidth;
175 ref_perLightData.length = pLightRenderData->m_fLength;
176 ref_perLightData.shadowDataOffset = pLightRenderData->m_uiShadowDataOffset;
177 ref_perLightData.upDirection = plShaderUtils::Float3ToRGB10(pLightRenderData->m_GlobalTransform.m_qRotation *
plVec3(0, 0, 1));
180 void FillPointLightData(plPerLightData& ref_perLightData,
const plPointLightRenderData* pPointLightRenderData)
182 FillLightData(ref_perLightData, pPointLightRenderData, LIGHT_TYPE_POINT);
184 ref_perLightData.position = pPointLightRenderData->m_GlobalTransform.m_vPosition;
185 ref_perLightData.invSqrAttRadius = 1.0f / (pPointLightRenderData->m_fRange * pPointLightRenderData->m_fRange);
186 ref_perLightData.falloff = pPointLightRenderData->m_fFalloff;
189 void FillSpotLightData(plPerLightData& ref_perLightData,
const plSpotLightRenderData* pSpotLightRenderData)
191 FillLightData(ref_perLightData, pSpotLightRenderData, LIGHT_TYPE_SPOT);
193 ref_perLightData.direction = plShaderUtils::Float3ToRGB10(pSpotLightRenderData->m_GlobalTransform.m_qRotation *
plVec3(-1, 0, 0));
194 ref_perLightData.position = pSpotLightRenderData->m_GlobalTransform.m_vPosition;
195 ref_perLightData.invSqrAttRadius = 1.0f / (pSpotLightRenderData->m_fRange * pSpotLightRenderData->m_fRange);
196 ref_perLightData.falloff = pSpotLightRenderData->m_fFalloff;
198 const float fCosInner =
plMath::Cos(pSpotLightRenderData->m_InnerSpotAngle * 0.5f);
199 const float fCosOuter =
plMath::Cos(pSpotLightRenderData->m_OuterSpotAngle * 0.5f);
200 const float fSpotParamScale = 1.0f /
plMath::Max(0.001f, (fCosInner - fCosOuter));
201 const float fSpotParamOffset = -fCosOuter * fSpotParamScale;
202 ref_perLightData.spotParams = plShaderUtils::Float2ToRG16F(
plVec2(fSpotParamScale, fSpotParamOffset));
207 FillLightData(ref_perLightData, pDirLightRenderData, LIGHT_TYPE_DIR);
209 ref_perLightData.direction = plShaderUtils::Float3ToRGB10(pDirLightRenderData->m_GlobalTransform.m_qRotation *
plVec3(-1, 0, 0));
212 void FillDecalData(plPerDecalData& ref_perDecalData,
const plDecalRenderData* pDecalRenderData)
214 plVec3 position = pDecalRenderData->m_GlobalTransform.m_vPosition;
215 plVec3 dirForwards = pDecalRenderData->m_GlobalTransform.m_qRotation *
plVec3(1.0f, 0.0, 0.0f);
216 plVec3 dirUp = pDecalRenderData->m_GlobalTransform.m_qRotation *
plVec3(0.0f, 0.0, 1.0f);
217 plVec3 scale = pDecalRenderData->m_GlobalTransform.m_vScale;
223 const plMat4 lookAt = plGraphicsUtils::CreateLookAtViewMatrix(position, position + dirForwards, dirUp);
226 ref_perDecalData.worldToDecalMatrix = scaleMat * lookAt;
227 ref_perDecalData.applyOnlyToId = pDecalRenderData->m_uiApplyOnlyToId;
228 ref_perDecalData.decalFlags = pDecalRenderData->m_uiFlags;
229 ref_perDecalData.angleFadeParams = pDecalRenderData->m_uiAngleFadeParams;
230 ref_perDecalData.baseColor = *
reinterpret_cast<const plUInt32*
>(&pDecalRenderData->m_BaseColor.r);
231 ref_perDecalData.emissiveColorRG = plShaderUtils::PackFloat16intoUint(pDecalRenderData->m_EmissiveColor.r, pDecalRenderData->m_EmissiveColor.g);
232 ref_perDecalData.emissiveColorBA = plShaderUtils::PackFloat16intoUint(pDecalRenderData->m_EmissiveColor.b, pDecalRenderData->m_EmissiveColor.a);
233 ref_perDecalData.baseColorAtlasScale = pDecalRenderData->m_uiBaseColorAtlasScale;
234 ref_perDecalData.baseColorAtlasOffset = pDecalRenderData->m_uiBaseColorAtlasOffset;
235 ref_perDecalData.normalAtlasScale = pDecalRenderData->m_uiNormalAtlasScale;
236 ref_perDecalData.normalAtlasOffset = pDecalRenderData->m_uiNormalAtlasOffset;
237 ref_perDecalData.ormAtlasScale = pDecalRenderData->m_uiORMAtlasScale;
238 ref_perDecalData.ormAtlasOffset = pDecalRenderData->m_uiORMAtlasOffset;
241 void FillReflectionProbeData(plPerReflectionProbeData& ref_perReflectionProbeData,
const plReflectionProbeRenderData* pReflectionProbeRenderData)
243 plVec3 position = pReflectionProbeRenderData->m_GlobalTransform.m_vPosition;
244 plVec3 scale = pReflectionProbeRenderData->m_GlobalTransform.m_vScale.
CompMul(pReflectionProbeRenderData->m_vHalfExtents);
247 auto trans = pReflectionProbeRenderData->m_GlobalTransform;
248 trans.m_vScale =
plVec3(1.0f, 1.0f, 1.0f);
249 auto inverse = trans.GetAsMat4().GetInverse();
254 ref_perReflectionProbeData.WorldToProbeProjectionMatrix = inverse;
257 ref_perReflectionProbeData.Scale = scale.
GetAsVec4(0.0f);
259 ref_perReflectionProbeData.InfluenceScale = pReflectionProbeRenderData->m_vInfluenceScale.
GetAsVec4(0.0f);
260 ref_perReflectionProbeData.InfluenceShift = pReflectionProbeRenderData->m_vInfluenceShift.
CompMul(
plVec3(1.0f) - pReflectionProbeRenderData->m_vInfluenceScale).
GetAsVec4(0.0f);
262 ref_perReflectionProbeData.PositiveFalloff = pReflectionProbeRenderData->m_vPositiveFalloff.
GetAsVec4(0.0f);
263 ref_perReflectionProbeData.NegativeFalloff = pReflectionProbeRenderData->m_vNegativeFalloff.
GetAsVec4(0.0f);
264 ref_perReflectionProbeData.Index = pReflectionProbeRenderData->m_uiIndex;
277 if (viewSpaceCenter.GetLength<3>() > radius && depth > radius)
287 plSimdVec4f nom = (pRadius2.CompMul(xxyy.CompMul(xxyy) - pRadius2 + one)).GetSqrt() - xxyy.CompMul(oneNegOne);
291 plSimdVec4f minXmaxX_minYmaxY = nom.CompDiv(denom).CompMul(oneNegOne).CompMul(projection);
293 mi = minXmaxX_minYmaxY.Get<plSwizzle::XZXX>();
294 ma = minXmaxX_minYmaxY.Get<plSwizzle::YWYY>();
302 mi.SetZ(depth - radius);
303 ma.SetZ(depth + radius);
308 template <
typename Cluster,
typename IntersectionFunc>
309 PL_FORCE_INLINE
void FillCluster(
const plSimdBBox& screenSpaceBounds, plUInt32 uiBlockIndex, plUInt32 uiMask, Cluster* pClusters, IntersectionFunc func)
314 plSimdVec4f mi = plSimdVec4f::MulAdd(screenSpaceBounds.m_Min, scale, bias);
315 plSimdVec4f ma = plSimdVec4f::MulAdd(screenSpaceBounds.m_Max, scale, bias);
319 plSimdVec4i maxClusterIndex =
plSimdVec4i(NUM_CLUSTERS_X, NUM_CLUSTERS_Y, NUM_CLUSTERS_X, NUM_CLUSTERS_Y);
320 minXY_maxXY = minXY_maxXY.CompMin(maxClusterIndex -
plSimdVec4i(1));
323 plUInt32 xMin = minXY_maxXY.x();
324 plUInt32 yMin = minXY_maxXY.w();
326 plUInt32 xMax = minXY_maxXY.z();
327 plUInt32 yMax = minXY_maxXY.y();
329 plUInt32 zMin = GetSliceIndexFromDepth(screenSpaceBounds.m_Min.z());
330 plUInt32 zMax = GetSliceIndexFromDepth(screenSpaceBounds.m_Max.z());
332 for (plUInt32 z = zMin; z <= zMax; ++z)
334 for (plUInt32 y = yMin; y <= yMax; ++y)
336 for (plUInt32 x = xMin; x <= xMax; ++x)
338 plUInt32 uiClusterIndex = GetClusterIndexFromCoord(x, y, z);
339 if (func(uiClusterIndex))
341 pClusters[uiClusterIndex].m_BitMask[uiBlockIndex] |= uiMask;
348 template <
typename Cluster>
352 plSimdBBox screenSpaceBounds = GetScreenSpaceBounds(pointLightSphere, mViewMatrix, mProjectionMatrix);
354 const plUInt32 uiBlockIndex = uiLightIndex / 32;
355 const plUInt32 uiMask = 1 << (uiLightIndex - uiBlockIndex * 32);
357 FillCluster(screenSpaceBounds, uiBlockIndex, uiMask, pClusters,
358 [&](plUInt32 uiClusterIndex) {
return pointLightSphere.
Overlaps(pClusterBoundingSpheres[uiClusterIndex]); });
369 template <
typename Cluster>
370 void RasterizeSpotLight(
const BoundingCone& spotLightCone, plUInt32 uiLightIndex,
const plSimdMat4f& mViewMatrix,
373 plSimdVec4f position = spotLightCone.m_PositionAndRange;
374 plSimdFloat range = spotLightCone.m_PositionAndRange.w();
375 plSimdVec4f forwardDir = spotLightCone.m_ForwardDir;
376 plSimdFloat sinAngle = spotLightCone.m_SinCosAngle.x();
377 plSimdFloat cosAngle = spotLightCone.m_SinCosAngle.y();
382 if (sinAngle > 0.707107f)
384 bSphereCenter = position + forwardDir * cosAngle * range;
385 bSphereRadius = sinAngle * range;
389 bSphereRadius = range / (cosAngle + cosAngle);
390 bSphereCenter = position + forwardDir * bSphereRadius;
394 plSimdBBox screenSpaceBounds = GetScreenSpaceBounds(spotLightSphere, mViewMatrix, mProjectionMatrix);
396 const plUInt32 uiBlockIndex = uiLightIndex / 32;
397 const plUInt32 uiMask = 1 << (uiLightIndex - uiBlockIndex * 32);
399 FillCluster(screenSpaceBounds, uiBlockIndex, uiMask, pClusters, [&](plUInt32 uiClusterIndex) {
400 plSimdBSphere clusterSphere = pClusterBoundingSpheres[uiClusterIndex];
403 plSimdVec4f toConePos = clusterSphere.m_CenterAndRadius - position;
404 plSimdFloat projected = forwardDir.Dot<3>(toConePos);
405 plSimdFloat distToConeSq = toConePos.Dot<3>(toConePos);
406 plSimdFloat distClosestP = cosAngle * (distToConeSq - projected * projected).GetSqrt() - projected * sinAngle;
408 bool angleCull = distClosestP > clusterRadius;
409 bool frontCull = projected > clusterRadius + range;
410 bool backCull = projected < -clusterRadius;
412 return !(angleCull || frontCull || backCull); });
415 template <
typename Cluster>
418 const plUInt32 uiBlockIndex = uiLightIndex / 32;
419 const plUInt32 uiMask = 1 << (uiLightIndex - uiBlockIndex * 32);
421 for (plUInt32 i = 0; i < clusters.
GetCount(); ++i)
423 clusters[i].m_BitMask[uiBlockIndex] |= uiMask;
427 template <
typename Cluster>
428 void RasterizeBox(
const plTransform& transform, plUInt32 uiDecalIndex,
const plSimdMat4f& mViewProjectionMatrix, Cluster* pClusters,
437 plSimdMat4f decalToScreen = mViewProjectionMatrix * decalToWorld;
439 bool bInsideBox =
false;
440 for (plUInt32 i = 0; i < 8; ++i)
442 plSimdVec4f corner = plSimdConversion::ToVec3(corners[i]);
447 screenSpaceCorner /= depth;
450 screenSpaceBounds.m_Min = screenSpaceBounds.m_Min.CompMin(screenSpaceCorner);
451 screenSpaceBounds.m_Max = screenSpaceBounds.m_Max.CompMax(screenSpaceCorner);
463 const plUInt32 uiBlockIndex = uiDecalIndex / 32;
464 const plUInt32 uiMask = 1 << (uiDecalIndex - uiBlockIndex * 32);
466 FillCluster(screenSpaceBounds, uiBlockIndex, uiMask, pClusters, [&](plUInt32 uiClusterIndex) {
467 plSimdBSphere clusterSphere = pClusterBoundingSpheres[uiClusterIndex];
470 return localDecalBounds.
Overlaps(clusterSphere); });
Float wrapper struct for a safe usage and conversions of angles.
Definition Angle.h:10
This class encapsulates an array and it's size. It is recommended to use this class instead of plain ...
Definition ArrayPtr.h:37
PL_ALWAYS_INLINE plUInt32 GetCount() const
Returns the number of elements in the array.
Definition ArrayPtr.h:142
void GetCorners(plVec3Template< Type > *out_pCorners) const
Writes the 8 different corners of the box to the given array.
Definition BoundingBox_inl.h:62
static plBoundingBoxTemplate< float > MakeFromMinMax(const plVec3Template< float > &vMin, const plVec3Template< float > &vMax)
Definition BoundingBox_inl.h:42
A camera class that stores the orientation and some basic camera settings.
Definition Camera.h:41
plVec3 GetPosition(plCameraEye eye=plCameraEye::Left) const
Returns the position of the camera that should be used for rendering etc.
Definition Camera.cpp:63
void GetProjectionMatrix(float fAspectRatioWidthDivHeight, plMat4 &out_mProjectionMatrix, plCameraEye eye=plCameraEye::Left, plClipSpaceDepthRange::Enum depthRange=plClipSpaceDepthRange::Default) const
Calculates the projection matrix from the current camera properties and stores it in out_projectionMa...
Definition Camera.cpp:280
plVec3 GetDirForwards(plCameraEye eye=plCameraEye::Left) const
Returns the forwards vector that should be used for rendering etc.
Definition Camera.cpp:68
plVec3 GetDirRight(plCameraEye eye=plCameraEye::Left) const
Returns the right vector that should be used for rendering etc.
Definition Camera.cpp:84
plVec3 GetDirUp(plCameraEye eye=plCameraEye::Left) const
Returns the up vector that should be used for rendering etc.
Definition Camera.cpp:76
A 8bit per channel unsigned normalized (values interpreted as 0-1) color storage format that represen...
Definition Color8UNorm.h:61
Definition DecalComponent.h:27
The render data object for directional lights.
Definition DirectionalLightComponent.h:10
Base class for light render data objects.
Definition LightComponent.h:10
static plMat4Template< float > MakeScaling(const plVec3Template< float > &vScale)
Definition Mat4_inl.h:158
static void ZeroFill(T *pDestination, size_t uiCount=1)
Zeros every byte in the provided memory buffer.
The render data object for point lights.
Definition PointLightComponent.h:11
Render data for a reflection probe.
Definition ReflectionProbeData.h:54
plVec3 m_vProbePosition
Probe position in world space.
Definition ReflectionProbeData.h:66
bool Overlaps(const plSimdBBox &rhs) const
Checks whether this box overlaps with the given box.
Definition SimdBBox_inl.h:133
static plSimdBBox MakeInvalid()
Creates a box that is in an invalid state. ExpandToInclude can then be used to make it into a boundin...
Definition SimdBBox_inl.h:16
Definition SimdBSphere.h:6
plSimdFloat GetRadius() const
Returns the radius.
Definition SimdBSphere_inl.h:85
static plSimdBSphere MakeFromPoints(const plSimdVec4f *pPoints, plUInt32 uiNumPoints, plUInt32 uiStride=sizeof(plSimdVec4f))
Creates a bounding sphere around the provided points.
Definition SimdBSphere_inl.h:30
void Transform(const plSimdTransform &t)
Transforms the sphere in its local space.
Definition SimdBSphere_inl.h:129
bool Overlaps(const plSimdBSphere &rhs) const
Checks whether the two objects overlap.
Definition SimdBSphere_inl.h:171
plSimdVec4f GetCenter() const
Returns the center.
Definition SimdBSphere_inl.h:80
static plSimdFloat MakeZero()
Creates an plSimdFloat that is initialized to zero.
Definition FPUFloat_inl.h:36
A 4x4 matrix class.
Definition SimdMat4f.h:7
plSimdMat4f GetInverse(const plSimdFloat &fEpsilon=plMath::SmallEpsilon< float >()) const
Returns the inverse of this matrix.
Definition SimdMat4f_inl.h:89
plSimdVec4f TransformPosition(const plSimdVec4f &v) const
Matrix-vector multiplication, assuming the 4th component of the vector is one (default behavior).
Definition SimdMat4f_inl.h:141
A 4-component SIMD vector class.
Definition SimdVec4f.h:8
static plSimdVec4f MakeZero()
Creates an plSimdVec4f that is initialized to zero.
Definition SimdVec4f_inl.h:8
plSimdVec4f GetCombined(const plSimdVec4f &other) const
x = this[s0], y = this[s1], z = other[s2], w = other[s3]
A SIMD 4-component vector class of signed 32b integers.
Definition SimdVec4i.h:9
static plSimdVec4i MakeZero()
Creates an plSimdVec4i that is initialized to zero.
Definition FPUVec4i_inl.h:25
The render data object for spot lights.
Definition SpotLightComponent.h:11
const plVec3Template< Type > CompDiv(const plVec3Template< Type > &rhs) const
Returns the component-wise division of *this and rhs.
Definition Vec3_inl.h:354
const plVec4Template< Type > GetAsVec4(Type w) const
Returns an plVec4Template with x,y,z from this vector and w set to the parameter.
Definition Vec4_inl.h:35
const plVec3Template< Type > CompMax(const plVec3Template< Type > &rhs) const
Returns the component-wise maximum of *this and rhs.
Definition Vec3_inl.h:326
const plVec3Template< Type > CompMul(const plVec3Template< Type > &rhs) const
Returns the component-wise multiplication of *this and rhs.
Definition Vec3_inl.h:345
constexpr PL_ALWAYS_INLINE T Clamp(T value, T min_val, T max_val)
Clamps "value" to the range [min; max]. Returns "value", if it is inside the range already.
Definition Math_inl.h:51
PL_ALWAYS_INLINE float Tan(plAngle a)
Takes an angle, returns its tangent.
Definition MathFloat_inl.h:72
PL_ALWAYS_INLINE float Cos(plAngle a)
Takes an angle, returns its cosine.
Definition MathFloat_inl.h:67
constexpr PL_ALWAYS_INLINE T Max(T f1, T f2)
Returns the greater value, f1 or f2.
Definition Math_inl.h:39