j'ai enfin réussi à convertir tout mon script JS de la map en C#. Ça n'as pas été de tout repos, mais ça valais le coup, un bon apprentissage.
j'en ai profité pour revoir quelques unes des fonctions, optimisé d'autres et le résultat est là. Je suis passé de 85 kTri/s à plus de 120kTri/s. (kTri/s => milliers de triangle en une seconde). le tout en plus pour un maillage bien plus propre et régulier, plus de triangles (1,0M a 1.5M) et plus de mesh créés ( 36 => 64).
Code : Tout sélectionner
// Pour utiliser le script, créer une scene avec une directional light
// une caméra et un GameObject Empty.
// Droper ce script sur le GameObject
// creer un GameObject "plane" de unity, avec shader reflection. Dropper le dans "Eau".
// creer un nouveaux material avec le shader dispo page 1. ajouter y autant de textures que le shader nécéssite.
// Dropper le materail crée dans "mat"
// == PLAY == //
using System;
using System.Collections.Generic;
using System.Threading;
using UnityEngine;
public class Map : MonoBehaviour {
// stock les infos de chaque point
// position, couleur, points adjacents, triangles adjacent
private struct POINT {
public float x;
public float y;
public float z;
public Color32 color;
public List<int> pointNext;
public List<int> triNext;
public POINT(float x , float y , float z , Color32 color , List<int> pointNext , List<int> triNext ) {
this.x = x;
this.y = y;
this.z = z;
this.color = color;
this.pointNext = pointNext;
this.triNext = triNext;
}
}
// mise de coté des premiers points crées, pour calculer le taux humidité des points
private struct ORIGIN {
public List<int> Next;
public int bas;
public float flux;
public float ToCote;
public ORIGIN ( List<int> Next, int bas, float flux, float ToCote){
this.Next = Next;
this.bas = bas;
this.flux = flux;
this.ToCote = ToCote;
}
}
// index des points pour chaque triangle
// P1, P2, P3 => point[x], point[y], point[z]
private struct TRI {
public int P1;
public int P2;
public int P3;
public TRI ( int P1 , int P2 , int P3){
this.P1 = P1;
this.P2 = P2;
this.P3 = P3;
}
}
private struct SEG {
public int x;
public int y;
public SEG ( int x, int y){
this.x = x;
this.y = y;
}
}
// données calculées en MultiThread pour créer les mesh en MainThread
private struct ZONE {
public Rect R;
public int[] tri;
public int triCount;
public Vector3[] Vert;
public Color32[] Col;
public Vector2[] UV;
public ZONE (Rect R , int[] tri, int triCount ,Vector3[] Vert , Color32[] Col,Vector2[] UV){
this.R = R;
this.tri = tri;
this.triCount = triCount;
this.Vert = Vert;
this.Col = Col;
this.UV = UV;
}
}
private POINT[] point ; private int PointCount ;
private TRI[] tri ; private int TriCount ;
private int[] Cote ; private int CoteCount;
private int[] River; private int RiverCount;
private TRI[] TriToAdd = new TRI[15];
private int[] DeleteTri = new int[10];
private SEG[] segment = new SEG[30];
private TRI TRI1;
private List<int> triTEMP;
private ORIGIN[] origin;
private int MaxPoint ;
private int MaxTri ;
Vector3 NewPoint;
private ZONE[] zone;
// les trois couleur de bases, utilisées jusqu'a
// l'utilisation des biomes.
private Color32 Blue = new Color32(0,0,255,100);
private Color32 Red = new Color32(255,0,0,100);
private Color32 White = new Color32(255,255,255,100);
private int Rayon = 20000;
private float Densite = 1000.0f;
private float Taille = 1.0f;
private int seuilRiver = 50;
//couleur a utiliser pour shader mixant 13 textures
private Color TropicalRainForest = new Color(1,0,0,0);
private Color TropicalSeasonForest = new Color(0,1,0,0);
private Color GrassLand = new Color(0,0,1,0);
private Color SubTropicalDesert = new Color(0,0,0,1);
private Color TemperateRainForest = new Color(1,1,0,0);
private Color TemperateDecidousForest = new Color(0,1,1,0);
private Color TemperateDesert = new Color(0,0,1,1);
private Color Taiga = new Color(1,0,0,1);
private Color ShrubLand = new Color(0,0,0,0);
private Color Snow = new Color(0,1,0,1);
private Color Tundra = new Color(1,0,1,0);
private Color Bare = new Color(-1,0,0,0);
private Color Scorched = new Color(0,-1,0,0);
//Couleur a utiliser pour shader sans texture, juste vertex Color
//private Color TropicalRainForest = new Color(0.60f,0.73f,0.66f,1.0f);
//private Color TropicalSeasonForest = new Color(0.66f,0.79f,0.64f,1.0f);
//private Color GrassLand = new Color(0.76f,0.82f,0.66f,1.0f);
//private Color SubTropicalDesert = new Color(0.91f,0.86f,0.78f,1.0f);
//private Color TemperateRainForest = new Color(0.64f,0.76f,0.65f,1.0f);
//private Color TemperateDecidousForest = new Color(0.70f,0.78f,0.66f,1.0f);
//private Color TemperateDesert = new Color(0.89f,0.90f,0.78f,1.0f);
//private Color Taiga = new Color(0.79f,0.82f,0.73f,1.0f);
//private Color ShrubLand = new Color(0.76f,0.79f,0.73f,1.0f);
//private Color Snow = new Color(0.96f,0.96f,0.96f,1.5f);
//private Color Tundra = new Color(0.86f,0.86f,0.73f,1.0f);
//private Color Bare = new Color(0.73f,0.73f,0.73f,1.0f);
//private Color Scorched = new Color(0.59f,0.59f,0.59f,1.0f);
private Transform Cam;
public Material mat;
public Transform Eau;
public bool ShowZone;
public bool ShowFluxWater;
public bool ShowRiver;
void Start () {
float TP = Time.realtimeSinceStartup;
MaxPoint = 750000;
MaxTri = 1500000;
TriCount = -1;
PointCount = -1;
point = new POINT[MaxPoint];
tri = new TRI[MaxTri];
Cote = new int[7500];
CoteCount = 0;
CreateBase();
for ( int i = 1 ; i < 14 ; i++){
AddPoint1(i*2,i/11.0f);
}
AddTerre(White);
Densite = 500.0f;
PointNEXT();
AddColor();
Border();
Smooth1();
Iteration(false);
//Instantiate origin NEXT //
origin = new ORIGIN[PointCount+1];
for ( int i = 0 ; i < origin.Length ; i++){
origin[i].Next = point[i].pointNext;
}
//================= //
Iteration(false);
propageCote();
RemoveIsolate();
elevation();
//Instantiate origin dist To COTE //
for ( int i = 0 ; i < origin.Length ; i++){
origin[i].ToCote = point[i].y*10.0f;
}
//================= //
Iteration(true);
elevation1();
Iteration(true);
Voisin();
Biome();
initialiseZONE();
CreateEau();
int RD = UnityEngine.Random.Range(3, PointCount);
Vector3 A = new Vector3( point[RD].x, point[RD].y , point[RD].z);
if( Camera.mainCamera){
Cam = Camera.mainCamera.transform;
Cam.position = A + Vector3.up*1.8f;
}
else{ print (" !! aucune camera !!"); }
print( " temps : " + ( Time.realtimeSinceStartup-TP) + "s , PointCount : " + PointCount + " , triCount : " + TriCount);
print( " ratio : " + Mathf.Round(TriCount/(Time.realtimeSinceStartup-TP)/10.0f) /100.0f + " kPoly/s");
}
void Update () {
// Drop la caméra à un nouvel emplacement
if( Input.GetKeyDown("a") && Cam){
int RD = UnityEngine.Random.Range(3, PointCount);
Vector3 A = new Vector3( point[RD].x, point[RD].y , point[RD].z);
Cam.position = A + Vector3.up*1.8f;
}
// afficher/cacher l'écoulement de l'eau => touche "e"
if ( Input.GetKeyDown("e")){ ShowFluxWater = !ShowFluxWater;}
for ( int i = 3 ; i < origin.Length ; i++){
if( ShowFluxWater){
int J = origin[i].bas;
if( J >2 && point[i].y >0 && point[J].y > 0){
Vector3 A = new Vector3( point[i].x, point[i].y , point[i].z);
Vector3 B = new Vector3( point[J].x, point[J].y , point[J].z);
Debug.DrawLine( A+Vector3.up*10.0f, B +Vector3.up*10.0f,new Color(1.0f,1.0f,1.0f,0.4f) );
}
}
}
// afficher/cacher les rivvieres retenues => touche "r"
if ( Input.GetKeyDown("r")){ ShowRiver = !ShowRiver;}
for ( int i = 0 ; i < RiverCount ; i++){
if( ShowRiver){
int I = River[i];
int J = origin[River[i]].bas;
if( point[I].y >0 && point[J].y > 0){
Vector3 A = new Vector3( point[I].x, point[I].y , point[I].z);
Vector3 B = new Vector3( point[J].x, point[J].y , point[J].z);
Debug.DrawLine( A+Vector3.up*10.0f, B +Vector3.up*10.0f,Color.blue );
}
}
}
// afficher/cacher les zones => touche "z"
if ( Input.GetKeyDown("z")){ ShowZone = !ShowZone;}
for ( int i = 0 ; i < zone.Length ; i++){
if( zone[i].triCount != 0 && ShowZone){
Vector3 A = new Vector3(zone[i].R.xMin, 0, zone[i].R.yMin);
Vector3 B = new Vector3(zone[i].R.xMax, 0, zone[i].R.yMax);
Vector3 C = new Vector3(zone[i].R.xMin, 0, zone[i].R.yMax);
Vector3 D = new Vector3(zone[i].R.xMax, 0, zone[i].R.yMin);
Debug.DrawLine(A,B);
Debug.DrawLine(C,D);
Debug.DrawLine(A,C);
Debug.DrawLine(A,D);
Debug.DrawLine(C,B);
Debug.DrawLine(D,B);
}
}
}
void CreateBase() {
//création tout premier triangle,
//doit pouvoir contenir TOUT les point qui seront crées.
float espace = 2* Mathf.PI /3;
for ( int i = 0 ; i < 3 ; i ++){
PointCount += 1;
point[PointCount].x = Mathf.Sin(i*espace)*Rayon*3.5f;
point[PointCount].z = Mathf.Cos( i*espace)*Rayon*3.5f;
point[PointCount].color = Blue;
point[PointCount].triNext = new List<int>();
}
TriCount +=1;
TRI1 = new TRI(0,1,2);
tri[TriCount] = TRI1;
point[TRI1.P1].triNext.Add(TriCount);
point[TRI1.P2].triNext.Add(TriCount);
point[TRI1.P2].triNext.Add(TriCount);
}
void AddPoint1( int NB, float ray) {
// création de cercle concentriques de points
// ray => rayon du cercle sur lequel placer les points.
// NB => nombre de points sur ce cercle.
float espace = 2.0f* Mathf.PI /NB;
for ( int p = 0 ; p < NB ; p ++){
NewPoint = new Vector3(Mathf.Sin(p*espace)*Rayon*ray,0,Mathf.Cos( p*espace)*Rayon*ray);
// recherche du point le plus proche de celui crée.
float Min = Mathf.Infinity;
for ( int i = 0 ; i < PointCount+1 ; i++){
float dist = ( new Vector3(point[i].x,point[i].y,point[i].z) - NewPoint).sqrMagnitude;
if( dist < Densite*Densite){ return; }
if( dist < Min ){
Min = dist;
triTEMP=(point[i].triNext);
}
}
PointCount += 1;
point[PointCount].x = NewPoint.x;
point[PointCount].z = NewPoint.z;
point[PointCount].color = White;
point[PointCount].triNext = new List<int>();
// définir les triangle éxistant pouvant etre modifier
// parmis les triangles proches au point le plus proche.
// triTEMP => les index des triangles trouvés.
List<int> Plist = new List<int>();
for ( int i = 0 ; i < triTEMP.Count ; i++){
TRI1 = tri[triTEMP[i]];
if ( Plist.Contains(TRI1.P1) == false){ Plist.Add(TRI1.P1); }
if ( Plist.Contains(TRI1.P2) == false){ Plist.Add(TRI1.P2); }
if ( Plist.Contains(TRI1.P3) == false){ Plist.Add(TRI1.P3); }
}
triTEMP = new List<int>();
for ( int j = 0 ; j < Plist.Count ; j++){
List<int> NEXT = point[Plist[j]].triNext;
for ( int k = 0 ; k < NEXT.Count ; k++){
if( triTEMP.Contains(NEXT[k]) == false){
triTEMP.Add(NEXT[k]);
}
}
}
Remove();
}
}
void Remove(){
// test des triangles => garder/modifier
// en utilisant la methode de Delaunay
int SegCount = 0;
int DeleteCount = 0;
Vector2 NewPointXZ = new Vector2(NewPoint.x ,NewPoint.z);
for ( int i = 0 ; i < triTEMP.Count ; i++){
int I = triTEMP[i];
TRI1 = tri[I];
Vector3 A = new Vector3( point[TRI1.P1].x ,point[TRI1.P1].y, point[TRI1.P1].z);
Vector3 B = new Vector3( point[TRI1.P2].x ,point[TRI1.P2].y, point[TRI1.P2].z);
Vector3 C = new Vector3( point[TRI1.P3].x ,point[TRI1.P3].y, point[TRI1.P3].z);
float Cx = -((C.x*C.x - B.x*B.x + C.z*C.z-B.z*B.z)/(2*(C.z-B.z))-(B.x*B.x - A.x*A.x + B.z*B.z-A.z*A.z)/(2*(B.z-A.z)))/((B.x-A.x)/(B.z-A.z) - (C.x-B.x)/(C.z-B.z));
float Cy = -(B.x-A.x)/(B.z-A.z)*Cx + (B.x*B.x - A.x*A.x + B.z*B.z-A.z*A.z)/(2*(B.z-A.z));
Vector2 midpoint = new Vector2( Cx , Cy);
float dist = ((midpoint- new Vector2(A.x , A.z) ).sqrMagnitude - (midpoint - NewPointXZ ).sqrMagnitude);
if( dist >= 0){
DeleteTri[DeleteCount] = I;
DeleteCount +=1;
segment[SegCount].x = TRI1.P1; segment[SegCount].y = TRI1.P2;
segment[SegCount+1].x = TRI1.P1; segment[SegCount+1].y = TRI1.P3;
segment[SegCount+2].x = TRI1.P3; segment[SegCount+2].y = TRI1.P2;
SegCount += 3;
}
}
int TriToAddCount = 0;
float angle = 0.0f;
for ( int i = 0 ; i < SegCount ; i++){
SEG S = segment[i];
Vector2 Seg = new Vector2(S.x,S.y);
bool seul = true;
for ( int l = 0 ; l < SegCount ; l++){
Vector2 Seg1 = new Vector2(segment[l].x,segment[l].y);
if( Seg.sqrMagnitude == Seg1.sqrMagnitude && i!=l){
seul = false;
break;
}
}
if( seul ){
Vector2 cA = new Vector2(point[S.x].x,point[S.x].z ) - NewPointXZ;
Vector2 cB = new Vector2(point[S.y].x,point[S.y].z ) - NewPointXZ;
angle += Vector2.Angle(cA ,cB );
TriToAdd[TriToAddCount].P1 = S.x ;
TriToAdd[TriToAddCount].P2 = S.y ;
TriToAdd[TriToAddCount].P3 = PointCount ;
TriToAddCount += 1;
}
}
if( Mathf.Approximately( 360 ,angle) ){
for ( int t = 0 ; t < DeleteCount ; t++){
TRI1 = tri[DeleteTri[t]];
int temp = DeleteTri[t];
TRI TRI2 = TriToAdd[t];
tri[temp] = TRI2;
if( TRI2.P1 != TRI1.P1){ point[TRI1.P1].triNext.Remove(DeleteTri[t]); point[TRI2.P1].triNext.Add(temp); }
if( TRI2.P2 != TRI1.P2){ point[TRI1.P2].triNext.Remove(DeleteTri[t]); point[TRI2.P2].triNext.Add(temp); }
if( TRI2.P3 != TRI1.P3){ point[TRI1.P3].triNext.Remove(DeleteTri[t]); point[TRI2.P3].triNext.Add(temp); }
}
for ( int t = DeleteCount ; t < TriToAddCount ; t++){
TriCount +=1;
tri[TriCount] = TriToAdd[t] ;
int temp = TriCount;
point[TriToAdd[t].P1].triNext.Add(temp);
point[TriToAdd[t].P2].triNext.Add(temp);
point[TriToAdd[t].P3].triNext.Add(temp);
}
}
else{
PointCount -= 1;
}
}
void PointNEXT(){
// pour chaque point, lister les ponts adjacent
// pour chaque triangle, chaque point est adjacent aux deux autres
for ( int j = 0 ; j < PointCount+1 ; j++){
point[j].pointNext = new List<int>();
}
for ( int i = 0 ; i < TriCount+1 ; i++){
TRI TRI = tri[i];
List<int> x = point[TRI.P1].pointNext; List<int> y = point[TRI.P2].pointNext; List<int> z = point[TRI.P3].pointNext;
if( x.Contains(TRI.P2) == false){ x.Add( TRI.P2); }
if( y.Contains(TRI.P1) == false){ y.Add( TRI.P1); }
if( x.Contains(TRI.P3) == false){ x.Add( TRI.P3); }
if( z.Contains(TRI.P1) == false){ z.Add( TRI.P1); }
if( z.Contains(TRI.P2) == false){ z.Add( TRI.P2); }
if( y.Contains(TRI.P3) == false){ y.Add( TRI.P3); }
point[TRI.P1].pointNext = x; point[TRI.P2].pointNext = y; point[TRI.P3].pointNext = z;
}
}
void AddColor(){
// jusque la, tout les points sont blanc
// en passer aléatoirement certains en rouge => terre
// et d'autre en bleu => eau
for ( int i = 0 ; i < 20.0f*Taille ; i++){ ChangeColor(Red); }
for ( int i = 0 ; i < 20.0f* (1.0f-Taille) ; i++){ ChangeColor(Blue); }
PropageColor();
}
void ChangeColor(Color Col){
int RD = UnityEngine.Random.Range(3, PointCount);
Vector2 P = new Vector2( point[RD].x, point[RD].z);
if( P.magnitude > Rayon * Taille && Col == Red){
ChangeColor(Col);
return;
}
if( P.magnitude < Rayon*0.9f * Taille && Col == Blue){
ChangeColor(Col);
return;
}
point[RD].color = Col;
}
void PropageColor(){
// propagation des couleur rouge et bleu
// pour chaque point bleu ou rouge, assigner sa couleur a tout ses adjacents
// reommencer tant qu'il reste du blanc.
for ( int i = 0 ; i < PointCount+1 ; i ++){
List<int> next = point[i].pointNext;
for ( int j = 0 ; j < next.Count ; j++){
if( point[next[j]].color.g == 255){
point[next[j]].color = point[i].color;
}
}
}
for ( int k = 0 ; k < PointCount+1 ; k ++){
if ( point[k].color.g == 255){
PropageColor();
return;
}
}
// prevenir la propagation
// maintenir la terre a un rayon donné
float Max = Mathf.Min(Rayon , Rayon * Taille * 1.2f);
Max *= Max;
for ( int i = 0 ; i < PointCount+1 ; i ++){
Vector3 P = new Vector3( point[i].x, point[i].y , point[i].z);
if(P.sqrMagnitude > Max){
point[i].color = Blue;
}
}
}
void Border(){
// création des cote, si un point
// a un voisin pas de sa propre couleur, il est
// entre terre et eau => cote.
CoteCount = 0;
for ( int i = 0 ; i < PointCount ; i ++){
if( point[i].color.r == 0){
for ( int k = 0 ; k < point[i].pointNext.Count ; k++){
int N = point[i].pointNext[k];
if( point[N].color.b == 0){
point[i].color = White;
Cote[CoteCount] = i;
CoteCount +=1;
break;
}
}
}
}
RemoveIsolate();
}
void RemoveIsolate(){
// si un point de cote a des adjacent uniquement
// de la meme couleur, il n'est plus Cote.
for ( var i = 0 ; i < CoteCount+1 ; i++){
int nb = 0 ;
for ( int j = 0 ; j < point[Cote[i]].pointNext.Count ; j++){
int P = point[Cote[i]].pointNext[j];
if( point[P].color.b == 0){
nb = 1;
break;
}
}
if( nb == 0){
point[Cote[i]].color = Blue;
}
}
int[] tempCote = Cote;
int CoteCount1 = 0;
for ( int i = 0 ; i < CoteCount+1 ; i++){
if( point[Cote[i]].color.g == 255 ){
tempCote[CoteCount1] = Cote[i];
CoteCount1 +=1;
}
}
CoteCount = CoteCount1;
Cote = tempCote;
}
void Smooth1(){
// lisser le contour des cotes
// adoucir les angles
for ( int l = 0 ; l < CoteCount+1 ; l ++ ){
Vector3 dist = new Vector3(0,0,0);
int L = Cote[l];
List<int> next = point[L].pointNext;
for( int i = 0 ; i < next.Count ; i++){
int I = next[i];
if(point[I].color.g == 255){
dist += (new Vector3(point[L].x, point[L].y, point[L].z) - new Vector3(point[I].x, point[I].y, point[I].z)) ;
}
}
point[L].x -= dist.x *0.15f;
point[L].y = 0.0f;
point[L].z -= dist.z *0.15f;
}
Smooth();
}
void Smooth(){
// positionner chaque point au barycentre de
// tout ses points adjacent => maillage plus régulier.
float Se = Rayon*Rayon*1.3f;
float mul = 0.5f ;
for ( int l = 0 ; l < PointCount+1 ; l ++ ){
Vector3 PL = new Vector3(point[l].x , point[l].y , point[l].z);
if(point[l].color.g == 0 && PL.sqrMagnitude < Se){
Vector3 P1 = PL*mul;
List<int> Next = point[l].pointNext;
for ( int j = 0 ; j < Next.Count ; j ++){
int J = Next[j];
P1 += new Vector3(point[J].x , point[J].y ,point[J].z);;
}
P1 /= Next.Count+mul;
point[l].x = P1.x ;
point[l].y = P1.y ;
point[l].z = P1.z ;
}
}
}
void Iteration(bool divide){
if( divide){
DivideCote();
}
Densite *=0.5f;
AddTerre(Red);
PointNEXT();
propageCote();
RemoveIsolate();
Smooth1();
}
void AddTerre( Color32 COL ){
// pour chaque triangle, ajouter un point au barycentre
// de ses trois sommets.
float MOY = Densite * Densite*3.0f;
float R = Rayon*Rayon*1.2f;
float Div3 = 1.0f/3.0f;
for ( int i = 0 ; i < TriCount ; i++){
TRI1 = tri[i];
float Col = (point[TRI1.P1].color.b + point[TRI1.P2].color.b + point[TRI1.P3].color.b)*Div3;
if( Col == COL.b){
Vector3 A = new Vector3(point[TRI1.P1].x, point[TRI1.P1].y, point[TRI1.P1].z);
Vector3 B = new Vector3(point[TRI1.P2].x, point[TRI1.P2].y, point[TRI1.P2].z);
Vector3 C = new Vector3(point[TRI1.P3].x, point[TRI1.P3].y, point[TRI1.P3].z);
NewPoint = ( A + B + C)*Div3;
if( ( (A - NewPoint).sqrMagnitude +(B - NewPoint).sqrMagnitude + (C - NewPoint).sqrMagnitude) > MOY && NewPoint.sqrMagnitude < R) {
triTEMP = new List<int>();
int[] tr = new int[2];
tr[0] = TRI1.P1; tr[1] = TRI1.P2;
for ( int j = 0 ; j < tr.Length ; j ++){
for ( int k = 0 ; k < point[tr[j]].triNext.Count ; k++){
int K = point[tr[j]].triNext[k];
if( triTEMP.Contains(K) == false){
triTEMP.Add(K);
}
}
}
PointCount += 1;
point[PointCount].x = NewPoint.x;
point[PointCount].y = NewPoint.y;
point[PointCount].z = NewPoint.z;
point[PointCount].color = COL;
point[PointCount].triNext = new List<int>();
Remove();
}
}
}
}
void propageCote(){
// deplace les cote d'un voisin sur la terre
// réduction legere de la surface terre
// mais augmentation de la densité du maillage
// de la cote partie eau.
int CoteC = CoteCount+1;
for (var n = 0 ; n < CoteC ; n ++){
List<int> TEMP = point[Cote[n]].pointNext ;
for ( int b = 0 ; b < TEMP.Count ; b ++){
if( point[TEMP[b]].color.b == 0){
point[TEMP[b]].color = White;
Cote[CoteCount] = TEMP[b];
CoteCount +=1;
}
}
point[Cote[n]].color = Blue;
}
}
void elevation(){
// pour chaque point, trouver le point de la
// la cote le plus proche, calculer la distance
// hauteur du point = distance a la cote.
// l'ile est connique, les versans sont reguliers.
float RAYON = Rayon * Rayon;
for ( int i = 3 ; i < PointCount+1 ; i++){
Vector2 P1 = new Vector2( point[i].x, point[i].z);
if( point[i].y == 0 && point[i].color.g == 0){
float Min = RAYON;
int proche = 0;
for ( int j = 0 ; j < CoteCount ; j+=5){
Vector2 P2 = new Vector2( point[Cote[j]].x, point[Cote[j]].z);
float dist = (P1 - P2).sqrMagnitude;
if( dist < Min){ Min = dist ; proche = Cote[j]; }
}
point[i].y = Mathf.Sqrt(Min)*0.5f * Mathf.Sign(1-point[i].color.b ) ;
Vector2 P3 = new Vector2(point[proche].x,point[proche].z);
if( point[i].color.r == 0 ){ continue;}
for ( int j = 0 ; j < point[i].pointNext.Count ; j ++){
int k = point[i].pointNext[j];
if( point[k].y == 0 && point[k].color.g == 0){
Min = ( P3 -new Vector2(point[k].x,point[k].z)).sqrMagnitude;
point[k].y = Mathf.Sqrt(Min)*0.5f * Mathf.Sign(1-point[k].color.b );
}
for ( int l= 0; l< point[k].pointNext.Count ; l ++){
int k1 = point[k].pointNext[l];
if( point[k1].y == 0 && point[k1].color.g == 0 ){
Min = ( P3 -new Vector2(point[k1].x,point[k1].z)).sqrMagnitude;
point[k1].y = Mathf.Sqrt(Min)*0.5f * Mathf.Sign(1-point[k1].color.b ) ;
}
}
}
}
}
}
void elevation1(){
// pour chaque point, calcul d'un mix de Perlin noise en fonction de sa position
// sa hauteur est multipliée a ce perlin
// l'ile est valonnée, escaprée.
float Rand = UnityEngine.Random.Range(10.0f,500.0f);
for ( int k = 0 ; k < PointCount+1 ; k++){
float Min = point[k].y*2.0f;
float Perlin = 0.0f;
float prev = 1.0f;
float tot = 0.0f;
float X = (point[k].x/16000.0f+ Rand);
float Z = (point[k].z/16000.0f+ Rand);
float inv = 1.0f;
for ( float p = 1.0f ; p < 60; p*=2.4f){
float P = 1.0f/(p -0.15f);
float test = Mathf.PerlinNoise(X*p * inv , Z*p *inv )*prev ;
test = (0.5f-Mathf.Abs( test-0.5f) )*(2.1f+0.5f*P) ;
prev = Perlin*P + test ;
Perlin += (test*test)*P;
tot += P;
inv = -inv;
}
Perlin /= tot;
Min *=0.000055f;
if( Min > 0){
if( Min< 0.155f){ point[k].y = Perlin* Min*Min*20000.0f+Min*10.0f ; }
else{ point[k].y = Perlin* (Mathf.Sqrt(Min*1.5f)-0.36205f) *4000.0f+Min*10.0f; }
}
else{
if( Min>-0.155f){ point[k].y = -Perlin* Min*Min*20000.0f +Min*10.0f; }
else{ point[k].y = -Perlin* (Mathf.Sqrt(-Min*1.5f)-0.36205f) *4000.0f+Min*10.0f; }
}
}
}
void Voisin(){
// pour chaque point de origin (points crée jusque la
// deuxieme itération), definir le point le plus bas
// ou l'eau coulera.
for( int i = 0 ; i < origin.Length ; i++ ){
int bas = i;
for ( int j = 0 ; j < origin[i].Next.Count ; j ++){
int K = origin[i].Next[j];
if( point[K].y < point[bas].y){
bas = K;
}
}
origin[i].bas = bas;
origin[i].flux = 1.0f;
}
// pour chaque point, suivre le chemin de point le plus bas en
// point le plus bas, c'est le parcours de l'eau
for( int i = 0 ; i < origin.Length ; i++){
FluxAdd(i);
}
River = new int[10000];
RiverCount = 0;
for (int i = 0 ; i < origin.Length ; i++){
if( origin[i].flux> seuilRiver){
River[RiverCount] = i;
RiverCount +=1;
}
}
}
void FluxAdd(int k){
if( k != origin[k].bas){
origin[origin[k].bas].flux += 1;
FluxAdd(origin[k].bas);
}
}
void DivideCote(){
// entre deux point de cote adjacent, inserer un
// nouveau point cote. => subdivision de la cote.
for ( int i = 0 ; i < CoteCount ; i++){
int a = Cote[i];
for ( int j = 0 ; j < point[a].pointNext.Count ; j++){
int b = point[a].pointNext[j];
if( a > b && point[b].color.g != 0){
Vector3 A = new Vector3(point[a].x, point[a].y, point[a].z);
Vector3 B = new Vector3(point[b].x, point[b].y, point[b].z);
NewPoint = ( A + B )*0.5f;
triTEMP = new List<int>();
int[] tr = new int[2];
tr[0] = a; tr[1] = b;
for ( int l = 0 ; l < tr.Length ; l ++){
for ( int k = 0 ; k < point[tr[l]].triNext.Count ; k++){
int K = point[tr[l]].triNext[k];
if( triTEMP.Contains(K) == false){
triTEMP.Add(K);
}
}
}
PointCount += 1;
point[PointCount].x = NewPoint.x;
point[PointCount].y = NewPoint.y;
point[PointCount].z = NewPoint.z;
point[PointCount].color = Red;
point[PointCount].triNext = new List<int>();
Remove();
}
}
}
for ( int i = 0 ; i < CoteCount ; i ++){
point[Cote[i]].color = Red;
}
Border();
}
void Biome(){
// en fonction du flux et de l'altitude de chaque point de origin
// assignation d'un biome représenté part un couleur
float MaxDist = 0.0f;
float MaxHeight = 0.0f;
for ( int i = 0 ; i < origin.Length; i++){
Vector2 A = new Vector2(point[i].x, point[i].z);
float TOCOTE = origin[i].ToCote;
TOCOTE *=TOCOTE;
for ( int j = 0 ; j < RiverCount ; j++ ){
Vector2 B = new Vector2(point[River[j]].x, point[River[j]].z);
float dist = (A - B).sqrMagnitude*1.3f;
if ( dist < TOCOTE){ TOCOTE =dist; }
}
if( point[i].y > MaxHeight){ MaxHeight = point[i].y;}
if( TOCOTE > MaxDist){ MaxDist = TOCOTE;}
origin[i].ToCote = Mathf.Sqrt(TOCOTE);
}
MaxDist = Mathf.Sqrt(MaxDist);
MaxHeight = Mathf.Max( MaxHeight, 3500.0f);
for ( int i = 0 ; i < origin.Length ; i++){
float heigh = point[i].y;
float dist = origin[i].ToCote;
if( heigh < 0.0f){ point[i].color = SubTropicalDesert;}
else if ( heigh < MaxHeight/4.0f){
if( dist < MaxDist/3.0f){ point[i].color = TropicalRainForest; }
else if( dist < MaxDist/3.0f*2.0f){ point[i].color = TropicalSeasonForest; }
else if( dist < MaxDist/6.0f*5.0f){ point[i].color = GrassLand ; }
else{ point[i].color = SubTropicalDesert; }
}
else if( heigh < MaxHeight/2.0f){
if( dist < MaxDist/6.0f){ point[i].color = TemperateRainForest; }
else if( dist < MaxDist/2.0f){ point[i].color = TemperateDecidousForest; }
else if( dist < MaxDist/6.0f*5.0f){ point[i].color = GrassLand; }
else{ point[i].color = TemperateDesert; }
}
else if( heigh < MaxHeight/4.0f*3.0f){
if( dist < MaxDist/3.0f){ point[i].color = Taiga; }
else if( dist < MaxDist/3.0f*2.0f){ point[i].color = ShrubLand; }
else{ point[i].color = TemperateDesert; }
}
else if( heigh < MaxHeight/4.0f*3.8f){
if( dist < MaxDist/1.5f){ point[i].color = Snow; }
else if( dist < MaxDist/3.0f*2.2f){ point[i].color = Tundra; }
else if( dist < MaxDist/6.0f*5.0f){ point[i].color = Bare; }
else{ point[i].color = Scorched; }
}
else{
point[i].color = Snow;
}
}
int[] ORI = new int[PointCount+1];
int oriCount = origin.Length;
for ( int i = 0 ; i < origin.Length ; i++){
ORI[i] = i;
}
// idem propageColor(), pour chaque point, propagation
// de sa coulur, donc son biome, aux points adjacent
for ( int i = 0 ; i < oriCount ; i++ ){
for ( int j = 0 ; j < point[ORI[i]].pointNext.Count ; j++ ){
int J = point[ORI[i]].pointNext[j];
if( point[J].color.a == 100){
if( point[J].y < 0.0f){
point[J].color = SubTropicalDesert;
}
else{
point[J].color = point[ORI[i]].color;
}
ORI[oriCount] = J;
oriCount +=1;
}
}
}
for ( int i = 0 ; i < CoteCount; i++){
for ( int j = 0 ; j < point[Cote[i]].pointNext.Count ; j++ ){
point[point[Cote[i]].pointNext[j]].color = SubTropicalDesert ;
}
}
}
void initialiseZONE(){
// subdivise/quadrille l'iles en zones.
// chaque zone correspondra a un gameObjet
int ZoneCount = 8;
float RAYON = Rayon*1.2f;
float zoneSize = (RAYON*2.0f/ZoneCount);
zone = new ZONE[ZoneCount*ZoneCount+1];
for ( int i = 0 ; i < ZoneCount ; i++){
zone[i].R = new Rect(-RAYON + zoneSize*i, -RAYON , zoneSize,zoneSize) ;
zone[i].tri = new int[200000];
}
for ( int j = ZoneCount ; j < ZoneCount*ZoneCount ; j++){
zone[j].R = new Rect( zone[j-ZoneCount].R.x , zone[j-ZoneCount].R.y + zoneSize , zoneSize , zoneSize);
zone[j].tri = new int[200000];
}
zone[zone.Length-1].R = new Rect( 0, 0, 0, 0);
zone[zone.Length-1].tri = new int[200000];
// assigne chaque triangle a une zone
// si la zone contiens le premier point du triangle.
// en repartissant les zone sur plusieurs Threads
Thread[] tache = new Thread[8];
for ( int k = 0 ; k < 8; k++){
int K = k;
tache[K] = new Thread(LaunchFin);
tache[K].Start(K);
}
for ( int i = 0 ; i < 8 ; i++){
tache[i].Join();
}
createParcel();
}
void LaunchFin(object K){
int k = Convert.ToInt16(K);
int start = k*8;
int end = (k+1)*8 ;
if( end == zone.Length-2){
end +=1;
}
for ( int i = 0; i < TriCount; i++){
if( tri[i].P1 < 3 || tri[i].P2 < 3 || tri[i].P3 < 3){ continue; }
if( point[tri[i].P1].y < 0 || point[tri[i].P2].y < 0 || point[tri[i].P3].y < 0){
if( end == zone.Length-1){
zone[zone.Length-1].tri[zone[zone.Length-1].triCount] = i;
zone[zone.Length-1].triCount +=1;
}
continue;
}
Vector2 A = new Vector2(point[tri[i].P1].x,point[tri[i].P1].z) ;
for ( int j = start ; j < end; j++){
if ( zone[j].R.Contains(A)){
zone[j].tri[zone[j].triCount] = i;
zone[j].triCount +=1;
break;
}
}
}
for ( int i = start ; i < end ; i++){
Finalzone(i);
if( i == zone.Length-2){
Finalzone(i+1);
}
}
}
void createParcel(){
// création d'un gameobject par zone
for ( int i = 0 ; i < zone.Length; i++){
if( zone[i].triCount != 0){
GameObject GO = new GameObject();
GO.transform.position = Vector3.zero;
GO.name = "parcel " + i;
GO.AddComponent("MeshFilter");
GO.AddComponent("MeshRenderer");
GO.renderer.material = mat;
Mesh mesh = GO.GetComponent<MeshFilter>().mesh;
mesh.Clear();
mesh.vertices = zone[i].Vert;
mesh.colors32 = zone[i].Col;
mesh.uv = zone[i].UV;
mesh.triangles = zone[i].tri;
mesh.Optimize();
mesh.RecalculateNormals();
}
}
}
void Finalzone(int k){
// apres assignation des triangles a chaque zone
// pour chaque tri réagence les points
// pour qu'ils tournent tous dans le même sens
// => chaque tri visble du même coté.
int zpCount = 0;
int ztCount = zone[k].triCount;
int[] Tri = new int[ztCount*3];
Vector3[] ZP = new Vector3[ztCount*3] ;
Color32[] color = new Color32[ztCount*3] ;
Dictionary<int,int> Corr = new Dictionary<int,int>() ;
for ( int i = 0 ; i < ztCount ; i++){
int I = zone[k].tri[i];
int a = tri[I].P1;
int b = tri[I].P2;
int c = tri[I].P3;
TRI triangle = new TRI(c,b,a);
if( point[a].x> point[b].x && point[a].x > point[c].x){ triangle = new TRI(a,b,c); }
else if( point[b].x> point[a].x && point[b].x > point[c].x){ triangle = new TRI(b,a,c); }
Vector2 A = new Vector2( point[triangle.P1].x ,point[triangle.P1].z);
Vector2 B = new Vector2( point[triangle.P2].x ,point[triangle.P2].z);
Vector2 C = new Vector2( point[triangle.P3].x ,point[triangle.P3].z);
Vector2 dirY = (B-A); Vector2 dirZ = (C-A);
float angleY = Vector2.Angle(new Vector2(0,1) , dirY );
float angleZ = Vector2.Angle(new Vector2(0,1) , dirZ );
if( angleY > angleZ){ a = triangle.P1; b = triangle.P2 ; c = triangle.P3; }
else{ a = triangle.P1; b = triangle.P3 ; c = triangle.P2; }
if( Corr.ContainsKey(a) == false){
color[zpCount] = point[a].color;
ZP[zpCount] = new Vector3( point[a].x, point[a].y, point[a].z);
Corr[a] = zpCount;
zpCount +=1;
}
if( Corr.ContainsKey(b) == false){
color[zpCount] = point[b].color;
ZP[zpCount] = new Vector3( point[b].x, point[b].y, point[b].z);
Corr[b] = zpCount;
zpCount +=1;
}
if( Corr.ContainsKey(c) == false){
color[zpCount] = point[c].color;
ZP[zpCount] = new Vector3( point[c].x, point[c].y, point[c].z);
Corr[c] = zpCount;
zpCount +=1;
}
Tri[i*3] = Corr[a]; Tri[i*3 +1] = Corr[b] ; Tri[i*3 +2] = Corr[c];
}
zone[k].tri = Tri;
zone[k].triCount = ztCount*3;
zone[k].Vert = new Vector3[zpCount];
zone[k].Col = new Color32[zpCount];
zone[k].UV = new Vector2[zpCount];
for ( int i = 0 ; i < zpCount ; i++){
if( ZP[i].y <0){ ZP[i].y -= 1.0f;}
else if(ZP[i].y > 0) { ZP[i].y += 1.0f;}
zone[k].Vert[i] = ZP[i] ;
zone[k].Col[i] = color[i];
zone[k].UV[i] = new Vector2(ZP[i].x , ZP[i].z)/20000.0f;
}
}
void CreateEau(){
// la derniere Zone contiens les points sous le niveaux de l'eau
// dupliquer le mesh au gameObject Eau
// Eau.scale.y = 0 ou deplacer chaque point a y = 0;
if( Eau == null){ return;}
int i = zone.Length-1;
Mesh mesh = Eau.GetComponent<MeshFilter>().mesh;
mesh.vertices = zone[i].Vert;
mesh.colors32 = zone[i].Col;
mesh.uv = zone[i].UV;
mesh.triangles = zone[i].tri;
mesh.Optimize();
mesh.RecalculateNormals();
}
}
-- affichage normal ------ affichage de zone ----- affichage parcours eau --- affichage rivière
Si vous avez des idées sur quoi ajouter/modifier/enlever je suis preneur. Avant de m'attaquer aux routes.
Vais voir aussi pour ajouter de l'érosion, pour gagner un peu en réalisme de topologie peut être.