cm_randomterrain.cpp 23 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087
  1. #include "../server/exe_headers.h"
  2. #include "cm_local.h"
  3. #include "cm_patch.h"
  4. #include "cm_landscape.h"
  5. #include "../game/genericparser2.h"
  6. #include "cm_randomterrain.h"
  7. #define NOISE_SIZE 256
  8. #define NOISE_MASK (NOISE_SIZE - 1)
  9. static float noiseTable[NOISE_SIZE];
  10. static int noisePerm[NOISE_SIZE];
  11. static void CM_NoiseInit( CCMLandScape *landscape )
  12. {
  13. int i;
  14. for ( i = 0; i < NOISE_SIZE; i++ )
  15. {
  16. noiseTable[i] = landscape->flrand(-1.0f, 1.0f);
  17. noisePerm[i] = (byte)landscape->irand(0, 255);
  18. }
  19. }
  20. #define VAL( a ) noisePerm[ ( a ) & ( NOISE_MASK )]
  21. #define INDEX( x, y, z, t ) VAL( x + VAL( y + VAL( z + VAL( t ) ) ) )
  22. #define LERP( a, b, w ) ( a * ( 1.0f - w ) + b * w )
  23. static float GetNoiseValue( int x, int y, int z, int t )
  24. {
  25. int index = INDEX( ( int ) x, ( int ) y, ( int ) z, ( int ) t );
  26. return noiseTable[index];
  27. }
  28. #if 0
  29. static float GetNoiseTime( int t )
  30. {
  31. int index = VAL( t );
  32. return (1 + noiseTable[index]);
  33. }
  34. #endif
  35. static float CM_NoiseGet4f( float x, float y, float z, float t )
  36. {
  37. int i;
  38. int ix, iy, iz, it;
  39. float fx, fy, fz, ft;
  40. float front[4];
  41. float back[4];
  42. float fvalue, bvalue, value[2], finalvalue;
  43. ix = ( int ) floor( x );
  44. fx = x - ix;
  45. iy = ( int ) floor( y );
  46. fy = y - iy;
  47. iz = ( int ) floor( z );
  48. fz = z - iz;
  49. it = ( int ) floor( t );
  50. ft = t - it;
  51. for ( i = 0; i < 2; i++ )
  52. {
  53. front[0] = GetNoiseValue( ix, iy, iz, it + i );
  54. front[1] = GetNoiseValue( ix+1, iy, iz, it + i );
  55. front[2] = GetNoiseValue( ix, iy+1, iz, it + i );
  56. front[3] = GetNoiseValue( ix+1, iy+1, iz, it + i );
  57. back[0] = GetNoiseValue( ix, iy, iz + 1, it + i );
  58. back[1] = GetNoiseValue( ix+1, iy, iz + 1, it + i );
  59. back[2] = GetNoiseValue( ix, iy+1, iz + 1, it + i );
  60. back[3] = GetNoiseValue( ix+1, iy+1, iz + 1, it + i );
  61. fvalue = LERP( LERP( front[0], front[1], fx ), LERP( front[2], front[3], fx ), fy );
  62. bvalue = LERP( LERP( back[0], back[1], fx ), LERP( back[2], back[3], fx ), fy );
  63. value[i] = LERP( fvalue, bvalue, fz );
  64. }
  65. finalvalue = LERP( value[0], value[1], ft );
  66. return finalvalue;
  67. }
  68. /****** lincrv.c ******/
  69. /* Ken Shoemake, 1994 */
  70. /* Perform a generic vector unary operation. */
  71. #define V_Op(vdst,gets,vsrc,n) {register int V_i;\
  72. for(V_i=(n)-1;V_i>=0;V_i--) (vdst)[V_i] gets ((vsrc)[V_i]);}
  73. static void lerp(float t, float a0, float a1, vec4_t p0, vec4_t p1, int m, vec4_t p)
  74. {
  75. register float t0=(a1-t)/(a1-a0), t1=1-t0;
  76. register int i;
  77. for (i=m-1; i>=0; i--) p[i] = t0*p0[i] + t1*p1[i];
  78. }
  79. /* DialASpline(t,a,p,m,n,work,Cn,interp,val) computes a point val at parameter
  80. t on a spline with knot values a and control points p. The curve will have
  81. Cn continuity, and if interp is TRUE it will interpolate the control points.
  82. Possibilities include Langrange interpolants, Bezier curves, Catmull-Rom
  83. interpolating splines, and B-spline curves. Points have m coordinates, and
  84. n+1 of them are provided. The work array must have room for n+1 points.
  85. */
  86. static int DialASpline(float t, float a[], vec4_t p[], int m, int n, vec4_t work[],
  87. unsigned int Cn, bool interp, vec4_t val)
  88. {
  89. register int i, j, k, h, lo, hi;
  90. if (Cn>n-1) Cn = n-1; /* Anything greater gives one polynomial */
  91. for (k=0; t> a[k]; k++); /* Find enclosing knot interval */
  92. for (h=k; t==a[k]; k++); /* May want to use fewer legs */
  93. if (k>n) {k = n; if (h>k) h = k;}
  94. h = 1+Cn - (k-h); k--;
  95. lo = k-Cn; hi = k+1+Cn;
  96. if (interp) { /* Lagrange interpolation steps */
  97. int drop=0;
  98. if (lo<0) {lo = 0; drop += Cn-k;
  99. if (hi-lo<Cn) {drop += Cn-hi; hi = Cn;}}
  100. if (hi>n) {hi = n; drop += k+1+Cn-n;
  101. if (hi-lo<Cn) {drop += lo-(n-Cn); lo = n-Cn;}}
  102. for (i=lo; i<=hi; i++) V_Op(work[i],=,p[i],m);
  103. for (j=1; j<=Cn; j++) {
  104. for (i=lo; i<=hi-j; i++) {
  105. lerp(t,a[i],a[i+j],work[i],work[i+1],m,work[i]);
  106. }
  107. }
  108. h = 1+Cn-drop;
  109. } else { /* Prepare for B-spline steps */
  110. if (lo<0) {h += lo; lo = 0;}
  111. for (i=lo; i<=lo+h; i++) V_Op(work[i],=,p[i],m);
  112. if (h<0) h = 0;
  113. }
  114. for (j=0; j<h; j++) {
  115. int tmp = 1+Cn-j;
  116. for (i=h-1; i>=j; i--) {
  117. lerp(t,a[lo+i],a[lo+i+tmp],work[lo+i],work[lo+i+1],m,work[lo+i+1]);
  118. }
  119. }
  120. V_Op(val,=,work[lo+h],m);
  121. return (k);
  122. }
  123. #define BIG (1.0e12)
  124. static vec_t Vector2Normalize( vec2_t v )
  125. {
  126. float length, ilength;
  127. length = v[0]*v[0] + v[1]*v[1];
  128. length = sqrt (length);
  129. if ( length )
  130. {
  131. ilength = 1/length;
  132. v[0] *= ilength;
  133. v[1] *= ilength;
  134. }
  135. return length;
  136. }
  137. CPathInfo::CPathInfo(CCMLandScape *landscape, int numPoints, float bx, float by, float ex, float ey,
  138. float minWidth, float maxWidth, float depth, float deviation, float breadth,
  139. CPathInfo *Connected, unsigned CreationFlags) :
  140. mNumPoints(numPoints),
  141. mMinWidth(minWidth),
  142. mMaxWidth(maxWidth),
  143. mDepth(depth),
  144. mDeviation(deviation),
  145. mBreadth(breadth)
  146. {
  147. int i, numConnected, index;
  148. float position, goal, deltaGoal;
  149. // float random, delta;
  150. bool horizontal;
  151. float *point;
  152. float currentWidth;
  153. float currentPosition;
  154. vec2_t testPoint, percPoint, diffPoint, normalizedPath;
  155. float distance, length;
  156. CreateCircle();
  157. numConnected = -1;
  158. if (Connected)
  159. { // we are connecting to an existing spline
  160. numConnected = Connected->GetNumPoints();
  161. if (numConnected >= SPLINE_MERGE_SIZE)
  162. { // plenty of points to choose from
  163. mNumPoints += SPLINE_MERGE_SIZE;
  164. }
  165. else
  166. { // the existing spline doesn't have enough points
  167. mNumPoints += numConnected;
  168. }
  169. }
  170. mPoints = (vec4_t *)malloc(sizeof(vec4_t) * mNumPoints);
  171. mWork = (vec4_t *)malloc(sizeof(vec4_t) * (mNumPoints+1));
  172. mWeights = (vec_t *)malloc(sizeof(vec_t) * (mNumPoints+1));
  173. length = sqrt((ex-bx)*(ex-bx) + (ey-by)*(ey-by));
  174. if (fabs(ex - bx) >= fabs(ey - by))
  175. { // this appears to be a horizontal path
  176. mInc = 1.0 / fabs(ex - bx);
  177. horizontal = true;
  178. position = by;
  179. goal = ey;
  180. deltaGoal = (ey-by) / (numPoints-1);
  181. }
  182. else
  183. { // this appears to be a vertical path
  184. mInc = 1.0 / fabs(ey - by);
  185. horizontal = false;
  186. position = bx;
  187. goal = ex;
  188. deltaGoal = (ex-bx) / (numPoints-1);
  189. }
  190. normalizedPath[0] = (ex-bx);
  191. normalizedPath[1] = (ey-by);
  192. Vector2Normalize(normalizedPath);
  193. // approx calculate how much we need to iterate through the spline to hit every point
  194. mInc /= 16;
  195. currentWidth = landscape->flrand(minWidth, maxWidth);
  196. currentPosition = 0.0;
  197. for(i=0;i<mNumPoints;i++)
  198. {
  199. // weights are evenly distributed
  200. mWeights[i] = (float)i / (mNumPoints-1);
  201. if (i < numConnected && i < SPLINE_MERGE_SIZE)
  202. { // we are connecting to an existing spline, so copy over the first few points
  203. if (CreationFlags & PATH_CREATION_CONNECT_FRONT)
  204. { // copy from the front
  205. index = i;
  206. }
  207. else
  208. { // copy from the end
  209. index = numConnected-SPLINE_MERGE_SIZE+i;
  210. }
  211. point = Connected->GetPoint(index);
  212. mPoints[i][0] = point[0];
  213. mPoints[i][1] = point[1];
  214. mPoints[i][3] = point[3];
  215. }
  216. else
  217. {
  218. if (horizontal)
  219. { // we appear to be going horizontal, so spread the randomness across the vertical
  220. mPoints[i][0] = ((ex - bx) * currentPosition) + bx;
  221. mPoints[i][1] = position;
  222. }
  223. else
  224. { // we appear to be going vertical, so spread the randomness across the horizontal
  225. mPoints[i][0] = position;
  226. mPoints[i][1] = ((ey - by) * currentPosition) + by;
  227. }
  228. currentPosition += 1.0 / (numPoints-1);
  229. // set the width of the spline
  230. mPoints[i][3] = currentWidth;
  231. currentWidth += landscape->flrand(-0.1f, 0.1f);
  232. if (currentWidth < minWidth)
  233. {
  234. currentWidth = minWidth;
  235. }
  236. else if (currentWidth > maxWidth)
  237. {
  238. currentWidth = maxWidth;
  239. }
  240. // see how far we are from the goal
  241. /* delta = (goal - position) * currentPosition;
  242. // calculate the randomness we are allowed at this place
  243. random = landscape->flrand(-mDeviation/1.0, mDeviation/1.0) * (1.0 - currentPosition);
  244. position += delta + random;*/
  245. if (i == mNumPoints-2)
  246. { // -2 because we are calculating for the next point
  247. position = goal;
  248. }
  249. else
  250. {
  251. if (i == 0)
  252. {
  253. position += deltaGoal + landscape->flrand(-mDeviation/10.0, mDeviation/10.0);
  254. }
  255. else
  256. {
  257. position += deltaGoal + landscape->flrand(-mDeviation*1.5, mDeviation*1.5);
  258. }
  259. }
  260. if (position > 0.9)
  261. { // too far over, so move back a bit
  262. position = 0.9 - landscape->flrand(0.02f, 0.1f);
  263. }
  264. if (position < 0.1)
  265. { // too near, so move bakc a bit
  266. position = 0.1 + landscape->flrand(0.02f, 0.1f);
  267. }
  268. // check our deviation from the straight line to the end
  269. if (horizontal)
  270. {
  271. testPoint[0] = ((ex - bx) * currentPosition) + bx;
  272. testPoint[1] = position;
  273. }
  274. else
  275. {
  276. testPoint[0] = position;
  277. testPoint[1] = ((ey - by) * currentPosition) + by;
  278. }
  279. // dot product of the normal of the path to the point we are at
  280. distance = ((testPoint[0]-bx)*normalizedPath[0]) + ((testPoint[1]-by)*normalizedPath[1]);
  281. // find the perpendicular place that is intersected by the point and the path
  282. percPoint[0] = (distance * normalizedPath[0]) + bx;
  283. percPoint[1] = (distance * normalizedPath[1]) + by;
  284. // calculate the difference between the perpendicular point and the test point
  285. diffPoint[0] = testPoint[0] - percPoint[0];
  286. diffPoint[1] = testPoint[1] - percPoint[1];
  287. // calculate the distance
  288. distance = sqrt((diffPoint[0]*diffPoint[0]) + (diffPoint[1]*diffPoint[1]));
  289. if (distance > mDeviation)
  290. { // we are beyond our allowed deviation, so head back
  291. if (horizontal)
  292. {
  293. position = (ey-by) * currentPosition + by;
  294. }
  295. else
  296. {
  297. position = (ex-bx) * currentPosition + bx;
  298. }
  299. position += landscape->flrand(-mDeviation/2.0, mDeviation/2.0);
  300. }
  301. }
  302. }
  303. mWeights[mNumPoints] = (float)BIG;
  304. }
  305. CPathInfo::~CPathInfo(void)
  306. {
  307. free(mWeights);
  308. free(mWork);
  309. free(mPoints);
  310. }
  311. void CPathInfo::CreateCircle(void)
  312. {
  313. int x, y;
  314. float r, d;
  315. memset(mCircleStamp, 0, sizeof(mCircleStamp));
  316. r = CIRCLE_STAMP_SIZE;
  317. for(x=0;x<CIRCLE_STAMP_SIZE;x++)
  318. {
  319. for(y=0;y<CIRCLE_STAMP_SIZE;y++)
  320. {
  321. d = sqrt((float)(x*x + y*y));
  322. if (d > r)
  323. {
  324. mCircleStamp[y][x] = 255;
  325. }
  326. else
  327. {
  328. mCircleStamp[y][x] = pow(sin((float)(d / r * M_PI / 2)), mBreadth) * 255;
  329. }
  330. }
  331. }
  332. }
  333. void CPathInfo::Stamp(int x, int y, int size, int depth, unsigned char *Data, int DataWidth, int DataHeight)
  334. {
  335. // int xPos;
  336. // float yPos;
  337. int dx, dy, fx, fy;
  338. float offset;
  339. byte value;
  340. byte invDepth;
  341. offset = (float)(CIRCLE_STAMP_SIZE-1) / size;
  342. invDepth = 255-depth;
  343. for(dx = -size; dx <= size; dx++)
  344. {
  345. for ( dy = -size; dy <= size; dy ++ )
  346. {
  347. float d;
  348. d = dx * dx + dy * dy ;
  349. if ( d > size * size )
  350. {
  351. continue;
  352. }
  353. fx = x + dx;
  354. if (fx < 2 || fx > DataWidth-2)
  355. {
  356. continue;
  357. }
  358. fy = y + dy;
  359. if (fy < 2 || fy > DataHeight-2)
  360. {
  361. continue;
  362. }
  363. value = pow ( sin ( (float)(d / (size * size) * M_PI / 2)), mBreadth ) * invDepth + depth;
  364. if (value < Data[(fy * DataWidth) + fx])
  365. {
  366. Data[(fy * DataWidth) + fx] = value;
  367. }
  368. }
  369. }
  370. /*
  371. fx = x + dx;
  372. if (fx < 2 || fx > DataWidth-2)
  373. {
  374. continue;
  375. }
  376. xPos = abs((int)(dx*offset));
  377. yPos = offset*size + offset;
  378. for(dy = -size; dy < 0; dy++)
  379. {
  380. yPos -= offset;
  381. fy = y + dy;
  382. if (fy < 2 || fy > DataHeight-2)
  383. {
  384. continue;
  385. }
  386. value = (invDepth * mCircleStamp[(int)yPos][xPos] / 256) + depth;
  387. if (value < Data[(fy * DataWidth) + fx])
  388. {
  389. Data[(fy * DataWidth) + fx] = value;
  390. }
  391. }
  392. yPos = -offset;
  393. for(; dy <= size; dy++)
  394. {
  395. yPos += offset;
  396. fy = y + dy;
  397. if (fy < 2 || fy > DataHeight-2)
  398. {
  399. continue;
  400. }
  401. value = (invDepth * mCircleStamp[(int)yPos][xPos] / 256) + depth;
  402. if (value < Data[(fy * DataWidth) + fx])
  403. {
  404. Data[(fy * DataWidth) + fx] = value;
  405. }
  406. }
  407. }
  408. */
  409. }
  410. void CPathInfo::GetInfo(float PercentInto, vec4_t Coord, vec4_t Vector)
  411. {
  412. vec4_t before, after;
  413. float testPercent;
  414. DialASpline(PercentInto, mWeights, mPoints, sizeof(vec4_t) / sizeof(vec_t), mNumPoints-1, mWork, 2, true, Coord);
  415. testPercent = PercentInto - 0.01;
  416. if (testPercent < 0)
  417. {
  418. testPercent = 0;
  419. }
  420. DialASpline(testPercent, mWeights, mPoints, sizeof(vec4_t) / sizeof(vec_t), mNumPoints-1, mWork, 2, true, before);
  421. testPercent = PercentInto + 0.01;
  422. if (testPercent > 1.0)
  423. {
  424. testPercent = 1.0;
  425. }
  426. DialASpline(testPercent, mWeights, mPoints, sizeof(vec4_t) / sizeof(vec_t), mNumPoints-1, mWork, 2, true, after);
  427. Coord[2] = mDepth;
  428. Vector[0] = after[0] - before[0];
  429. Vector[1] = after[1] - before[1];
  430. }
  431. void CPathInfo::DrawPath(unsigned char *Data, int DataWidth, int DataHeight )
  432. {
  433. float t;
  434. vec4_t val, vector;//, perp;
  435. int size;
  436. float inc;
  437. int x, y, lastX, lastY;
  438. float depth;
  439. inc = mInc / DataWidth;
  440. lastX = lastY = -999;
  441. for (t=0.0; t<=1.0; t+=inc)
  442. {
  443. GetInfo(t, val, vector);
  444. /* perp[0] = -vector[1];
  445. perp[1] = vector[0];
  446. if (fabs(perp[0]) > fabs(perp[1]))
  447. {
  448. perp[1] /= fabs(perp[0]);
  449. perp[0] /= fabs(perp[0]);
  450. }
  451. else
  452. {
  453. perp[0] /= fabs(perp[1]);
  454. perp[1] /= fabs(perp[1]);
  455. }
  456. */
  457. x = val[0] * DataWidth;
  458. y = val[1] * DataHeight;
  459. if (x == lastX && y == lastY)
  460. {
  461. continue;
  462. }
  463. lastX = x;
  464. lastY = y;
  465. size = val[3] * DataWidth;
  466. depth = mDepth * 255.0f;
  467. Stamp(x, y, size, (int)depth, Data, DataWidth, DataHeight);
  468. }
  469. }
  470. CRandomTerrain::CRandomTerrain(void)
  471. {
  472. memset(mPaths, 0, sizeof(mPaths));
  473. }
  474. CRandomTerrain::~CRandomTerrain(void)
  475. {
  476. Shutdown();
  477. }
  478. void CRandomTerrain::Init(CCMLandScape *landscape, byte *grid, int width, int height)
  479. {
  480. Shutdown();
  481. mLandScape = landscape;
  482. mWidth = width;
  483. mHeight = height;
  484. mArea = mWidth * mHeight;
  485. mBorder = (width + height) >> 6;
  486. mGrid = grid;
  487. }
  488. void CRandomTerrain::ClearPaths(void)
  489. {
  490. int i;
  491. for(i=0;i<MAX_RANDOM_PATHS;i++)
  492. {
  493. if (mPaths[i])
  494. {
  495. delete mPaths[i];
  496. mPaths[i] = 0;
  497. }
  498. }
  499. memset(mPaths, 0, sizeof(mPaths));
  500. }
  501. void CRandomTerrain::Shutdown(void)
  502. {
  503. ClearPaths ( );
  504. }
  505. bool CRandomTerrain::CreatePath(int PathID, int ConnectedID, unsigned CreationFlags, int numPoints,
  506. float bx, float by, float ex, float ey,
  507. float minWidth, float maxWidth, float depth, float deviation, float breadth )
  508. {
  509. CPathInfo *connected = 0;
  510. if (PathID < 0 || PathID >= MAX_RANDOM_PATHS || mPaths[PathID])
  511. {
  512. return false;
  513. }
  514. if (ConnectedID >= 0 && ConnectedID < MAX_RANDOM_PATHS)
  515. {
  516. connected = mPaths[ConnectedID];
  517. }
  518. mPaths[PathID] = new CPathInfo(mLandScape, numPoints, bx, by, ex, ey,
  519. minWidth, maxWidth, depth, deviation, breadth,
  520. connected, CreationFlags );
  521. return true;
  522. }
  523. bool CRandomTerrain::GetPathInfo(int PathNum, float PercentInto, vec4_t Coord, vec4_t Vector)
  524. {
  525. if (PathNum < 0 || PathNum >= MAX_RANDOM_PATHS || !mPaths[PathNum])
  526. {
  527. return false;
  528. }
  529. mPaths[PathNum]->GetInfo(PercentInto, Coord, Vector);
  530. return true;
  531. }
  532. void CRandomTerrain::ParseGenerate(const char *GenerateFile)
  533. {
  534. }
  535. void CRandomTerrain::Smooth ( void )
  536. {
  537. // Scale down to 1/4 size then back up to smooth out the terrain
  538. byte *temp;
  539. int x, y, o;
  540. temp = mLandScape->GetFlattenMap ( );
  541. // Copy over anything in the flatten map
  542. if (temp)
  543. {
  544. for ( o = 0; o < mHeight * mWidth; o++)
  545. {
  546. if ( temp[o] > 0 )
  547. {
  548. mGrid[o] = (byte)temp[o] & 0x7F;
  549. }
  550. }
  551. }
  552. temp = (byte *)Z_Malloc(mWidth * mHeight, TAG_TEMP_WORKSPACE, qfalse);
  553. #if 1
  554. unsigned total, count;
  555. for(x=1;x<mWidth-1;x++)
  556. {
  557. for(y=1;y<mHeight-1;y++)
  558. {
  559. total = 0;
  560. count = 2;
  561. // Left
  562. total += mGrid[((y)*mWidth)+(x-1)];
  563. count++;
  564. // Right
  565. total += mGrid[((y)*mWidth)+(x+1)];
  566. count++;
  567. // Up
  568. total += mGrid[((y-1)*mWidth)+(x)];
  569. count++;
  570. // Down
  571. total += mGrid[((y+1)*mWidth)+(x)];
  572. count++;
  573. // Up-Left
  574. total += mGrid[((y-1)*mWidth)+(x-1)];
  575. count++;
  576. // Down-Left
  577. total += mGrid[((y+1)*mWidth)+(x-1)];
  578. count++;
  579. // Up-Right
  580. total += mGrid[((y-1)*mWidth)+(x+1)];
  581. count++;
  582. // Down-Right
  583. total += mGrid[((y+1)*mWidth)+(x+1)];
  584. count++;
  585. total += (unsigned)mGrid[((y)*mWidth)+(x)] * 2;
  586. temp[((y)*mWidth)+(x)] = total / count;
  587. }
  588. }
  589. memcpy(mGrid, temp, mWidth * mHeight);
  590. #else
  591. float smoothKernel[FILTER_SIZE][FILTER_SIZE];
  592. int xx, yy, dx, dy;
  593. float total, num;
  594. R_Resample(mGrid, mWidth, mHeight, temp, mWidth >> 1, mHeight >> 1, 1);
  595. R_Resample(temp, mWidth >> 1, mHeight >> 1, mGrid, mWidth, mHeight, 1);
  596. // now lets filter it.
  597. memcpy(temp, mGrid, mWidth * mHeight);
  598. for (dy = -KERNEL_SIZE; dy <= KERNEL_SIZE; dy++)
  599. {
  600. for (dx = -KERNEL_SIZE; dx <= KERNEL_SIZE; dx++)
  601. {
  602. smoothKernel[dy + KERNEL_SIZE][dx + KERNEL_SIZE] =
  603. 1.0f / (1.0f + fabs(float(dx) * float(dx) * float(dx)) + fabs(float(dy) * float(dy) * float(dy)));
  604. }
  605. }
  606. for (y = 0; y < mHeight; y++)
  607. {
  608. for (x = 0; x < mWidth; x++)
  609. {
  610. total = 0.0f;
  611. num = 0.0f;
  612. for (dy = -KERNEL_SIZE; dy <= KERNEL_SIZE; dy++)
  613. {
  614. for (dx = -KERNEL_SIZE; dx <= KERNEL_SIZE; dx++)
  615. {
  616. xx = x + dx;
  617. if (xx >= 0 && xx < mWidth)
  618. {
  619. yy = y + dy;
  620. if (yy >= 0 && yy < mHeight)
  621. {
  622. total += smoothKernel[dy + KERNEL_SIZE][dx + KERNEL_SIZE] * (float)temp[yy * mWidth + xx];
  623. num += smoothKernel[dy + KERNEL_SIZE][dx + KERNEL_SIZE];
  624. }
  625. }
  626. }
  627. }
  628. total /= num;
  629. mGrid[y * mWidth + x] = (byte)Com_Clamp(0, 255, (int)Round(total));
  630. }
  631. }
  632. #endif
  633. Z_Free(temp);
  634. /* Uncomment to see the symmetry line on the map
  635. for ( x = 0; x < mWidth; x ++ )
  636. {
  637. mGrid[x * mWidth + x] = 255;
  638. }
  639. */
  640. }
  641. void CRandomTerrain::Generate(int symmetric)
  642. {
  643. int i,j;
  644. // Clear out all existing data
  645. memset(mGrid, 255, mArea);
  646. // make landscape a little bumpy
  647. float t1 = mLandScape->flrand(0, 2);
  648. float t2 = mLandScape->flrand(0, 2);
  649. float t3 = mLandScape->flrand(0, 2);
  650. CM_NoiseInit(mLandScape);
  651. int x, y;
  652. for (y = 0; y < mHeight; y++)
  653. for (x = 0; x < mWidth; x++)
  654. {
  655. i = x + y*mWidth;
  656. byte val = (byte)Com_Clamp(0, 255, (int)(220 + (CM_NoiseGet4f( x*0.25, y*0.25, 0, t3 ) * 20)) + (CM_NoiseGet4f( x*0.5, y*0.5, 0, t2 ) * 15));
  657. mGrid[i] = val;
  658. }
  659. for ( i = 0; mPaths[i] != 0; i ++ )
  660. {
  661. mPaths[i]->DrawPath(mGrid, mWidth, mHeight);
  662. }
  663. for (y = 0; y < mHeight; y++)
  664. for (x = 0; x < mWidth; x++)
  665. {
  666. i = x + y*mWidth;
  667. byte val = (byte)Com_Clamp(0, 255, (int)(mGrid[i] + (CM_NoiseGet4f( x, y, 0, t1 ) * 5)));
  668. mGrid[i] = val;
  669. }
  670. // if symmetric, do this now
  671. if (symmetric)
  672. {
  673. assert (mWidth == mHeight); // must be square
  674. for (y = 0; y < mHeight; y++)
  675. for (x = 0; x < (mWidth-y); x++)
  676. {
  677. i = x + y*mWidth;
  678. j = (mWidth-1 - x) + (mHeight-1 - y)*mWidth;
  679. byte val = mGrid[i] < mGrid[j] ? mGrid[i] : mGrid[j];
  680. mGrid[i] = mGrid[j] = val;
  681. }
  682. }
  683. }
  684. typedef enum
  685. {
  686. RMG_CP_NONE = -1,
  687. RMG_CP_CONSONANT,
  688. RMG_CP_COMPLEX_CONSONANT,
  689. RMG_CP_VOWEL,
  690. RMG_CP_COMPLEX_VOWEL,
  691. RMG_CP_ENDING,
  692. RMG_CP_NUM_PIECES,
  693. } ECPType;
  694. typedef struct SCharacterPiece
  695. {
  696. char *mPiece;
  697. int mCommonality;
  698. } TCharacterPiece;
  699. static TCharacterPiece Consonants[] =
  700. {
  701. { "b", 6 },
  702. { "c", 8 },
  703. { "d", 6 },
  704. { "f", 5 },
  705. { "g", 4 },
  706. { "h", 5 },
  707. { "j", 2 },
  708. { "k", 4 },
  709. { "l", 4 },
  710. { "m", 7 },
  711. { "n", 7 },
  712. { "r", 6 },
  713. { "s", 10 },
  714. { "t", 10 },
  715. { "v", 1 },
  716. { "w", 2 },
  717. { "x", 1 },
  718. { "z", 1 },
  719. { 0, 0 }
  720. };
  721. static TCharacterPiece ComplexConsonants[] =
  722. {
  723. { "st", 10 },
  724. { "ck", 10 },
  725. { "ss", 10 },
  726. { "tt", 7 },
  727. { "ll", 8 },
  728. { "nd", 10 },
  729. { "rn", 6 },
  730. { "nc", 6 },
  731. { "mp", 4 },
  732. { "sc", 10 },
  733. { "sl", 10 },
  734. { "tch", 6 },
  735. { "th", 4 },
  736. { "rn", 5 },
  737. { "cl", 10 },
  738. { "sp", 10 },
  739. { "st", 10 },
  740. { "fl", 4 },
  741. { "sh", 7 },
  742. { "ng", 4 },
  743. // { "" },
  744. { 0, 0 }
  745. };
  746. static TCharacterPiece Vowels[] =
  747. {
  748. { "a", 10 },
  749. { "e", 10 },
  750. { "i", 10 },
  751. { "o", 10 },
  752. { "u", 2 },
  753. // { "" },
  754. { 0, 0 }
  755. };
  756. static TCharacterPiece ComplexVowels[] =
  757. {
  758. { "ea", 10 },
  759. { "ue", 3 },
  760. { "oi", 10 },
  761. { "ai", 8 },
  762. { "oo", 10 },
  763. { "io", 10 },
  764. { "oe", 10 },
  765. { "au", 3 },
  766. { "ee", 7 },
  767. { "ei", 7 },
  768. { "ou", 7 },
  769. { "ia", 4 },
  770. // { "" },
  771. { 0, 0 }
  772. };
  773. static TCharacterPiece Endings[] =
  774. {
  775. { "ing", 10 },
  776. { "ed", 10 },
  777. { "ute", 10 },
  778. { "ance", 10 },
  779. { "ey", 10 },
  780. { "ation", 10 },
  781. { "ous", 10 },
  782. { "ent", 10 },
  783. { "ate", 10 },
  784. { "ible", 10 },
  785. { "age", 10 },
  786. { "ity", 10 },
  787. { "ist", 10 },
  788. { "ism", 10 },
  789. { "ime", 10 },
  790. { "ic", 10 },
  791. { "ant", 10 },
  792. { "etry", 10 },
  793. { "ious", 10 },
  794. { "ative", 10 },
  795. { "er", 10 },
  796. { "ize", 10 },
  797. { "able", 10 },
  798. { "itude", 10 },
  799. // { "" },
  800. { 0, 0 }
  801. };
  802. static void FindPiece(ECPType type, char *&pos)
  803. {
  804. TCharacterPiece *search, *start;
  805. int count = 0;
  806. switch(type)
  807. {
  808. case RMG_CP_CONSONANT:
  809. default:
  810. start = Consonants;
  811. break;
  812. case RMG_CP_COMPLEX_CONSONANT:
  813. start = ComplexConsonants;
  814. break;
  815. case RMG_CP_VOWEL:
  816. start = Vowels;
  817. break;
  818. case RMG_CP_COMPLEX_VOWEL:
  819. start = ComplexVowels;
  820. break;
  821. case RMG_CP_ENDING:
  822. start = Endings;
  823. break;
  824. }
  825. search = start;
  826. while(search->mPiece)
  827. {
  828. count += search->mCommonality;
  829. search++;
  830. }
  831. count = Q_irand(0, count-1);
  832. search = start;
  833. while(count > search->mCommonality)
  834. {
  835. count -= search->mCommonality;
  836. search++;
  837. }
  838. strcpy(pos, search->mPiece);
  839. pos += strlen(search->mPiece);
  840. }
  841. unsigned RMG_CreateSeed(char *TextSeed)
  842. {
  843. int Length;
  844. char Ending[256], *pos;
  845. int ComplexVowelChance, ComplexConsonantChance;
  846. ECPType LookingFor;
  847. unsigned SeedValue = 0, high;
  848. Length = Q_irand(4, 9);
  849. if (Q_irand(0, 100) < 20)
  850. {
  851. LookingFor = RMG_CP_VOWEL;
  852. }
  853. else
  854. {
  855. LookingFor = RMG_CP_CONSONANT;
  856. }
  857. Ending[0] = 0;
  858. if (Q_irand(0, 100) < 55)
  859. {
  860. pos = Ending;
  861. FindPiece(RMG_CP_ENDING, pos);
  862. Length -= (pos - Ending);
  863. }
  864. pos = TextSeed;
  865. *pos = 0;
  866. ComplexVowelChance = -1;
  867. ComplexConsonantChance = -1;
  868. while((pos - TextSeed) < Length || LookingFor == RMG_CP_CONSONANT)
  869. {
  870. if (LookingFor == RMG_CP_VOWEL)
  871. {
  872. if (Q_irand(0, 100) < ComplexVowelChance)
  873. {
  874. ComplexVowelChance = -1;
  875. LookingFor = RMG_CP_COMPLEX_VOWEL;
  876. }
  877. else
  878. {
  879. ComplexVowelChance += 10;
  880. }
  881. FindPiece(LookingFor, pos);
  882. LookingFor = RMG_CP_CONSONANT;
  883. }
  884. else
  885. {
  886. if (Q_irand(0, 100) < ComplexConsonantChance)
  887. {
  888. ComplexConsonantChance = -1;
  889. LookingFor = RMG_CP_COMPLEX_CONSONANT;
  890. }
  891. else
  892. {
  893. ComplexConsonantChance += 45;
  894. }
  895. FindPiece(LookingFor, pos);
  896. LookingFor = RMG_CP_VOWEL;
  897. }
  898. }
  899. if (Ending[0])
  900. {
  901. strcpy(pos, Ending);
  902. }
  903. pos = TextSeed;
  904. while(*pos)
  905. {
  906. high = SeedValue >> 28;
  907. SeedValue ^= (SeedValue << 4) + ((*pos)-'a');
  908. SeedValue ^= high;
  909. pos++;
  910. }
  911. return SeedValue;
  912. }