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

View File

@@ -93,7 +93,7 @@ void ViewportIconsRendererService::DrawIcons(RenderContext& renderContext, Scene
Matrix m1, m2, world; Matrix m1, m2, world;
for (Actor* icon : icons) for (Actor* icon : icons)
{ {
BoundingSphere sphere(icon->GetPosition(), ICON_RADIUS); BoundingSphere sphere(icon->GetPosition() - renderContext.View.Origin, ICON_RADIUS);
IconTypes iconType; IconTypes iconType;
if (frustum.Intersects(sphere) && ActorTypeToIconType.TryGet(icon->GetTypeHandle(), iconType)) if (frustum.Intersects(sphere) && ActorTypeToIconType.TryGet(icon->GetTypeHandle(), iconType))
{ {
@@ -120,7 +120,7 @@ void ViewportIconsRendererService::DrawIcons(RenderContext& renderContext, Actor
auto& view = renderContext.View; auto& view = renderContext.View;
const BoundingFrustum frustum = view.Frustum; const BoundingFrustum frustum = view.Frustum;
Matrix m1, m2, world; Matrix m1, m2, world;
BoundingSphere sphere(actor->GetPosition(), ICON_RADIUS); BoundingSphere sphere(actor->GetPosition() - renderContext.View.Origin, ICON_RADIUS);
IconTypes iconType; IconTypes iconType;
if (frustum.Intersects(sphere) && ActorTypeToIconType.TryGet(actor->GetTypeHandle(), 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> /// <param name="view">The view.</param>
public void CopyViewData(ref RenderView view) public void CopyViewData(ref RenderView view)
{ {
// Create matrices Vector3 position = ViewPosition;
CreateProjectionMatrix(out view.Projection); LargeWorlds.UpdateOrigin(ref view.Origin, position);
CreateViewMatrix(out view.View); view.Position = position - view.Origin;
// Copy data
view.Position = ViewPosition;
view.Direction = ViewDirection; view.Direction = ViewDirection;
view.Near = _nearPlane; view.Near = _nearPlane;
view.Far = _farPlane; view.Far = _farPlane;
CreateProjectionMatrix(out view.Projection);
CreateViewMatrix(view.Position, out view.View);
view.UpdateCachedData(); view.UpdateCachedData();
} }
@@ -828,10 +828,10 @@ namespace FlaxEditor.Viewport
/// <summary> /// <summary>
/// Creates the view matrix. /// Creates the view matrix.
/// </summary> /// </summary>
/// <param name="position">The view position.</param>
/// <param name="result">The result.</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 direction = ViewDirection;
var target = position + direction; var target = position + direction;
var right = Float3.Normalize(Float3.Cross(Float3.Up, direction)); var right = Float3.Normalize(Float3.Cross(Float3.Up, direction));
@@ -862,7 +862,7 @@ namespace FlaxEditor.Viewport
// Prepare // Prepare
var viewport = new FlaxEngine.Viewport(0, 0, Width, Height); var viewport = new FlaxEngine.Viewport(0, 0, Width, Height);
CreateProjectionMatrix(out var p); 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); Matrix.Multiply(ref v, ref p, out var ivp);
ivp.Invert(); 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); 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 BoundingBox Model::GetBox(const Matrix& world, int32 lodIndex) const
{ {
return LODs[lodIndex].GetBox(world); return LODs[lodIndex].GetBox(world);

View File

@@ -134,6 +134,18 @@ public:
/// <returns>True whether the two objects intersected</returns> /// <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); 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> /// <summary>
/// Gets the model bounding box in custom matrix world space. /// Gets the model bounding box in custom matrix world space.
/// </summary> /// </summary>
@@ -142,6 +154,17 @@ public:
/// <returns>The bounding box.</returns> /// <returns>The bounding box.</returns>
API_FUNCTION() BoundingBox GetBox(const Matrix& world, int32 lodIndex = 0) const; 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> /// <summary>
/// Gets the model bounding box in local space. /// Gets the model bounding box in local space.
/// </summary> /// </summary>

View File

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

View File

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

View File

@@ -3,6 +3,7 @@
#pragma once #pragma once
#include "Engine/Core/Math/Vector3.h" #include "Engine/Core/Math/Vector3.h"
#include "Engine/Core/Math/Transform.h"
#include "Engine/Core/Math/Ray.h" #include "Engine/Core/Math/Ray.h"
#include "Engine/Core/Math/CollisionsHelper.h" #include "Engine/Core/Math/CollisionsHelper.h"
#include "Engine/Core/Collections/Array.h" #include "Engine/Core/Collections/Array.h"
@@ -74,4 +75,26 @@ public:
} }
return false; 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 "ModelInstanceEntry.h"
#include "Engine/Content/Assets/Material.h" #include "Engine/Content/Assets/Material.h"
#include "Engine/Content/Assets/Model.h" #include "Engine/Content/Assets/Model.h"
#include "Engine/Core/Math/Transform.h"
#include "Engine/Graphics/GPUContext.h" #include "Engine/Graphics/GPUContext.h"
#include "Engine/Graphics/GPUDevice.h" #include "Engine/Graphics/GPUDevice.h"
#include "Engine/Graphics/RenderTask.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 // Get bounding box of the mesh bounds transformed by the instance world matrix
Vector3 corners[8]; Vector3 corners[8];
GetCorners(corners); _box.GetCorners(corners);
Vector3 tmp; Vector3 tmp;
Vector3::Transform(corners[0], world, tmp); Vector3::Transform(corners[0], world, tmp);
Vector3 min = 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 // Use exact test on raw geometry
return _collisionProxy.Intersects(ray, world, distance, normal); 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; distance = 0;
normal = Vector3::Up; normal = Vector3::Up;
return false; return false;

View File

@@ -259,13 +259,14 @@ public:
bool Intersects(const Ray& ray, const Matrix& world, Real& distance, Vector3& normal) const; bool Intersects(const Ray& ray, const Matrix& world, Real& distance, Vector3& normal) const;
/// <summary> /// <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> /// </summary>
/// <param name="corners">An array of points representing the eight corners of the bounding box.</param> /// <param name="ray">The ray to test</param>
FORCE_INLINE void GetCorners(Vector3 corners[8]) const /// <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>
_box.GetCorners(corners); /// <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: public:
/// <summary> /// <summary>

View File

@@ -1,6 +1,7 @@
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved. // Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
#include "ModelLOD.h" #include "ModelLOD.h"
#include "Engine/Core/Math/Transform.h"
#include "Engine/Graphics/GPUDevice.h" #include "Engine/Graphics/GPUDevice.h"
#include "Engine/Serialization/MemoryReadStream.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) bool ModelLOD::Intersects(const Ray& ray, const Matrix& world, Real& distance, Vector3& normal, Mesh** mesh)
{ {
// Check all meshes
bool result = false; bool result = false;
Real closest = MAX_Real; Real closest = MAX_Real;
Vector3 closestNormal = Vector3::Up; Vector3 closestNormal = Vector3::Up;
for (int32 i = 0; i < Meshes.Count(); i++) for (int32 i = 0; i < Meshes.Count(); i++)
{ {
// Test intersection with mesh and check if is closer than previous
Real dst; Real dst;
Vector3 nrm; Vector3 nrm;
if (Meshes[i].Intersects(ray, world, dst, nrm) && dst < closest) 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; 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; distance = closest;
normal = closestNormal; normal = closestNormal;
return result; 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 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 tmp, min = Vector3::Maximum, max = Vector3::Minimum;
Vector3 corners[8]; Vector3 corners[8];
for (int32 j = 0; j < Meshes.Count(); j++) for (int32 j = 0; j < Meshes.Count(); j++)
{ {
const auto& mesh = Meshes[j]; const auto& mesh = Meshes[j];
mesh.GetCorners(corners); mesh.GetBox().GetCorners(corners);
for (int32 i = 0; i < 8; i++) for (int32 i = 0; i < 8; i++)
{ {
Vector3::Transform(corners[i], world, tmp); Vector3::Transform(corners[i], world, tmp);
@@ -100,42 +118,39 @@ BoundingBox ModelLOD::GetBox(const Matrix& world) const
max = Vector3::Max(max, tmp); max = Vector3::Max(max, tmp);
} }
} }
return BoundingBox(min, max); 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 tmp, min = Vector3::Maximum, max = Vector3::Minimum;
Vector3 corners[8]; Vector3 corners[8];
const auto& mesh = Meshes[meshIndex]; for (int32 j = 0; j < Meshes.Count(); j++)
mesh.GetCorners(corners); {
const auto& mesh = Meshes[j];
mesh.GetBox().GetCorners(corners);
for (int32 i = 0; i < 8; i++) for (int32 i = 0; i < 8; i++)
{ {
Vector3::Transform(corners[i], world, tmp); transform.LocalToWorld(corners[i], tmp);
min = Vector3::Min(min, tmp); min = Vector3::Min(min, tmp);
max = Vector3::Max(max, tmp); max = Vector3::Max(max, tmp);
} }
}
return BoundingBox(min, max); return BoundingBox(min, max);
} }
BoundingBox ModelLOD::GetBox() const BoundingBox ModelLOD::GetBox() const
{ {
// Find minimum and maximum points of the mesh in given world
Vector3 min = Vector3::Maximum, max = Vector3::Minimum; Vector3 min = Vector3::Maximum, max = Vector3::Minimum;
Vector3 corners[8]; Vector3 corners[8];
for (int32 j = 0; j < Meshes.Count(); j++) for (int32 j = 0; j < Meshes.Count(); j++)
{ {
Meshes[j].GetCorners(corners); Meshes[j].GetBox().GetCorners(corners);
for (int32 i = 0; i < 8; i++) for (int32 i = 0; i < 8; i++)
{ {
min = Vector3::Min(min, corners[i]); min = Vector3::Min(min, corners[i]);
max = Vector3::Max(max, corners[i]); max = Vector3::Max(max, corners[i]);
} }
} }
return BoundingBox(min, max); 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); bool Intersects(const Ray& ray, const Matrix& world, Real& distance, Vector3& normal, Mesh** mesh);
/// <summary> /// <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> /// </summary>
/// <param name="world">World matrix</param> /// <param name="world">World matrix</param>
/// <returns>Bounding box</returns> /// <returns>Bounding box</returns>
BoundingBox GetBox(const Matrix& world) const; BoundingBox GetBox(const Matrix& world) const;
/// <summary> /// <summary>
/// Get model bounding box in transformed world for given instance buffer for only one mesh /// Get model bounding box in transformed world.
/// </summary> /// </summary>
/// <param name="world">World matrix</param> /// <param name="transform">The instance transformation.</param>
/// <param name="meshIndex">esh index</param>
/// <returns>Bounding box</returns> /// <returns>Bounding box</returns>
BoundingBox GetBox(const Matrix& world, int32 meshIndex) const; BoundingBox GetBox(const Transform& transform) const;
/// <summary> /// <summary>
/// Gets the bounding box combined for all meshes in this model LOD. /// 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) 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) if ((ActorsSource & ActorsSources::Scenes) != 0)
{ {
Level::CollectPostFxVolumes(renderContext); Level::CollectPostFxVolumes(renderContext);

View File

@@ -1,6 +1,7 @@
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved. // Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
#include "RenderView.h" #include "RenderView.h"
#include "Engine/Level/LargeWorlds.h"
#include "Engine/Level/Actors/Camera.h" #include "Engine/Level/Actors/Camera.h"
#include "Engine/Renderer/RenderList.h" #include "Engine/Renderer/RenderList.h"
#include "Engine/Renderer/RendererPass.h" #include "Engine/Renderer/RendererPass.h"
@@ -70,6 +71,8 @@ void RenderView::PrepareCache(RenderContext& renderContext, float width, float h
TemporalAAJitter.X = temporalAAJitter.X; TemporalAAJitter.X = temporalAAJitter.X;
TemporalAAJitter.Y = temporalAAJitter.Y; TemporalAAJitter.Y = temporalAAJitter.Y;
WorldPosition = Origin + Position;
// Ortho views have issues with screen size LOD culling // Ortho views have issues with screen size LOD culling
const float modelLODDistanceFactor = (renderContext.LodProxyView ? renderContext.LodProxyView->IsOrthographicProjection() : IsOrthographicProjection()) ? 100.0f : ModelLODDistanceFactor; const float modelLODDistanceFactor = (renderContext.LodProxyView ? renderContext.LodProxyView->IsOrthographicProjection() : IsOrthographicProjection()) ? 100.0f : ModelLODDistanceFactor;
ModelLODDistanceFactorSqrt = modelLODDistanceFactor * modelLODDistanceFactor; ModelLODDistanceFactorSqrt = modelLODDistanceFactor * modelLODDistanceFactor;
@@ -161,31 +164,15 @@ void RenderView::SetProjector(float nearPlane, float farPlane, const Float3& pos
CullingFrustum = Frustum; 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) 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(); Direction = camera->GetDirection();
Near = camera->GetNearPlane(); Near = camera->GetNearPlane();
Far = camera->GetFarPlane(); Far = camera->GetFarPlane();
View = camera->GetView(); camera->GetMatrices(View, Projection, viewport ? *viewport : camera->GetViewport(), Origin);
camera->GetMatrices(View, Projection, *viewport);
Frustum.SetMatrix(View, Projection); Frustum.SetMatrix(View, Projection);
NonJitteredProjection = Projection; NonJitteredProjection = Projection;
Matrix::Invert(View, IV); Matrix::Invert(View, IV);
@@ -211,3 +198,9 @@ DrawPass RenderView::GetShadowsDrawPassMask(ShadowsCastingMode shadowsMode) cons
return DrawPass::All; 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> /// <param name="camera">The camera.</param>
public void CopyFrom(Camera camera) public void CopyFrom(Camera camera)
{ {
Position = camera.Position; var viewport = camera.Viewport;
Direction = camera.Direction; CopyFrom(camera, ref viewport);
Near = camera.NearPlane;
Far = camera.FarPlane;
View = camera.View;
Projection = camera.Projection;
NonJitteredProjection = Projection;
TemporalAAJitter = Float4.Zero;
RenderLayersMask = camera.RenderLayersMask;
UpdateCachedData();
} }
/// <summary> /// <summary>
/// Copies render view data from the camera. /// Copies render view data from the camera.
/// </summary> /// </summary>
/// <param name="camera">The camera.</param> /// <param name="camera">The camera.</param>
/// <param name="customViewport">The custom viewport to use for view/projeection matrices override.</param> /// <param name="viewport">The custom viewport to use for view/projeection matrices override.</param>
public void CopyFrom(Camera camera, ref Viewport customViewport) 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; Direction = camera.Direction;
Near = camera.NearPlane; Near = camera.NearPlane;
Far = camera.FarPlane; Far = camera.FarPlane;
camera.GetMatrices(out View, out Projection, ref customViewport); camera.GetMatrices(out View, out Projection, ref viewport, ref Origin);
NonJitteredProjection = Projection; NonJitteredProjection = Projection;
TemporalAAJitter = Float4.Zero; TemporalAAJitter = Float4.Zero;
RenderLayersMask = camera.RenderLayersMask; RenderLayersMask = camera.RenderLayersMask;

View File

@@ -24,7 +24,17 @@ API_STRUCT() struct FLAXENGINE_API RenderView
DECLARE_SCRIPTING_TYPE_MINIMAL(RenderView); DECLARE_SCRIPTING_TYPE_MINIMAL(RenderView);
/// <summary> /// <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> /// </summary>
API_FIELD() Float3 Position; API_FIELD() Float3 Position;
@@ -264,14 +274,10 @@ public:
/// <param name="angle">Camera's FOV angle (in degrees)</param> /// <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); 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 // Copy view data from camera
// @param camera Camera to copy its data // @param camera Camera to copy its data
// @param camera The custom viewport to use for view/projection matrices override. // @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: public:
DrawPass GetShadowsDrawPassMask(ShadowsCastingMode shadowsMode) const; DrawPass GetShadowsDrawPassMask(ShadowsCastingMode shadowsMode) const;
@@ -288,4 +294,7 @@ public:
{ {
return Frustum.GetMatrix(); 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; const auto prevBounds = _box;
_size = value; _size = value;
OrientedBoundingBox::CreateCentered(Vector3::Zero, _size, _bounds); OrientedBoundingBox::CreateCentered(Vector3::Zero, _size, _bounds);
_bounds.Transform(_transform.GetWorld()); _bounds.Transform(_transform);
_bounds.GetBoundingBox(_box); _bounds.GetBoundingBox(_box);
BoundingSphere::FromBox(_box, _sphere); BoundingSphere::FromBox(_box, _sphere);
OnBoundsChanged(prevBounds); OnBoundsChanged(prevBounds);
@@ -142,7 +142,7 @@ void BoxVolume::OnTransformChanged()
const auto prevBounds = _box; const auto prevBounds = _box;
OrientedBoundingBox::CreateCentered(Vector3::Zero, _size, _bounds); OrientedBoundingBox::CreateCentered(Vector3::Zero, _size, _bounds);
_bounds.Transform(_transform.GetWorld()); _bounds.Transform(_transform);
_bounds.GetBoundingBox(_box); _bounds.GetBoundingBox(_box);
BoundingSphere::FromBox(_box, _sphere); BoundingSphere::FromBox(_box, _sphere);
OnBoundsChanged(prevBounds); OnBoundsChanged(prevBounds);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -14,7 +14,6 @@ API_CLASS() class FLAXENGINE_API StaticModel : public ModelInstanceActor
{ {
DECLARE_SCENE_OBJECT(StaticModel); DECLARE_SCENE_OBJECT(StaticModel);
private: private:
Matrix _world;
GeometryDrawStateData _drawState; GeometryDrawStateData _drawState;
float _scaleInLightmap; float _scaleInLightmap;
float _boundsScale; float _boundsScale;
@@ -50,15 +49,6 @@ public:
LightmapEntry Lightmap; LightmapEntry Lightmap;
public: 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> /// <summary>
/// Gets the model scale in lightmap (applied to all the meshes). /// Gets the model scale in lightmap (applied to all the meshes).
/// </summary> /// </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 "Level.h"
#include "ActorsCache.h" #include "ActorsCache.h"
#include "LargeWorlds.h"
#include "SceneQuery.h" #include "SceneQuery.h"
#include "SceneObjectsFactory.h" #include "SceneObjectsFactory.h"
#include "Scene/Scene.h" #include "Scene/Scene.h"
@@ -39,6 +40,19 @@
#include "Engine/Serialization/JsonSerializer.h" #include "Engine/Serialization/JsonSerializer.h"
#endif #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 bool LayersMask::HasLayer(const StringView& layerName) const
{ {
return HasLayer(Level::GetLayerIndex(layerName)); return HasLayer(Level::GetLayerIndex(layerName));

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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