001package com.fs.starfarer.api.impl.campaign.velfield; 002 003import java.awt.Color; 004import java.util.ArrayList; 005import java.util.List; 006import java.util.Random; 007 008import org.lwjgl.opengl.GL11; 009import org.lwjgl.util.vector.Vector2f; 010 011import com.fs.starfarer.api.Global; 012import com.fs.starfarer.api.impl.campaign.velfield.SlipstreamTerrainPlugin2.SlipstreamParams2; 013import com.fs.starfarer.api.impl.campaign.velfield.SlipstreamTerrainPlugin2.SlipstreamSegment; 014import com.fs.starfarer.api.util.Misc; 015import com.fs.starfarer.api.util.WeightedRandomPicker; 016 017public class SlipstreamBuilder { 018 019 public static enum StreamType { 020 NORMAL, 021 WIDE, 022 NARROW, 023 } 024 025 public static float MIN_SPACING = 200f; 026 public static float MAX_SPACING = 400f; 027 public static float WIDTH_TO_SPACING_MULT = 0.2f; 028 029 public static float MIN_NARROW = 0.5f; 030 public static float MAX_NARROW = 0.7f; 031 public static float MIN_NORMAL = 0.9f; 032 public static float MAX_NORMAL = 1.1f; 033 public static float MIN_WIDE = 1.3f; 034 public static float MAX_WIDE = 1.6f; 035 public static float MIN_VERY_WIDE = 1.8f; 036 public static float MAX_VERY_WIDE = 2.1f; 037 038 public static float WCHANGE_SLOW_T_MULT_MIN = 1f; 039 public static float WCHANGE_SLOW_T_MULT_MIN_MAX = 1.2f; 040 public static float WCHANGE_MEDIUM_T_MULT_MIN = 1.8f; 041 public static float WCHANGE_MEDIUM_T_MULT_MAX = 2.2f; 042 public static float WCHANGE_FAST_T_MULT_MIN = 2.8f; 043 public static float WCHANGE_FAST_T_MULT_MAX = 3.2f; 044 045 public static float WRAND_NONE_MULT = 0f; 046 public static float WRAND_LOW_MULT = 0.1f; 047 public static float WRAND_MEDIUM_MULT = 0.17f; 048 public static float WRAND_HIGH_MULT = 0.25f; 049 050 public static float FLUCT_LENGTH_MIN = 2500f; 051 public static float FLUCT_LENGTH_MAX = 4000f; 052 public static float FLUCT_MAG_MIN = 350f; 053 public static float FLUCT_MAG_MAX = 500f; 054 055 public static float FLUCT_NONE_MULT = 0f; 056 public static float FLUCT_LOW_MULT = 0.33f; 057 public static float FLUCT_MEDIUM_MULT = 0.67f; 058 public static float FLUCT_HIGH_MULT = 1f; 059 060 061 // width 062 public static final int WIDTH_NARROW = 0; 063 public static final int WIDTH_NORMAL = 1; 064 public static final int WIDTH_WIDE = 2; 065 public static final int WIDTH_VERY_WIDE = 3; 066 067 // width randomness 068 public static final int WRAND_NONE = 0; 069 public static final int WRAND_LOW = 1; 070 public static final int WRAND_MEDIUM = 2; 071 public static final int WRAND_HIGH = 3; 072 073 // how quickly width changes 074 public static final int WCHANGE_SLOW = 0; 075 public static final int WCHANGE_MEDIUM = 1; 076 public static final int WCHANGE_FAST = 2; 077 078 // path fluctuations within sections 079 public static final int FLUCT_NONE = 0; 080 public static final int FLUCT_LOW = 1; 081 public static final int FLUCT_MEDIUM = 2; 082 public static final int FLUCT_HIGH = 3; 083 084 085 public void initTransitionMatrices() { 086 widthTM = new float[4][4]; 087 if (type == StreamType.NORMAL) { 088 widthTM[WIDTH_NARROW] = new float [] {0.5f, 1f, 0.4f, 0.3f}; 089 widthTM[WIDTH_NORMAL] = new float [] { 1f, 1f, 0.5f, 0.3f}; 090 widthTM[WIDTH_WIDE] = new float [] {0.7f, 1f, 0.2f, 0.5f}; 091 widthTM[WIDTH_VERY_WIDE] = new float [] { 1f, 1f, 1f, 0.1f}; 092 } else if (type == StreamType.WIDE) { 093 widthTM[WIDTH_NARROW] = new float [] {0.1f, 1f, 0.6f, 0.4f}; 094 widthTM[WIDTH_NORMAL] = new float [] {0.2f, 0.5f, 1f, 0.5f}; 095 widthTM[WIDTH_WIDE] = new float [] {0.1f, 0.5f, 1f, 0.5f}; 096 widthTM[WIDTH_VERY_WIDE] = new float [] { 0f, 0.5f, 1f, 0.3f}; 097 } else if (type == StreamType.NARROW) { 098 widthTM[WIDTH_NARROW] = new float [] {0.8f, 1f, 0.3f, 0.1f}; 099 widthTM[WIDTH_NORMAL] = new float [] { 1f, 1f, 0.3f, 0.1f}; 100 widthTM[WIDTH_WIDE] = new float [] {0.7f, 1f, 0.2f, 0.2f}; 101 widthTM[WIDTH_VERY_WIDE] = new float [] { 1f, 1f, 1f, 0.1f}; 102 } 103 104 wrandTM = new float[4][4]; 105 wrandTM[WRAND_NONE] = new float [] { 1f, 1f, 0.1f, 0.1f}; 106 wrandTM[WRAND_LOW] = new float [] { 1f, 1f, 1f, 0.1f}; 107 wrandTM[WRAND_MEDIUM] = new float [] {0.1f, 1f, 0.5f, 0.2f}; 108 wrandTM[WRAND_HIGH] = new float [] {0.1f, 0.1f, 1f, 0.1f}; 109 110 wchangeTM = new float[3][3]; 111 wchangeTM[WCHANGE_SLOW] = new float [] { 1f, 1f, 0.1f}; 112 wchangeTM[WCHANGE_MEDIUM] = new float [] { 1f, 1f, 1f}; 113 wchangeTM[WCHANGE_FAST] = new float [] {0.1f, 1f, 1f}; 114 115 fluctTM = new float[4][4]; 116 fluctTM[FLUCT_NONE] = new float [] { 1f, 1f, 0.1f, 0.1f}; 117 fluctTM[FLUCT_LOW] = new float [] { 1f, 1f, 1f, 0.1f}; 118 fluctTM[FLUCT_MEDIUM] = new float [] {0.1f, 1f, 0.5f, 0.2f}; 119 fluctTM[FLUCT_HIGH] = new float [] {0.1f, 0.1f, 1f, 0.1f}; 120 } 121 122 123 public static class SlipstreamSection { 124 public Vector2f from = new Vector2f(); 125 public Vector2f to = new Vector2f(); 126 public Vector2f control = new Vector2f(); 127 128 public int width; 129 public int wrand; 130 public int wchange; 131 public int fluct; 132 133 public boolean subdivision = false; 134 public float approxCurveLength; 135 } 136 137 public static class AddedSegment { 138 public SlipstreamSegment segment; 139 public Vector2f dir; 140 public Vector2f perp; 141 } 142 143 protected float [][] widthTM; 144 protected float [][] wrandTM; 145 protected float [][] wchangeTM; 146 protected float [][] fluctTM; 147 148 protected Random random; 149 protected SlipstreamParams2 params; 150 protected SlipstreamTerrainPlugin2 plugin; 151 protected List<SlipstreamSection> sections = new ArrayList<SlipstreamSection>(); 152 153 protected int currWidth = 0; 154 protected int currWRand = 0; 155 protected int currWChange = 0; 156 protected int currFluct = 0; 157 158 protected Vector2f start; 159 protected StreamType type; 160 161 public SlipstreamBuilder(Vector2f start, SlipstreamTerrainPlugin2 plugin, StreamType type, Random random) { 162 this.plugin = plugin; 163 this.type = type != null ? type : StreamType.NORMAL; 164 this.params = plugin.getParams(); 165 166 if (random == null) random = Misc.random; 167 this.random = random; 168 this.start = start; 169 initTransitionMatrices(); 170 for (int i = 0; i < 10; i++) { 171 pickAllNext(); 172 } 173 } 174 175 protected float maxAngleVarianceForCurve = 30f; 176 protected float maxAngleVariance = 60f; 177 public float getMaxAngleVariance() { 178 return maxAngleVariance; 179 } 180 181 public void setMaxAngleVariance(float maxAngleVariance) { 182 this.maxAngleVariance = maxAngleVariance; 183 } 184 public float getMaxAngleVarianceForCurve() { 185 return maxAngleVarianceForCurve; 186 } 187 public void setMaxAngleVarianceForCurve(float maxAngleVarianceForCurve) { 188 this.maxAngleVarianceForCurve = maxAngleVarianceForCurve; 189 } 190 191 public void buildToDestination(Vector2f control, Vector2f control2, Vector2f to) { 192 Vector2f p0 = new Vector2f(start); 193 Vector2f p1 = new Vector2f(control); 194 Vector2f p2 = new Vector2f(control2); 195 Vector2f p3 = new Vector2f(to); 196 197 float len = getApproximateBezierLength(p0, p1, p2, p3); 198 199 float t = 0f; 200 Vector2f prev = new Vector2f(p0); 201 while (true) { 202 float length = 3000f + random.nextInt(2000); 203 float tIncr = length / len; 204 t += tIncr; 205 if (t > 1f) t = 1f; 206 207 Vector2f loc = Misc.bezierCubic(p0, p1, p2, p3, t); 208 boolean wide = currWidth == WIDTH_WIDE || currWidth == WIDTH_VERY_WIDE; 209 pickAllNext(); 210 wide |= currWidth == WIDTH_WIDE || currWidth == WIDTH_VERY_WIDE; 211 float angle = random.nextFloat() * maxAngleVarianceForCurve - maxAngleVarianceForCurve/2f; 212 if (wide) angle *= 0.5f; 213 if (t >= 1f) angle = 0f; 214 215 loc = Misc.rotateAroundOrigin(loc, angle, prev); 216 float actualDist = Misc.getDistance(prev, loc); 217 if (actualDist > length * 0.5f) { 218 prev.set(loc); 219 addSection(new Vector2f(loc), true); 220 } 221 222 if (t >= 1f) break; 223 } 224 225 generate(); 226 } 227 228 public void buildToDestination(Vector2f control, Vector2f to) { 229 Vector2f p0 = new Vector2f(start); 230 Vector2f p1 = new Vector2f(control); 231 Vector2f p2 = new Vector2f(to); 232 233 float len = getApproximateBezierLength(p0, p1, p2); 234 235 float t = 0f; 236 Vector2f prev = new Vector2f(p0); 237 while (true) { 238 float length = 3000f + random.nextInt(2000); 239 float tIncr = length / len; 240 t += tIncr; 241 if (t > 1f) t = 1f; 242 243 Vector2f loc = Misc.bezier(p0, p1, p2, t); 244 boolean wide = currWidth == WIDTH_WIDE || currWidth == WIDTH_VERY_WIDE; 245 pickAllNext(); 246 wide |= currWidth == WIDTH_WIDE || currWidth == WIDTH_VERY_WIDE; 247 248 float angle = random.nextFloat() * maxAngleVarianceForCurve - maxAngleVarianceForCurve/2f; 249 if (wide) angle *= 0.5f; 250 if (t >= 1f) angle = 0f; 251 252 loc = Misc.rotateAroundOrigin(loc, angle, prev); 253 float actualDist = Misc.getDistance(prev, loc); 254 if (actualDist > length * 0.5f) { 255 prev.set(loc); 256 addSection(new Vector2f(loc), true); 257 } 258 259 if (t >= 1f) break; 260 } 261 262 generate(); 263 } 264 265 public void buildToDestination(Vector2f to) { 266 Vector2f loc = new Vector2f(start); 267 Vector2f p0 = new Vector2f(loc); 268 Vector2f p1 = new Vector2f(to); 269 270 float len = Misc.getDistance(p0, p1); 271 Vector2f dir = Misc.getUnitVector(p0, p1); 272 float dirAngle = Misc.getAngleInDegrees(dir); 273 274 float distSoFar = 0f; 275 float prevAngle = 0f; 276 for (int i = 0; distSoFar < len; i++) { 277 float angle = random.nextFloat() * maxAngleVariance - maxAngleVariance/2f; 278 279 boolean wide = currWidth == WIDTH_WIDE || currWidth == WIDTH_VERY_WIDE; 280 pickAllNext(); 281 wide |= currWidth == WIDTH_WIDE || currWidth == WIDTH_VERY_WIDE; 282 //if (wide) angle *= 0.5f; 283 284 angle += dirAngle; 285 if (i % 2 == 1) angle = prevAngle; 286 prevAngle = angle; 287 //if (i == 0) angle = 90f; 288 Vector2f add = Misc.getUnitVectorAtDegreeAngle(angle); 289 float length = 3000f + random.nextInt(2000); 290 distSoFar += length; 291 //length *= 0.25f; 292 add.scale(length); 293 Vector2f.add(loc, add, loc); 294 addSection(new Vector2f(loc), true); 295 } 296 Vector2f end = sections.get(sections.size() - 1).to; 297 298 299 float actualAngle = Misc.getAngleInDegrees(p0, end); 300 float angleDiff = Misc.getAngleDiff(dirAngle, actualAngle); 301 float turnDir = Misc.getClosestTurnDirection(actualAngle, dirAngle); 302 303 for (SlipstreamSection section : sections) { 304 section.from = Misc.rotateAroundOrigin(section.from, angleDiff * turnDir, p0); 305 section.to = Misc.rotateAroundOrigin(section.to, angleDiff * turnDir, p0); 306 } 307 308 end = sections.get(sections.size() - 1).to; 309 float actualDist = Misc.getDistance(p0, end); 310 float distToAdd = len - actualDist; 311 312 //System.out.println("Distance short: " + distToAdd); 313 if (distToAdd > 1000) { 314 angleDiff = Misc.getAngleDiff(dirAngle, prevAngle); 315 turnDir = Misc.getClosestTurnDirection(actualAngle, dirAngle); 316 float turnAmount = Math.min(30f, angleDiff) * turnDir; 317 //float turnAmount = angleDiff * turnDir; 318 319 Vector2f add = Misc.getUnitVectorAtDegreeAngle(prevAngle + turnAmount); 320 float length = distToAdd; 321 add.scale(length); 322 loc = Vector2f.add(end, add, loc); 323 addSection(new Vector2f(loc)); 324 } 325 326 generate(); 327 } 328 329 330 protected float [] buildNoise; 331 public void buildTest() { 332 333 if (false) { 334 Vector2f loc = new Vector2f(start); 335 336 Vector2f p0 = loc; 337 Vector2f p1 = new Vector2f(loc); 338 float w = 15000; 339 float h = 15000; 340 h = 0f; 341 w = 25000f; 342 w = 20000f; 343 p1.x += w; 344 p1.y += h; 345 float len = Misc.getDistance(p0, p1); 346 Vector2f dir = Misc.getUnitVector(p0, p1); 347 Vector2f perp = new Vector2f(-dir.y, dir.x); 348 349 float averageSection = 4000; 350 351 int numNoisePoints = 32; 352 while (numNoisePoints < len / averageSection) { 353 numNoisePoints *= 2f; 354 } 355 if (numNoisePoints > 2048) numNoisePoints = 2048; 356 float spikes = 0.75f; 357 float [] noise = initNoise1D(random, numNoisePoints, spikes); 358 noise[0] = 0.5f; 359 noise[noise.length - 1] = 0.5f; 360 genNoise1D(random, noise, numNoisePoints, spikes); 361 normalizeNoise1D(noise); 362 buildNoise = noise; 363 364 float startLength = 1500 + random.nextFloat() * 1500f; 365 float endLength = 1500 + random.nextFloat() * 1500f; 366 Vector2f curr = new Vector2f(dir); 367 curr.scale(startLength); 368 Vector2f.add(curr, p0, curr); 369 addSection(curr); 370 371 372 float mid = len - startLength - endLength; 373 float remaining = mid; 374 float distSoFar = startLength; 375 376 //float maxMag = 500f + mid * 0.025f + random.nextFloat() * (2000f + mid * 0.05f); 377 //float maxMag = 500f + mid * 0.025f + 1f * (2000f + mid * 0.05f); 378 float maxMag = 500f + mid * 0.01f + 1f * (1000f + mid * 0.025f); 379 380 while (remaining > 0) { 381 float segLen = 3000f + random.nextFloat() * 2000f; 382 segLen = Math.min(segLen, remaining); 383 remaining -= segLen; 384 if (remaining < segLen * 0.5f) { 385 segLen += remaining; 386 remaining = 0f; 387 } 388 distSoFar += segLen; 389 390 float t = (distSoFar - startLength) / mid; 391 float n = getInterpNoise(noise, t) - 0.5f; 392 393 if (t < 0.25f) { 394 n *= t / 0.25f; 395 } else if (t > 0.75f) { 396 n *= (1f - t) / 0.25f; 397 } 398 399 n *= 2f; 400 401 curr = new Vector2f(dir); 402 curr.scale(distSoFar); 403 Vector2f.add(curr, p0, curr); 404 curr.x += perp.x * maxMag * n; 405 curr.y += perp.y * maxMag * n; 406 addSection(curr); 407 } 408 409 addSection(p1); 410 411 generate(); 412 return; 413 } 414 415 if (true) { 416 Vector2f loc = new Vector2f(start); 417 Vector2f p0 = new Vector2f(loc); 418 Vector2f p1 = new Vector2f(loc); 419 float w = 15000; 420 float h = 15000; 421 h = 0f; 422 w = 25000f; 423 //w = 80000f; 424 w = 50000f; 425// h = 50000f; 426// w = 164000f; 427// h = 104000f; 428 p1.x += w; 429 p1.y += h; 430 float len = Misc.getDistance(p0, p1); 431 Vector2f dir = Misc.getUnitVector(p0, p1); 432 Vector2f perp = new Vector2f(-dir.y, dir.x); 433 float dirAngle = Misc.getAngleInDegrees(dir); 434 435 float distSoFar = 0f; 436 float prevAngle = 0f; 437 for (int i = 0; distSoFar < len; i++) { 438 float angle = random.nextFloat() * 60f - 30f; 439 //float angle = StarSystemGenerator.getNormalRandom(random, -30f, 30f); 440 angle += dirAngle; 441 if (i % 2 == 1) angle = prevAngle; 442 prevAngle = angle; 443 //if (i == 0) angle = 90f; 444 Vector2f add = Misc.getUnitVectorAtDegreeAngle(angle); 445 float length = 3000f + random.nextInt(2000); 446 distSoFar += length; 447 //length *= 0.25f; 448 add.scale(length); 449 Vector2f.add(loc, add, loc); 450 addSection(new Vector2f(loc)); 451 } 452 Vector2f end = sections.get(sections.size() - 1).to; 453 454 455 float actualAngle = Misc.getAngleInDegrees(p0, end); 456 float angleDiff = Misc.getAngleDiff(dirAngle, actualAngle); 457 float turnDir = Misc.getClosestTurnDirection(actualAngle, dirAngle); 458 459 for (SlipstreamSection section : sections) { 460 section.from = Misc.rotateAroundOrigin(section.from, angleDiff * turnDir, p0); 461 section.to = Misc.rotateAroundOrigin(section.to, angleDiff * turnDir, p0); 462 } 463 464 end = sections.get(sections.size() - 1).to; 465 float actualDist = Misc.getDistance(p0, end); 466 float distToAdd = len - actualDist; 467 //System.out.println("Distance short: " + distToAdd); 468 if (distToAdd > 1000) { 469 angleDiff = Misc.getAngleDiff(dirAngle, prevAngle); 470 turnDir = Misc.getClosestTurnDirection(actualAngle, dirAngle); 471 float turnAmount = Math.min(30f, angleDiff) * turnDir; 472 //float turnAmount = angleDiff * turnDir; 473 474 Vector2f add = Misc.getUnitVectorAtDegreeAngle(prevAngle + turnAmount); 475 float length = distToAdd; 476 add.scale(length); 477 loc = Vector2f.add(end, add, loc); 478 addSection(new Vector2f(loc)); 479 } 480 481 generate(); 482 483 //System.out.println("Segments: " + plugin.getSegments().size()); 484 return; 485 } 486 487 if (true) { 488 Vector2f loc = new Vector2f(start); 489 float prevAngle = 0f; 490 for (int i = 0; i < 20; i++) { 491 float angle = random.nextFloat() * 60f - 30f; 492 if (i % 2 == 1) angle = prevAngle; 493 prevAngle = angle; 494 //if (i == 0) angle = 90f; 495 Vector2f add = Misc.getUnitVectorAtDegreeAngle(angle); 496 float length = 3000f + random.nextInt(2000); 497 //length *= 0.25f; 498 add.scale(length); 499 Vector2f.add(loc, add, loc); 500 addSection(new Vector2f(loc)); 501 } 502 generate(); 503 return; 504 } 505 506 if (true) { 507 Vector2f loc = new Vector2f(start); 508 509 Vector2f p0 = loc; 510 Vector2f p1 = new Vector2f(loc); 511 float w = 15000; 512 float h = 15000; 513 h = 0f; 514 w = 25000f; 515 w = 80000f; 516 p1.x += w; 517 p1.y += h; 518 float len = Misc.getDistance(p0, p1); 519 Vector2f dir = Misc.getUnitVector(p0, p1); 520 Vector2f perp = new Vector2f(-dir.y, dir.x); 521 522 //System.out.println("LEN: " + len); 523 524 if (len <= 7000f) { 525 addSection(p1); 526 generate(); 527 return; 528 } 529 if (len <= 15000f) { 530 float midLength = len * (0.4f + random.nextFloat() * 0.2f); 531 532 Vector2f curr = new Vector2f(dir); 533 curr.scale(1000f + random.nextFloat() * 1000f); 534 Vector2f.add(curr, p0, curr); 535 addSection(new Vector2f(curr)); 536 537 curr = new Vector2f(dir); 538 curr.scale(midLength); 539 540 float mag = 0f + random.nextFloat() * 1000f + len * 0.05f; 541 if (random.nextFloat() < 0.75f) { 542 if (random.nextBoolean()) { 543 perp.negate(); 544 } 545 curr.x += perp.x * mag; 546 curr.y += perp.y * mag; 547 } 548 549 Vector2f.add(curr, p0, curr); 550 addSection(curr); 551 552 float endLength = 1000f + random.nextFloat() * 1000f; 553 curr = new Vector2f(dir); 554 curr.scale(len - endLength); 555 Vector2f.add(curr, p0, curr); 556 addSection(curr); 557 558 addSection(p1); 559 560 generate(); 561 return; 562 } 563 564 565 566 float startLength = 1500 + random.nextFloat() * 1500f; 567 float endLength = 1500 + random.nextFloat() * 1500f; 568 Vector2f curr = new Vector2f(dir); 569 curr.scale(startLength); 570 Vector2f.add(curr, p0, curr); 571 addSection(curr); 572 573 float remaining = len - startLength - endLength; 574 float distSoFar = startLength; 575 while (remaining > 0) { 576 float lengthForArc = 10000f + random.nextFloat() * 20000f; 577 lengthForArc = Math.min(lengthForArc, remaining); 578 remaining -= lengthForArc; 579 if (remaining < 10000f) { 580 lengthForArc += remaining; 581 remaining = 0f; 582 } 583 584 if (random.nextBoolean()) { 585 perp.negate(); 586 } 587 588 float maxMag = 500f + random.nextFloat() * (2000f + lengthForArc * 0.1f); 589 maxMag = 4000; 590 float t = 0f; 591 while (t < 1f) { 592 float perIter = 3000f + random.nextFloat() * 2000f; 593 float tForIter = perIter / lengthForArc; 594 t += tForIter; 595 if (t > 1f - tForIter * 0.5f) { 596 tForIter += 1f - t; 597 t = 1f; 598 } 599 perIter = tForIter * lengthForArc; 600 distSoFar += perIter; 601 602 float mag = getFluctuationFunc(t) * maxMag; 603 curr = new Vector2f(dir); 604 curr.scale(distSoFar); 605 Vector2f.add(curr, p0, curr); 606 curr.x += perp.x * mag; 607 curr.y += perp.y * mag; 608 addSection(new Vector2f(curr)); 609 } 610 } 611 612 addSection(p1); 613 generate(); 614 return; 615 } 616 617 618 if (false) { 619 Vector2f loc = new Vector2f(start); 620 621 Vector2f p0 = loc; 622 Vector2f p3 = new Vector2f(loc); 623 float w = 30000; 624 float h = 30000; 625 p3.x += w; 626 p3.y += h; 627 float len = Misc.getDistance(p0, p3); 628 629// Vector2f p1 = new Vector2f(p0); 630// Vector2f p2 = new Vector2f(p3); 631 632 Vector2f p1 = Vector2f.add(p0, p3, new Vector2f()); 633 p1.scale(0.5f); 634 Vector2f p2 = new Vector2f(p1); 635 636 p1.x -= len * 0.2f; 637 p1.y += len * 0.2f; 638 p2.x += len * 0.2f; 639 p2.y -= len * 0.2f; 640 641 float dist1 = len * 0.5f + random.nextFloat() * len * 0.5f; 642 float dist2 = len * 0.5f + random.nextFloat() * len * 0.5f; 643 644 p1 = Misc.getPointAtRadius(p0, dist1, random); 645 p2 = Misc.getPointAtRadius(p3, dist2, random); 646 647 p1 = new Vector2f(p0.x, p3.y); 648 p2 = new Vector2f(p3.x, p0.y); 649 650 if (random.nextBoolean()) { 651 Vector2f temp = p1; 652 p1 = p2; 653 p2 = temp; 654 } 655 656 float veryApproxPathLength = len * 1.5f; 657 float tPerIter = 8000f / (veryApproxPathLength + 1f); 658 659 float t = 0f; 660 while (t < 1f) { 661 t += tPerIter * (0.8f + 0.4f * random.nextFloat()); 662 if (t > 1f - tPerIter * 0.5f) t = 1f; 663 664 Vector2f curr = Misc.bezierCubic(p0, p1, p2, p3, t); 665 addSection(new Vector2f(curr)); 666 } 667 generate(); 668 return; 669 } 670 671 if (false) { 672 Vector2f loc = new Vector2f(start); 673 674 Vector2f p0 = loc; 675 Vector2f p2 = new Vector2f(loc); 676 float w = 30000; 677 float h = 30000; 678 p2.x += w; 679 p2.y += h; 680 float len = Misc.getDistance(p0, p2); 681 682 Vector2f p1 = Vector2f.add(p0, p2, new Vector2f()); 683 p1.scale(0.5f); 684 p1.x -= len * 0.25f; 685 p1.y += len * 0.25f; 686 687 float approxPathLength = getApproximateBezierLength(p0, p1, p2); 688 float tPerIter = 8000f / (approxPathLength + 1f); 689 690 float t = 0f; 691 while (t < 1f) { 692 t += tPerIter * (0.8f + 0.4f * random.nextFloat()); 693 if (t > 1f - tPerIter * 0.5f) t = 1f; 694 695 Vector2f curr = Misc.bezier(p0, p1, p2, t); 696 addSection(new Vector2f(curr)); 697 } 698 generate(); 699 return; 700 } 701 702 703 if (false) { 704 Vector2f loc = new Vector2f(start); 705 loc.x += 5000; 706 addSection(loc, 1, 0, 0, 3); 707 loc.x += 5000; 708 addSection(loc, 1, 0, 0, 3); 709 loc.x += 5000; 710 addSection(loc, 1, 0, 0, 3); 711 loc.x += 5000; 712 addSection(loc, 1, 0, 0, 3); 713 714 loc.x += 5000; 715 loc.y += 5000; 716 addSection(loc, 0, 0, 0, 3); 717 loc.y += 2000; 718 addSection(loc, 0, 0, 0, 3); 719 loc.x -= 5000; 720 loc.y += 5000; 721 addSection(loc, 0, 0, 0, 3); 722 loc.x -= 5000; 723 addSection(loc, 0, 0, 0, 3); 724 generate(); 725 return; 726 } 727 728 Vector2f loc = new Vector2f(start); 729 float prevAngle = 0f; 730 for (int i = 0; i < 20; i++) { 731 float angle = random.nextFloat() * 60f - 30f; 732 if (i % 2 == 1) angle = prevAngle; 733 prevAngle = angle; 734 //if (i == 0) angle = 90f; 735 Vector2f add = Misc.getUnitVectorAtDegreeAngle(angle); 736 float length = 3000f + random.nextInt(2000); 737 //length *= 0.25f; 738 add.scale(length); 739 Vector2f.add(loc, add, loc); 740 addSection(new Vector2f(loc)); 741 } 742 743 generate(); 744 } 745 746 public void generate() { 747 748// MIN_SPACING = 200f; 749// MAX_SPACING = 400f; 750// WIDTH_TO_SPACING_MULT = 0.2f; 751// MIN_SPACING = 300f; 752// MAX_SPACING = 600f; 753// WIDTH_TO_SPACING_MULT = 0.4f; 754// long seed = 23895464576452L + 4384357483229348234L; 755// seed = 1181783497276652981L ^ seed; 756// this.random = new Random(seed); 757 758 computeControlPoints(); 759 buildStream(); 760 } 761 762 763 public void buildStream() { 764 plugin.setBuilder(this); 765 if (sections.isEmpty()) return; 766 767 768 float totalCurveLength = 0f; 769 for (SlipstreamSection curr : sections) { 770 totalCurveLength += curr.approxCurveLength; 771 } 772 773 int numNoisePoints = 32; 774 while (numNoisePoints * (MIN_SPACING + MAX_SPACING) / 2f < totalCurveLength) { 775 numNoisePoints *= 2f; 776 } 777 if (numNoisePoints > 2048) numNoisePoints = 2048; 778 779 float spikes = 0.67f; 780 float [] noiseForWidth = initNoise1D(random, numNoisePoints, spikes); 781 noiseForWidth[0] = 0.5f; 782 noiseForWidth[noiseForWidth.length - 1] = 0.5f; 783 genNoise1D(random, noiseForWidth, numNoisePoints, spikes); 784 normalizeNoise1D(noiseForWidth); 785 786 float width = getGoalWidth(sections.get(0)); 787 //width *= 3f; 788 //width *= .5f; 789 790 791 float curveLengthSoFar = 0f; 792 for (SlipstreamSection curr : sections) { 793 //curr.fluct = FLUCT_NONE; 794 //curr.fluct = FLUCT_LOW; 795 //curr.fluct = FLUCT_MEDIUM; 796 //curr.fluct = FLUCT_HIGH; 797 798 float startingWidth = width; 799 float goalWidth = getGoalWidth(curr); 800 float changeRate = getWChangeMult(curr); 801 float wrandMult = getWRandMult(curr); 802 float fluctMult = getFluctMult(curr); 803 804 805 float desiredSpacing = Math.max(MIN_SPACING, width * WIDTH_TO_SPACING_MULT); 806 if (desiredSpacing > MAX_SPACING) desiredSpacing = MAX_SPACING; 807 808 int segments = (int) (curr.approxCurveLength / desiredSpacing); 809 if (segments < 2) segments = 2; 810 float spacing = curr.approxCurveLength / segments; 811 812 List<AddedSegment> added = new ArrayList<AddedSegment>(); 813 814 int startIndex = 1; 815 if (getPrev(curr) == null) startIndex = 0; 816 for (int i = startIndex; i < segments; i++) { 817 float f = (float)i / ((float)segments - 1f); 818 float f2 = (float)(i + 0.1f) / ((float)segments - 1f); 819 820 width = Misc.interpolate(startingWidth, goalWidth, Math.min(1f, f * f * changeRate)); 821 float t = (curveLengthSoFar + f * curr.approxCurveLength) / totalCurveLength; 822 float wNoise = (getInterpNoise(noiseForWidth, t) - 0.5f) * 2f; 823 wNoise *= wrandMult; 824 //wNoise *= 3f; 825 width *= (1f + wNoise); 826 827 Vector2f loc = Misc.bezier(curr.from, curr.control, curr.to, f); 828 Vector2f loc2 = Misc.bezier(curr.from, curr.control, curr.to, f2); 829 Vector2f dir = Vector2f.sub(loc2, loc, new Vector2f()); 830 Misc.normalise(dir); 831 Vector2f perp = new Vector2f(-dir.y, dir.x); 832 833 plugin.addSegment(loc, width); 834 AddedSegment seg = new AddedSegment(); 835 seg.segment = plugin.getSegments().get(plugin.getSegments().size() - 1); 836 seg.dir = new Vector2f(dir); 837 seg.perp = new Vector2f(perp); 838 added.add(seg); 839 } 840 841 float distPerFluct = FLUCT_LENGTH_MIN + random.nextFloat() * (FLUCT_LENGTH_MAX - FLUCT_LENGTH_MIN); 842 float fluctAmount = FLUCT_MAG_MIN + random.nextFloat() * (FLUCT_MAG_MAX - FLUCT_MAG_MIN); 843 float fluctDir = Math.signum(random.nextFloat() - 0.5f); 844 float distSoFar = 0f; 845 List<AddedSegment> temp = new ArrayList<AddedSegment>(); 846 AddedSegment prev = null; 847 for (AddedSegment seg : added) { 848 if (prev != null) { 849 distSoFar += Misc.getDistance(seg.segment.loc, prev.segment.loc); 850 } 851 temp.add(seg); 852 prev = seg; 853 854 if (distSoFar > distPerFluct && temp.size() >= 4) { 855 for (int i = 0; i < temp.size(); i++) { 856 float t = i / (temp.size() - 1f); 857 AddedSegment seg2 = temp.get(i); 858 859 float fluctMag = getFluctuationFunc(t); 860 fluctMag *= fluctMult; 861 //fluctDir = 1f; 862 863 fluctMag *= fluctAmount * fluctDir; 864 865 seg2.segment.loc.x += seg2.perp.x * fluctMag; 866 seg2.segment.loc.y += seg2.perp.y * fluctMag; 867 868 } 869 temp.clear(); 870 distSoFar = 0f; 871 distPerFluct = FLUCT_LENGTH_MIN + random.nextFloat() * (FLUCT_LENGTH_MAX - FLUCT_LENGTH_MIN); 872 fluctAmount = FLUCT_MAG_MIN + random.nextFloat() * (FLUCT_MAG_MAX - FLUCT_MAG_MIN); 873 fluctDir = Math.signum(random.nextFloat() - 0.5f); 874 } 875 } 876 877 curveLengthSoFar += curr.approxCurveLength; 878 } 879 880 adjustSharpInflectionPoints(); 881 882 float fadeDist = 500f; 883 float distSoFar = 0f; 884 SlipstreamSegment prev = null; 885 for (int i = 0; i < plugin.getSegments().size(); i++) { 886 SlipstreamSegment curr = plugin.getSegments().get(i); 887 if (prev != null) { 888 distSoFar += Misc.getDistance(prev.loc, curr.loc); 889 } 890 if (distSoFar >= fadeDist) { 891 break; 892 } 893 894 float b = distSoFar / fadeDist; 895 if (b < 0f) b = 0f; 896 if (b > 1f) b = 1f; 897 curr.bMult = b; 898 prev = curr; 899 } 900 901 distSoFar = 0f; 902 prev = null; 903 for (int i = plugin.getSegments().size() - 1; i >= 0; i--) { 904 SlipstreamSegment curr = plugin.getSegments().get(i); 905 if (prev != null) { 906 distSoFar += Misc.getDistance(prev.loc, curr.loc); 907 } 908 if (distSoFar >= fadeDist) { 909 break; 910 } 911 912 float b = distSoFar / fadeDist; 913 if (b < 0f) b = 0f; 914 if (b > 1f) b = 1f; 915 curr.bMult = b; 916 prev = curr; 917 } 918 } 919 920 protected float getFluctuationFunc(float t) { 921 float pi = (float) Math.PI; 922 return ((float)Math.cos(pi + 2f * pi * t) + 1f) * 0.5f; 923 } 924 925 protected void adjustSharpInflectionPoints() { 926 for (int i = 1; i < plugin.getSegments().size() - 1; i++) { 927 SlipstreamSegment prev = plugin.getSegments().get(i - 1); 928 SlipstreamSegment curr = plugin.getSegments().get(i); 929 SlipstreamSegment next = plugin.getSegments().get(i + 1); 930 931 float dir1 = Misc.getAngleInDegrees(prev.loc, curr.loc); 932 float dir2 = Misc.getAngleInDegrees(curr.loc, next.loc); 933 float diff = Misc.getAngleDiff(dir1, dir2); 934 if (diff > 5f) { 935 Vector2f avg = Vector2f.add(prev.loc, next.loc, new Vector2f()); 936 avg.scale(0.5f); 937 curr.loc.set(Misc.interpolateVector(curr.loc, avg, 0.67f)); 938 //i++; 939 } 940 } 941 } 942 943 protected float getWRandMult(SlipstreamSection curr) { 944 float mult = 0f; 945 if (curr.wrand == WRAND_NONE) { 946 mult = WRAND_NONE_MULT; 947 } else if (curr.wrand == WRAND_LOW) { 948 mult = WRAND_LOW_MULT; 949 } else if (curr.wrand == WRAND_MEDIUM) { 950 mult = WRAND_MEDIUM_MULT; 951 } else if (curr.wrand == WRAND_HIGH) { 952 mult = WRAND_HIGH_MULT; 953 } 954 //mult *= 0.9f + 0.2f * random.nextFloat(); 955 return mult; 956 } 957 958 protected float getFluctMult(SlipstreamSection curr) { 959 float mult = 0f; 960 if (curr.fluct == FLUCT_NONE) { 961 mult = FLUCT_NONE_MULT; 962 } else if (curr.fluct == WRAND_LOW) { 963 mult = FLUCT_LOW_MULT; 964 } else if (curr.fluct == FLUCT_MEDIUM) { 965 mult = FLUCT_MEDIUM_MULT; 966 } else if (curr.fluct == FLUCT_HIGH) { 967 mult = FLUCT_HIGH_MULT; 968 } 969 mult *= 0.9f + 0.2f * random.nextFloat(); 970 return mult; 971 } 972 973 protected float getWChangeMult(SlipstreamSection curr) { 974 float mult = 1f; 975 if (curr.wchange == WCHANGE_SLOW) { 976 mult = WCHANGE_SLOW_T_MULT_MIN + random.nextFloat() * (WCHANGE_SLOW_T_MULT_MIN_MAX - WCHANGE_SLOW_T_MULT_MIN); 977 } else if (curr.wchange == WCHANGE_MEDIUM) { 978 mult = WCHANGE_MEDIUM_T_MULT_MIN + random.nextFloat() * (WCHANGE_MEDIUM_T_MULT_MAX - WCHANGE_MEDIUM_T_MULT_MIN); 979 } else if (curr.wchange == WCHANGE_FAST) { 980 mult = WCHANGE_FAST_T_MULT_MIN + random.nextFloat() * (WCHANGE_FAST_T_MULT_MAX - WCHANGE_FAST_T_MULT_MIN); 981 } 982 return mult; 983 } 984 985 protected float getGoalWidth(SlipstreamSection curr) { 986 float goalWidth = params.baseWidth; 987 float mult = 1f; 988 if (curr.width == WIDTH_NARROW) { 989 mult = MIN_NARROW + random.nextFloat() * (MAX_NARROW - MIN_NARROW); 990 } else if (curr.width == WIDTH_NORMAL) { 991 mult = MIN_NORMAL + random.nextFloat() * (MAX_NORMAL - MIN_NORMAL); 992 } else if (curr.width == WIDTH_WIDE) { 993 mult = MIN_WIDE + random.nextFloat() * (MAX_WIDE - MIN_WIDE); 994 } else if (curr.width == WIDTH_VERY_WIDE) { 995 mult = MIN_VERY_WIDE + random.nextFloat() * (MAX_VERY_WIDE - MIN_VERY_WIDE); 996 } 997 return goalWidth * mult; 998 } 999 1000 protected void computeControlPoints() { 1001 float angleLimit = 30f; 1002 for (SlipstreamSection curr : sections) { 1003 SlipstreamSection prev = getPrev(curr); 1004 SlipstreamSection next = getNext(curr); 1005 1006 1007 if (prev == null && next == null) { 1008 curr.control = Vector2f.add(curr.from, curr.to, new Vector2f()); 1009 curr.control.scale(0.5f); 1010 } else if (prev == null && next != null) { 1011 Vector2f p1 = curr.from; 1012 Vector2f p2 = curr.to; 1013 Vector2f p3 = next.to; 1014 float angleP2ToControl = Misc.getAngleInDegrees(p3, p2); 1015 Vector2f dirP2ToControl = Misc.getUnitVectorAtDegreeAngle(angleP2ToControl); 1016 Vector2f p2ToP1 = Vector2f.sub(p1, p2, new Vector2f()); 1017 float angleP2toP1 = Misc.getAngleInDegrees(p2ToP1); 1018 float angleToControlAndP2toP1 = Vector2f.angle(dirP2ToControl, p2ToP1) * Misc.DEG_PER_RAD; 1019 if (angleToControlAndP2toP1 > angleLimit) { 1020 angleToControlAndP2toP1 = angleLimit; 1021 float turnDir = Misc.getClosestTurnDirection(angleP2toP1, angleP2ToControl); 1022 angleP2ToControl = angleP2toP1 + turnDir * angleToControlAndP2toP1; 1023 dirP2ToControl = Misc.getUnitVectorAtDegreeAngle(angleP2ToControl); 1024 } 1025 float dist = Misc.getDistance(p1, p2); 1026 dist /= 2f; 1027 float h = dist * (float) Math.tan(angleToControlAndP2toP1 * Misc.RAD_PER_DEG); 1028 float b = (float) Math.sqrt(dist * dist + h * h); 1029 dirP2ToControl.scale(b); 1030 Vector2f.add(dirP2ToControl, p2, curr.control); 1031 } else { 1032 Vector2f p1 = prev.control; 1033 Vector2f p2 = curr.from; 1034 Vector2f p3 = curr.to; 1035 float angleP2ToControl = Misc.getAngleInDegrees(p1, p2); 1036 Vector2f dirP2ToControl = Misc.getUnitVectorAtDegreeAngle(angleP2ToControl); 1037 Vector2f p2ToP3 = Vector2f.sub(p3, p2, new Vector2f()); 1038 float angleP2ToP3 = Misc.getAngleInDegrees(p2ToP3); 1039 float angleToControlAndP2toP3 = Vector2f.angle(dirP2ToControl, p2ToP3) * Misc.DEG_PER_RAD; 1040 if (angleToControlAndP2toP3 > angleLimit) { 1041 angleToControlAndP2toP3 = angleLimit; 1042 float turnDir = Misc.getClosestTurnDirection(angleP2ToP3, angleP2ToControl); 1043 angleP2ToControl = angleP2ToP3 + turnDir * angleToControlAndP2toP3; 1044 dirP2ToControl = Misc.getUnitVectorAtDegreeAngle(angleP2ToControl); 1045 } 1046 1047 float dist = Misc.getDistance(p2, p3); 1048 dist /= 2f; 1049 float h = dist * (float) Math.tan(angleToControlAndP2toP3 * Misc.RAD_PER_DEG); 1050 float b = (float) Math.sqrt(dist * dist + h * h); 1051 dirP2ToControl.scale(b); 1052 Vector2f.add(dirP2ToControl, p2, curr.control); 1053 } 1054 1055 curr.approxCurveLength = getApproximateBezierLength(curr.from, curr.control, curr.to); 1056 } 1057 } 1058 1059 public void addSection(Vector2f to) { 1060 addSection(to, false); 1061 } 1062 public void addSection(Vector2f to, boolean alreadyPicked) { 1063 if (!alreadyPicked) { 1064 pickAllNext(); 1065 } 1066 addSection(to, currWidth, currWRand, currWChange, currFluct); 1067 } 1068 1069 public void addSection(Vector2f to, int width, int wrand, int wchange, int fluct) { 1070 addSection(to, width, wrand, wchange, fluct, -1); 1071 } 1072 1073 public void addSection(Vector2f to, int width, int wrand, int wchange, int fluct, int insertIndex) { 1074 SlipstreamSection s = new SlipstreamSection(); 1075 s.to.set(to); 1076 s.width = width; 1077 s.wrand = wrand; 1078 s.wchange = wchange; 1079 s.fluct = fluct; 1080 1081 if (insertIndex < 0) { 1082 sections.add(s); 1083 } else { 1084 sections.add(insertIndex, s); 1085 } 1086 1087 SlipstreamSection p = getPrev(s); 1088 if (p != null) { 1089 s.from.set(p.to); 1090 } else { 1091 s.from.set(start); 1092 } 1093 } 1094 1095 1096 public SlipstreamSection getPrev(SlipstreamSection s) { 1097 int index = sections.indexOf(s); 1098 if (index < 1) return null; 1099 return sections.get(index - 1); 1100 } 1101 public SlipstreamSection getNext(SlipstreamSection s) { 1102 int index = sections.indexOf(s); 1103 if (index < 0 || index >= sections.size() - 1) return null; 1104 return sections.get(index + 1); 1105 } 1106 1107 public void pickAllNext() { 1108 currWidth = pickWidth(currWidth); 1109 currWRand = pickWRand(currWRand); 1110 currWChange = pickWChange(currWChange); 1111 currFluct = pickFluct(currFluct); 1112 1113 if (currWidth == WIDTH_NARROW) { 1114 if (currFluct == FLUCT_HIGH) { 1115 currFluct = FLUCT_LOW; 1116 } else if (currFluct == FLUCT_MEDIUM) { 1117 currFluct = FLUCT_NONE; 1118 } 1119 } 1120 } 1121 1122 public int pickWidth(int curr) { 1123 return pickNext(curr, widthTM); 1124 } 1125 1126 public int pickWRand(int curr) { 1127 return pickNext(curr, wrandTM); 1128 } 1129 1130 public int pickWChange(int curr) { 1131 return pickNext(curr, wchangeTM); 1132 } 1133 1134 public int pickFluct(int curr) { 1135 return pickNext(curr, fluctTM); 1136 } 1137 1138 public int pickNext(int curr, float [][] matrix) { 1139 float [] weights = matrix[curr]; 1140 WeightedRandomPicker<Integer> picker = new WeightedRandomPicker<Integer>(random); 1141 for (int i = 0; i < weights.length; i++) { 1142 picker.add(i, weights[i]); 1143 } 1144 return picker.pick(); 1145 } 1146 1147 1148 public void renderDebug(float alpha) { 1149 //if (true) return; 1150 1151 GL11.glDisable(GL11.GL_TEXTURE_2D); 1152 GL11.glEnable(GL11.GL_BLEND); 1153 GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); 1154 1155// float scale = 0.25f; 1156// //scale = 1f; 1157// GL11.glPushMatrix(); 1158// GL11.glTranslatef(sections.get(0).from.x, sections.get(0).from.y, 0f); 1159// GL11.glScalef(scale, scale, 1f); 1160// GL11.glTranslatef(sections.get(0).from.x * -scale, sections.get(0).from.y * -scale, 0f); 1161 1162 if (false) { 1163 Misc.setColor(Color.white); 1164 GL11.glEnable(GL11.GL_LINE_SMOOTH); 1165 GL11.glLineWidth(3f); 1166 GL11.glBegin(GL11.GL_LINE_STRIP); 1167 Vector2f p0 = new Vector2f(0, 0); 1168 Vector2f p1 = new Vector2f(10000, 0); 1169 Vector2f p2 = new Vector2f(0, 10000); 1170 Vector2f p3 = new Vector2f(10000, 10000); 1171 for (float t = 0f; t <= 1f; t += 0.02f) { 1172 Vector2f p = Misc.bezierCubic(p0, p1, p2, p3, t); 1173 GL11.glVertex2f(p.x, p.y); 1174 } 1175 GL11.glEnd(); 1176 } 1177 1178 if (false) { 1179 GL11.glEnable(GL11.GL_POINT_SMOOTH); 1180 GL11.glPointSize(10f); 1181 GL11.glBegin(GL11.GL_POINTS); 1182 Misc.setColor(Color.yellow); 1183 for (SlipstreamSection curr : sections) { 1184 Misc.setColor(Color.yellow); 1185 if (curr.subdivision) { 1186 Misc.setColor(Color.green); 1187 } 1188 Vector2f p = curr.from; 1189 GL11.glVertex2f(p.x, p.y); 1190 p = curr.to; 1191 GL11.glVertex2f(p.x, p.y); 1192 1193 Misc.setColor(Color.cyan); 1194 p = curr.control; 1195 GL11.glVertex2f(p.x, p.y); 1196 } 1197 GL11.glEnd(); 1198 1199 Misc.setColor(Color.white); 1200 GL11.glEnable(GL11.GL_LINE_SMOOTH); 1201 GL11.glLineWidth(3f); 1202 GL11.glBegin(GL11.GL_LINE_STRIP); 1203 for (SlipstreamSection curr : sections) { 1204 for (float t = 0f; t <= 1f; t += 0.02f) { 1205 Vector2f p = Misc.bezier(curr.from, curr.control, curr.to, t); 1206 GL11.glVertex2f(p.x, p.y); 1207 } 1208 } 1209 GL11.glEnd(); 1210 } 1211 1212 for (BoundingBox box : plugin.getBounds()) { 1213 Misc.setColor(Color.cyan); 1214 GL11.glEnable(GL11.GL_LINE_SMOOTH); 1215 GL11.glLineWidth(3f); 1216 GL11.glBegin(GL11.GL_LINE_LOOP); 1217 if (box != null) { 1218 for (Vector2f p : box.box) { 1219 GL11.glVertex2f(p.x, p.y); 1220 } 1221 } 1222 GL11.glEnd(); 1223 } 1224 1225 GL11.glPopMatrix(); 1226 1227 if (false) { 1228 GL11.glPushMatrix(); 1229 Vector2f loc = Global.getSector().getPlayerFleet().getLocation(); 1230 GL11.glTranslatef(loc.x - 1000, loc.y + 100, 0f); 1231// 1232// long seed = 23895464576452L + 4384357483229348234L; 1233// //seed += System.nanoTime() / 1000000000L; 1234// seed = 1181783497276652981L ^ seed; 1235// Random random = new Random(seed); 1236// float spikes = 0.67f; 1237// float [] noise = initNoise1D(random, 128, spikes); 1238// noise[0] = 0.5f; 1239// noise[noise.length - 1] = 0.5f; 1240// genNoise1D(random, noise, 128, spikes); 1241// normalizeNoise1D(noise); 1242 1243 Misc.setColor(Color.orange); 1244 GL11.glEnable(GL11.GL_LINE_SMOOTH); 1245 GL11.glLineWidth(3f); 1246 GL11.glBegin(GL11.GL_LINE_STRIP); 1247 float horz = 50f; 1248 float vert = 500f; 1249 for (int i = 0; i < buildNoise.length; i++) { 1250 float f = buildNoise[i]; 1251 GL11.glVertex2f(i * horz, f * vert); 1252 } 1253 GL11.glEnd(); 1254// GL11.glBegin(GL11.GL_LINE_STRIP); 1255// float iter = 2000f; 1256// for (int i = 0; i < iter; i++) { 1257// float f = getInterpNoise(noise, i / iter); 1258// GL11.glVertex2f(i, f * vert); 1259// } 1260// GL11.glEnd(); 1261 Misc.setColor(Color.white); 1262 GL11.glBegin(GL11.GL_LINES); 1263 GL11.glVertex2f(0, 0); 1264 GL11.glVertex2f(buildNoise.length * horz, 0); 1265 GL11.glVertex2f(0, 0); 1266 GL11.glVertex2f(0, vert); 1267 GL11.glEnd(); 1268 1269 GL11.glPopMatrix(); 1270 } 1271 } 1272 1273 public static float getInterpNoise(float [] noise, float t) { 1274 t *= noise.length; 1275 int index = (int) t; 1276 if (index >= noise.length) index = noise.length - 1; 1277 if (index < 0) index = 0; 1278 1279 int index2 = index + 1; 1280 if (index2 >= noise.length) index2 = index - 1; 1281 1282 float f = t - index; 1283 1284 return Misc.interpolate(noise[index], noise[index2], f); 1285 } 1286 1287 1288 /** 1289 * To [0, 1] 1290 * @param noise 1291 */ 1292 public static void normalizeNoise1D(float [] noise) { 1293 float min = Float.MAX_VALUE; 1294 float max = -Float.MAX_VALUE; 1295 for (float f : noise) { 1296 if (f < min) min = f; 1297 if (f > max) max = f; 1298 } 1299 1300 if (max <= min) { 1301 for (int i = 0; i < noise.length; i++) { 1302 noise[i] = 0.5f; 1303 } 1304 return; 1305 } 1306 1307 float range = max - min; 1308 for (int i = 0; i < noise.length; i++) { 1309 noise[i] = (noise[i] - min) / range; 1310 } 1311 } 1312 1313 public static float [] initNoise1D(Random random, int size, float spikes) { 1314 float [] noise = new float[size]; 1315 for (int i = 0; i < noise.length; i++) { 1316 noise[i] = -1f; 1317 } 1318 noise[0] = random.nextFloat() * spikes; 1319 noise[noise.length - 1] = random.nextFloat() * spikes; 1320 return noise; 1321 } 1322 public static void genNoise1D(Random random, float [] noise, int size, float spikes) { 1323 genNoise1DFill(random, noise, 0, noise.length - 1, 1, spikes); 1324 } 1325 1326 public static void genNoise1DFill(Random random, float [] noise, int x1, int x2, int iter, float spikes) { 1327 if (x1 + 1 >= x2) return; 1328 1329 int midX = (x1 + x2) / 2; 1330 1331 float avg = (noise[x1] + noise[x2]) / 2f; 1332 noise[midX] = avg + ((float) Math.pow(spikes, (iter)) * (float) (random.nextFloat() - .5f)); 1333 1334 genNoise1DFill(random, noise, x1, midX, iter + 1, spikes); 1335 genNoise1DFill(random, noise, midX, x2, iter + 1, spikes); 1336 } 1337 1338 public static float getApproximateBezierLength(Vector2f p0, Vector2f p1, Vector2f p2) { 1339 float total = 0f; 1340 Vector2f prev = p0; 1341 for (float f = 0; f <= 1.01f; f += 0.1f) { 1342 Vector2f curr = Misc.bezier(p0, p1, p2, f); 1343 total += Misc.getDistance(prev, curr); 1344 prev = curr; 1345 } 1346 return total; 1347 } 1348 public static float getApproximateBezierLength(Vector2f p0, Vector2f p1, Vector2f p2, Vector2f p3) { 1349 float total = 0f; 1350 Vector2f prev = p0; 1351 for (float f = 0; f <= 1.01f; f += 0.05f) { 1352 Vector2f curr = Misc.bezierCubic(p0, p1, p2, p3, f); 1353 total += Misc.getDistance(prev, curr); 1354 prev = curr; 1355 } 1356 return total; 1357 } 1358} 1359 1360 1361 1362 1363