Add relative-to-camera rendering for large worlds

This commit is contained in:
Wojtek Figat
2022-06-21 20:03:13 +02:00
parent f3bd0e469c
commit 134c8b99aa
35 changed files with 331 additions and 195 deletions

View File

@@ -80,9 +80,10 @@ struct GeometryLookup
PROFILE_CPU_NAMED("StaticModel");
// Check model meshes
Matrix world;
staticModel->GetWorld(&world);
const bool isDeterminantPositive = staticModel->GetTransform().GetDeterminant() >= 0.0f;
Transform transform = staticModel->GetTransform();
Matrix worldMatrix;
transform.GetWorld(worldMatrix);
const bool isDeterminantPositive = transform.GetDeterminant() >= 0.0f;
auto& lod = staticModel->Model->LODs[0];
for (int32 meshIndex = 0; meshIndex < lod.Meshes.Count(); meshIndex++)
{
@@ -96,9 +97,9 @@ struct GeometryLookup
// Transform triangle vertices from mesh space to world space
Vector3 t0, t1, t2;
Vector3::Transform(t.V0, world, t0);
Vector3::Transform(t.V1, world, t1);
Vector3::Transform(t.V2, world, t2);
Vector3::Transform(t.V0, worldMatrix, t0);
Vector3::Transform(t.V1, worldMatrix, t1);
Vector3::Transform(t.V2, worldMatrix, t2);
// Check if triangles intersects with the brush
if (CollisionsHelper::SphereIntersectsTriangle(brush, t0, t1, t2))

View File

@@ -93,7 +93,7 @@ void ViewportIconsRendererService::DrawIcons(RenderContext& renderContext, Scene
Matrix m1, m2, world;
for (Actor* icon : icons)
{
BoundingSphere sphere(icon->GetPosition(), ICON_RADIUS);
BoundingSphere sphere(icon->GetPosition() - renderContext.View.Origin, ICON_RADIUS);
IconTypes iconType;
if (frustum.Intersects(sphere) && ActorTypeToIconType.TryGet(icon->GetTypeHandle(), iconType))
{
@@ -120,7 +120,7 @@ void ViewportIconsRendererService::DrawIcons(RenderContext& renderContext, Actor
auto& view = renderContext.View;
const BoundingFrustum frustum = view.Frustum;
Matrix m1, m2, world;
BoundingSphere sphere(actor->GetPosition(), ICON_RADIUS);
BoundingSphere sphere(actor->GetPosition() - renderContext.View.Origin, ICON_RADIUS);
IconTypes iconType;
if (frustum.Intersects(sphere) && ActorTypeToIconType.TryGet(actor->GetTypeHandle(), iconType))
{

View File

@@ -777,16 +777,16 @@ namespace FlaxEditor.Viewport
/// <param name="view">The view.</param>
public void CopyViewData(ref RenderView view)
{
// Create matrices
CreateProjectionMatrix(out view.Projection);
CreateViewMatrix(out view.View);
// Copy data
view.Position = ViewPosition;
Vector3 position = ViewPosition;
LargeWorlds.UpdateOrigin(ref view.Origin, position);
view.Position = position - view.Origin;
view.Direction = ViewDirection;
view.Near = _nearPlane;
view.Far = _farPlane;
CreateProjectionMatrix(out view.Projection);
CreateViewMatrix(view.Position, out view.View);
view.UpdateCachedData();
}
@@ -828,10 +828,10 @@ namespace FlaxEditor.Viewport
/// <summary>
/// Creates the view matrix.
/// </summary>
/// <param name="position">The view position.</param>
/// <param name="result">The result.</param>
protected virtual void CreateViewMatrix(out Matrix result)
protected virtual void CreateViewMatrix(Float3 position, out Matrix result)
{
var position = (Float3)ViewPosition; // TODO: large-worlds
var direction = ViewDirection;
var target = position + direction;
var right = Float3.Normalize(Float3.Cross(Float3.Up, direction));
@@ -862,7 +862,7 @@ namespace FlaxEditor.Viewport
// Prepare
var viewport = new FlaxEngine.Viewport(0, 0, Width, Height);
CreateProjectionMatrix(out var p);
CreateViewMatrix(out var v);
CreateViewMatrix(Float3.Zero, out var v); // TODO: large-worlds
Matrix.Multiply(ref v, ref p, out var ivp);
ivp.Invert();

View File

@@ -172,6 +172,11 @@ bool Model::Intersects(const Ray& ray, const Matrix& world, Real& distance, Vect
return LODs[lodIndex].Intersects(ray, world, distance, normal, mesh);
}
bool Model::Intersects(const Ray& ray, const Transform& transform, Real& distance, Vector3& normal, Mesh** mesh, int32 lodIndex)
{
return LODs[lodIndex].Intersects(ray, transform, distance, normal, mesh);
}
BoundingBox Model::GetBox(const Matrix& world, int32 lodIndex) const
{
return LODs[lodIndex].GetBox(world);

View File

@@ -134,6 +134,18 @@ public:
/// <returns>True whether the two objects intersected</returns>
bool Intersects(const Ray& ray, const Matrix& world, Real& distance, Vector3& normal, Mesh** mesh, int32 lodIndex = 0);
/// <summary>
/// Determines if there is an intersection between the Model and a Ray in given world using given instance.
/// </summary>
/// <param name="ray">The ray to test</param>
/// <param name="transform">The instance transformation.</param>
/// <param name="distance">When the method completes, contains the distance of the intersection (if any valid).</param>
/// <param name="normal">When the method completes, contains the intersection surface normal vector (if any valid).</param>
/// <param name="mesh">Mesh, or null</param>
/// <param name="lodIndex">Level Of Detail index</param>
/// <returns>True whether the two objects intersected</returns>
bool Intersects(const Ray& ray, const Transform& transform, Real& distance, Vector3& normal, Mesh** mesh, int32 lodIndex = 0);
/// <summary>
/// Gets the model bounding box in custom matrix world space.
/// </summary>
@@ -142,6 +154,17 @@ public:
/// <returns>The bounding box.</returns>
API_FUNCTION() BoundingBox GetBox(const Matrix& world, int32 lodIndex = 0) const;
/// <summary>
/// Gets the model bounding box in custom transformation.
/// </summary>
/// <param name="transform">The instance transformation.</param>
/// <param name="lodIndex">The Level Of Detail index.</param>
/// <returns>The bounding box.</returns>
API_FUNCTION() BoundingBox GetBox(const Transform& transform, int32 lodIndex = 0) const
{
return LODs[lodIndex].GetBox(transform);
}
/// <summary>
/// Gets the model bounding box in local space.
/// </summary>

View File

@@ -134,8 +134,7 @@ float BoundingFrustum::GetHeightAtDepth(float depth) const
ContainmentType BoundingFrustum::Contains(const Vector3& point) const
{
PlaneIntersectionType result = PlaneIntersectionType::Front;
PlaneIntersectionType planeResult = PlaneIntersectionType::Front;
for (int i = 0; i < 6; i++)
for (int32 i = 0; i < 6; i++)
{
const PlaneIntersectionType planeResult = _planes[i].Intersects(point);
switch (planeResult)
@@ -159,7 +158,7 @@ ContainmentType BoundingFrustum::Contains(const Vector3& point) const
ContainmentType BoundingFrustum::Contains(const BoundingSphere& sphere) const
{
auto result = PlaneIntersectionType::Front;
for (int i = 0; i < 6; i++)
for (int32 i = 0; i < 6; i++)
{
const PlaneIntersectionType planeResult = _planes[i].Intersects(sphere);
switch (planeResult)

View File

@@ -427,7 +427,7 @@ void Foliage::AddInstance(const FoliageInstance& instance)
auto& meshes = type->Model->LODs[0].Meshes;
for (int32 j = 0; j < meshes.Count(); j++)
{
meshes[j].GetCorners(corners);
meshes[j].GetBox().GetCorners(corners);
for (int32 k = 0; k < 8; k++)
{
@@ -469,7 +469,7 @@ void Foliage::SetInstanceTransform(int32 index, const Transform& value)
auto& meshes = type->Model->LODs[0].Meshes;
for (int32 j = 0; j < meshes.Count(); j++)
{
meshes[j].GetCorners(corners);
meshes[j].GetBox().GetCorners(corners);
for (int32 k = 0; k < 8; k++)
{
@@ -512,7 +512,7 @@ void Foliage::OnFoliageTypeModelLoaded(int32 index)
for (int32 j = 0; j < meshes.Count(); j++)
{
// TODO: cache bounds for all model meshes and reuse later
meshes[j].GetCorners(corners);
meshes[j].GetBox().GetCorners(corners);
// TODO: use SIMD
for (int32 k = 0; k < 8; k++)
@@ -1282,7 +1282,7 @@ void Foliage::OnTransformChanged()
auto& meshes = type->Model->LODs[0].Meshes;
for (int32 j = 0; j < meshes.Count(); j++)
{
meshes[j].GetCorners(corners);
meshes[j].GetBox().GetCorners(corners);
for (int32 k = 0; k < 8; k++)
{

View File

@@ -3,6 +3,7 @@
#pragma once
#include "Engine/Core/Math/Vector3.h"
#include "Engine/Core/Math/Transform.h"
#include "Engine/Core/Math/Ray.h"
#include "Engine/Core/Math/CollisionsHelper.h"
#include "Engine/Core/Collections/Array.h"
@@ -74,4 +75,26 @@ public:
}
return false;
}
bool Intersects(const Ray& ray, const Transform& transform, Real& distance, Vector3& normal) const
{
for (int32 i = 0; i < Triangles.Count(); i++)
{
CollisionTriangle triangle = Triangles[i];
Vector3 v0, v1, v2;
transform.LocalToWorld(triangle.V0, v0);
transform.LocalToWorld(triangle.V1, v1);
transform.LocalToWorld(triangle.V2, v2);
// TODO: use 32-bit precision for intersection
Real d;
if (CollisionsHelper::RayIntersectsTriangle(ray, triangle.V0, triangle.V1, triangle.V2, d))
{
normal = Vector3::Normalize((triangle.V1 - triangle.V0) ^ (triangle.V2 - triangle.V0));
distance = d;
return true;
}
}
return false;
}
};

View File

@@ -4,6 +4,7 @@
#include "ModelInstanceEntry.h"
#include "Engine/Content/Assets/Material.h"
#include "Engine/Content/Assets/Model.h"
#include "Engine/Core/Math/Transform.h"
#include "Engine/Graphics/GPUContext.h"
#include "Engine/Graphics/GPUDevice.h"
#include "Engine/Graphics/RenderTask.h"
@@ -319,7 +320,7 @@ bool Mesh::Intersects(const Ray& ray, const Matrix& world, Real& distance, Vecto
{
// Get bounding box of the mesh bounds transformed by the instance world matrix
Vector3 corners[8];
GetCorners(corners);
_box.GetCorners(corners);
Vector3 tmp;
Vector3::Transform(corners[0], world, tmp);
Vector3 min = tmp;
@@ -339,7 +340,38 @@ bool Mesh::Intersects(const Ray& ray, const Matrix& world, Real& distance, Vecto
// Use exact test on raw geometry
return _collisionProxy.Intersects(ray, world, distance, normal);
}
distance = 0;
normal = Vector3::Up;
return false;
#else
return transformedBox.Intersects(ray, distance, normal);
#endif
}
bool Mesh::Intersects(const Ray& ray, const Transform& transform, Real& distance, Vector3& normal) const
{
// Get bounding box of the mesh bounds transformed by the instance world matrix
Vector3 corners[8];
_box.GetCorners(corners);
Vector3 tmp;
transform.LocalToWorld(corners[0], tmp);
Vector3 min = tmp;
Vector3 max = tmp;
for (int32 i = 1; i < 8; i++)
{
transform.LocalToWorld(corners[i], tmp);
min = Vector3::Min(min, tmp);
max = Vector3::Max(max, tmp);
}
const BoundingBox transformedBox(min, max);
// Test ray on box
#if USE_PRECISE_MESH_INTERSECTS
if (transformedBox.Intersects(ray, distance))
{
// Use exact test on raw geometry
return _collisionProxy.Intersects(ray, transform, distance, normal);
}
distance = 0;
normal = Vector3::Up;
return false;

View File

@@ -259,13 +259,14 @@ public:
bool Intersects(const Ray& ray, const Matrix& world, Real& distance, Vector3& normal) const;
/// <summary>
/// Retrieves the eight corners of the bounding box.
/// Determines if there is an intersection between the mesh and a ray in given world
/// </summary>
/// <param name="corners">An array of points representing the eight corners of the bounding box.</param>
FORCE_INLINE void GetCorners(Vector3 corners[8]) const
{
_box.GetCorners(corners);
}
/// <param name="ray">The ray to test</param>
/// <param name="transform">The instance transformation.</param>
/// <param name="distance">When the method completes and returns true, contains the distance of the intersection (if any valid).</param>
/// <param name="normal">When the method completes, contains the intersection surface normal vector (if any valid).</param>
/// <returns>True whether the two objects intersected</returns>
bool Intersects(const Ray& ray, const Transform& transform, Real& distance, Vector3& normal) const;
public:
/// <summary>

View File

@@ -1,6 +1,7 @@
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
#include "ModelLOD.h"
#include "Engine/Core/Math/Transform.h"
#include "Engine/Graphics/GPUDevice.h"
#include "Engine/Serialization/MemoryReadStream.h"
@@ -60,13 +61,11 @@ void ModelLOD::Dispose()
bool ModelLOD::Intersects(const Ray& ray, const Matrix& world, Real& distance, Vector3& normal, Mesh** mesh)
{
// Check all meshes
bool result = false;
Real closest = MAX_Real;
Vector3 closestNormal = Vector3::Up;
for (int32 i = 0; i < Meshes.Count(); i++)
{
// Test intersection with mesh and check if is closer than previous
Real dst;
Vector3 nrm;
if (Meshes[i].Intersects(ray, world, dst, nrm) && dst < closest)
@@ -77,7 +76,28 @@ bool ModelLOD::Intersects(const Ray& ray, const Matrix& world, Real& distance, V
closestNormal = nrm;
}
}
distance = closest;
normal = closestNormal;
return result;
}
bool ModelLOD::Intersects(const Ray& ray, const Transform& transform, Real& distance, Vector3& normal, Mesh** mesh)
{
bool result = false;
Real closest = MAX_Real;
Vector3 closestNormal = Vector3::Up;
for (int32 i = 0; i < Meshes.Count(); i++)
{
Real dst;
Vector3 nrm;
if (Meshes[i].Intersects(ray, transform, dst, nrm) && dst < closest)
{
result = true;
*mesh = &Meshes[i];
closest = dst;
closestNormal = nrm;
}
}
distance = closest;
normal = closestNormal;
return result;
@@ -85,14 +105,12 @@ bool ModelLOD::Intersects(const Ray& ray, const Matrix& world, Real& distance, V
BoundingBox ModelLOD::GetBox(const Matrix& world) const
{
// Find minimum and maximum points of all the meshes
Vector3 tmp, min = Vector3::Maximum, max = Vector3::Minimum;
Vector3 corners[8];
for (int32 j = 0; j < Meshes.Count(); j++)
{
const auto& mesh = Meshes[j];
mesh.GetCorners(corners);
mesh.GetBox().GetCorners(corners);
for (int32 i = 0; i < 8; i++)
{
Vector3::Transform(corners[i], world, tmp);
@@ -100,42 +118,39 @@ BoundingBox ModelLOD::GetBox(const Matrix& world) const
max = Vector3::Max(max, tmp);
}
}
return BoundingBox(min, max);
}
BoundingBox ModelLOD::GetBox(const Matrix& world, int32 meshIndex) const
BoundingBox ModelLOD::GetBox(const Transform& transform) const
{
// Find minimum and maximum points of the mesh
Vector3 tmp, min = Vector3::Maximum, max = Vector3::Minimum;
Vector3 corners[8];
const auto& mesh = Meshes[meshIndex];
mesh.GetCorners(corners);
for (int32 j = 0; j < Meshes.Count(); j++)
{
const auto& mesh = Meshes[j];
mesh.GetBox().GetCorners(corners);
for (int32 i = 0; i < 8; i++)
{
Vector3::Transform(corners[i], world, tmp);
transform.LocalToWorld(corners[i], tmp);
min = Vector3::Min(min, tmp);
max = Vector3::Max(max, tmp);
}
}
return BoundingBox(min, max);
}
BoundingBox ModelLOD::GetBox() const
{
// Find minimum and maximum points of the mesh in given world
Vector3 min = Vector3::Maximum, max = Vector3::Minimum;
Vector3 corners[8];
for (int32 j = 0; j < Meshes.Count(); j++)
{
Meshes[j].GetCorners(corners);
Meshes[j].GetBox().GetCorners(corners);
for (int32 i = 0; i < 8; i++)
{
min = Vector3::Min(min, corners[i]);
max = Vector3::Max(max, corners[i]);
}
}
return BoundingBox(min, max);
}

View File

@@ -79,19 +79,29 @@ public:
bool Intersects(const Ray& ray, const Matrix& world, Real& distance, Vector3& normal, Mesh** mesh);
/// <summary>
/// Get model bounding box in transformed world for given instance buffer
/// Determines if there is an intersection between the Model and a Ray in given world using given instance
/// </summary>
/// <param name="ray">The ray to test</param>
/// <param name="transform">The instance transformation.</param>
/// <param name="distance">When the method completes, contains the distance of the intersection (if any valid).</param>
/// <param name="normal">When the method completes, contains the intersection surface normal vector (if any valid).</param>
/// <param name="mesh">Mesh, or null</param>
/// <returns>True whether the two objects intersected</returns>
bool Intersects(const Ray& ray, const Transform& transform, Real& distance, Vector3& normal, Mesh** mesh);
/// <summary>
/// Get model bounding box in transformed world matrix.
/// </summary>
/// <param name="world">World matrix</param>
/// <returns>Bounding box</returns>
BoundingBox GetBox(const Matrix& world) const;
/// <summary>
/// Get model bounding box in transformed world for given instance buffer for only one mesh
/// Get model bounding box in transformed world.
/// </summary>
/// <param name="world">World matrix</param>
/// <param name="meshIndex">esh index</param>
/// <param name="transform">The instance transformation.</param>
/// <returns>Bounding box</returns>
BoundingBox GetBox(const Matrix& world, int32 meshIndex) const;
BoundingBox GetBox(const Transform& transform) const;
/// <summary>
/// Gets the bounding box combined for all meshes in this model LOD.

View File

@@ -255,6 +255,9 @@ void SceneRenderTask::ClearCustomActors()
void SceneRenderTask::CollectPostFxVolumes(RenderContext& renderContext)
{
// Cache WorldPosition used for PostFx volumes blending (RenderView caches it later on)
renderContext.View.WorldPosition = renderContext.View.Origin + renderContext.View.Position;
if ((ActorsSource & ActorsSources::Scenes) != 0)
{
Level::CollectPostFxVolumes(renderContext);

View File

@@ -1,6 +1,7 @@
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
#include "RenderView.h"
#include "Engine/Level/LargeWorlds.h"
#include "Engine/Level/Actors/Camera.h"
#include "Engine/Renderer/RenderList.h"
#include "Engine/Renderer/RendererPass.h"
@@ -70,6 +71,8 @@ void RenderView::PrepareCache(RenderContext& renderContext, float width, float h
TemporalAAJitter.X = temporalAAJitter.X;
TemporalAAJitter.Y = temporalAAJitter.Y;
WorldPosition = Origin + Position;
// Ortho views have issues with screen size LOD culling
const float modelLODDistanceFactor = (renderContext.LodProxyView ? renderContext.LodProxyView->IsOrthographicProjection() : IsOrthographicProjection()) ? 100.0f : ModelLODDistanceFactor;
ModelLODDistanceFactorSqrt = modelLODDistanceFactor * modelLODDistanceFactor;
@@ -161,31 +164,15 @@ void RenderView::SetProjector(float nearPlane, float farPlane, const Float3& pos
CullingFrustum = Frustum;
}
void RenderView::CopyFrom(Camera* camera)
{
Position = camera->GetPosition();
Direction = camera->GetDirection();
Near = camera->GetNearPlane();
Far = camera->GetFarPlane();
View = camera->GetView();
Projection = camera->GetProjection();
NonJitteredProjection = Projection;
Frustum = camera->GetFrustum();
Matrix::Invert(View, IV);
Matrix::Invert(Projection, IP);
Frustum.GetInvMatrix(IVP);
CullingFrustum = Frustum;
RenderLayersMask = camera->RenderLayersMask;
}
void RenderView::CopyFrom(Camera* camera, Viewport* viewport)
{
Position = camera->GetPosition();
const Vector3 cameraPos = camera->GetPosition();
LargeWorlds::UpdateOrigin(Origin, cameraPos);
Position = cameraPos - Origin;
Direction = camera->GetDirection();
Near = camera->GetNearPlane();
Far = camera->GetFarPlane();
View = camera->GetView();
camera->GetMatrices(View, Projection, *viewport);
camera->GetMatrices(View, Projection, viewport ? *viewport : camera->GetViewport(), Origin);
Frustum.SetMatrix(View, Projection);
NonJitteredProjection = Projection;
Matrix::Invert(View, IV);
@@ -211,3 +198,9 @@ DrawPass RenderView::GetShadowsDrawPassMask(ShadowsCastingMode shadowsMode) cons
return DrawPass::All;
}
}
void RenderView::GetWorldMatrix(const Transform& transform, Matrix& world) const
{
const Vector3 translation = transform.Translation - Origin;
Matrix::Transformation(transform.Scale, transform.Orientation, translation, world);
}

View File

@@ -79,31 +79,24 @@ namespace FlaxEngine
/// <param name="camera">The camera.</param>
public void CopyFrom(Camera camera)
{
Position = camera.Position;
Direction = camera.Direction;
Near = camera.NearPlane;
Far = camera.FarPlane;
View = camera.View;
Projection = camera.Projection;
NonJitteredProjection = Projection;
TemporalAAJitter = Float4.Zero;
RenderLayersMask = camera.RenderLayersMask;
UpdateCachedData();
var viewport = camera.Viewport;
CopyFrom(camera, ref viewport);
}
/// <summary>
/// Copies render view data from the camera.
/// </summary>
/// <param name="camera">The camera.</param>
/// <param name="customViewport">The custom viewport to use for view/projeection matrices override.</param>
public void CopyFrom(Camera camera, ref Viewport customViewport)
/// <param name="viewport">The custom viewport to use for view/projeection matrices override.</param>
public void CopyFrom(Camera camera, ref Viewport viewport)
{
Position = camera.Position;
Vector3 cameraPos = camera.Position;
LargeWorlds.UpdateOrigin(ref Origin, cameraPos);
Position = cameraPos - Origin;
Direction = camera.Direction;
Near = camera.NearPlane;
Far = camera.FarPlane;
camera.GetMatrices(out View, out Projection, ref customViewport);
camera.GetMatrices(out View, out Projection, ref viewport, ref Origin);
NonJitteredProjection = Projection;
TemporalAAJitter = Float4.Zero;
RenderLayersMask = camera.RenderLayersMask;

View File

@@ -24,7 +24,17 @@ API_STRUCT() struct FLAXENGINE_API RenderView
DECLARE_SCRIPTING_TYPE_MINIMAL(RenderView);
/// <summary>
/// The position of the view.
/// The position of the view origin (in world-units). Used for camera-relative rendering to achieve large worlds support with keeping 32-bit precision for coordinates in scene rendering.
/// </summary>
API_FIELD() Vector3 Origin = Vector3::Zero;
/// <summary>
/// The global position of the view (Origin+Position).
/// </summary>
API_FIELD() Vector3 WorldPosition;
/// <summary>
/// The position of the view (relative to the origin).
/// </summary>
API_FIELD() Float3 Position;
@@ -264,14 +274,10 @@ public:
/// <param name="angle">Camera's FOV angle (in degrees)</param>
void SetProjector(float nearPlane, float farPlane, const Float3& position, const Float3& direction, const Float3& up, float angle);
// Copy view data from camera
// @param camera Camera to copy its data
void CopyFrom(Camera* camera);
// Copy view data from camera
// @param camera Camera to copy its data
// @param camera The custom viewport to use for view/projection matrices override.
void CopyFrom(Camera* camera, Viewport* viewport);
void CopyFrom(Camera* camera, Viewport* viewport = nullptr);
public:
DrawPass GetShadowsDrawPassMask(ShadowsCastingMode shadowsMode) const;
@@ -288,4 +294,7 @@ public:
{
return Frustum.GetMatrix();
}
// Calculates the world matrix for the given transformation instance rendering.
void GetWorldMatrix(const Transform& transform, Matrix& world) const;
};

View File

@@ -17,7 +17,7 @@ void BoxVolume::SetSize(const Vector3& value)
const auto prevBounds = _box;
_size = value;
OrientedBoundingBox::CreateCentered(Vector3::Zero, _size, _bounds);
_bounds.Transform(_transform.GetWorld());
_bounds.Transform(_transform);
_bounds.GetBoundingBox(_box);
BoundingSphere::FromBox(_box, _sphere);
OnBoundsChanged(prevBounds);
@@ -142,7 +142,7 @@ void BoxVolume::OnTransformChanged()
const auto prevBounds = _box;
OrientedBoundingBox::CreateCentered(Vector3::Zero, _size, _bounds);
_bounds.Transform(_transform.GetWorld());
_bounds.Transform(_transform);
_bounds.GetBoundingBox(_box);
BoundingSphere::FromBox(_box, _sphere);
OnBoundsChanged(prevBounds);

View File

@@ -189,10 +189,15 @@ Viewport Camera::GetViewport() const
void Camera::GetMatrices(Matrix& view, Matrix& projection) const
{
GetMatrices(view, projection, GetViewport());
GetMatrices(view, projection, GetViewport(), Vector3::Zero);
}
void Camera::GetMatrices(Matrix& view, Matrix& projection, const Viewport& viewport) const
{
GetMatrices(view, projection, viewport, Vector3::Zero);
}
void Camera::GetMatrices(Matrix& view, Matrix& projection, const Viewport& viewport, const Vector3& origin) const
{
// Create projection matrix
if (_usePerspective)
@@ -207,10 +212,11 @@ void Camera::GetMatrices(Matrix& view, Matrix& projection, const Viewport& viewp
// Create view matrix
const Float3 direction = GetDirection();
const Vector3 target = _transform.Translation + direction;
Vector3 up;
Vector3::Transform(Vector3::Up, GetOrientation(), up);
Matrix::LookAt(_transform.Translation, target, up, view);
const Float3 position = _transform.Translation - origin;
const Float3 target = position + direction;
Float3 up;
Float3::Transform(Float3::Up, GetOrientation(), up);
Matrix::LookAt(position, target, up, view);
}
#if USE_EDITOR
@@ -253,16 +259,19 @@ void Camera::Draw(RenderContext& renderContext)
&& _previewModel
&& _previewModel->IsLoaded())
{
Matrix world;
renderContext.View.GetWorldMatrix(_transform, world);
GeometryDrawStateData drawState;
Mesh::DrawInfo draw;
draw.Buffer = &_previewModelBuffer;
draw.World = &_previewModelWorld;
draw.World = &world;
draw.DrawState = &drawState;
draw.Lightmap = nullptr;
draw.LightmapUVs = nullptr;
draw.Flags = StaticFlags::Transform;
draw.DrawModes = (DrawPass)((DrawPass::Depth | DrawPass::GBuffer | DrawPass::Forward) & renderContext.View.Pass);
BoundingSphere::FromBox(_previewModelBox, draw.Bounds);
draw.Bounds.Center -= renderContext.View.Origin;
draw.PerInstanceRandom = GetPerInstanceRandom();
draw.LODBias = 0;
draw.ForcedLOD = -1;
@@ -288,32 +297,33 @@ void Camera::OnDebugDrawSelected()
void Camera::UpdateCache()
{
// Update view and projection matrix
GetMatrices(_view, _projection);
// Calculate view and projection matrices
Matrix view, projection;
GetMatrices(view, projection);
// Update frustum and bounding box
_frustum.SetMatrix(_view, _projection);
_frustum.SetMatrix(view, projection);
_frustum.GetBox(_box);
BoundingSphere::FromBox(_box, _sphere);
#if USE_EDITOR
// Update editor preview model cache
Matrix rot, world;
_transform.GetWorld(world);
Matrix rot, tmp, world;
_transform.GetWorld(tmp);
Matrix::RotationY(PI * -0.5f, rot);
Matrix::Multiply(rot, world, _previewModelWorld);
Matrix::Multiply(rot, tmp, world);
// Calculate snap box for preview model
if (_previewModel && _previewModel->IsLoaded())
{
_previewModelBox = _previewModel->GetBox(_previewModelWorld);
_previewModelBox = _previewModel->GetBox(world);
}
else
{
Vector3 min(-10.0f), max(10.0f);
min = Vector3::Transform(min, _previewModelWorld);
max = Vector3::Transform(max, _previewModelWorld);
min = Vector3::Transform(min, world);
max = Vector3::Transform(max, world);
_previewModelBox = BoundingBox(min, max);
}

View File

@@ -35,7 +35,6 @@ API_CLASS(Sealed) class FLAXENGINE_API Camera : public Actor
API_PROPERTY() static Camera* GetMainCamera();
private:
Matrix _view, _projection;
BoundingFrustum _frustum;
// Camera Settings
@@ -50,27 +49,10 @@ private:
AssetReference<Model> _previewModel;
ModelInstanceEntries _previewModelBuffer;
BoundingBox _previewModelBox;
Matrix _previewModelWorld;
int32 _sceneRenderingKey = -1;
#endif
public:
/// <summary>
/// Gets the view matrix.
/// </summary>
API_PROPERTY() FORCE_INLINE Matrix GetView() const
{
return _view;
}
/// <summary>
/// Gets the projection matrix.
/// </summary>
API_PROPERTY() FORCE_INLINE Matrix GetProjection() const
{
return _projection;
}
/// <summary>
/// Gets the frustum.
/// </summary>
@@ -218,8 +200,17 @@ public:
/// </summary>
/// <param name="view">The result camera view matrix.</param>
/// <param name="projection">The result camera projection matrix.</param>
/// <param name="viewport">The custom output viewport. Use null to skip it.</param>
API_FUNCTION() virtual void GetMatrices(API_PARAM(Out) Matrix& view, API_PARAM(Out) Matrix& projection, API_PARAM(Ref) const Viewport& viewport) const;
/// <param name="viewport">The custom output viewport.</param>
API_FUNCTION() void GetMatrices(API_PARAM(Out) Matrix& view, API_PARAM(Out) Matrix& projection, API_PARAM(Ref) const Viewport& viewport) const;
/// <summary>
/// Calculates the view and the projection matrices for the camera. Support using custom viewport and view origin.
/// </summary>
/// <param name="view">The result camera view matrix.</param>
/// <param name="projection">The result camera projection matrix.</param>
/// <param name="viewport">The custom output viewport.</param>
/// <param name="origin">The rendering view origin (for relative-to-camera rendering).</param>
API_FUNCTION() void GetMatrices(API_PARAM(Out) Matrix& view, API_PARAM(Out) Matrix& projection, API_PARAM(Ref) const Viewport& viewport, API_PARAM(Ref) const Vector3& origin) const;
#if USE_EDITOR
// Intersection check for editor picking the camera

View File

@@ -18,13 +18,14 @@ void DirectionalLight::Draw(RenderContext& renderContext)
{
float brightness = Brightness;
AdjustBrightness(renderContext.View, brightness);
const Float3 position = GetPosition() - renderContext.View.Origin;
if (Brightness > ZeroTolerance
&& (renderContext.View.Flags & ViewFlags::DirectionalLights) != 0
&& renderContext.View.Pass & DrawPass::GBuffer
&& (ViewDistance < ZeroTolerance || Vector3::DistanceSquared(renderContext.View.Position, GetPosition()) < ViewDistance * ViewDistance))
&& (ViewDistance < ZeroTolerance || Float3::DistanceSquared(renderContext.View.Position, position) < ViewDistance * ViewDistance))
{
RendererDirectionalLightData data;
data.Position = GetPosition(); // TODO: large-worlds
data.Position = position;
data.MinRoughness = MinRoughness;
data.ShadowsDistance = ShadowsDistance;
data.Color = Color.ToFloat3() * (Color.A * brightness);

View File

@@ -103,15 +103,16 @@ void PointLight::Draw(RenderContext& renderContext)
{
float brightness = ComputeBrightness();
AdjustBrightness(renderContext.View, brightness);
const Float3 position = GetPosition() - renderContext.View.Origin;
const float radius = GetScaledRadius();
if ((renderContext.View.Flags & ViewFlags::PointLights) != 0
&& brightness > ZeroTolerance
&& renderContext.View.Pass & DrawPass::GBuffer
&& radius > ZeroTolerance
&& (ViewDistance < ZeroTolerance || Vector3::DistanceSquared(renderContext.View.Position, GetPosition()) < ViewDistance * ViewDistance))
&& (ViewDistance < ZeroTolerance || Vector3::DistanceSquared(renderContext.View.Position, position) < ViewDistance * ViewDistance))
{
RendererPointLightData data;
data.Position = GetPosition(); // TODO: large-worlds
data.Position = position;
data.MinRoughness = MinRoughness;
data.ShadowsDistance = ShadowsDistance;
data.Color = Color.ToFloat3() * (Color.A * brightness);

View File

@@ -22,7 +22,7 @@ void PostFxVolume::Collect(RenderContext& renderContext)
if (_isBounded)
{
Real distance;
if (_bounds.Contains(renderContext.View.Position, &distance) == ContainmentType::Contains)
if (_bounds.Contains(renderContext.View.WorldPosition, &distance) == ContainmentType::Contains)
{
if (_blendRadius > 0.0f)
weight = Math::Saturate((float)distance / _blendRadius) * weight;

View File

@@ -100,13 +100,14 @@ void SkyLight::Draw(RenderContext& renderContext)
{
float brightness = Brightness;
AdjustBrightness(renderContext.View, brightness);
const Float3 position = GetPosition() - renderContext.View.Origin;
if ((renderContext.View.Flags & ViewFlags::SkyLights) != 0
&& renderContext.View.Pass & DrawPass::GBuffer
&& brightness > ZeroTolerance
&& (ViewDistance < ZeroTolerance || Vector3::DistanceSquared(renderContext.View.Position, GetPosition()) < ViewDistance * ViewDistance))
&& (ViewDistance < ZeroTolerance || Vector3::DistanceSquared(renderContext.View.Position, position) < ViewDistance * ViewDistance))
{
RendererSkyLightData data;
data.Position = GetPosition(); // TODO: large-worlds
data.Position = position;
data.Color = Color.ToFloat3() * (Color.A * brightness);
data.VolumetricScatteringIntensity = VolumetricScatteringIntensity;
data.CastVolumetricShadow = CastVolumetricShadow;

View File

@@ -136,7 +136,7 @@ void SplineModel::OnSplineUpdated()
for (int32 j = 0; j < meshes.Count(); j++)
{
const auto& mesh = meshes[j];
mesh.GetCorners(corners);
mesh.GetBox().GetCorners(corners);
for (int32 i = 0; i < 8; i++)
{

View File

@@ -151,6 +151,7 @@ void SpotLight::Draw(RenderContext& renderContext)
{
float brightness = ComputeBrightness();
AdjustBrightness(renderContext.View, brightness);
const Float3 position = GetPosition() - renderContext.View.Origin;
const float radius = GetScaledRadius();
const float outerConeAngle = GetOuterConeAngle();
if ((renderContext.View.Flags & ViewFlags::SpotLights) != 0
@@ -158,10 +159,10 @@ void SpotLight::Draw(RenderContext& renderContext)
&& brightness > ZeroTolerance
&& radius > ZeroTolerance
&& outerConeAngle > ZeroTolerance
&& (ViewDistance < ZeroTolerance || Vector3::DistanceSquared(renderContext.View.Position, GetPosition()) < ViewDistance * ViewDistance))
&& (ViewDistance < ZeroTolerance || Vector3::DistanceSquared(renderContext.View.Position, position) < ViewDistance * ViewDistance))
{
RendererSpotLightData data;
data.Position = GetPosition(); // TODO: large-worlds
data.Position = position;
data.MinRoughness = MinRoughness;
data.ShadowsDistance = ShadowsDistance;
data.Color = Color.ToFloat3() * (Color.A * brightness);

View File

@@ -18,7 +18,6 @@
StaticModel::StaticModel(const SpawnParams& params)
: ModelInstanceActor(params)
, _world(Matrix::Identity)
, _scaleInLightmap(1.0f)
, _boundsScale(1.0f)
, _lodBias(0)
@@ -210,16 +209,9 @@ void StaticModel::UpdateBounds()
{
if (Model && Model->IsLoaded())
{
Matrix world;
if (Math::IsOne(_boundsScale))
world = _world;
else
{
Transform t = _transform;
t.Scale *= _boundsScale;
t.GetWorld(world);
}
_box = Model->GetBox(world);
Transform transform = _transform;
transform.Scale *= _boundsScale;
_box = Model->GetBox(transform);
}
else
{
@@ -240,17 +232,21 @@ void StaticModel::Draw(RenderContext& renderContext)
const DrawPass drawModes = (DrawPass)(DrawModes & renderContext.View.Pass);
if (!Model || !Model->IsLoaded() || !Model->CanBeRendered() || drawModes == DrawPass::None)
return;
Matrix world;
renderContext.View.GetWorldMatrix(_transform, world);
if (renderContext.View.Pass == DrawPass::GlobalSDF)
{
GlobalSignDistanceFieldPass::Instance()->RasterizeModelSDF(this, Model->SDF, _world, _box);
GlobalSignDistanceFieldPass::Instance()->RasterizeModelSDF(this, Model->SDF, world, _box);
return;
}
if (renderContext.View.Pass == DrawPass::GlobalSurfaceAtlas)
{
GlobalSurfaceAtlasPass::Instance()->RasterizeActor(this, this, _sphere, _world, Model->LODs.Last().GetBox());
GlobalSurfaceAtlasPass::Instance()->RasterizeActor(this, this, _sphere, world, Model->LODs.Last().GetBox());
return;
}
GEOMETRY_DRAW_STATE_EVENT_BEGIN(_drawState, _world);
GEOMETRY_DRAW_STATE_EVENT_BEGIN(_drawState, world);
// Flush vertex colors if need to
if (_vertexColorsDirty)
@@ -282,18 +278,19 @@ void StaticModel::Draw(RenderContext& renderContext)
#if USE_EDITOR
// Disable motion blur effects in editor without play mode enabled to hide minor artifacts on objects moving
if (!Editor::IsPlayMode)
_drawState.PrevWorld = _world;
_drawState.PrevWorld = world;
#endif
Mesh::DrawInfo draw;
draw.Buffer = &Entries;
draw.World = &_world;
draw.World = &world;
draw.DrawState = &_drawState;
draw.Lightmap = _scene->LightmapsData.GetReadyLightmap(Lightmap.TextureIndex);
draw.LightmapUVs = &Lightmap.UVsArea;
draw.Flags = _staticFlags;
draw.DrawModes = drawModes;
draw.Bounds = _sphere;
draw.Bounds.Center -= renderContext.View.Origin;
draw.PerInstanceRandom = GetPerInstanceRandom();
draw.LODBias = _lodBias;
draw.ForcedLOD = _forcedLod;
@@ -301,7 +298,7 @@ void StaticModel::Draw(RenderContext& renderContext)
Model->Draw(renderContext, draw);
GEOMETRY_DRAW_STATE_EVENT_END(_drawState, _world);
GEOMETRY_DRAW_STATE_EVENT_END(_drawState, world);
}
bool StaticModel::IntersectsItself(const Ray& ray, Real& distance, Vector3& normal)
@@ -311,7 +308,7 @@ bool StaticModel::IntersectsItself(const Ray& ray, Real& distance, Vector3& norm
if (Model != nullptr && Model->IsLoaded())
{
Mesh* mesh;
result = Model->Intersects(ray, _world, distance, normal, &mesh);
result = Model->Intersects(ray, _transform, distance, normal, &mesh);
}
return result;
@@ -473,7 +470,7 @@ bool StaticModel::IntersectsEntry(int32 entryIndex, const Ray& ray, Real& distan
for (int32 i = 0; i < meshes.Count(); i++)
{
const auto& mesh = meshes[i];
if (mesh.GetMaterialSlotIndex() == entryIndex && mesh.Intersects(ray, _world, distance, normal))
if (mesh.GetMaterialSlotIndex() == entryIndex && mesh.Intersects(ray, _transform, distance, normal))
return true;
}
@@ -500,7 +497,7 @@ bool StaticModel::IntersectsEntry(const Ray& ray, Real& distance, Vector3& norma
const auto& mesh = meshes[i];
Real dst;
Vector3 nrm;
if (mesh.Intersects(ray, _world, dst, nrm) && dst < closest)
if (mesh.Intersects(ray, _transform, dst, nrm) && dst < closest)
{
result = true;
closest = dst;
@@ -520,7 +517,6 @@ void StaticModel::OnTransformChanged()
// Base
ModelInstanceActor::OnTransformChanged();
_transform.GetWorld(_world);
UpdateBounds();
}

View File

@@ -14,7 +14,6 @@ API_CLASS() class FLAXENGINE_API StaticModel : public ModelInstanceActor
{
DECLARE_SCENE_OBJECT(StaticModel);
private:
Matrix _world;
GeometryDrawStateData _drawState;
float _scaleInLightmap;
float _boundsScale;
@@ -50,15 +49,6 @@ public:
LightmapEntry Lightmap;
public:
/// <summary>
/// Gets the model world matrix transform.
/// </summary>
/// <param name="world">The result world matrix.</param>
FORCE_INLINE void GetWorld(Matrix* world) const
{
*world = _world;
}
/// <summary>
/// Gets the model scale in lightmap (applied to all the meshes).
/// </summary>

View File

@@ -0,0 +1,31 @@
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
#pragma once
#include "Engine/Core/Types/BaseTypes.h"
/// <summary>
/// The engine utility for large worlds support. Contains constants and tools for using 64-bit precision coordinates in various game systems (eg. scene rendering).
/// </summary>
API_CLASS(Static) class FLAXENGINE_API LargeWorlds
{
DECLARE_SCRIPTING_TYPE_MINIMAL(LargeWorlds);
/// <summary>
/// Enables large worlds usage in the engine. If true, scene rendering and other systems will use origin shifting to achieve higher precision in large worlds.
/// </summary>
API_FIELD() static bool Enable;
/// <summary>
/// Defines the size of a single chunk. Large world (64-bit) gets divided into smaller chunks so all the math operations (32-bit) can be performed relative to the chunk origin without precision loss.
/// </summary>
API_FIELD() static constexpr Real ChunkSize = 1024;
/// <summary>
/// Updates the large world origin to match the input position. The origin is snapped to the best matching chunk location.
/// </summary>
/// <param name="origin">The current origin of teh large world. Gets updated with the input position.</param>
/// <param name="position">The current input position (eg. render view location or auto listener position).</param>
/// <remarks>Used only if LargeWorlds::Enabled is true.</remarks>
API_FUNCTION() static void UpdateOrigin(API_PARAM(Ref) Vector3& origin, const Vector3& position);
};

View File

@@ -2,6 +2,7 @@
#include "Level.h"
#include "ActorsCache.h"
#include "LargeWorlds.h"
#include "SceneQuery.h"
#include "SceneObjectsFactory.h"
#include "Scene/Scene.h"
@@ -39,6 +40,19 @@
#include "Engine/Serialization/JsonSerializer.h"
#endif
#if USE_LARGE_WORLDS
bool LargeWorlds::Enable = true;
#else
bool LargeWorlds::Enable = false;
#endif
void LargeWorlds::UpdateOrigin(Vector3& origin, const Vector3& position)
{
if (Enable)
{
}
}
bool LayersMask::HasLayer(const StringView& layerName) const
{
return HasLayer(Level::GetLayerIndex(layerName));

View File

@@ -33,6 +33,7 @@ void SceneRendering::Draw(RenderContext& renderContext)
ScopeLock lock(Locker);
auto& view = renderContext.View;
const BoundingFrustum frustum = view.CullingFrustum;
const Vector3 origin = view.Origin;
renderContext.List->Scenes.Add(this);
// Draw all visual components
@@ -40,7 +41,8 @@ void SceneRendering::Draw(RenderContext& renderContext)
{
for (int32 i = 0; i < Actors.Count(); i++)
{
auto& e = Actors[i];
auto e = Actors[i];
e.Bounds.Center -= origin;
if (view.RenderLayersMask.Mask & e.LayerMask && (e.NoCulling || frustum.Intersects(e.Bounds)) && e.Actor->GetStaticFlags() & view.StaticFlagsMask)
{
#if SCENE_RENDERING_USE_PROFILER
@@ -54,7 +56,8 @@ void SceneRendering::Draw(RenderContext& renderContext)
{
for (int32 i = 0; i < Actors.Count(); i++)
{
auto& e = Actors[i];
auto e = Actors[i];
e.Bounds.Center -= origin;
if (view.RenderLayersMask.Mask & e.LayerMask && (e.NoCulling || frustum.Intersects(e.Bounds)))
{
#if SCENE_RENDERING_USE_PROFILER

View File

@@ -129,7 +129,7 @@ void BoxCollider::UpdateBounds()
{
// Cache bounds
OrientedBoundingBox::CreateCentered(_center, _size, _bounds);
_bounds.Transform(_transform.GetWorld());
_bounds.Transform(_transform);
_bounds.GetBoundingBox(_box);
BoundingSphere::FromBox(_box, _sphere);
}

View File

@@ -99,7 +99,7 @@ void CapsuleCollider::UpdateBounds()
// Cache bounds
const float radiusTwice = _radius * 2.0f;
OrientedBoundingBox::CreateCentered(_center, Vector3(_height + radiusTwice, radiusTwice, radiusTwice), _orientedBox);
_orientedBox.Transform(_transform.GetWorld());
_orientedBox.Transform(_transform);
_orientedBox.GetBoundingBox(_box);
BoundingSphere::FromBox(_box, _sphere);
}

View File

@@ -5,7 +5,6 @@
#include "Engine/Core/Math/Math.h"
#include "Engine/Level/Actors/BoxBrush.h"
#include "Engine/Level/Actors/StaticModel.h"
#include "Engine/ContentImporters/ImportTexture.h"
#include "Engine/Level/Scene/Scene.h"
#include "Engine/Level/Level.h"
#include "Engine/Terrain/Terrain.h"
@@ -40,10 +39,6 @@ bool cacheStaticGeometryTree(Actor* actor, ShadowsOfMordor::Builder::SceneBuildC
entry.AsStaticModel.Actor = staticModel;
entry.Scale = Math::Clamp(staticModel->GetScaleInLightmap(), 0.0f, LIGHTMAP_SCALE_MAX);
// Spawn entry for each mesh
Matrix world;
staticModel->GetWorld(&world);
// Use the first LOD
const int32 lodIndex = 0;
auto& lod = model->LODs[lodIndex];
@@ -61,18 +56,16 @@ bool cacheStaticGeometryTree(Actor* actor, ShadowsOfMordor::Builder::SceneBuildC
}
else
{
LOG(Warning, "Model \'{0}\' mesh index {1} (lod: {2}) has missing lightmap UVs (at actor: {3})",
model->GetPath(),
meshIndex,
lodIndex,
staticModel->GetNamePath());
LOG(Warning, "Model \'{0}\' mesh index {1} (lod: {2}) has missing lightmap UVs (at actor: {3})", model->GetPath(), meshIndex, lodIndex, staticModel->GetNamePath());
}
}
}
if (useLightmap && anyValid && entry.Scale > ZeroTolerance)
{
entry.Box = model->GetBox(world);
Matrix worldMatrix;
staticModel->GetTransform().GetWorld(worldMatrix);
entry.Box = model->GetBox(worldMatrix);
results.Add(entry);
}
else

View File

@@ -124,7 +124,7 @@ void ShadowsOfMordor::Builder::onJobRender(GPUContext* context)
auto& lod = staticModel->Model->LODs[0];
Matrix worldMatrix;
staticModel->GetWorld(&worldMatrix);
staticModel->GetTransform().GetWorld(worldMatrix);
Matrix::Transpose(worldMatrix, shaderData.WorldMatrix);
shaderData.LightmapArea = staticModel->Lightmap.UVsArea;

View File

@@ -189,7 +189,7 @@ void TerrainChunk::UpdateBounds()
{
const Vector3 boundsExtent = _patch->_terrain->_boundsExtent;
const float size = (float)_patch->_terrain->_chunkSize * TERRAIN_UNITS_PER_VERTEX;
Transform terrainTransform = _patch->_terrain->_transform;
const Transform terrainTransform = _patch->_terrain->_transform;
Transform localTransform;
localTransform.Translation = _patch->_offset + Vector3(_x * size, _yOffset, _z * size);
@@ -197,11 +197,8 @@ void TerrainChunk::UpdateBounds()
localTransform.Scale = Vector3(size, _yHeight, size);
localTransform = terrainTransform.LocalToWorld(localTransform);
Matrix world;
localTransform.GetWorld(world);
OrientedBoundingBox obb(Vector3::Zero, Vector3::One);
obb.Transform(world);
obb.Transform(localTransform);
obb.GetBoundingBox(_bounds);
_boundsCenter = _bounds.GetCenter();