/*
* Copyright (c) 2003-onwards Shaven Puppy Ltd
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'Shaven Puppy' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.shavenpuppy.jglib.sprites;
import java.nio.*;
import org.lwjgl.opengl.ContextCapabilities;
import org.lwjgl.opengl.GLContext;
import org.lwjgl.util.ReadableColor;
import org.lwjgl.util.vector.Vector3f;
import com.shavenpuppy.jglib.*;
import com.shavenpuppy.jglib.algorithms.RadixSort;
import com.shavenpuppy.jglib.opengl.GLBaseTexture;
import com.shavenpuppy.jglib.opengl.GLVertexBufferObject;
import com.shavenpuppy.jglib.util.FPMath;
import com.shavenpuppy.jglib.util.FloatList;
import static org.lwjgl.opengl.ARBBufferObject.*;
import static org.lwjgl.opengl.ARBMultitexture.*;
import static org.lwjgl.opengl.ARBVertexBufferObject.*;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL13.*;
/**
* A default sprite renderer. This sorts incoming sprites by layer, Z, then
* optionally by Y coordinate, state, then texture. and stashes vertex, texture,
* and colour coordinates in a buffer. The sprites are rendered by OpenGL11.
*/
class DefaultSpriteRenderer extends Resource implements SpriteRenderer {
private static final long serialVersionUID = 1L;
/** Scratch vector */
private static final Vector3f offset = new Vector3f();
/**
* Ring buffer, used by all the sprite engines.
*/
private static class RingBuffer {
private static final int DEFAULT_STATE_RUNS = 1024;
private class StateRun {
GLBaseTexture texture0, texture1;
Style style;
int start, length;
void render() {
if (style != lastRenderedStyle) {
if (lastRenderedStyle != null) {
lastRenderedStyle.resetState();
}
if (style != null) {
style.setupState();
}
lastRenderedStyle = style;
}
if (texture0 != lastRenderedTexture0) {
if (texture0 != null) {
texture0.render();
}
lastRenderedTexture0 = texture0;
}
if (texture1 != lastRenderedTexture1) {
if (texture1 != null) {
texture1.render();
}
lastRenderedTexture1 = texture1;
}
if (length == 0) {
return;
}
if (style.getRenderSprite()) {
glDrawArrays(GL_QUADS, start, length);
} else {
style.render(start);
}
}
}
int bufferSize, bufferSizeInVertices;
int numBuffers;
boolean useVBOs;
GLVertexBufferObject[] vbo;
MultiBuffer[] buffer;
int sequence = -1, mark = -1;
MultiBuffer current;
StateRun[] stateRun;
GLBaseTexture lastRenderedTexture0, lastRenderedTexture1, currentTexture0, currentTexture1;
Style lastRenderedStyle, currentStyle;
int vertexCursor;
int numRuns;
StateRun currentRun;
RingBuffer() {
}
void create() {
useVBOs = GLContext.getCapabilities().GL_ARB_vertex_buffer_object;
if (useVBOs) {
bufferSize = 1024 * 256;
bufferSizeInVertices = bufferSize / VERTEX_SIZE;
numBuffers = 32;
} else {
bufferSize = 1024 * 1024 * 4;
bufferSizeInVertices = bufferSize / VERTEX_SIZE;
numBuffers = 1;
}
buffer = new MultiBuffer[numBuffers];
if (useVBOs) {
vbo = new GLVertexBufferObject[numBuffers];
for (int i = 0; i < numBuffers; i ++) {
vbo[i] = new GLVertexBufferObject(bufferSize, GL_ARRAY_BUFFER_ARB, GL_STREAM_DRAW_ARB);
vbo[i].create();
}
} else {
for (int i = 0; i < numBuffers; i ++) {
buffer[i] = new MultiBuffer(bufferSize);
}
}
stateRun = new StateRun[DEFAULT_STATE_RUNS];
for (int i = 0; i < stateRun.length; i ++) {
stateRun[i] = new StateRun();
}
}
private void next() {
// Select the next buffer in the sequence.
sequence ++;
if (sequence == numBuffers) {
sequence = 0;
}
if (sequence == mark) {
System.out.println("Buffer overrun");
}
if (useVBOs) {
vbo[sequence].render();
ByteBuffer buf = vbo[sequence].map();
if (buffer[sequence] == null || buffer[sequence].bytes != buf) {
buffer[sequence] = new MultiBuffer(buf);
}
}
current = buffer[sequence];
current.bytes.clear();
current.floats.clear();
current.ints.clear();
ContextCapabilities capabilities = GLContext.getCapabilities();
if (useVBOs) {
glVertexPointer(3, GL_FLOAT, VERTEX_SIZE, 0);
if (capabilities.GL_ARB_multitexture) {
glClientActiveTextureARB(GL_TEXTURE1_ARB);
glTexCoordPointer(2, GL_FLOAT, VERTEX_SIZE, TEXTURE1_COORD_OFFSET);
glClientActiveTextureARB(GL_TEXTURE0_ARB);
} else if (capabilities.OpenGL13) {
glClientActiveTexture(GL_TEXTURE1);
glTexCoordPointer(2, GL_FLOAT, VERTEX_SIZE, TEXTURE1_COORD_OFFSET);
glClientActiveTexture(GL_TEXTURE1);
}
glTexCoordPointer(2, GL_FLOAT, VERTEX_SIZE, TEXTURE0_COORD_OFFSET);
glColorPointer(4, GL_UNSIGNED_BYTE, VERTEX_SIZE, COLOR_OFFSET);
} else {
glVertexPointer(3, VERTEX_SIZE, current.floats);
if (capabilities.GL_ARB_multitexture) {
glClientActiveTextureARB(GL_TEXTURE1_ARB);
glTexCoordPointer(2, VERTEX_SIZE, Memory.chop(current.bytes, TEXTURE1_COORD_OFFSET).asFloatBuffer());
glClientActiveTextureARB(GL_TEXTURE0_ARB);
} else if (capabilities.OpenGL13) {
glClientActiveTexture(GL_TEXTURE1);
glTexCoordPointer(2, VERTEX_SIZE, Memory.chop(current.bytes, TEXTURE1_COORD_OFFSET).asFloatBuffer());
glClientActiveTexture(GL_TEXTURE1);
}
glTexCoordPointer(2, VERTEX_SIZE, Memory.chop(current.bytes, TEXTURE0_COORD_OFFSET).asFloatBuffer());
glColorPointer(4, true, VERTEX_SIZE, Memory.chop(current.bytes, COLOR_OFFSET));
}
vertexCursor = 0;
numRuns = 0;
currentRun = null;
}
void begin() {
currentStyle = null;
currentTexture0 = null;
currentTexture1 = null;
next();
mark = sequence;
glEnableClientState(GL_VERTEX_ARRAY);
}
void finish() {
// Render out what's left over
render();
if (useVBOs) {
glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
}
}
void growStateRuns() {
StateRun[] newStateRun = new StateRun[(int)(stateRun.length * 1.5f)];
System.arraycopy(stateRun, 0, newStateRun, 0, stateRun.length);
for (int i = numRuns; i < newStateRun.length; i ++) {
newStateRun[i] = new StateRun();
}
stateRun = newStateRun;
}
void add(Sprite s, Style newStyle, float engineAlpha) {
if (vertexCursor + 4 > bufferSizeInVertices) {
// Sprite won't fit, so flush first
render();
next();
}
SpriteImage image = s.getImage();
GLBaseTexture newTexture0 = image != null ? image.getTexture() : null;
GLBaseTexture newTexture1 = s.getTexture();
if (currentRun == null || newStyle != currentStyle || newTexture0 != currentTexture0 || newTexture1 != currentTexture1) {
// Changed state. Start new state.
currentRun = stateRun[numRuns];
currentRun.start = vertexCursor;
currentRun.length = 0;
currentRun.style = newStyle;
currentRun.texture0 = newTexture0;
currentRun.texture1 = newTexture1;
numRuns ++;
if (numRuns == stateRun.length) {
// Grow the array
growStateRuns();
}
currentStyle = newStyle;
currentTexture0 = newTexture0;
currentTexture1 = newTexture1;
}
final float w = image.getWidth();
final float h = image.getHeight();
final float tx0 = image.getTx0();
final float tx1 = image.getTx1();
final float ty0 = image.getTy0();
final float ty1 = image.getTy1();
final int xscale = s.getXScale(); // 8 bits fraction
final int yscale = s.getYScale(); // 8 bits fraction
s.getOffset(offset);
final float x = s.getX() + offset.getX();
final float y = s.getY() + offset.getY();
final float z = s.getZ() + offset.getZ();
final float alphaDiv = 1.0f / 255.0f;
final float alpha = engineAlpha * s.getAlpha() * alphaDiv;
final double angle = FPMath.doubleValue(s.getAngle()) * Math.PI * 2.0;
// First scale then rotate coordinates
float scaledx0 = -image.getHotspotX();
float scaledy0 = -image.getHotspotY();
float scaledx1 = scaledx0 + w;
float scaledy1 = scaledy0 + h;
// Scale 'em first
if (xscale != NO_SCALE || yscale != NO_SCALE) {
float fxScale = FPMath.floatValue(xscale);
float fyScale = FPMath.floatValue(yscale);
scaledx0 = scaledx0 * fxScale;
scaledx1 = scaledx1 * fxScale;
scaledy0 = scaledy0 * fyScale;
scaledy1 = scaledy1 * fyScale;
}
float scaledx00, scaledx10, scaledx11, scaledx01, scaledy00, scaledy10, scaledy11, scaledy01;
// Then rotate
if (angle != 0) {
double cos = Math.cos(angle);
double sin = Math.sin(angle);
scaledx00 = (float) (cos * scaledx0 - sin * scaledy0);
scaledx10 = (float) (cos * scaledx1 - sin * scaledy0);
scaledx11 = (float) (cos * scaledx1 - sin * scaledy1);
scaledx01 = (float) (cos * scaledx0 - sin * scaledy1);
scaledy00 = (float) (sin * scaledx0 + cos * scaledy0);
scaledy10 = (float) (sin * scaledx1 + cos * scaledy0);
scaledy11 = (float) (sin * scaledx1 + cos * scaledy1);
scaledy01 = (float) (sin * scaledx0 + cos * scaledy1);
} else {
scaledx00 = scaledx0;
scaledx10 = scaledx1;
scaledx11 = scaledx1;
scaledx01 = scaledx0;
scaledy00 = scaledy0;
scaledy10 = scaledy0;
scaledy11 = scaledy1;
scaledy01 = scaledy1;
}
// Then translate them
final float x00 = scaledx00 + x;
final float x01 = scaledx01 + x;
final float x11 = scaledx11 + x;
final float x10 = scaledx10 + x;
final float y00 = scaledy00 + y;
final float y01 = scaledy01 + y;
final float y11 = scaledy11 + y;
final float y10 = scaledy10 + y;
FloatBuffer floats = current.floats;
IntBuffer ints = current.ints;
int vertex = vertexCursor * VERTEX_SIZE;
floats.position(vertex >> 2);
ints.position((vertex + COLOR_OFFSET) >> 2);
floats.put(x00);
floats.put(y00);
floats.put(z);
floats.put(s.isMirrored() ? tx1 : tx0);
floats.put(s.isFlipped() ? ty0 : ty1);
floats.put(s.getTx00());
floats.put(s.getTy00());
ReadableColor color = s.getColor(0);
ints.put((color.getRed() << 0) | (color.getGreen() << 8) | (color.getBlue() << 16)
| (int) (color.getAlpha() * alpha) << 24);
vertex += VERTEX_SIZE;
floats.position(vertex >> 2);
ints.position((vertex + COLOR_OFFSET) >> 2);
floats.put(x10);
floats.put(y10);
floats.put(z);
floats.put(s.isMirrored() ? tx0 : tx1);
floats.put(s.isFlipped() ? ty0 : ty1);
floats.put(s.getTx10());
floats.put(s.getTy10());
color = s.getColor(1);
ints.put((color.getRed() << 0) | (color.getGreen() << 8) | (color.getBlue() << 16)
| (int) (color.getAlpha() * alpha) << 24);
vertex += VERTEX_SIZE;
floats.position(vertex >> 2);
ints.position((vertex + COLOR_OFFSET) >> 2);
floats.put(x11);
floats.put(y11);
floats.put(z);
floats.put(s.isMirrored() ? tx0 : tx1);
floats.put(s.isFlipped() ? ty1 : ty0);
floats.put(s.getTx11());
floats.put(s.getTy11());
color = s.getColor(2);
ints.put((color.getRed() << 0) | (color.getGreen() << 8) | (color.getBlue() << 16)
| (int) (color.getAlpha() * alpha) << 24);
vertex += VERTEX_SIZE;
floats.position(vertex >> 2);
ints.position((vertex + COLOR_OFFSET) >> 2);
floats.put(x01);
floats.put(y01);
floats.put(z);
floats.put(s.isMirrored() ? tx1 : tx0);
floats.put(s.isFlipped() ? ty1 : ty0);
floats.put(s.getTx01());
floats.put(s.getTy01());
color = s.getColor(3);
ints.put((color.getRed() << 0) | (color.getGreen() << 8) | (color.getBlue() << 16)
| (int) (color.getAlpha() * alpha) << 24);
vertexCursor += 4;
currentRun.length += 4;
}
void add(Style s) {
// Build the geometry
FloatList data = s.build();
// Does it fit?
int vertsToWrite = data.size() >> 3;
if (vertexCursor + vertsToWrite > bufferSizeInVertices) {
// No. Flush what we have so far.
// System.out.println(" FLUSH 2");
render();
next();
}
// Always create a new run
currentRun = stateRun[numRuns];
currentRun.start = vertexCursor;
currentRun.length = vertsToWrite;
currentRun.style = s;
currentRun.texture0 = null;
currentRun.texture1 = null;
numRuns ++;
if (numRuns == stateRun.length) {
growStateRuns();
}
// Write the data out
current.floats.position(vertexCursor * VERTEX_SIZE >> 2);
current.floats.put(data.array(), 0, data.size());
vertexCursor += vertsToWrite;
currentStyle = null;
currentTexture0 = null;
currentTexture1 = null;
}
void render() {
//System.out.println(" RENDER "+numRuns+" RUNS");
if (useVBOs) {
vbo[sequence].unmap();
}
lastRenderedStyle = null;
lastRenderedTexture0 = null;
lastRenderedTexture1 = null;
for (int i = 0; i < numRuns; i ++) {
stateRun[i].render();
}
if (lastRenderedStyle != null) {
lastRenderedStyle.resetState();
}
}
}
private static RingBuffer ringBuffer;
/** A radix sorter for sorting */
private final RadixSort sort = new RadixSort();
/** Handy constant */
private static final int NO_SCALE = FPMath.ONE;
/** Debug */
private static final boolean DEBUG = true;
/** Alpha */
private float alpha;
/** Whether sprites are unique (rendered only once) */
private final boolean uniqueSprites;
/*
* Arrays for sorting
*/
private int[] sort_z;
private int[] sort_y;
private int[] sort_x;
private int[] sort_layer;
private int[] sort_sublayer;
private int[] sort_style;
private int[] sort_texture0;
private int[] sort_texture1;
/** Whether to sort Y coords */
private final boolean sortY;
/** The sprites being rendered */
private Sprite[] sprite;
/** Sprite styles */
private Style[] style;
/** The number of sprites being rendered */
private int numSprites;
/** Sort layer - sprites above this layer won't be Y sorted */
private int sortLayer;
/** The size of a single vertex in bytes: x,y,z,tx,ty,tx1,ty1,r,g,b,a */
static final int VERTEX_SIZE = 32;
static final int TEXTURE0_COORD_OFFSET = 12;
static final int TEXTURE1_COORD_OFFSET = 20;
static final int COLOR_OFFSET = 28;
/**
* Constructor for SpriteRenderer.
* @param sortY
* sort the sprites by Y coordinate
* @param sortLayer TODO
* @param uniqueSprites
* If true, then you may submit a sprite more than once to the
* render() function; this comes at some penalty in speed and
* memory bandwidth. If false, then each sprite may be rendered
* only once. Set to true if you are doing scrolling and need to
* render sprites multiple times.
*/
DefaultSpriteRenderer(boolean sortY, int sortLayer, boolean uniqueSprites) {
this.sortY = sortY;
this.sortLayer = sortLayer;
this.uniqueSprites = uniqueSprites;
sprite = new Sprite[1];
if (uniqueSprites) {
for (int i = 0; i < sprite.length; i++) {
sprite[i] = new Sprite(null);
}
}
style = new Style[1];
// Initialize radix sort
sort_layer = new int[1];
sort_sublayer = new int[1];
sort_texture0 = new int[1];
sort_texture1 = new int[1];
sort_style = new int[1];
sort_z = new int[1];
if (sortY) {
sort_y = new int[1];
sort_x = new int[1];
} else {
sort_y = null;
sort_x = null;
}
sort.resetIndices();
}
@Override
protected void doCreate() {
if (ringBuffer == null) {
ringBuffer = new RingBuffer();
ringBuffer.create();
}
// Ensure the flash style is created too
if (FlashStyle.instance == null) {
FlashStyle.instance = new FlashStyle("flash.style");
FlashStyle.instance.create();
}
}
@Override
protected void doDestroy() {
}
@Override
public void render(Sprite s) {
if (!s.isVisible()) {
return;
}
SpriteImage image = s.getImage();
Style spriteStyle = s.getStyle();
if (image == null && spriteStyle == null) {
return; // No point in doing anything with this sprite
}
// Check for flashing sprites, which overrides their default image rendering style with the FlashingStyle:
if (s.isFlashing() && GLContext.getCapabilities().GL_EXT_secondary_color) {
// Only need to render the sprite once, which will make it stark
// white:
addSprite(FlashStyle.instance, s);
style[numSprites] = FlashStyle.instance;
// if (uniqueSprites) {
// Sprite copy = sprite[numSprites++];
// copy.copy(s);
// } else {
// sprite[numSprites++] = s;
// }
} else {
addSprite(spriteStyle, s);
// style[numSprites] = spriteStyle;
// if (uniqueSprites) {
// sprite[numSprites++].copy(s);
// } else {
// sprite[numSprites++] = s;
// }
}
}
private void addSprite(Style spriteStyle, Sprite s) {
if (numSprites == sprite.length) {
int[] old_sort_z = sort_z;
sort_z = new int[numSprites * 2];
System.arraycopy(old_sort_z, 0, sort_z, 0, numSprites);
old_sort_z = null;
if (sortY) {
int[] old_sort_y = sort_y;
sort_y = new int[numSprites * 2];
System.arraycopy(old_sort_y, 0, sort_y, 0, numSprites);
old_sort_y = null;
int[] old_sort_x = sort_x;
sort_x = new int[numSprites * 2];
System.arraycopy(old_sort_x, 0, sort_x, 0, numSprites);
old_sort_x = null;
}
int[] old_sort_layer = sort_layer;
sort_layer = new int[numSprites * 2];
System.arraycopy(old_sort_layer, 0, sort_layer, 0, numSprites);
old_sort_layer = null;
int[] old_sort_sublayer = sort_sublayer;
sort_sublayer = new int[numSprites * 2];
System.arraycopy(old_sort_sublayer, 0, sort_sublayer, 0, numSprites);
old_sort_sublayer = null;
int[] old_sort_style = sort_style;
sort_style = new int[numSprites * 2];
System.arraycopy(old_sort_style, 0, sort_style, 0, numSprites);
old_sort_style = null;
int[] old_sort_texture0 = sort_texture0;
sort_texture0 = new int[numSprites * 2];
System.arraycopy(old_sort_texture0, 0, sort_texture0, 0, numSprites);
old_sort_texture0 = null;
int[] old_sort_texture1 = sort_texture1;
sort_texture1 = new int[numSprites * 2];
System.arraycopy(old_sort_texture1, 0, sort_texture1, 0, numSprites);
old_sort_z = null;
Sprite[] old_sprite = sprite;
sprite = new Sprite[numSprites * 2];
System.arraycopy(old_sprite, 0, sprite, 0, numSprites);
old_sprite = null;
Style[] old_style = style;
style = new Style[numSprites * 2];
System.arraycopy(old_style, 0, style, 0, numSprites);
old_style = null;
}
style[numSprites] = spriteStyle;
if (uniqueSprites) {
Sprite copy = sprite[numSprites];
if (copy == null) {
copy = sprite[numSprites] = new Sprite(null);
}
copy.copy(s);
} else {
sprite[numSprites] = s;
}
numSprites ++;
}
@Override
public void postRender() {
// If there are no sprites, do nothing
if (numSprites == 0) {
return;
}
// Sort sprites first
sort();
// Build the texture/style runs up
ringBuffer.begin();
build();
ringBuffer.finish();
}
/**
* Build up a list of texture runs. We scan through the sorted list of
* sprites so far and build up runs of sprites which share the same texture.
*/
private void build() {
final int[] index = sort.getIndices();
for (int i = 0; i < numSprites; i++) {
int idx = index[i];
Sprite s = sprite[idx];
if (s.isVisible()) {
Style newStyle = style[idx];
if (newStyle.getRenderSprite()) {
// It's a sprite
ringBuffer.add(s, newStyle, alpha);
} else {
// It's geometry; we build it to find out how many vertices it's got
ringBuffer.add(newStyle);
}
}
}
}
@Override
public void preRender() {
// Reset everything
numSprites = 0;
}
/**
* Sort sprites
*/
private void sort() {
final int n = numSprites;
if (sortY) {
for (int i = 0; i < n; i++) {
final Sprite s = sprite[i];
if (s.isVisible()) {
sort_x[i] = (int) s.getX();
sort_layer[i] = s.getLayer();
sort_sublayer[i] = s.getSubLayer();
sort_style[i] = style[i].getStyleID();
if (s.getStyle().getRenderSprite()) {
final SpriteImage si = s.getImage();
GLBaseTexture tex = si.getTexture();
sort_texture0[i] = tex == null ? 0 : tex.getID();
GLBaseTexture t1 = s.getTexture();
if (t1 != null) {
sort_texture1[i] = t1.getID();
} else {
sort_texture1[i] = -1;
}
if (s.getLayer() >= sortLayer) {
sort_y[i] = 0;
} else {
sort_y[i] = (int) -(s.getY() + s.getYSortOffset());
}
} else {
sort_texture0[i] = 0;
sort_texture1[i] = -1;
sort_y[i] = 0;
}
}
}
sort.resetIndices().sort(sort_style, n).sort(sort_texture0, n).sort(sort_texture1, n).sort(sort_x, n).sort(
sort_sublayer, n).sort(sort_y, n).sort(sort_layer, n);
} else {
for (int i = 0; i < n; i++) {
final Sprite s = sprite[i];
if (s.isVisible()) {
sort_z[i] = (int) s.getZ();
sort_layer[i] = s.getLayer();
sort_sublayer[i] = s.getSubLayer();
sort_style[i] = style[i].getStyleID();
if (s.getStyle().getRenderSprite()) {
final SpriteImage si = s.getImage();
GLBaseTexture tex = si.getTexture();
sort_texture0[i] = tex == null ? 0 : tex.getID();
GLBaseTexture t1 = s.getTexture();
if (t1 != null) {
sort_texture1[i] = t1.getID();
} else {
sort_texture1[i] = -1;
}
} else {
sort_texture0[i] = 0;
sort_texture1[i] = -1;
}
}
}
sort.resetIndices().sort(sort_style, n).sort(sort_texture0, n).sort(sort_texture1, n).sort(sort_sublayer, n).sort(
sort_z, n).sort(sort_layer, n);
}
}
/**
* @param alpha
* The alpha to set.
*/
void setAlpha(float alpha) {
this.alpha = alpha;
}
}