001package com.fs.starfarer.api.impl.campaign.terrain; 002 003import java.awt.Color; 004import java.nio.ByteBuffer; 005import java.nio.ByteOrder; 006import java.nio.FloatBuffer; 007 008import org.lwjgl.opengl.GL11; 009import org.lwjgl.util.vector.Vector2f; 010 011import com.fs.starfarer.api.graphics.SpriteAPI; 012import com.fs.starfarer.api.util.Misc; 013 014public class PulsarRenderer { 015 public static interface PulsarRendererDelegate { 016 float getPulsarInnerRadius(); 017 float getPulsarOuterRadius(); 018 Vector2f getPulsarCenterLoc(); 019 020 float getPulsarInnerWidth(); 021 float getPulsarOuterWidth(); 022 023 Color getPulsarColorForAngle(float angle); 024 025 SpriteAPI getPulsarTexture(); 026 RangeBlockerUtil getPulsarBlocker(); 027 028 float getPulsarScrollSpeed(); 029 } 030 031 private PulsarRendererDelegate delegate; 032 private float texOffset = 0f; 033 public PulsarRenderer(PulsarRendererDelegate delegate) { 034 this.delegate = delegate; 035 } 036 037 private float currAngle; 038 public float getCurrAngle() { 039 return currAngle; 040 } 041 042 043 public void setCurrAngle(float currAngle) { 044 this.currAngle = currAngle; 045 } 046 047 048 public void advance(float amount) { 049 //float days = Global.getSector().getClock().convertToDays(amount); 050 //texOffset += days * delegate.getFlareScrollSpeed(); 051 float imageWidth = delegate.getPulsarTexture().getWidth(); 052 texOffset += amount * delegate.getPulsarScrollSpeed() / imageWidth; 053 while (texOffset > 1) texOffset--; 054 055 if (!rendered && vertexBuffer != null) { 056 Misc.cleanBuffer(vertexBuffer); 057 Misc.cleanBuffer(textureBuffer); 058 Misc.cleanBuffer(colorBuffer); 059 vertexBuffer = textureBuffer = null; 060 colorBuffer = null; 061 } 062 rendered = false; 063 } 064 065 transient private FloatBuffer vertexBuffer, textureBuffer; 066 transient private ByteBuffer colorBuffer; 067 transient private boolean rendered = false; 068 public void render(float alphaMult) { 069 if (alphaMult <= 0) return; 070 071 float distClose = delegate.getPulsarInnerRadius(); 072 float distFar = delegate.getPulsarOuterRadius(); 073 074 if (distFar < distClose + 10f) distFar = distClose + 10f; 075 076 float length = distFar - distClose; 077 078 float wClose = delegate.getPulsarInnerWidth(); 079 float wFar = delegate.getPulsarOuterWidth(); 080 081 float pixelsPerSegment = 25f; 082 float segments = Math.round(wFar / pixelsPerSegment); 083 pixelsPerSegment = wFar / segments; 084 085 086 Vector2f loc = delegate.getPulsarCenterLoc(); 087 float x = loc.x; 088 float y = loc.y; 089 090 091 GL11.glPushMatrix(); 092 GL11.glTranslatef(x, y, 0); 093 094 GL11.glEnable(GL11.GL_TEXTURE_2D); 095 //GL11.glDisable(GL11.GL_TEXTURE_2D); 096 097 //GL11.glShadeModel(GL11.GL_SMOOTH); 098 099 //delegate.getPulsarTexture().bindTexture(); 100 101 102 GL11.glEnable(GL11.GL_BLEND); 103 GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE); 104 105 //GL11.glEnable(GL11.GL_DITHER); 106 107 float texHeight = delegate.getPulsarTexture().getTextureHeight(); 108 float imageHeight = delegate.getPulsarTexture().getHeight(); 109 float texPerSegment = texHeight / segments; 110 111 //texPerSegment *= 20f; 112 113 float texWidth = delegate.getPulsarTexture().getTextureWidth(); 114 float imageWidth = delegate.getPulsarTexture().getWidth(); 115 116 RangeBlockerUtil blocker = delegate.getPulsarBlocker(); 117 118 float numIter = (float)Math.ceil(distFar - distClose) / (imageWidth * texWidth); 119 float widthFactor = ((wClose + wFar) / 2f) / (imageHeight * texHeight); 120 numIter /= widthFactor; 121 122 float texPerUnitLength = 1f / (imageWidth * widthFactor); 123 124 float angle = currAngle; 125 126 //Vector2f dir = Misc.getUnitVectorAtDegreeAngle(angle); 127 128 float fadeInDist = Math.min(1000f, length * 0.25f); 129 float fadeOutDist = Math.min(1500f, length * 0.25f); 130 131 boolean wireframe = false; 132 //wireframe = true; 133 if (wireframe) { 134 GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_LINE); 135 GL11.glDisable(GL11.GL_TEXTURE_2D); 136 //GL11.glDisable(GL11.GL_BLEND); 137 } 138 139 float [] rPrev = new float [(int) segments + 1]; 140 float [] blockedPrev = new float [(int) segments + 1]; 141 142 float [] xPrev = new float [(int) segments + 1]; 143 float [] yPrev = new float [(int) segments + 1]; 144 145 float [] texPrev = new float [(int) segments + 1]; 146 147 int numInnerSegments = (int) ((length - fadeInDist - fadeOutDist) / (pixelsPerSegment * 5f)); 148 if (numInnerSegments < 1) numInnerSegments = 1; 149 numInnerSegments = 1; 150 151 int numSegments = 2 + numInnerSegments; 152 float distPerInnerSegment = (length - fadeInDist - fadeOutDist) / (float) numInnerSegments; 153 154 boolean arrays = false; 155 //arrays = true; 156 157 int numVertices = (numSegments) * ((int) segments + 1) * 2; 158 //System.out.println("Num: " + numVertices); 159 if (arrays) { 160 if (vertexBuffer == null) { 161 vertexBuffer = ByteBuffer.allocateDirect(numVertices * 4 * 2).order(ByteOrder.nativeOrder()).asFloatBuffer(); 162 } 163 if (textureBuffer == null) { 164 textureBuffer = ByteBuffer.allocateDirect(numVertices * 4 * 2).order(ByteOrder.nativeOrder()).asFloatBuffer(); 165 } 166 if (colorBuffer == null) { 167 colorBuffer = ByteBuffer.allocateDirect(numVertices * 4).order(ByteOrder.nativeOrder()); 168 } 169 170 vertexBuffer.clear(); 171 textureBuffer.clear(); 172 colorBuffer.clear(); 173 } 174 175 rendered = true; 176 177// for (int t = 0; t < 2; t++) { 178// 179// SpriteAPI tex = Global.getSettings().getSprite("terrain", "pulsar"); 180// if (t == 1) { 181// tex = Global.getSettings().getSprite("terrain", "pulsar2"); 182// } 183 delegate.getPulsarTexture().bindTexture(); 184 //int count = 0; 185 //for (int j = 0; j < 3; j++) { 186 for (int j = 0; j < numSegments; j++) { 187 //for (int j = 1; j < 2; j++) { 188 189 boolean isFirst = j == 0; 190 boolean isLast = j == numSegments - 1; 191 boolean isMid = !isFirst && !isLast; 192 193 float alphaCloser = 1f; 194 float alphaFarther = 1f; 195 float r1 = distClose; 196 float r2 = distFar; 197 198 if (isFirst) { 199 alphaCloser = 0f; 200 alphaFarther = 1f; 201 r1 = distClose; 202 r2 = distClose + fadeInDist; 203 } else if (isMid) { 204 alphaCloser = 1f; 205 alphaFarther = 1f; 206 207 //r1 = distClose + fadeInDist; 208 //r2 = distFar - fadeOutDist; 209 r1 = distClose + (j - 1) * distPerInnerSegment + fadeInDist; 210 r2 = r1 + distPerInnerSegment; 211 } else if (isLast) { 212 alphaCloser = 1f; 213 alphaFarther = 0f; 214// r1 = distFar; 215// r2 = distFar + fadeOutDist; 216 r1 = distFar - fadeOutDist; 217 //r1 = distClose + (j - 1) * distPerInnerSegment + fadeInDist; 218 r2 = distFar; 219 } 220 221 222 float w1 = wClose + (wFar - wClose) * (r1 - distClose) / length; 223 float w2 = wClose + (wFar - wClose) * (r2 - distClose) / length; 224 225 float arcClose = (float) Math.toRadians(Misc.computeAngleSpan(w1 / 2f, r1)); 226 float arcFar = (float) Math.toRadians(Misc.computeAngleSpan(w2 / 2f, r2)); 227 228 float closeAnglePerSegment = arcClose / segments; 229 float farAnglePerSegment = arcFar / segments; 230 231 float currCloseAngle = (float) Math.toRadians(angle) - arcClose / 2f; 232 float currFarAngle = (float) Math.toRadians(angle) - arcFar / 2f; 233 234 //float closeTX = 0f - texOffset; 235 //float farTX = texWidth * numIter - texOffset; 236 //texOffset = 0f; 237// float closeTX = texWidth * texPerUnitLength * (r1 - distClose) - texOffset; 238// float farTX = texWidth * texPerUnitLength * (r2 - distClose) - texOffset; 239 240 // horizontal, i.e. along width of beam 241 float texProgress = 0f; 242 243 //texPerUnitLength * (r2 - r1) 244 GL11.glBegin(GL11.GL_QUAD_STRIP); 245 for (float i = 0; i < segments + 1; i++) { 246 float blockedAt = 1f; 247 float blockerMax = 100000f; 248 if (isMid && blocker != null) { 249 blockerMax = blocker.getCurrMaxAt((float) Math.toDegrees((currCloseAngle))); 250 if (blockerMax > blocker.getMaxRange()) { 251 blockerMax = blocker.getMaxRange(); 252 } 253 if (blockerMax < fadeInDist + 100) { 254 blockerMax = fadeInDist + 100; 255 } 256 blockedAt = (blockerMax - r1) / (r2 - r1); 257 if (blockedAt > 1) blockedAt = 1; 258 if (blockedAt < 0) blockedAt = 0; 259 260 rPrev[(int) i] = Math.min(r2, blockerMax); 261 blockedPrev[(int) i] = blockedAt; 262 } 263 264 float curr1 = r1; 265 float curr2 = r2; 266 267 float extraAlpha = 1f; 268// if (isMid || isLast) { 269// if (curr1 > blockerMax) { 270// curr1 = blockerMax; 271// //curr2 = curr1 + distPerInnerSegment; 272// curr2 = curr1; 273// //blockedAt = 0f; 274// //extraAlpha = 0f; 275// } 276// } 277 278 if (isLast) { 279 curr1 = rPrev[(int) i]; 280 float block = blockedPrev[(int) i]; 281 curr2 = curr1 + Math.max(300f, fadeOutDist * block); 282 283// if (block > 0.5f) { 284// curr2 = distFar; 285// } 286 287 //curr2 = curr1 + 200f; 288 289 w2 = wClose + (wFar - wClose) * (curr2 - distClose) / length; 290 arcFar = (float) Math.toRadians(Misc.computeAngleSpan(w2 / 2f, curr2)); 291 farAnglePerSegment = arcFar / segments; 292 currFarAngle = (float) Math.toRadians(angle) - arcFar / 2f + farAnglePerSegment * i; 293 } 294 295 296 float cosClose = (float) Math.cos(currCloseAngle); 297 float sinClose = (float) Math.sin(currCloseAngle); 298 299 float cosFar = (float) Math.cos(currFarAngle); 300 float sinFar = (float) Math.sin(currFarAngle); 301 302 float x1 = cosClose * curr1; 303 float y1 = sinClose * curr1; 304 float x2 = cosFar * curr2; 305 float y2 = sinFar * curr2; 306 307 //if (j == 1 || j == 2) { 308 if (isMid || isLast) { 309 x1 = xPrev[(int) i]; 310 y1 = yPrev[(int) i]; 311 } 312 313 //blockedAt = 1f; 314 x2 = x1 + (x2 - x1) * blockedAt; 315 y2 = y1 + (y2 - y1) * blockedAt; 316 317 xPrev[(int) i] = x2; 318 yPrev[(int) i] = y2; 319 320 float closeTX = texWidth * texPerUnitLength * (curr1 - distClose) - texOffset; 321 float farTX = texWidth * texPerUnitLength * ((curr1 + (curr2 - curr1) * blockedAt) - distClose) - texOffset; 322 323 if (isMid || isLast) { 324 closeTX = texPrev[(int) i]; 325 } 326 texPrev[(int) i] = farTX; 327 328 float edgeMult = 1f; 329 float max = 10; 330 if (i < max) { 331 edgeMult = i / max; 332 } else if (i > segments - 1 - max) { 333 edgeMult = 1f - (i - (segments - max)) / max; 334 } 335 336 Color color = delegate.getPulsarColorForAngle(angle); 337 //color = new Color(100,165,255,200); 338 339 if (arrays) { 340 vertexBuffer.put(x1).put(y1).put(x2).put(y2); 341// vertexBuffer.put((float) Math.random() * -100f).put((float) Math.random() * -100f). 342// put((float) Math.random() * -100f).put((float) Math.random() * -100f); 343 textureBuffer.put(closeTX).put(texProgress).put(farTX).put(texProgress); 344 colorBuffer.put((byte)color.getRed()). 345 put((byte)color.getGreen()). 346 put((byte)color.getBlue()). 347 put((byte)((float) color.getAlpha() * alphaMult * alphaCloser * edgeMult * extraAlpha)); 348 colorBuffer.put((byte)color.getRed()). 349 put((byte)color.getGreen()). 350 put((byte)color.getBlue()). 351 put((byte)((float) color.getAlpha() * alphaMult * alphaFarther * edgeMult * extraAlpha)); 352 } else { 353 GL11.glColor4ub((byte)color.getRed(), 354 (byte)color.getGreen(), 355 (byte)color.getBlue(), 356 (byte)((float) color.getAlpha() * alphaMult * alphaCloser * edgeMult * extraAlpha)); 357 358 GL11.glTexCoord2f(closeTX, texProgress); 359 GL11.glVertex2f(x1, y1); 360 361 GL11.glColor4ub((byte)color.getRed(), 362 (byte)color.getGreen(), 363 (byte)color.getBlue(), 364 (byte)((float) color.getAlpha() * alphaMult * alphaFarther * edgeMult * extraAlpha)); 365 GL11.glTexCoord2f(farTX, texProgress); 366 GL11.glVertex2f(x2, y2); 367 368 //count += 2; 369 } 370 371 texProgress += texPerSegment * 1f; 372 currCloseAngle += closeAnglePerSegment; 373 currFarAngle += farAnglePerSegment; 374 } 375 GL11.glEnd(); 376 } 377 378 //System.out.println("Count: " + count); 379 380 381 if (arrays) { 382 //System.out.println("Pos: " + colorBuffer.position() + ", size: " + colorBuffer.capacity()); 383 vertexBuffer.position(0); 384 textureBuffer.position(0); 385 colorBuffer.position(0); 386 387 GL11.glEnableClientState(GL11.GL_VERTEX_ARRAY); 388 GL11.glEnableClientState(GL11.GL_TEXTURE_COORD_ARRAY); 389 GL11.glEnableClientState(GL11.GL_COLOR_ARRAY); 390 391 GL11.glTexCoordPointer(2, 0, textureBuffer); 392 GL11.glColorPointer(4, true, 0, colorBuffer); 393 GL11.glVertexPointer(2, 0, vertexBuffer); 394 395 396 GL11.glDrawArrays(GL11.GL_QUAD_STRIP, 0, numVertices); 397 398 GL11.glDisableClientState(GL11.GL_VERTEX_ARRAY); 399 GL11.glDisableClientState(GL11.GL_TEXTURE_COORD_ARRAY); 400 GL11.glDisableClientState(GL11.GL_COLOR_ARRAY); 401 } 402 403 404 if (wireframe) GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_FILL); 405 406 GL11.glPopMatrix(); 407 408// GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_FILL); 409 } 410 411} 412 413 414 415