# 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.