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