Merge branch 'Withaust-terrainscripting'

This commit is contained in:
Wojtek Figat
2024-02-16 23:49:40 +01:00
9 changed files with 195 additions and 158 deletions

View File

@@ -20,7 +20,7 @@ bool TerrainTools::TryGetPatchCoordToAdd(Terrain* terrain, const Ray& ray, Int2&
{
CHECK_RETURN(terrain, true);
result = Int2::Zero;
const float patchSize = terrain->GetChunkSize() * TERRAIN_UNITS_PER_VERTEX * TerrainPatch::CHUNKS_COUNT_EDGE;
const float patchSize = terrain->GetChunkSize() * TERRAIN_UNITS_PER_VERTEX * Terrain::ChunksCountEdge;
// Try to pick any of the patch edges
for (int32 patchIndex = 0; patchIndex < terrain->GetPatchesCount(); patchIndex++)
@@ -179,7 +179,7 @@ bool TerrainTools::GenerateTerrain(Terrain* terrain, const Int2& numberOfPatches
terrain->AddPatches(numberOfPatches);
// Prepare data
const auto heightmapSize = terrain->GetChunkSize() * TerrainPatch::CHUNKS_COUNT_EDGE + 1;
const auto heightmapSize = terrain->GetChunkSize() * Terrain::ChunksCountEdge + 1;
Array<float> heightmapData;
heightmapData.Resize(heightmapSize * heightmapSize);
@@ -380,7 +380,7 @@ bool TerrainTools::ExportTerrain(Terrain* terrain, String outputFolder)
const auto firstPatch = terrain->GetPatch(0);
// Calculate texture size
const int32 patchEdgeVertexCount = terrain->GetChunkSize() * TerrainPatch::CHUNKS_COUNT_EDGE + 1;
const int32 patchEdgeVertexCount = terrain->GetChunkSize() * Terrain::ChunksCountEdge + 1;
const int32 patchVertexCount = patchEdgeVertexCount * patchEdgeVertexCount;
// Find size of heightmap in patches

View File

@@ -84,7 +84,7 @@ bool cacheStaticGeometryTree(Actor* actor, ShadowsOfMordor::Builder::SceneBuildC
{
auto patch = terrain->GetPatch(patchIndex);
entry.AsTerrain.PatchIndex = patchIndex;
for (int32 chunkIndex = 0; chunkIndex < TerrainPatch::CHUNKS_COUNT; chunkIndex++)
for (int32 chunkIndex = 0; chunkIndex < Terrain::ChunksCount; chunkIndex++)
{
auto chunk = patch->Chunks[chunkIndex];
entry.AsTerrain.ChunkIndex = chunkIndex;

View File

@@ -165,7 +165,7 @@ void ShadowsOfMordor::Builder::onJobRender(GPUContext* context)
Matrix::Transpose(world, shaderData.WorldMatrix);
shaderData.LightmapArea = chunk->Lightmap.UVsArea;
shaderData.TerrainChunkSizeLOD0 = TERRAIN_UNITS_PER_VERTEX * chunkSize;
chunk->GetHeightmapUVScaleBias(&shaderData.HeightmapUVScaleBias);
shaderData.HeightmapUVScaleBias = chunk->GetHeightmapUVScaleBias();
// Extract per axis scales from LocalToWorld transform
const float scaleX = Float3(world.M11, world.M12, world.M13).Length();

View File

@@ -59,7 +59,7 @@ void Terrain::CacheNeighbors()
for (int32 pathIndex = 0; pathIndex < _patches.Count(); pathIndex++)
{
const auto patch = _patches[pathIndex];
for (int32 chunkIndex = 0; chunkIndex < TerrainPatch::CHUNKS_COUNT; chunkIndex++)
for (int32 chunkIndex = 0; chunkIndex < Terrain::ChunksCount; chunkIndex++)
{
patch->Chunks[chunkIndex].CacheNeighbors();
}
@@ -215,7 +215,7 @@ void Terrain::DrawPatch(const RenderContext& renderContext, const Int2& patchCoo
auto patch = GetPatch(patchCoord);
if (patch)
{
for (int32 i = 0; i < TerrainPatch::CHUNKS_COUNT; i++)
for (int32 i = 0; i < Terrain::ChunksCount; i++)
patch->Chunks[i].Draw(renderContext, material, lodIndex);
}
}
@@ -544,7 +544,7 @@ void Terrain::Draw(RenderContext& renderContext)
Matrix localToWorld, worldToLocal;
BoundingSphere chunkSphere;
BoundingBox localBounds;
for (int32 chunkIndex = 0; chunkIndex < TerrainPatch::CHUNKS_COUNT; chunkIndex++)
for (int32 chunkIndex = 0; chunkIndex < Terrain::ChunksCount; chunkIndex++)
{
TerrainChunk* chunk = &patch->Chunks[chunkIndex];
chunk->GetTransform().GetWorld(localToWorld); // TODO: large-worlds
@@ -574,7 +574,7 @@ void Terrain::Draw(RenderContext& renderContext)
continue;
// Frustum vs Box culling for chunks
for (int32 chunkIndex = 0; chunkIndex < TerrainPatch::CHUNKS_COUNT; chunkIndex++)
for (int32 chunkIndex = 0; chunkIndex < Terrain::ChunksCount; chunkIndex++)
{
auto chunk = &patch->Chunks[chunkIndex];
chunk->_cachedDrawLOD = 0;
@@ -592,7 +592,7 @@ void Terrain::Draw(RenderContext& renderContext)
else
{
// Reset cached LOD for chunks (prevent LOD transition from invisible chunks)
for (int32 chunkIndex = 0; chunkIndex < TerrainPatch::CHUNKS_COUNT; chunkIndex++)
for (int32 chunkIndex = 0; chunkIndex < Terrain::ChunksCount; chunkIndex++)
{
auto chunk = &patch->Chunks[chunkIndex];
chunk->_cachedDrawLOD = 0;
@@ -620,10 +620,10 @@ void Terrain::OnDebugDrawSelected()
for (int32 pathIndex = 0; pathIndex < _patches.Count(); pathIndex++)
{
const auto patch = _patches[pathIndex];
for (int32 chunkIndex = 0; chunkIndex < TerrainPatch::CHUNKS_COUNT; chunkIndex++)
for (int32 chunkIndex = 0; chunkIndex < Terrain::ChunksCount; chunkIndex++)
{
auto chunk = &patch->Chunks[chunkIndex];
DebugDraw::DrawBox(chunk->_bounds, Color(chunk->_x / (float)TerrainPatch::CHUNKS_COUNT_EDGE, 1.0f, chunk->_z / (float)TerrainPatch::CHUNKS_COUNT_EDGE));
DebugDraw::DrawBox(chunk->_bounds, Color(chunk->_x / (float)Terrain::ChunksCountEdge, 1.0f, chunk->_z / (float)Terrain::ChunksCountEdge));
}
}
*/

View File

@@ -44,6 +44,22 @@ API_CLASS(Sealed) class FLAXENGINE_API Terrain : public PhysicsColliderActor
friend TerrainPatch;
friend TerrainChunk;
/// <summary>
/// Various defines regarding terrain configuration.
/// </summary>
API_ENUM() enum Config
{
/// <summary>
/// The maximum allowed amount of chunks per patch.
/// </summary>
ChunksCount = 16,
/// <summary>
/// The maximum allowed amount of chunks per chunk.
/// </summary>
ChunksCountEdge = 4,
};
private:
char _lodBias;
char _forcedLod;
@@ -223,7 +239,7 @@ public:
/// </summary>
/// <param name="patchCoord">The patch location (x and z).</param>
/// <returns>The patch.</returns>
TerrainPatch* GetPatch(const Int2& patchCoord) const;
API_FUNCTION() TerrainPatch* GetPatch(API_PARAM(Ref) const Int2& patchCoord) const;
/// <summary>
/// Gets the patch at the given location.
@@ -231,7 +247,7 @@ public:
/// <param name="x">The patch location x.</param>
/// <param name="z">The patch location z.</param>
/// <returns>The patch.</returns>
TerrainPatch* GetPatch(int32 x, int32 z) const;
API_FUNCTION() TerrainPatch* GetPatch(int32 x, int32 z) const;
/// <summary>
/// Gets the zero-based index of the terrain patch in the terrain patches collection.
@@ -245,7 +261,7 @@ public:
/// </summary>
/// <param name="index">The index.</param>
/// <returns>The patch.</returns>
FORCE_INLINE TerrainPatch* GetPatch(int32 index) const
API_FUNCTION() FORCE_INLINE TerrainPatch* GetPatch(int32 index) const
{
return _patches[index];
}
@@ -316,7 +332,6 @@ public:
public:
#if TERRAIN_EDITING
/// <summary>
/// Setups the terrain. Clears the existing data.
/// </summary>
@@ -341,7 +356,6 @@ public:
/// </summary>
/// <param name="patchCoord">The patch location (x and z).</param>
API_FUNCTION() void RemovePatch(API_PARAM(Ref) const Int2& patchCoord);
#endif
/// <summary>
@@ -374,7 +388,7 @@ public:
/// <param name="resultChunk">The raycast result hit chunk. Valid only if raycast hits anything.</param>
/// <param name="maxDistance">The maximum distance the ray should check for collisions.</param>
/// <returns>True if ray hits an object, otherwise false.</returns>
bool RayCast(const Vector3& origin, const Vector3& direction, float& resultHitDistance, TerrainChunk*& resultChunk, float maxDistance = MAX_float) const;
API_FUNCTION() bool RayCast(const Vector3& origin, const Vector3& direction, API_PARAM(Out) float& resultHitDistance, API_PARAM(Out) TerrainChunk*& resultChunk, float maxDistance = MAX_float) const;
/// <summary>
/// Performs a raycast against this terrain collision shape. Returns the hit chunk.

View File

@@ -11,7 +11,14 @@
#include "Engine/Renderer/RenderList.h"
#include "Engine/Core/Math/OrientedBoundingBox.h"
#include "Engine/Level/Scene/Scene.h"
#if USE_EDITOR
#include "Engine/Level/Prefabs/PrefabManager.h"
#endif
TerrainChunk::TerrainChunk(const SpawnParams& params)
: ScriptingObject(params)
{
}
void TerrainChunk::Init(TerrainPatch* patch, uint16 x, uint16 z)
{
@@ -21,7 +28,7 @@ void TerrainChunk::Init(TerrainPatch* patch, uint16 x, uint16 z)
_z = z;
_yOffset = 0;
_yHeight = 1;
_heightmapUVScaleBias = Float4(1.0f, 1.0f, _x, _z) * (1.0f / TerrainPatch::CHUNKS_COUNT_EDGE);
_heightmapUVScaleBias = Float4(1.0f, 1.0f, _x, _z) * (1.0f / Terrain::ChunksCountEdge);
_perInstanceRandom = (_patch->_terrain->_id.C ^ _x ^ _z) * (1.0f / (float)MAX_uint32);
OverrideMaterial = nullptr;
}
@@ -51,8 +58,8 @@ bool TerrainChunk::PrepareDraw(const RenderContext& renderContext)
//lod = 0;
//lod = 10;
//lod = (_x + _z + TerrainPatch::CHUNKS_COUNT_EDGE * (_patch->_x + _patch->_z));
//lod = (int32)Vector2::Distance(Vector2(2, 2), Vector2(_patch->_x, _patch->_z) * TerrainPatch::CHUNKS_COUNT_EDGE + Vector2(_x, _z));
//lod = (_x + _z + Terrain::ChunksCountEdge * (_patch->_x + _patch->_z));
//lod = (int32)Vector2::Distance(Vector2(2, 2), Vector2(_patch->_x, _patch->_z) * Terrain::ChunksCountEdge + Vector2(_x, _z));
//lod = (int32)(Vector3::Distance(_bounds.GetCenter(), view.Position) / 10000.0f);
}
lod = Math::Clamp(lod, minStreamedLod, lodCount - 1);
@@ -93,7 +100,7 @@ void TerrainChunk::Draw(const RenderContext& renderContext) const
drawCall.ObjectRadius = _sphere.Radius;
drawCall.Terrain.Patch = _patch;
drawCall.Terrain.HeightmapUVScaleBias = _heightmapUVScaleBias;
drawCall.Terrain.OffsetUV = Vector2((float)(_patch->_x * TerrainPatch::CHUNKS_COUNT_EDGE + _x), (float)(_patch->_z * TerrainPatch::CHUNKS_COUNT_EDGE + _z));
drawCall.Terrain.OffsetUV = Vector2((float)(_patch->_x * Terrain::ChunksCountEdge + _x), (float)(_patch->_z * Terrain::ChunksCountEdge + _z));
drawCall.Terrain.CurrentLOD = (float)lod;
drawCall.Terrain.ChunkSizeNextLOD = (float)(((chunkSize + 1) >> (lod + 1)) - 1);
drawCall.Terrain.TerrainChunkSizeLOD0 = TERRAIN_UNITS_PER_VERTEX * chunkSize;
@@ -151,7 +158,7 @@ void TerrainChunk::Draw(const RenderContext& renderContext, MaterialBase* materi
drawCall.ObjectRadius = _sphere.Radius;
drawCall.Terrain.Patch = _patch;
drawCall.Terrain.HeightmapUVScaleBias = _heightmapUVScaleBias;
drawCall.Terrain.OffsetUV = Vector2((float)(_patch->_x * TerrainPatch::CHUNKS_COUNT_EDGE + _x), (float)(_patch->_z * TerrainPatch::CHUNKS_COUNT_EDGE + _z));
drawCall.Terrain.OffsetUV = Vector2((float)(_patch->_x * Terrain::ChunksCountEdge + _x), (float)(_patch->_z * Terrain::ChunksCountEdge + _z));
drawCall.Terrain.CurrentLOD = (float)lod;
drawCall.Terrain.ChunkSizeNextLOD = (float)(((chunkSize + 1) >> (lod + 1)) - 1);
drawCall.Terrain.TerrainChunkSizeLOD0 = TERRAIN_UNITS_PER_VERTEX * chunkSize;
@@ -232,46 +239,46 @@ void TerrainChunk::CacheNeighbors()
_neighbors[0] = this;
if (_z > 0)
{
_neighbors[0] = &_patch->Chunks[(_z - 1) * TerrainPatch::CHUNKS_COUNT_EDGE + _x];
_neighbors[0] = &_patch->Chunks[(_z - 1) * Terrain::ChunksCountEdge + _x];
}
else
{
const auto patch = _patch->_terrain->GetPatch(_patch->_x, _patch->_z - 1);
if (patch)
_neighbors[0] = &patch->Chunks[(TerrainPatch::CHUNKS_COUNT_EDGE - 1) * TerrainPatch::CHUNKS_COUNT_EDGE + _x];
_neighbors[0] = &patch->Chunks[(Terrain::ChunksCountEdge - 1) * Terrain::ChunksCountEdge + _x];
}
// 1: left
_neighbors[1] = this;
if (_x > 0)
{
_neighbors[1] = &_patch->Chunks[_z * TerrainPatch::CHUNKS_COUNT_EDGE + (_x - 1)];
_neighbors[1] = &_patch->Chunks[_z * Terrain::ChunksCountEdge + (_x - 1)];
}
else
{
const auto patch = _patch->_terrain->GetPatch(_patch->_x - 1, _patch->_z);
if (patch)
_neighbors[1] = &patch->Chunks[_z * TerrainPatch::CHUNKS_COUNT_EDGE + (TerrainPatch::CHUNKS_COUNT_EDGE - 1)];
_neighbors[1] = &patch->Chunks[_z * Terrain::ChunksCountEdge + (Terrain::ChunksCountEdge - 1)];
}
// 2: right
_neighbors[2] = this;
if (_x < TerrainPatch::CHUNKS_COUNT_EDGE - 1)
if (_x < Terrain::ChunksCountEdge - 1)
{
_neighbors[2] = &_patch->Chunks[_z * TerrainPatch::CHUNKS_COUNT_EDGE + (_x + 1)];
_neighbors[2] = &_patch->Chunks[_z * Terrain::ChunksCountEdge + (_x + 1)];
}
else
{
const auto patch = _patch->_terrain->GetPatch(_patch->_x + 1, _patch->_z);
if (patch)
_neighbors[2] = &patch->Chunks[_z * TerrainPatch::CHUNKS_COUNT_EDGE];
_neighbors[2] = &patch->Chunks[_z * Terrain::ChunksCountEdge];
}
// 3: top
_neighbors[3] = this;
if (_z < TerrainPatch::CHUNKS_COUNT_EDGE - 1)
if (_z < Terrain::ChunksCountEdge - 1)
{
_neighbors[3] = &_patch->Chunks[(_z + 1) * TerrainPatch::CHUNKS_COUNT_EDGE + _x];
_neighbors[3] = &_patch->Chunks[(_z + 1) * Terrain::ChunksCountEdge + _x];
}
else
{

View File

@@ -17,14 +17,14 @@ struct RenderContext;
/// <summary>
/// Represents a single terrain chunk.
/// </summary>
class FLAXENGINE_API TerrainChunk : public ISerializable
API_CLASS(Sealed, NoSpawn) class FLAXENGINE_API TerrainChunk : public ScriptingObject, public ISerializable
{
DECLARE_SCRIPTING_TYPE(TerrainChunk);
friend Terrain;
friend TerrainPatch;
friend TerrainChunk;
private:
TerrainPatch* _patch;
uint16 _x, _z;
Float4 _heightmapUVScaleBias;
@@ -41,11 +41,10 @@ private:
void Init(TerrainPatch* patch, uint16 x, uint16 z);
public:
/// <summary>
/// The material to override the terrain default one for this chunk.
/// </summary>
AssetReference<MaterialBase> OverrideMaterial;
API_FIELD() AssetReference<MaterialBase> OverrideMaterial;
/// <summary>
/// The baked lightmap entry info for this chunk.
@@ -53,11 +52,10 @@ public:
LightmapEntry Lightmap;
public:
/// <summary>
/// Gets the x coordinate.
/// </summary>
FORCE_INLINE int32 GetX() const
API_FUNCTION() FORCE_INLINE int32 GetX() const
{
return _x;
}
@@ -65,7 +63,7 @@ public:
/// <summary>
/// Gets the z coordinate.
/// </summary>
FORCE_INLINE int32 GetZ() const
API_FUNCTION() FORCE_INLINE int32 GetZ() const
{
return _z;
}
@@ -73,7 +71,7 @@ public:
/// <summary>
/// Gets the patch.
/// </summary>
FORCE_INLINE TerrainPatch* GetPatch() const
API_FUNCTION() FORCE_INLINE TerrainPatch* GetPatch() const
{
return _patch;
}
@@ -81,7 +79,7 @@ public:
/// <summary>
/// Gets the chunk world bounds.
/// </summary>
FORCE_INLINE const BoundingBox& GetBounds() const
API_FUNCTION() FORCE_INLINE const BoundingBox& GetBounds() const
{
return _bounds;
}
@@ -89,7 +87,7 @@ public:
/// <summary>
/// Gets the chunk transformation (world to local).
/// </summary>
FORCE_INLINE const Transform& GetTransform() const
API_FUNCTION() FORCE_INLINE const Transform& GetTransform() const
{
return _transform;
}
@@ -97,10 +95,9 @@ public:
/// <summary>
/// Gets the scale (in XY) and bias (in ZW) applied to the vertex UVs to get the chunk coordinates.
/// </summary>
/// <param name="result">The result.</param>
FORCE_INLINE void GetHeightmapUVScaleBias(Float4* result) const
API_FUNCTION() FORCE_INLINE const Float4& GetHeightmapUVScaleBias() const
{
*result = _heightmapUVScaleBias;
return _heightmapUVScaleBias;
}
/// <summary>
@@ -120,7 +117,6 @@ public:
}
public:
/// <summary>
/// Prepares for drawing chunk. Cached LOD and material.
/// </summary>
@@ -140,7 +136,7 @@ public:
/// <param name="renderContext">The rendering context.</param>
/// <param name="material">The material to use for rendering.</param>
/// <param name="lodIndex">The LOD index.</param>
void Draw(const RenderContext& renderContext, MaterialBase* material, int32 lodIndex = 0) const;
API_FUNCTION() void Draw(API_PARAM(Ref) const RenderContext& renderContext, MaterialBase* material, int32 lodIndex = 0) const;
/// <summary>
/// Determines if there is an intersection between the terrain chunk and a point
@@ -148,7 +144,7 @@ public:
/// <param name="ray">The ray.</param>
/// <param name="distance">The output distance.</param>
/// <returns>True if chunk intersects with the ray, otherwise false.</returns>
bool Intersects(const Ray& ray, Real& distance);
API_FUNCTION() bool Intersects(const Ray& ray, API_PARAM(Out) Real& distance);
/// <summary>
/// Updates the cached bounds of the chunk.
@@ -166,7 +162,6 @@ public:
void CacheNeighbors();
public:
// [ISerializable]
void Serialize(SerializeStream& stream, const void* otherObj) override;
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override;

View File

@@ -44,6 +44,11 @@ struct TerrainCollisionDataHeader
float ScaleXZ;
};
TerrainPatch::TerrainPatch(const SpawnParams& params)
: ScriptingObject(params)
{
}
void TerrainPatch::Init(Terrain* terrain, int16 x, int16 z)
{
ScopeLock lock(_collisionLocker);
@@ -54,13 +59,13 @@ void TerrainPatch::Init(Terrain* terrain, int16 x, int16 z)
_physicsHeightField = nullptr;
_x = x;
_z = z;
const float size = _terrain->_chunkSize * TERRAIN_UNITS_PER_VERTEX * CHUNKS_COUNT_EDGE;
const float size = _terrain->_chunkSize * TERRAIN_UNITS_PER_VERTEX * Terrain::Terrain::ChunksCountEdge;
_offset = Float3(_x * size, 0.0f, _z * size);
_yOffset = 0.0f;
_yHeight = 1.0f;
for (int32 i = 0; i < CHUNKS_COUNT; i++)
for (int32 i = 0; i < Terrain::ChunksCount; i++)
{
Chunks[i].Init(this, i % CHUNKS_COUNT_EDGE, i / CHUNKS_COUNT_EDGE);
Chunks[i].Init(this, i % Terrain::Terrain::ChunksCountEdge, i / Terrain::Terrain::ChunksCountEdge);
}
Heightmap = nullptr;
for (int32 i = 0; i < TERRAIN_MAX_SPLATMAPS_COUNT; i++)
@@ -111,7 +116,7 @@ void TerrainPatch::UpdateBounds()
PROFILE_CPU();
Chunks[0].UpdateBounds();
_bounds = Chunks[0]._bounds;
for (int32 i = 1; i < CHUNKS_COUNT; i++)
for (int32 i = 1; i < Terrain::ChunksCount; i++)
{
Chunks[i].UpdateBounds();
BoundingBox::Merge(_bounds, Chunks[i]._bounds, _bounds);
@@ -130,7 +135,7 @@ void TerrainPatch::UpdateTransform()
}
// Update chunks cache
for (int32 i = 0; i < CHUNKS_COUNT; i++)
for (int32 i = 0; i < Terrain::ChunksCount; i++)
{
Chunks[i].UpdateTransform();
}
@@ -168,9 +173,9 @@ struct TerrainDataUpdateInfo
{
ChunkSize = patch->GetTerrain()->GetChunkSize();
VertexCountEdge = ChunkSize + 1;
HeightmapSize = ChunkSize * TerrainPatch::CHUNKS_COUNT_EDGE + 1;
HeightmapSize = ChunkSize * Terrain::ChunksCountEdge + 1;
HeightmapLength = HeightmapSize * HeightmapSize;
TextureSize = VertexCountEdge * TerrainPatch::CHUNKS_COUNT_EDGE;
TextureSize = VertexCountEdge * Terrain::ChunksCountEdge;
}
bool UsePhysicalMaterials() const
@@ -231,7 +236,7 @@ FORCE_INLINE bool ReadIsHole(const Color32& raw)
return (raw.B + raw.A) >= (int32)(1.9f * MAX_uint8);
}
void CalculateHeightmapRange(Terrain* terrain, TerrainDataUpdateInfo& info, const float* heightmap, float chunkOffsets[TerrainPatch::CHUNKS_COUNT], float chunkHeights[TerrainPatch::CHUNKS_COUNT])
void CalculateHeightmapRange(Terrain* terrain, TerrainDataUpdateInfo& info, const float* heightmap, float chunkOffsets[Terrain::ChunksCount], float chunkHeights[Terrain::ChunksCount])
{
PROFILE_CPU_NAMED("Terrain.CalculateRange");
@@ -240,10 +245,10 @@ void CalculateHeightmapRange(Terrain* terrain, TerrainDataUpdateInfo& info, cons
float minPatchHeight = MAX_float;
float maxPatchHeight = MIN_float;
for (int32 chunkIndex = 0; chunkIndex < TerrainPatch::CHUNKS_COUNT; chunkIndex++)
for (int32 chunkIndex = 0; chunkIndex < Terrain::ChunksCount; chunkIndex++)
{
const int32 chunkX = (chunkIndex % TerrainPatch::CHUNKS_COUNT_EDGE) * info.ChunkSize;
const int32 chunkZ = (chunkIndex / TerrainPatch::CHUNKS_COUNT_EDGE) * info.ChunkSize;
const int32 chunkX = (chunkIndex % Terrain::ChunksCountEdge) * info.ChunkSize;
const int32 chunkZ = (chunkIndex / Terrain::ChunksCountEdge) * info.ChunkSize;
float minHeight = MAX_float;
float maxHeight = MIN_float;
@@ -286,10 +291,10 @@ void UpdateHeightMap(const TerrainDataUpdateInfo& info, const float* heightmap,
const auto heightmapPtr = heightmap;
const auto ptr = (Color32*)data;
for (int32 chunkIndex = 0; chunkIndex < TerrainPatch::CHUNKS_COUNT; chunkIndex++)
for (int32 chunkIndex = 0; chunkIndex < Terrain::ChunksCount; chunkIndex++)
{
const int32 chunkX = (chunkIndex % TerrainPatch::CHUNKS_COUNT_EDGE);
const int32 chunkZ = (chunkIndex / TerrainPatch::CHUNKS_COUNT_EDGE);
const int32 chunkX = (chunkIndex % Terrain::ChunksCountEdge);
const int32 chunkZ = (chunkIndex / Terrain::ChunksCountEdge);
const int32 chunkTextureX = chunkX * info.VertexCountEdge;
const int32 chunkTextureZ = chunkZ * info.VertexCountEdge;
@@ -328,10 +333,10 @@ void UpdateSplatMap(const TerrainDataUpdateInfo& info, const Color32* splatMap,
const auto splatPtr = splatMap;
const auto ptr = (Color32*)data;
for (int32 chunkIndex = 0; chunkIndex < TerrainPatch::CHUNKS_COUNT; chunkIndex++)
for (int32 chunkIndex = 0; chunkIndex < Terrain::ChunksCount; chunkIndex++)
{
const int32 chunkX = (chunkIndex % TerrainPatch::CHUNKS_COUNT_EDGE);
const int32 chunkZ = (chunkIndex / TerrainPatch::CHUNKS_COUNT_EDGE);
const int32 chunkX = (chunkIndex % Terrain::ChunksCountEdge);
const int32 chunkZ = (chunkIndex / Terrain::ChunksCountEdge);
const int32 chunkTextureX = chunkX * info.VertexCountEdge;
const int32 chunkTextureZ = chunkZ * info.VertexCountEdge;
@@ -450,10 +455,10 @@ void UpdateNormalsAndHoles(const TerrainDataUpdateInfo& info, const float* heigh
// Write back to the data container
const auto ptr = (Color32*)data;
for (int32 chunkIndex = 0; chunkIndex < TerrainPatch::CHUNKS_COUNT; chunkIndex++)
for (int32 chunkIndex = 0; chunkIndex < Terrain::ChunksCount; chunkIndex++)
{
const int32 chunkX = (chunkIndex % TerrainPatch::CHUNKS_COUNT_EDGE);
const int32 chunkZ = (chunkIndex / TerrainPatch::CHUNKS_COUNT_EDGE);
const int32 chunkX = (chunkIndex % Terrain::ChunksCountEdge);
const int32 chunkZ = (chunkIndex / Terrain::ChunksCountEdge);
const int32 chunkTextureX = chunkX * info.VertexCountEdge;
const int32 chunkTextureZ = chunkZ * info.VertexCountEdge;
@@ -543,9 +548,9 @@ void FixMips(const TerrainDataUpdateInfo& info, TextureBase::InitData* initData,
const int32 textureSizeMipHigher = textureSizeMip << 1;
// Make heightmap values on left edge the same as the left edge of the chunk on the higher LOD
for (int32 chunkX = 0; chunkX < TerrainPatch::CHUNKS_COUNT_EDGE; chunkX++)
for (int32 chunkX = 0; chunkX < Terrain::ChunksCountEdge; chunkX++)
{
for (int32 chunkZ = 0; chunkZ < TerrainPatch::CHUNKS_COUNT_EDGE; chunkZ++)
for (int32 chunkZ = 0; chunkZ < Terrain::ChunksCountEdge; chunkZ++)
{
const int32 chunkTextureX = chunkX * vertexCountEdgeMip;
const int32 chunkTextureZ = chunkZ * vertexCountEdgeMip;
@@ -558,11 +563,11 @@ void FixMips(const TerrainDataUpdateInfo& info, TextureBase::InitData* initData,
int32 x = 0, xCount = vertexCountEdgeMip;
if (chunkX == 0)
x = 1;
else if (chunkX == TerrainPatch::CHUNKS_COUNT_EDGE - 1)
else if (chunkX == Terrain::ChunksCountEdge - 1)
xCount--;
if (chunkZ == 0)
z = 1;
else if (chunkZ == TerrainPatch::CHUNKS_COUNT_EDGE - 1)
else if (chunkZ == Terrain::ChunksCountEdge - 1)
zCount--;
for (; z < zCount; z++)
@@ -638,7 +643,7 @@ bool CookCollision(TerrainDataUpdateInfo& info, TextureBase::InitData* initData,
const int32 collisionLOD = Math::Clamp<int32>(collisionLod, 0, initData->Mips.Count() - 1);
const int32 collisionLODInv = (int32)Math::Pow(2.0f, (float)collisionLOD);
const int32 heightFieldChunkSize = ((info.ChunkSize + 1) >> collisionLOD) - 1;
const int32 heightFieldSize = heightFieldChunkSize * TerrainPatch::CHUNKS_COUNT_EDGE + 1;
const int32 heightFieldSize = heightFieldChunkSize * Terrain::ChunksCountEdge + 1;
const int32 heightFieldLength = heightFieldSize * heightFieldSize;
GET_TERRAIN_SCRATCH_BUFFER(heightFieldData, heightFieldLength, PhysicsBackend::HeightFieldSample);
PhysicsBackend::HeightFieldSample sample;
@@ -649,11 +654,11 @@ bool CookCollision(TerrainDataUpdateInfo& info, TextureBase::InitData* initData,
const auto& mip = initData->Mips[collisionLOD];
const int32 vertexCountEdgeMip = info.VertexCountEdge >> collisionLOD;
const int32 textureSizeMip = info.TextureSize >> collisionLOD;
for (int32 chunkX = 0; chunkX < TerrainPatch::CHUNKS_COUNT_EDGE; chunkX++)
for (int32 chunkX = 0; chunkX < Terrain::ChunksCountEdge; chunkX++)
{
const int32 chunkTextureX = chunkX * vertexCountEdgeMip;
const int32 chunkStartX = chunkX * heightFieldChunkSize;
for (int32 chunkZ = 0; chunkZ < TerrainPatch::CHUNKS_COUNT_EDGE; chunkZ++)
for (int32 chunkZ = 0; chunkZ < Terrain::ChunksCountEdge; chunkZ++)
{
const int32 chunkTextureZ = chunkZ * vertexCountEdgeMip;
const int32 chunkStartZ = chunkZ * heightFieldChunkSize;
@@ -709,7 +714,7 @@ bool ModifyCollision(TerrainDataUpdateInfo& info, TextureBase::InitData* initDat
const int32 collisionLOD = Math::Clamp<int32>(collisionLod, 0, initData->Mips.Count() - 1);
const int32 collisionLODInv = (int32)Math::Pow(2.0f, (float)collisionLOD);
const int32 heightFieldChunkSize = ((info.ChunkSize + 1) >> collisionLOD) - 1;
const int32 heightFieldSize = heightFieldChunkSize * TerrainPatch::CHUNKS_COUNT_EDGE + 1;
const int32 heightFieldSize = heightFieldChunkSize * Terrain::ChunksCountEdge + 1;
const Int2 samplesOffset(Vector2::Floor(modifiedOffsetRatio * (float)heightFieldSize));
Int2 samplesSize(Vector2::Ceil(modifiedSizeRatio * (float)heightFieldSize));
samplesSize.X = Math::Max(samplesSize.X, 1);
@@ -729,14 +734,14 @@ bool ModifyCollision(TerrainDataUpdateInfo& info, TextureBase::InitData* initDat
const auto& mip = initData->Mips[collisionLOD];
const int32 vertexCountEdgeMip = info.VertexCountEdge >> collisionLOD;
const int32 textureSizeMip = info.TextureSize >> collisionLOD;
for (int32 chunkX = 0; chunkX < TerrainPatch::CHUNKS_COUNT_EDGE; chunkX++)
for (int32 chunkX = 0; chunkX < Terrain::ChunksCountEdge; chunkX++)
{
const int32 chunkTextureX = chunkX * vertexCountEdgeMip;
const int32 chunkStartX = chunkX * heightFieldChunkSize;
if (chunkStartX >= samplesEnd.X || chunkStartX + vertexCountEdgeMip < samplesOffset.X)
continue; // Skip unmodified chunks
for (int32 chunkZ = 0; chunkZ < TerrainPatch::CHUNKS_COUNT_EDGE; chunkZ++)
for (int32 chunkZ = 0; chunkZ < Terrain::ChunksCountEdge; chunkZ++)
{
const int32 chunkTextureZ = chunkZ * vertexCountEdgeMip;
const int32 chunkStartZ = chunkZ * heightFieldChunkSize;
@@ -806,15 +811,15 @@ bool TerrainPatch::SetupHeightMap(int32 heightMapLength, const float* heightMap,
const int32 lodCount = Math::Min<int32>(_terrain->_lodCount, MipLevelsCount(info.VertexCountEdge) - 2);
// Process heightmap to get per-patch height normalization values
float chunkOffsets[CHUNKS_COUNT];
float chunkHeights[CHUNKS_COUNT];
float chunkOffsets[Terrain::ChunksCount];
float chunkHeights[Terrain::ChunksCount];
CalculateHeightmapRange(_terrain, info, heightMap, chunkOffsets, chunkHeights);
// Prepare
#if USE_EDITOR
const bool useVirtualStorage = Editor::IsPlayMode || forceUseVirtualStorage;
#else
const bool useVirtualStorage = true;
const bool useVirtualStorage = true;
#endif
#if USE_EDITOR
String heightMapPath, heightFieldPath;
@@ -910,11 +915,11 @@ bool TerrainPatch::SetupHeightMap(int32 heightMapLength, const float* heightMap,
}
}
#else
else
{
// Not supported
CRASH;
}
else
{
// Not supported
CRASH;
}
#endif
// Prepare collision data destination container
@@ -975,7 +980,7 @@ bool TerrainPatch::SetupHeightMap(int32 heightMapLength, const float* heightMap,
// Update data
_yOffset = info.PatchOffset;
_yHeight = info.PatchHeight;
for (int32 chunkIndex = 0; chunkIndex < CHUNKS_COUNT; chunkIndex++)
for (int32 chunkIndex = 0; chunkIndex < Terrain::ChunksCount; chunkIndex++)
{
auto& chunk = Chunks[chunkIndex];
chunk._yOffset = chunkOffsets[chunkIndex];
@@ -1031,7 +1036,7 @@ bool TerrainPatch::SetupSplatMap(int32 index, int32 splatMapLength, const Color3
#if USE_EDITOR
const bool useVirtualStorage = Editor::IsPlayMode || forceUseVirtualStorage;
#else
const bool useVirtualStorage = true;
const bool useVirtualStorage = true;
#endif
#if USE_EDITOR
String splatMapPath;
@@ -1126,11 +1131,11 @@ bool TerrainPatch::SetupSplatMap(int32 index, int32 splatMapLength, const Color3
}
}
#else
else
{
// Not supported
CRASH;
}
else
{
// Not supported
CRASH;
}
#endif
#if TERRAIN_UPDATING
@@ -1147,7 +1152,7 @@ bool TerrainPatch::SetupSplatMap(int32 index, int32 splatMapLength, const Color3
bool TerrainPatch::InitializeHeightMap()
{
PROFILE_CPU_NAMED("Terrain.InitializeHeightMap");
const auto heightmapSize = _terrain->GetChunkSize() * TerrainPatch::CHUNKS_COUNT_EDGE + 1;
const auto heightmapSize = _terrain->GetChunkSize() * Terrain::ChunksCountEdge + 1;
Array<float> heightmap;
heightmap.Resize(heightmapSize * heightmapSize);
heightmap.SetAll(0.0f);
@@ -1242,7 +1247,7 @@ void TerrainPatch::CacheHeightData()
const float patchHeight = _yHeight;
const auto heightmapPtr = _cachedHeightMap.Get();
const auto holesMaskPtr = _cachedHolesMask.Get();
for (int32 chunkIndex = 0; chunkIndex < CHUNKS_COUNT; chunkIndex++)
for (int32 chunkIndex = 0; chunkIndex < Terrain::ChunksCount; chunkIndex++)
{
const int32 chunkTextureX = Chunks[chunkIndex]._x * info.VertexCountEdge;
const int32 chunkTextureZ = Chunks[chunkIndex]._z * info.VertexCountEdge;
@@ -1315,7 +1320,7 @@ void TerrainPatch::CacheSplatData()
// Extract splatmap data
const auto splatMapPtr = static_cast<Color32*>(_cachedSplatMap[index].Get());
for (int32 chunkIndex = 0; chunkIndex < CHUNKS_COUNT; chunkIndex++)
for (int32 chunkIndex = 0; chunkIndex < Terrain::ChunksCount; chunkIndex++)
{
const int32 chunkTextureX = Chunks[chunkIndex]._x * info.VertexCountEdge;
const int32 chunkTextureZ = Chunks[chunkIndex]._z * info.VertexCountEdge;
@@ -1394,8 +1399,8 @@ bool TerrainPatch::ModifyHeightMap(const float* samples, const Int2& modifiedOff
}
// Process heightmap to get per-patch height normalization values
float chunkOffsets[CHUNKS_COUNT];
float chunkHeights[CHUNKS_COUNT];
float chunkOffsets[Terrain::ChunksCount];
float chunkHeights[Terrain::ChunksCount];
CalculateHeightmapRange(_terrain, info, heightMap, chunkOffsets, chunkHeights);
// TODO: maybe calculate chunk ranges for only modified chunks
const bool wasHeightRangeChanged = Math::NotNearEqual(_yOffset, info.PatchOffset) || Math::NotNearEqual(_yHeight, info.PatchHeight);
@@ -1425,7 +1430,7 @@ bool TerrainPatch::ModifyHeightMap(const float* samples, const Int2& modifiedOff
// Update all the stuff
_yOffset = info.PatchOffset;
_yHeight = info.PatchHeight;
for (int32 chunkIndex = 0; chunkIndex < CHUNKS_COUNT; chunkIndex++)
for (int32 chunkIndex = 0; chunkIndex < Terrain::ChunksCount; chunkIndex++)
{
auto& chunk = Chunks[chunkIndex];
chunk._yOffset = chunkOffsets[chunkIndex];
@@ -1631,7 +1636,7 @@ bool TerrainPatch::ModifySplatMap(int32 index, const Color32* samples, const Int
#if USE_EDITOR
const bool useVirtualStorage = Editor::IsPlayMode || Heightmap->IsVirtual();
#else
const bool useVirtualStorage = true;
const bool useVirtualStorage = true;
#endif
// Save the splatmap data to the asset
@@ -1675,11 +1680,11 @@ bool TerrainPatch::ModifySplatMap(int32 index, const Color32* samples, const Int
}
}
#else
else
{
// Not supported
CRASH;
}
else
{
// Not supported
CRASH;
}
#endif
}
@@ -1987,7 +1992,7 @@ bool TerrainPatch::RayCast(const Vector3& origin, const Vector3& direction, floa
// Find hit chunk
resultChunk = nullptr;
const auto hitPoint = origin + direction * hitDistance;
for (int32 chunkIndex = 0; chunkIndex < CHUNKS_COUNT; chunkIndex++)
for (int32 chunkIndex = 0; chunkIndex < Terrain::ChunksCount; chunkIndex++)
{
const auto box = Chunks[chunkIndex]._bounds;
if (box.Minimum.X <= hitPoint.X && box.Maximum.X >= hitPoint.X &&
@@ -2042,7 +2047,7 @@ void TerrainPatch::ClosestPoint(const Vector3& position, Vector3& result) const
void TerrainPatch::UpdatePostManualDeserialization()
{
// Update data
for (int32 chunkIndex = 0; chunkIndex < CHUNKS_COUNT; chunkIndex++)
for (int32 chunkIndex = 0; chunkIndex < Terrain::ChunksCount; chunkIndex++)
{
auto& chunk = Chunks[chunkIndex];
chunk.UpdateTransform();
@@ -2101,7 +2106,7 @@ void TerrainPatch::CreateCollision()
// Create shape
JsonAsset* materials[8];
for (int32 i = 0;i<8;i++)
for (int32 i = 0; i < 8; i++)
materials[i] = _terrain->GetPhysicalMaterials()[i];
_physicsShape = PhysicsBackend::CreateShape(_terrain, shape, ToSpan(materials, 8), _terrain->IsActiveInHierarchy(), false);
PhysicsBackend::SetShapeLocalPose(_physicsShape, Vector3(0, _yOffset * terrainTransform.Scale.Y, 0), Quaternion::Identity);
@@ -2295,7 +2300,7 @@ const Array<Vector3>& TerrainPatch::GetCollisionTriangles()
#define GET_VERTEX(x, y) Vector3 v##x##y((float)(row + (x)), PhysicsBackend::GetHeightFieldHeight(_physicsHeightField, row + (x), col + (y)) / TERRAIN_PATCH_COLLISION_QUANTIZATION, (float)(col + (y))); Vector3::Transform(v##x##y, world, v##x##y)
const float size = _terrain->_chunkSize * TERRAIN_UNITS_PER_VERTEX * CHUNKS_COUNT_EDGE;
const float size = _terrain->_chunkSize * TERRAIN_UNITS_PER_VERTEX * Terrain::Terrain::ChunksCountEdge;
const Transform terrainTransform = _terrain->_transform;
Transform localTransform(Vector3(_x * size, _yOffset, _z * size), Quaternion::Identity, Vector3(_collisionScaleXZ, _yHeight, _collisionScaleXZ));
const Matrix world = localTransform.GetWorld() * terrainTransform.GetWorld();
@@ -2344,7 +2349,7 @@ void TerrainPatch::GetCollisionTriangles(const BoundingSphere& bounds, Array<Vec
// Prepare
const auto& triangles = GetCollisionTriangles();
const float size = _terrain->_chunkSize * TERRAIN_UNITS_PER_VERTEX * CHUNKS_COUNT_EDGE;
const float size = _terrain->_chunkSize * TERRAIN_UNITS_PER_VERTEX * Terrain::Terrain::ChunksCountEdge;
Transform transform;
transform.Translation = _offset + Vector3(0, _yOffset, 0);
transform.Orientation = Quaternion::Identity;
@@ -2450,7 +2455,7 @@ void TerrainPatch::ExtractCollisionGeometry(Array<Float3>& vertexBuffer, Array<i
ScopeLock lock(Level::ScenesLock);
if (_collisionVertices.IsEmpty())
{
const float size = _terrain->_chunkSize * TERRAIN_UNITS_PER_VERTEX * CHUNKS_COUNT_EDGE;
const float size = _terrain->_chunkSize * TERRAIN_UNITS_PER_VERTEX * Terrain::Terrain::ChunksCountEdge;
const Transform terrainTransform = _terrain->_transform;
const Transform localTransform(Vector3(_x * size, _yOffset, _z * size), Quaternion::Identity, Float3(_collisionScaleXZ, _yHeight, _collisionScaleXZ));
const Matrix world = localTransform.GetWorld() * terrainTransform.GetWorld();
@@ -2512,7 +2517,7 @@ void TerrainPatch::Serialize(SerializeStream& stream, const void* otherObj)
stream.JKEY("Chunks");
stream.StartArray();
for (int32 i = 0; i < CHUNKS_COUNT; i++)
for (int32 i = 0; i < Terrain::Terrain::ChunksCount; i++)
{
stream.StartObject();
Chunks[i].Serialize(stream, other ? &other->Chunks[i] : nullptr);
@@ -2539,15 +2544,14 @@ void TerrainPatch::Deserialize(DeserializeStream& stream, ISerializeModifier* mo
DESERIALIZE_MEMBER(Heightfield, _heightfield);
// Update offset (x or/and z may be modified)
const float size = _terrain->_chunkSize * TERRAIN_UNITS_PER_VERTEX * CHUNKS_COUNT_EDGE;
const float size = _terrain->_chunkSize * TERRAIN_UNITS_PER_VERTEX * Terrain::ChunksCountEdge;
_offset = Vector3(_x * size, 0.0f, _z * size);
auto member = stream.FindMember("Chunks");
if (member != stream.MemberEnd() && member->value.IsArray())
{
auto& chunksData = member->value;
const auto chunksCount = Math::Min<int32>((int32)chunksData.Size(), CHUNKS_COUNT);
const auto chunksCount = Math::Min<int32>((int32)chunksData.Size(), Terrain::ChunksCount);
for (int32 i = 0; i < chunksCount; i++)
{
Chunks[i].Deserialize(chunksData[i], modifier);

View File

@@ -15,19 +15,13 @@ class TerrainMaterialShader;
/// <summary>
/// Represents single terrain patch made of 16 terrain chunks.
/// </summary>
class FLAXENGINE_API TerrainPatch : public ISerializable
API_CLASS(Sealed, NoSpawn) class FLAXENGINE_API TerrainPatch : public ScriptingObject, public ISerializable
{
DECLARE_SCRIPTING_TYPE(TerrainPatch);
friend Terrain;
friend TerrainPatch;
friend TerrainChunk;
public:
enum
{
CHUNKS_COUNT = 16,
CHUNKS_COUNT_EDGE = 4,
};
private:
Terrain* _terrain;
int16 _x, _z;
@@ -69,12 +63,12 @@ public:
/// <summary>
/// The chunks contained within the patch. Organized in 4x4 square.
/// </summary>
TerrainChunk Chunks[CHUNKS_COUNT];
TerrainChunk Chunks[Terrain::ChunksCount];
/// <summary>
/// The heightmap texture.
/// </summary>
AssetReference<Texture> Heightmap;
API_FIELD() AssetReference<Texture> Heightmap;
/// <summary>
/// The splatmap textures.
@@ -85,7 +79,7 @@ public:
/// <summary>
/// Gets the Y axis heightmap offset from terrain origin.
/// </summary>
FORCE_INLINE float GetOffsetY() const
API_FUNCTION() FORCE_INLINE float GetOffsetY() const
{
return _yOffset;
}
@@ -93,7 +87,7 @@ public:
/// <summary>
/// Gets the Y axis heightmap height.
/// </summary>
FORCE_INLINE float GetHeightY() const
API_FUNCTION() FORCE_INLINE float GetHeightY() const
{
return _yHeight;
}
@@ -101,7 +95,7 @@ public:
/// <summary>
/// Gets the x coordinate.
/// </summary>
FORCE_INLINE int32 GetX() const
API_FUNCTION() FORCE_INLINE int32 GetX() const
{
return _x;
}
@@ -109,7 +103,7 @@ public:
/// <summary>
/// Gets the z coordinate.
/// </summary>
FORCE_INLINE int32 GetZ() const
API_FUNCTION() FORCE_INLINE int32 GetZ() const
{
return _z;
}
@@ -117,7 +111,7 @@ public:
/// <summary>
/// Gets the terrain.
/// </summary>
FORCE_INLINE Terrain* GetTerrain() const
API_FUNCTION() FORCE_INLINE Terrain* GetTerrain() const
{
return _terrain;
}
@@ -127,9 +121,9 @@ public:
/// </summary>
/// <param name="index">The chunk zero-based index.</param>
/// <returns>The chunk.</returns>
TerrainChunk* GetChunk(int32 index)
API_FUNCTION() TerrainChunk* GetChunk(int32 index)
{
if (index < 0 || index >= CHUNKS_COUNT)
if (index < 0 || index >= Terrain::ChunksCount)
return nullptr;
return &Chunks[index];
}
@@ -139,9 +133,9 @@ public:
/// </summary>
/// <param name="chunkCoord">The chunk location (x and z).</param>
/// <returns>The chunk.</returns>
TerrainChunk* GetChunk(const Int2& chunkCoord)
API_FUNCTION() TerrainChunk* GetChunk(API_PARAM(Ref) const Int2& chunkCoord)
{
return GetChunk(chunkCoord.Y * CHUNKS_COUNT_EDGE + chunkCoord.X);
return GetChunk(chunkCoord.Y * Terrain::ChunksCountEdge + chunkCoord.X);
}
/// <summary>
@@ -150,15 +144,38 @@ public:
/// <param name="x">The chunk location x.</param>
/// <param name="z">The chunk location z.</param>
/// <returns>The chunk.</returns>
TerrainChunk* GetChunk(int32 x, int32 z)
API_FUNCTION() TerrainChunk* GetChunk(int32 x, int32 z)
{
return GetChunk(z * CHUNKS_COUNT_EDGE + x);
return GetChunk(z * Terrain::ChunksCountEdge + x);
}
/// <summary>
/// Gets the splatmap assigned to this patch.
/// </summary>
/// <param name="index">The zero-based index of the splatmap.</param>
/// <returns>The splatmap texture.</returns>
API_FUNCTION() AssetReference<Texture> GetSplatmap(int32 index)
{
if (index < 0 || index >= TERRAIN_MAX_SPLATMAPS_COUNT)
return nullptr;
return Splatmap[index];
}
/// <summary>
/// Sets a splatmap to this patch.
/// </summary>
/// <param name="index">The zero-based index of the splatmap.</param>
/// <param name="splatMap">Splatmap texture.</param>
API_FUNCTION() void SetSplatmap(int32 index, const AssetReference<Texture>& splatMap)
{
if (index >= 0 && index < TERRAIN_MAX_SPLATMAPS_COUNT)
Splatmap[index] = splatMap;
}
/// <summary>
/// Gets the patch world bounds.
/// </summary>
FORCE_INLINE const BoundingBox& GetBounds() const
API_FUNCTION() FORCE_INLINE const BoundingBox& GetBounds() const
{
return _bounds;
}
@@ -184,7 +201,7 @@ public:
/// Initializes the patch heightmap and collision to the default flat level.
/// </summary>
/// <returns>True if failed, otherwise false.</returns>
bool InitializeHeightMap();
API_FUNCTION() bool InitializeHeightMap();
/// <summary>
/// Setups the terrain patch using the specified heightmap data.
@@ -194,7 +211,7 @@ public:
/// <param name="holesMask">The holes mask (optional). Normalized to 0-1 range values with holes mask per-vertex. Must match the heightmap dimensions.</param>
/// <param name="forceUseVirtualStorage">If set to <c>true</c> patch will use virtual storage by force. Otherwise it can use normal texture asset storage on drive (valid only during Editor). Runtime-created terrain can only use virtual storage (in RAM).</param>
/// <returns>True if failed, otherwise false.</returns>
bool SetupHeightMap(int32 heightMapLength, const float* heightMap, const byte* holesMask = nullptr, bool forceUseVirtualStorage = false);
API_FUNCTION() bool SetupHeightMap(int32 heightMapLength, API_PARAM(Ref) const float* heightMap, API_PARAM(Ref) const byte* holesMask = nullptr, bool forceUseVirtualStorage = false);
/// <summary>
/// Setups the terrain patch layer weights using the specified splatmaps data.
@@ -204,7 +221,7 @@ public:
/// <param name="splatMap">The splat map. Each array item contains 4 layer weights.</param>
/// <param name="forceUseVirtualStorage">If set to <c>true</c> patch will use virtual storage by force. Otherwise it can use normal texture asset storage on drive (valid only during Editor). Runtime-created terrain can only use virtual storage (in RAM).</param>
/// <returns>True if failed, otherwise false.</returns>
bool SetupSplatMap(int32 index, int32 splatMapLength, const Color32* splatMap, bool forceUseVirtualStorage = false);
API_FUNCTION() bool SetupSplatMap(int32 index, int32 splatMapLength, API_PARAM(Ref) const Color32* splatMap, bool forceUseVirtualStorage = false);
#endif
#if TERRAIN_UPDATING
@@ -212,40 +229,40 @@ public:
/// Gets the raw pointer to the heightmap data.
/// </summary>
/// <returns>The heightmap data.</returns>
float* GetHeightmapData();
API_FUNCTION() float* GetHeightmapData();
/// <summary>
/// Clears cache of the heightmap data.
/// </summary>
void ClearHeightmapCache();
API_FUNCTION() void ClearHeightmapCache();
/// <summary>
/// Gets the raw pointer to the holes mask data.
/// </summary>
/// <returns>The holes mask data.</returns>
byte* GetHolesMaskData();
API_FUNCTION() byte* GetHolesMaskData();
/// <summary>
/// Clears cache of the holes mask data.
/// </summary>
void ClearHolesMaskCache();
API_FUNCTION() void ClearHolesMaskCache();
/// <summary>
/// Gets the raw pointer to the splat map data.
/// </summary>
/// <param name="index">The zero-based index of the splatmap texture.</param>
/// <returns>The splat map data.</returns>
Color32* GetSplatMapData(int32 index);
API_FUNCTION() Color32* GetSplatMapData(int32 index);
/// <summary>
/// Clears cache of the splat map data.
/// </summary>
void ClearSplatMapCache();
API_FUNCTION() void ClearSplatMapCache();
/// <summary>
/// Clears all caches.
/// </summary>
void ClearCache();
API_FUNCTION() void ClearCache();
/// <summary>
/// Modifies the terrain patch heightmap with the given samples.
@@ -254,7 +271,7 @@ public:
/// <param name="modifiedOffset">The offset from the first row and column of the heightmap data (offset destination x and z start position).</param>
/// <param name="modifiedSize">The size of the heightmap to modify (x and z). Amount of samples in each direction.</param>
/// <returns>True if failed, otherwise false.</returns>
bool ModifyHeightMap(const float* samples, const Int2& modifiedOffset, const Int2& modifiedSize);
API_FUNCTION() bool ModifyHeightMap(API_PARAM(Ref) const float* samples, API_PARAM(Ref) const Int2& modifiedOffset, API_PARAM(Ref) const Int2& modifiedSize);
/// <summary>
/// Modifies the terrain patch holes mask with the given samples.
@@ -263,7 +280,7 @@ public:
/// <param name="modifiedOffset">The offset from the first row and column of the holes map data (offset destination x and z start position).</param>
/// <param name="modifiedSize">The size of the holes map to modify (x and z). Amount of samples in each direction.</param>
/// <returns>True if failed, otherwise false.</returns>
bool ModifyHolesMask(const byte* samples, const Int2& modifiedOffset, const Int2& modifiedSize);
API_FUNCTION() bool ModifyHolesMask(API_PARAM(Ref) const byte* samples, API_PARAM(Ref) const Int2& modifiedOffset, API_PARAM(Ref) const Int2& modifiedSize);
/// <summary>
/// Modifies the terrain patch splat map (layers mask) with the given samples.
@@ -273,7 +290,7 @@ public:
/// <param name="modifiedOffset">The offset from the first row and column of the splat map data (offset destination x and z start position).</param>
/// <param name="modifiedSize">The size of the splat map to modify (x and z). Amount of samples in each direction.</param>
/// <returns>True if failed, otherwise false.</returns>
bool ModifySplatMap(int32 index, const Color32* samples, const Int2& modifiedOffset, const Int2& modifiedSize);
API_FUNCTION() bool ModifySplatMap(int32 index, API_PARAM(Ref) const Color32* samples, API_PARAM(Ref) const Int2& modifiedOffset, API_PARAM(Ref) const Int2& modifiedSize);
private:
bool UpdateHeightData(struct TerrainDataUpdateInfo& info, const Int2& modifiedOffset, const Int2& modifiedSize, bool wasHeightRangeChanged, bool wasHeightChanged);
@@ -293,7 +310,7 @@ public:
/// <param name="resultHitDistance">The raycast result hit position distance from the ray origin. Valid only if raycast hits anything.</param>
/// <param name="maxDistance">The maximum distance the ray should check for collisions.</param>
/// <returns>True if ray hits an object, otherwise false.</returns>
bool RayCast(const Vector3& origin, const Vector3& direction, float& resultHitDistance, float maxDistance = MAX_float) const;
API_FUNCTION() bool RayCast(const Vector3& origin, const Vector3& direction, API_PARAM(Out) float& resultHitDistance, float maxDistance = MAX_float) const;
/// <summary>
/// Performs a raycast against this terrain collision shape.
@@ -304,7 +321,7 @@ public:
/// <param name="resultHitNormal">The raycast result hit position normal vector. Valid only if raycast hits anything.</param>
/// <param name="maxDistance">The maximum distance the ray should check for collisions.</param>
/// <returns>True if ray hits an object, otherwise false.</returns>
bool RayCast(const Vector3& origin, const Vector3& direction, float& resultHitDistance, Vector3& resultHitNormal, float maxDistance = MAX_float) const;
API_FUNCTION() bool RayCast(const Vector3& origin, const Vector3& direction, API_PARAM(Out) float& resultHitDistance, API_PARAM(Out) Vector3& resultHitNormal, float maxDistance = MAX_float) const;
/// <summary>
/// Performs a raycast against this terrain collision shape. Returns the hit chunk.
@@ -315,7 +332,7 @@ public:
/// <param name="resultChunk">The raycast result hit chunk. Valid only if raycast hits anything.</param>
/// <param name="maxDistance">The maximum distance the ray should check for collisions.</param>
/// <returns>True if ray hits an object, otherwise false.</returns>
bool RayCast(const Vector3& origin, const Vector3& direction, float& resultHitDistance, TerrainChunk*& resultChunk, float maxDistance = MAX_float) const;
API_FUNCTION() bool RayCast(const Vector3& origin, const Vector3& direction, API_PARAM(Out) float& resultHitDistance, API_PARAM(Out) TerrainChunk*& resultChunk, float maxDistance = MAX_float) const;
/// <summary>
/// Performs a raycast against terrain collision, returns results in a RaycastHit structure.
@@ -325,14 +342,14 @@ public:
/// <param name="hitInfo">The result hit information. Valid only when method returns true.</param>
/// <param name="maxDistance">The maximum distance the ray should check for collisions.</param>
/// <returns>True if ray hits an object, otherwise false.</returns>
bool RayCast(const Vector3& origin, const Vector3& direction, RayCastHit& hitInfo, float maxDistance = MAX_float) const;
API_FUNCTION() bool RayCast(const Vector3& origin, const Vector3& direction, API_PARAM(Out) RayCastHit& hitInfo, float maxDistance = MAX_float) const;
/// <summary>
/// Gets a point on the terrain collider that is closest to a given location. Can be used to find a hit location or position to apply explosion force or any other special effects.
/// </summary>
/// <param name="position">The position to find the closest point to it.</param>
/// <param name="result">The result point on the collider that is closest to the specified location.</param>
void ClosestPoint(const Vector3& position, Vector3& result) const;
API_FUNCTION() void ClosestPoint(API_PARAM(Ref) const Vector3& position, API_PARAM(Out) Vector3& result) const;
#if USE_EDITOR
/// <summary>
@@ -354,7 +371,7 @@ public:
/// </summary>
/// <param name="bounds">The world-space bounds to find terrain triangles that intersect with it.</param>
/// <param name="result">The result triangles that intersect with the given bounds (in world-space).</param>
void GetCollisionTriangles(const BoundingSphere& bounds, Array<Vector3>& result);
void GetCollisionTriangles(API_PARAM(Ref) const BoundingSphere& bounds, API_PARAM(Out) Array<Vector3>& result);
#endif
/// <summary>
@@ -362,7 +379,7 @@ public:
/// </summary>
/// <param name="vertexBuffer">The output vertex buffer.</param>
/// <param name="indexBuffer">The output index buffer.</param>
void ExtractCollisionGeometry(Array<Float3>& vertexBuffer, Array<int32>& indexBuffer);
API_FUNCTION() void ExtractCollisionGeometry(API_PARAM(Out) Array<Float3>& vertexBuffer, API_PARAM(Out) Array<int32>& indexBuffer);
private:
/// <summary>