| 736 | } |
| 737 | |
| 738 | bool ModifyCollision(TerrainDataUpdateInfo& info, TextureBase::InitData* initData, int32 collisionLod, const Int2& modifiedOffset, const Int2& modifiedSize, void* heightField) |
| 739 | { |
| 740 | info.GetSplatMaps(); |
| 741 | PROFILE_CPU_NAMED("Terrain.ModifyCollision"); |
| 742 | |
| 743 | // Prepare data |
| 744 | const Vector2 modifiedOffsetRatio((float)modifiedOffset.X / info.HeightmapSize, (float)modifiedOffset.Y / info.HeightmapSize); |
| 745 | const Vector2 modifiedSizeRatio((float)modifiedSize.X / info.HeightmapSize, (float)modifiedSize.Y / info.HeightmapSize); |
| 746 | const int32 collisionLOD = Math::Clamp<int32>(collisionLod, 0, initData->Mips.Count() - 1); |
| 747 | const int32 collisionLODInv = (int32)Math::Pow(2.0f, (float)collisionLOD); |
| 748 | const int32 heightFieldChunkSize = ((info.ChunkSize + 1) >> collisionLOD) - 1; |
| 749 | const int32 heightFieldSize = heightFieldChunkSize * Terrain::ChunksCountEdge + 1; |
| 750 | const Int2 samplesOffset(Vector2::Floor(modifiedOffsetRatio * (float)heightFieldSize)); |
| 751 | Int2 samplesSize(Vector2::Ceil(modifiedSizeRatio * (float)heightFieldSize)); |
| 752 | samplesSize.X = Math::Max(samplesSize.X, 1); |
| 753 | samplesSize.Y = Math::Max(samplesSize.Y, 1); |
| 754 | Int2 samplesEnd = samplesOffset + samplesSize; |
| 755 | samplesEnd.X = Math::Min(samplesEnd.X, heightFieldSize); |
| 756 | samplesEnd.Y = Math::Min(samplesEnd.Y, heightFieldSize); |
| 757 | |
| 758 | // Allocate data |
| 759 | const int32 heightFieldDataLength = samplesSize.X * samplesSize.Y; |
| 760 | GET_TERRAIN_SCRATCH_BUFFER(heightFieldData, info.HeightmapLength, PhysicsBackend::HeightFieldSample); |
| 761 | PhysicsBackend::HeightFieldSample sample; |
| 762 | Platform::MemoryClear(&sample, sizeof(PhysicsBackend::HeightFieldSample)); |
| 763 | Platform::MemoryClear(heightFieldData, sizeof(PhysicsBackend::HeightFieldSample) * heightFieldDataLength); |
| 764 | |
| 765 | // Setup terrain collision information |
| 766 | const auto& mip = initData->Mips[collisionLOD]; |
| 767 | const int32 vertexCountEdgeMip = info.VertexCountEdge >> collisionLOD; |
| 768 | const int32 textureSizeMip = info.TextureSize >> collisionLOD; |
| 769 | for (int32 chunkX = 0; chunkX < Terrain::ChunksCountEdge; chunkX++) |
| 770 | { |
| 771 | const int32 chunkTextureX = chunkX * vertexCountEdgeMip; |
| 772 | const int32 chunkStartX = chunkX * heightFieldChunkSize; |
| 773 | if (chunkStartX >= samplesEnd.X || chunkStartX + vertexCountEdgeMip < samplesOffset.X) |
| 774 | continue; // Skip unmodified chunks |
| 775 | |
| 776 | for (int32 chunkZ = 0; chunkZ < Terrain::ChunksCountEdge; chunkZ++) |
| 777 | { |
| 778 | const int32 chunkTextureZ = chunkZ * vertexCountEdgeMip; |
| 779 | const int32 chunkStartZ = chunkZ * heightFieldChunkSize; |
| 780 | if (chunkStartZ >= samplesEnd.Y || chunkStartZ + vertexCountEdgeMip < samplesOffset.Y) |
| 781 | continue; // Skip unmodified chunks |
| 782 | |
| 783 | // TODO: adjust loop range to reduce iterations count for edge cases (skip checking unmodified samples) |
| 784 | for (int32 z = 0; z < vertexCountEdgeMip; z++) |
| 785 | { |
| 786 | const int32 heightmapZ = chunkStartZ + z; |
| 787 | const int32 heightmapLocalZ = heightmapZ - samplesOffset.Y; |
| 788 | if (heightmapLocalZ < 0 || heightmapLocalZ >= samplesSize.Y) |
| 789 | continue; // Skip unmodified columns |
| 790 | |
| 791 | // TODO: adjust loop range to reduce iterations count for edge cases (skip checking unmodified samples) |
| 792 | for (int32 x = 0; x < vertexCountEdgeMip; x++) |
| 793 | { |
| 794 | const int32 heightmapX = chunkStartX + x; |
| 795 | const int32 heightmapLocalX = heightmapX - samplesOffset.X; |
no test coverage detected