# Deformation de vertices par materiau

Inspiree du talk Roblox SIGGRAPH 2020 (p.19). Chaque materiau definit une deformation
procedurale appliquee aux vertices Surface Nets apres le calcul du centroide.
Donne un caractere visuel distinct a chaque materiau sans cout GPU supplementaire.

## Objectif

Actuellement, tous les materiaux smooth produisent les memes blobs lisses uniformes.
Avec la deformation par materiau :
- La **pierre** aurait des aretes plus marquees (cubify)
- Le **sable** aurait des surfaces ondulees (shift)
- La **neige** resterait lisse (aucune deformation)
- La **glace** aurait des facettes cristallines (quantize)

## Modes de deformation (Roblox)

| Mode | Effet | Formule | Materiaux cibles |
|------|-------|---------|-----------------|
| `None` | Aucune deformation | identity | snow, water |
| `Shift` | Offset pseudo-random | `pos += hash(pos) * amplitude` | sand, dirt |
| `Cubify` | Lerp vers centre du cube | `pos = lerp(pos, round(pos) + 0.5, factor)` | stone, rock |
| `Quantize` | Arrondi a pas fixe | `pos = round(pos * K) / K` | ice, crystal |
| `Barrel` | Cubify uniquement en Y | `pos.y = lerp(pos.y, round(pos.y) + 0.5, f)` | pillars, trunks |

## Integration dans le code existant

### 1. Etendre MaterialDesc (VoxelTypes.h)

```cpp
struct MaterialDesc {
    // ... champs existants ...
    uint8_t deformMode = 0;     // 0=None, 1=Shift, 2=Cubify, 3=Quantize, 4=Barrel
    uint8_t deformStrength = 0; // 0-255 -> 0.0-1.0
    // remplace _pad ou ajoute 2 bytes (struct reste 16-aligned)
};
```

Pas de changement GPU : la deformation est CPU-only dans le mesher.

### 2. Modifier SmoothMesher (VoxelMesher.cpp)

Le point d'insertion est apres le calcul du centroide, avant l'ecriture dans le buffer
de sortie. Actuellement dans `meshSurfaceNets()` :

```
centroid = average(edge_crossings)
normal = average(triangle_normals)
-> INSERER DEFORMATION ICI
write SmoothVertex(centroid, normal, material)
```

Implementation :

```cpp
// Apres le calcul du centroid et avant l'ecriture du vertex
XMFLOAT3 deformVertex(XMFLOAT3 pos, const MaterialDesc& mat) {
    switch (mat.deformMode) {
    case 1: { // Shift
        float strength = mat.deformStrength / 255.0f;
        // Hash stable base sur position entiere (pas de flicker en animation)
        uint32_t h = hash3(int(pos.x), int(pos.y), int(pos.z));
        float rx = ((h & 0xFF) / 255.0f - 0.5f) * strength;
        float ry = (((h >> 8) & 0xFF) / 255.0f - 0.5f) * strength;
        float rz = (((h >> 16) & 0xFF) / 255.0f - 0.5f) * strength;
        return { pos.x + rx, pos.y + ry, pos.z + rz };
    }
    case 2: { // Cubify
        float f = mat.deformStrength / 255.0f;
        float cx = floorf(pos.x) + 0.5f;
        float cy = floorf(pos.y) + 0.5f;
        float cz = floorf(pos.z) + 0.5f;
        return { lerp(pos.x, cx, f), lerp(pos.y, cy, f), lerp(pos.z, cz, f) };
    }
    case 3: { // Quantize
        float K = 2.0f + (mat.deformStrength / 255.0f) * 6.0f; // 2-8 steps
        return { roundf(pos.x * K) / K, roundf(pos.y * K) / K, roundf(pos.z * K) / K };
    }
    case 4: { // Barrel (cubify Y only)
        float f = mat.deformStrength / 255.0f;
        float cy = floorf(pos.y) + 0.5f;
        return { pos.x, lerp(pos.y, cy, f), pos.z };
    }
    default: return pos;
    }
}
```

### 3. Recalculer les normales apres deformation

Les normales moyennees doivent etre recalculees APRES la deformation, sinon elles ne
correspondent plus a la geometrie deformee. Deux options :

**Option A (simple) :** Recalculer les face normals des triangles adjacents apres deformation.
C'est ce que fait deja le pass de normales dans `meshSurfaceNets()`, il suffit de le
deplacer apres la deformation.

**Option B (rapide) :** Garder les normales originales. La deformation est subtile,
l'erreur de normale est visuellement acceptable. Recommande pour le prototype.

### 4. Soft/hard edges par materiau

Roblox controle aussi les aretes douces/dures par materiau. On peut ajouter :

```cpp
uint8_t edgeHardness = 0; // 0=smooth normals, 255=flat/geometric normals
```

Dans le PS, interpoler entre les smooth normals et les geometric normals (deja
disponibles via le triplanar). Cout zero cote mesher, petit calcul PS.

### 5. Configurer les materiaux existants

```cpp
// Dans VoxelWorld::initMaterials() ou equivalent
materials[5].deformMode = 0; materials[5].deformStrength = 0;   // snow: lisse
materials[3].deformMode = 2; materials[3].deformStrength = 180; // stone: cubify fort
materials[6].deformMode = 2; materials[6].deformStrength = 100; // smoothstone: cubify leger
materials[4].deformMode = 1; materials[4].deformStrength = 60;  // sand: shift subtil
materials[2].deformMode = 1; materials[2].deformStrength = 30;  // dirt: shift tres leger
```

## Risques et precautions

- **Self-intersection** : une deformation trop forte peut creer des triangles inverses.
  Limiter `deformStrength` a ~200 max et verifier visuellement.
- **Coutures chunk** : la deformation doit etre identique des deux cotes d'une frontiere
  de chunk. Le hash base sur la position monde (pas locale) garantit la coherence.
- **Animation** : en mode animation (terrain regenere a 30Hz), la deformation doit etre
  stable. Utiliser la position entiere (pas le centroide) comme seed du hash.

## Estimation d'effort

- Etendre MaterialDesc : 15 min
- Fonction deformVertex : 30 min
- Integration dans meshSurfaceNets : 30 min
- Tuning des parametres par materiau : 1h
- **Total : ~2h**

Aucun changement shader, aucun changement GPU buffer, aucun impact performance.
