j
This commit is contained in:
1 parent
f030eec08a
commit
db558d9e14
18 files changed
+1570
-264
No files matched your search
@@ -89,7 +89,10 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(const FVoxelChunk& Chunk,
|
||||
// Les vertices partagés permettent des normales lisses et réduisent le count ~3x.
|
||||
TMap<FIntVector, int32> VertexMap;
|
||||
|
||||
auto GetOrCreateVertex = [&](const FVector& WorldPos) -> int32
|
||||
// Normale fournie par l'appelant (gradient lu dans la grille de densité, T1.b) —
|
||||
// plus d'échantillonnage de densité par vertex. RawNormal pointe solide→air ; on la
|
||||
// normalise ici (fallback up si dégénérée).
|
||||
auto GetOrCreateVertex = [&](const FVector& WorldPos, const FVector& RawNormal) -> int32
|
||||
{
|
||||
const FIntVector Key(
|
||||
FMath::RoundToInt(WorldPos.X * 100.0f),
|
||||
@@ -107,18 +110,12 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(const FVoxelChunk& Chunk,
|
||||
|
||||
MeshData.Vertices.Add(WorldPos);
|
||||
|
||||
// Normale par gradient de densité (shading lisse).
|
||||
if (Generator)
|
||||
FVector Normal = RawNormal;
|
||||
if (!Normal.Normalize())
|
||||
{
|
||||
const float VoxelX = WorldPos.X / VOXEL_SIZE;
|
||||
const float VoxelY = WorldPos.Y / VOXEL_SIZE;
|
||||
const float VoxelZ = WorldPos.Z / VOXEL_SIZE;
|
||||
MeshData.Normals.Add(ComputeGradientNormal(VoxelX, VoxelY, VoxelZ));
|
||||
}
|
||||
else
|
||||
{
|
||||
MeshData.Normals.Add(FVector(0.0f, 0.0f, 1.0f));
|
||||
Normal = FVector(0.0f, 0.0f, 1.0f); // dégénéré (zone plate)
|
||||
}
|
||||
MeshData.Normals.Add(Normal);
|
||||
|
||||
// UVs planaires — le triplanar mapping se fait dans le matériau.
|
||||
MeshData.UVs.Add(FVector2D(WorldPos.X / VOXEL_SIZE, WorldPos.Y / VOXEL_SIZE));
|
||||
@@ -161,23 +158,44 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(const FVoxelChunk& Chunk,
|
||||
// CellsPerAxis = nombre de cellules par axe ; GridDim = points de grille (+1).
|
||||
// Le point de grille (gx,gy,gz) correspond au voxel monde (gx,gy,gz)*Step.
|
||||
// Ordre de remplissage z→y→x : garde le cache SDF (search-box) bien chaud.
|
||||
// On échantillonne avec un anneau de marge de 1 point de chaque côté (indices -1..GridDim)
|
||||
// pour pouvoir calculer les normales par GRADIENT DE GRILLE (T1.b) — différences centrales
|
||||
// sur la grille au lieu de 6 appels densité frais par vertex. La marge utilise les mêmes
|
||||
// échantillons monde purs qu'un chunk voisin, donc les normales restent continues aux bords
|
||||
// de chunk (pas de couture de shading). La géométrie est inchangée au bit près (mêmes
|
||||
// positions d'arête) ; seules les normales changent.
|
||||
const int32 CellsPerAxis = CHUNK_SIZE / Step;
|
||||
const int32 GridDim = CellsPerAxis + 1;
|
||||
const int32 MDim = GridDim + 2; // +1 marge de chaque côté
|
||||
|
||||
TArray<float> DensityGrid;
|
||||
DensityGrid.SetNumUninitialized(GridDim * GridDim * GridDim);
|
||||
for (int32 gz = 0; gz < GridDim; gz++)
|
||||
DensityGrid.SetNumUninitialized(MDim * MDim * MDim);
|
||||
for (int32 gz = -1; gz <= GridDim; gz++)
|
||||
{
|
||||
for (int32 gy = 0; gy < GridDim; gy++)
|
||||
for (int32 gy = -1; gy <= GridDim; gy++)
|
||||
{
|
||||
for (int32 gx = 0; gx < GridDim; gx++)
|
||||
for (int32 gx = -1; gx <= GridDim; gx++)
|
||||
{
|
||||
DensityGrid[(gz * GridDim + gy) * GridDim + gx] =
|
||||
DensityGrid[((gz + 1) * MDim + (gy + 1)) * MDim + (gx + 1)] =
|
||||
GetDensity(Chunk, gx * Step, gy * Step, gz * Step);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Lecture grille (avec offset de marge) + gradient central depuis la grille.
|
||||
auto SampleG = [&](int32 gx, int32 gy, int32 gz) -> float
|
||||
{
|
||||
return DensityGrid[((gz + 1) * MDim + (gy + 1)) * MDim + (gx + 1)];
|
||||
};
|
||||
auto GradAt = [&](int32 gx, int32 gy, int32 gz) -> FVector
|
||||
{
|
||||
// Densité négative=solide, positive=air → le gradient pointe vers l'air (sortant).
|
||||
return FVector(
|
||||
SampleG(gx + 1, gy, gz) - SampleG(gx - 1, gy, gz),
|
||||
SampleG(gx, gy + 1, gz) - SampleG(gx, gy - 1, gz),
|
||||
SampleG(gx, gy, gz + 1) - SampleG(gx, gy, gz - 1));
|
||||
};
|
||||
|
||||
//=========================================================================
|
||||
// ITÉRATION SUR LES CELLULES
|
||||
//=========================================================================
|
||||
@@ -190,9 +208,10 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(const FVoxelChunk& Chunk,
|
||||
{
|
||||
for (int32 cx = 0; cx < CellsPerAxis; cx++)
|
||||
{
|
||||
// Densités + positions aux 8 coins (lues dans la grille pré-calculée)
|
||||
// Densités + positions + gradients aux 8 coins (lus dans la grille).
|
||||
float Densities[8];
|
||||
FVector Positions[8];
|
||||
FVector Gradients[8];
|
||||
|
||||
for (int32 i = 0; i < 8; i++)
|
||||
{
|
||||
@@ -200,9 +219,10 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(const FVoxelChunk& Chunk,
|
||||
const int32 GY = cy + CornerOffsets[i].Y;
|
||||
const int32 GZ = cz + CornerOffsets[i].Z;
|
||||
|
||||
Densities[i] = DensityGrid[(GZ * GridDim + GY) * GridDim + GX];
|
||||
Positions[i] = ChunkWorldPos
|
||||
Densities[i] = SampleG(GX, GY, GZ);
|
||||
Positions[i] = ChunkWorldPos
|
||||
+ FVector(GX * Step, GY * Step, GZ * Step) * VOXEL_SIZE;
|
||||
Gradients[i] = GradAt(GX, GY, GZ);
|
||||
}
|
||||
|
||||
// Index de cas MC (8 bits, un par coin)
|
||||
@@ -217,18 +237,23 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(const FVoxelChunk& Chunk,
|
||||
|
||||
if (MCEdgeTable[CaseIndex] == 0) continue; // Pas de surface ici
|
||||
|
||||
// Interpolation des positions sur les arêtes traversées
|
||||
// Interpolation des positions + normales sur les arêtes traversées. Le t est
|
||||
// calculé exactement comme InterpolateEdge → positions bit-identiques (topologie
|
||||
// inchangée) ; la normale interpole les gradients de coin par le même t.
|
||||
FVector EdgeVertices[12];
|
||||
FVector EdgeNormals[12];
|
||||
for (int32 i = 0; i < 12; i++)
|
||||
{
|
||||
if (MCEdgeTable[CaseIndex] & (1 << i))
|
||||
{
|
||||
const int32 A = EdgeCorners[i][0];
|
||||
const int32 B = EdgeCorners[i][1];
|
||||
EdgeVertices[i] = InterpolateEdge(
|
||||
Positions[A], Positions[B],
|
||||
Densities[A], Densities[B]
|
||||
);
|
||||
const float D1 = Densities[A], D2 = Densities[B];
|
||||
const float T = (FMath::Abs(D2 - D1) < KINDA_SMALL_NUMBER)
|
||||
? 0.5f
|
||||
: FMath::Clamp((IsoLevel - D1) / (D2 - D1), 0.0f, 1.0f);
|
||||
EdgeVertices[i] = Positions[A] + T * (Positions[B] - Positions[A]);
|
||||
EdgeNormals[i] = Gradients[A] + T * (Gradients[B] - Gradients[A]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,9 +261,12 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(const FVoxelChunk& Chunk,
|
||||
// Ordre 0, 2, 1 (pas 0, 1, 2) pour le winding attendu par RealtimeMesh.
|
||||
for (int32 i = 0; MCTriTable[CaseIndex][i] != -1; i += 3)
|
||||
{
|
||||
const int32 Idx0 = GetOrCreateVertex(EdgeVertices[MCTriTable[CaseIndex][i]]);
|
||||
const int32 Idx1 = GetOrCreateVertex(EdgeVertices[MCTriTable[CaseIndex][i + 1]]);
|
||||
const int32 Idx2 = GetOrCreateVertex(EdgeVertices[MCTriTable[CaseIndex][i + 2]]);
|
||||
const int32 E0 = MCTriTable[CaseIndex][i];
|
||||
const int32 E1 = MCTriTable[CaseIndex][i + 1];
|
||||
const int32 E2 = MCTriTable[CaseIndex][i + 2];
|
||||
const int32 Idx0 = GetOrCreateVertex(EdgeVertices[E0], EdgeNormals[E0]);
|
||||
const int32 Idx1 = GetOrCreateVertex(EdgeVertices[E1], EdgeNormals[E1]);
|
||||
const int32 Idx2 = GetOrCreateVertex(EdgeVertices[E2], EdgeNormals[E2]);
|
||||
|
||||
MeshData.Triangles.Add(Idx0);
|
||||
MeshData.Triangles.Add(Idx2);
|
||||
|
||||
Reference in new issue
Block a user