My favorites | Sign in
Project Home Downloads Wiki Issues Source
Checkout   Browse   Changes    
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
/*******************************************************************************
* Copyright 2011 See AUTHORS file.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/

package com.badlogic.gdx.graphics.g2d;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.GL11;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.GLCommon;
import com.badlogic.gdx.graphics.Mesh;
import com.badlogic.gdx.graphics.Mesh.VertexDataType;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.VertexAttribute;
import com.badlogic.gdx.graphics.VertexAttributes.Usage;
import com.badlogic.gdx.graphics.glutils.ShaderProgram;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Matrix4;
import com.badlogic.gdx.utils.Disposable;
import com.badlogic.gdx.utils.NumberUtils;

/** <p>
* A SpriteBatch is used to draw 2D rectangles that reference a texture (region). The class will batch the drawing commands and
* optimize them for processing by the GPU.
* </p>
*
* <p>
* To draw something with a SpriteBatch one has to first call the {@link SpriteBatch#begin()} method which will setup appropriate
* render states. When you are done with drawing you have to call {@link SpriteBatch#end()} which will actually draw the things
* you specified.
* </p>
*
* <p>
* All drawing commands of the SpriteBatch operate in screen coordinates. The screen coordinate system has an x-axis pointing to
* the right, an y-axis pointing upwards and the origin is in the lower left corner of the screen. You can also provide your own
* transformation and projection matrices if you so wish.
* </p>
*
* <p>
* A SpriteBatch is managed. In case the OpenGL context is lost all OpenGL resources a SpriteBatch uses internally get
* invalidated. A context is lost when a user switches to another application or receives an incoming call on Android. A
* SpriteBatch will be automatically reloaded after the OpenGL context is restored.
* </p>
*
* <p>
* A SpriteBatch is a pretty heavy object so you should only ever have one in your program.
* </p>
*
* <p>
* A SpriteBatch works with OpenGL ES 1.x and 2.0. In the case of a 2.0 context it will use its own custom shader to draw all
* provided sprites. You can set your own custom shader via {@link #setShader(ShaderProgram)}.
* </p>
*
* <p>
* A SpriteBatch has to be disposed if it is no longer used.
* </p>
*
* @author mzechner */
public class SpriteBatch implements Disposable {
private Mesh mesh;
private Mesh[] buffers;

private Texture lastTexture = null;
private float invTexWidth = 0;
private float invTexHeight = 0;

private int idx = 0;
private int currBufferIdx = 0;
private final float[] vertices;

private final Matrix4 transformMatrix = new Matrix4();
private final Matrix4 projectionMatrix = new Matrix4();
private final Matrix4 combinedMatrix = new Matrix4();

private boolean drawing = false;

private boolean blendingDisabled = false;
private int blendSrcFunc = GL11.GL_SRC_ALPHA;
private int blendDstFunc = GL11.GL_ONE_MINUS_SRC_ALPHA;

private final ShaderProgram shader;
private boolean ownsShader;

float color = Color.WHITE.toFloatBits();
private Color tempColor = new Color(1, 1, 1, 1);

/** number of render calls since last {@link #begin()} **/
public int renderCalls = 0;

/** number of rendering calls ever, will not be reset, unless it's done manually **/
public int totalRenderCalls = 0;

/** the maximum number of sprites rendered in one batch so far **/
public int maxSpritesInBatch = 0;
private ShaderProgram customShader = null;

/** Constructs a new SpriteBatch. Sets the projection matrix to an orthographic projection with y-axis point upwards, x-axis
* point to the right and the origin being in the bottom left corner of the screen. The projection will be pixel perfect with
* respect to the screen resolution. */
public SpriteBatch () {
this(1000);
}

/** Constructs a SpriteBatch with the specified size and (if GL2) the default shader. See
* {@link #SpriteBatch(int, ShaderProgram)}. */
public SpriteBatch (int size) {
this(size, null);
}

/** <p>
* Constructs a new SpriteBatch. Sets the projection matrix to an orthographic projection with y-axis point upwards, x-axis
* point to the right and the origin being in the bottom left corner of the screen. The projection will be pixel perfect with
* respect to the screen resolution.
* </p>
*
* <p>
* The size parameter specifies the maximum size of a single batch in number of sprites
* </p>
*
* <p>
* The defaultShader specifies the shader to use. Note that the names for uniforms for this default
* shader are different than the ones expect for shaders set with {@link #setShader(ShaderProgram)}.
* See the {@link #createDefaultShader()} method.
* </p>
*
* @param size the batch size in number of sprites
* @param defaultShader the default shader to use. This is not owned by the SpriteBatch and must be disposed separately. */
public SpriteBatch (int size, ShaderProgram defaultShader) {
this(size, 1, defaultShader);
}

/** Constructs a SpriteBatch with the specified size and number of buffers and (if GL2) the default shader. See
* {@link #SpriteBatch(int, int, ShaderProgram)}. */
public SpriteBatch (int size, int buffers) {
this(size, buffers, null);
}

/** <p>
* Constructs a new SpriteBatch. Sets the projection matrix to an orthographic projection with y-axis point upwards, x-axis
* point to the right and the origin being in the bottom left corner of the screen. The projection will be pixel perfect with
* respect to the screen resolution.
* </p>
*
* <p>
* The size parameter specifies the maximum size of a single batch in number of sprites
* </p>
*
* <p>
* The defaultShader specifies the shader to use. Note that the names for uniforms for this default
* shader are different than the ones expect for shaders set with {@link #setShader(ShaderProgram)}.
* See the {@link #createDefaultShader()} method.
* </p>
*
* @param size the batch size in number of sprites
* @param buffers the number of buffers to use. only makes sense with VBOs. This is an expert function.
* @param defaultShader the default shader to use. This is not owned by the SpriteBatch and must be disposed separately. */
public SpriteBatch (int size, int buffers, ShaderProgram defaultShader) {
this.buffers = new Mesh[buffers];

for (int i = 0; i < buffers; i++) {
this.buffers[i] = new Mesh(VertexDataType.VertexArray, false, size * 4, size * 6, new VertexAttribute(Usage.Position, 2,
ShaderProgram.POSITION_ATTRIBUTE), new VertexAttribute(Usage.ColorPacked, 4, ShaderProgram.COLOR_ATTRIBUTE),
new VertexAttribute(Usage.TextureCoordinates, 2, ShaderProgram.TEXCOORD_ATTRIBUTE + "0"));
}

projectionMatrix.setToOrtho2D(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());

vertices = new float[size * Sprite.SPRITE_SIZE];

int len = size * 6;
short[] indices = new short[len];
short j = 0;
for (int i = 0; i < len; i += 6, j += 4) {
indices[i + 0] = (short)(j + 0);
indices[i + 1] = (short)(j + 1);
indices[i + 2] = (short)(j + 2);
indices[i + 3] = (short)(j + 2);
indices[i + 4] = (short)(j + 3);
indices[i + 5] = (short)(j + 0);
}
for (int i = 0; i < buffers; i++) {
this.buffers[i].setIndices(indices);
}
mesh = this.buffers[0];

if (Gdx.graphics.isGL20Available() && defaultShader == null) {
shader = createDefaultShader();
ownsShader = true;
} else
shader = defaultShader;
}

/** Returns a new instance of the default shader used by SpriteBatch for GL2 when no shader is specified. */
static public ShaderProgram createDefaultShader () {
String vertexShader = "attribute vec4 " + ShaderProgram.POSITION_ATTRIBUTE + ";\n" //
+ "attribute vec4 " + ShaderProgram.COLOR_ATTRIBUTE + ";\n" //
+ "attribute vec2 " + ShaderProgram.TEXCOORD_ATTRIBUTE + "0;\n" //
+ "uniform mat4 u_projectionViewMatrix;\n" //
+ "varying vec4 v_color;\n" //
+ "varying vec2 v_texCoords;\n" //
+ "\n" //
+ "void main()\n" //
+ "{\n" //
+ " v_color = " + ShaderProgram.COLOR_ATTRIBUTE + ";\n" //
+ " v_texCoords = " + ShaderProgram.TEXCOORD_ATTRIBUTE + "0;\n" //
+ " gl_Position = u_projectionViewMatrix * " + ShaderProgram.POSITION_ATTRIBUTE + ";\n" //
+ "}\n";
String fragmentShader = "#ifdef GL_ES\n" //
+ "#define LOWP lowp\n" //
+ "precision mediump float;\n" //
+ "#else\n" //
+ "#define LOWP \n" //
+ "#endif\n" //
+ "varying LOWP vec4 v_color;\n" //
+ "varying vec2 v_texCoords;\n" //
+ "uniform sampler2D u_texture;\n" //
+ "void main()\n"//
+ "{\n" //
+ " gl_FragColor = v_color * texture2D(u_texture, v_texCoords);\n" //
+ "}";

ShaderProgram shader = new ShaderProgram(vertexShader, fragmentShader);
if (shader.isCompiled() == false) throw new IllegalArgumentException("couldn't compile shader: " + shader.getLog());
return shader;
}

/** Sets up the SpriteBatch for drawing. This will disable depth buffer writting. It enables blending and texturing. If you have
* more texture units enabled than the first one you have to disable them before calling this. Uses a screen coordinate system
* by default where everything is given in pixels. You can specify your own projection and modelview matrices via
* {@link #setProjectionMatrix(Matrix4)} and {@link #setTransformMatrix(Matrix4)}. */
public void begin () {
if (drawing) throw new IllegalStateException("you have to call SpriteBatch.end() first");
renderCalls = 0;

Gdx.gl.glDepthMask(false);
if (Gdx.graphics.isGL20Available()) {
if (customShader != null)
customShader.begin();
else
shader.begin();
} else {
Gdx.gl.glEnable(GL10.GL_TEXTURE_2D);
}
setupMatrices();

idx = 0;
lastTexture = null;
drawing = true;
}

/** Finishes off rendering. Enables depth writes, disables blending and texturing. Must always be called after a call to
* {@link #begin()} */
public void end () {
if (!drawing) throw new IllegalStateException("SpriteBatch.begin must be called before end.");
if (idx > 0) renderMesh();
lastTexture = null;
idx = 0;
drawing = false;

GLCommon gl = Gdx.gl;
gl.glDepthMask(true);
if (isBlendingEnabled()) gl.glDisable(GL10.GL_BLEND);

if (Gdx.graphics.isGL20Available()) {
if (customShader != null)
customShader.end();
else
shader.end();
} else {
gl.glDisable(GL10.GL_TEXTURE_2D);
}
}

/** Sets the color used to tint images when they are added to the SpriteBatch. Default is {@link Color#WHITE}. */
public void setColor (Color tint) {
color = tint.toFloatBits();
}

/** @see #setColor(Color) */
public void setColor (float r, float g, float b, float a) {
int intBits = (int)(255 * a) << 24 | (int)(255 * b) << 16 | (int)(255 * g) << 8 | (int)(255 * r);
color = NumberUtils.intToFloatColor(intBits);
}

/** @see #setColor(Color)
* @see Color#toFloatBits() */
public void setColor (float color) {
this.color = color;
}

/** @return the rendering color of this SpriteBatch. Manipulating the returned instance has no effect. */
public Color getColor () {
int intBits = NumberUtils.floatToIntColor(color);
Color color = this.tempColor;
color.r = (intBits & 0xff) / 255f;
color.g = ((intBits >>> 8) & 0xff) / 255f;
color.b = ((intBits >>> 16) & 0xff) / 255f;
color.a = ((intBits >>> 24) & 0xff) / 255f;
return color;
}

/** Draws a rectangle with the bottom left corner at x,y having the given width and height in pixels. The rectangle is offset by
* originX, originY relative to the origin. Scale specifies the scaling factor by which the rectangle should be scaled around
* originX, originY. Rotation specifies the angle of counter clockwise rotation of the rectangle around originX, originY. The
* portion of the {@link Texture} given by srcX, srcY and srcWidth, srcHeight is used. These coordinates and sizes are given in
* texels. FlipX and flipY specify whether the texture portion should be fliped horizontally or vertically.
*
* @param texture the Texture
* @param x the x-coordinate in screen space
* @param y the y-coordinate in screen space
* @param originX the x-coordinate of the scaling and rotation origin relative to the screen space coordinates
* @param originY the y-coordinate of the scaling and rotation origin relative to the screen space coordinates
* @param width the width in pixels
* @param height the height in pixels
* @param scaleX the scale of the rectangle around originX/originY in x
* @param scaleY the scale of the rectangle around originX/originY in y
* @param rotation the angle of counter clockwise rotation of the rectangle around originX/originY
* @param srcX the x-coordinate in texel space
* @param srcY the y-coordinate in texel space
* @param srcWidth the source with in texels
* @param srcHeight the source height in texels
* @param flipX whether to flip the sprite horizontally
* @param flipY whether to flip the sprite vertically */
public void draw (Texture texture, float x, float y, float originX, float originY, float width, float height, float scaleX,
float scaleY, float rotation, int srcX, int srcY, int srcWidth, int srcHeight, boolean flipX, boolean flipY) {
if (!drawing) throw new IllegalStateException("SpriteBatch.begin must be called before draw.");

if (texture != lastTexture) {
switchTexture(texture);
} else if (idx == vertices.length) renderMesh();

// bottom left and top right corner points relative to origin
final float worldOriginX = x + originX;
final float worldOriginY = y + originY;
float fx = -originX;
float fy = -originY;
float fx2 = width - originX;
float fy2 = height - originY;

// scale
if (scaleX != 1 || scaleY != 1) {
fx *= scaleX;
fy *= scaleY;
fx2 *= scaleX;
fy2 *= scaleY;
}

// construct corner points, start from top left and go counter clockwise
final float p1x = fx;
final float p1y = fy;
final float p2x = fx;
final float p2y = fy2;
final float p3x = fx2;
final float p3y = fy2;
final float p4x = fx2;
final float p4y = fy;

float x1;
float y1;
float x2;
float y2;
float x3;
float y3;
float x4;
float y4;

// rotate
if (rotation != 0) {
final float cos = MathUtils.cosDeg(rotation);
final float sin = MathUtils.sinDeg(rotation);

x1 = cos * p1x - sin * p1y;
y1 = sin * p1x + cos * p1y;

x2 = cos * p2x - sin * p2y;
y2 = sin * p2x + cos * p2y;

x3 = cos * p3x - sin * p3y;
y3 = sin * p3x + cos * p3y;

x4 = x1 + (x3 - x2);
y4 = y3 - (y2 - y1);
} else {
x1 = p1x;
y1 = p1y;

x2 = p2x;
y2 = p2y;

x3 = p3x;
y3 = p3y;

x4 = p4x;
y4 = p4y;
}

x1 += worldOriginX;
y1 += worldOriginY;
x2 += worldOriginX;
y2 += worldOriginY;
x3 += worldOriginX;
y3 += worldOriginY;
x4 += worldOriginX;
y4 += worldOriginY;

float u = srcX * invTexWidth;
float v = (srcY + srcHeight) * invTexHeight;
float u2 = (srcX + srcWidth) * invTexWidth;
float v2 = srcY * invTexHeight;

if (flipX) {
float tmp = u;
u = u2;
u2 = tmp;
}

if (flipY) {
float tmp = v;
v = v2;
v2 = tmp;
}

vertices[idx++] = x1;
vertices[idx++] = y1;
vertices[idx++] = color;
vertices[idx++] = u;
vertices[idx++] = v;

vertices[idx++] = x2;
vertices[idx++] = y2;
vertices[idx++] = color;
vertices[idx++] = u;
vertices[idx++] = v2;

vertices[idx++] = x3;
vertices[idx++] = y3;
vertices[idx++] = color;
vertices[idx++] = u2;
vertices[idx++] = v2;

vertices[idx++] = x4;
vertices[idx++] = y4;
vertices[idx++] = color;
vertices[idx++] = u2;
vertices[idx++] = v;
}

/** Draws a rectangle with the bottom left corner at x,y having the given width and height in pixels. The portion of the
* {@link Texture} given by srcX, srcY and srcWidth, srcHeight is used. These coordinates and sizes are given in texels. FlipX
* and flipY specify whether the texture portion should be fliped horizontally or vertically.
*
* @param texture the Texture
* @param x the x-coordinate in screen space
* @param y the y-coordinate in screen space
* @param width the width in pixels
* @param height the height in pixels
* @param srcX the x-coordinate in texel space
* @param srcY the y-coordinate in texel space
* @param srcWidth the source with in texels
* @param srcHeight the source height in texels
* @param flipX whether to flip the sprite horizontally
* @param flipY whether to flip the sprite vertically */
public void draw (Texture texture, float x, float y, float width, float height, int srcX, int srcY, int srcWidth,
int srcHeight, boolean flipX, boolean flipY) {
if (!drawing) throw new IllegalStateException("SpriteBatch.begin must be called before draw.");

if (texture != lastTexture) {
switchTexture(texture);
} else if (idx == vertices.length) renderMesh();

float u = srcX * invTexWidth;
float v = (srcY + srcHeight) * invTexHeight;
float u2 = (srcX + srcWidth) * invTexWidth;
float v2 = srcY * invTexHeight;
final float fx2 = x + width;
final float fy2 = y + height;

if (flipX) {
float tmp = u;
u = u2;
u2 = tmp;
}

if (flipY) {
float tmp = v;
v = v2;
v2 = tmp;
}

vertices[idx++] = x;
vertices[idx++] = y;
vertices[idx++] = color;
vertices[idx++] = u;
vertices[idx++] = v;

vertices[idx++] = x;
vertices[idx++] = fy2;
vertices[idx++] = color;
vertices[idx++] = u;
vertices[idx++] = v2;

vertices[idx++] = fx2;
vertices[idx++] = fy2;
vertices[idx++] = color;
vertices[idx++] = u2;
vertices[idx++] = v2;

vertices[idx++] = fx2;
vertices[idx++] = y;
vertices[idx++] = color;
vertices[idx++] = u2;
vertices[idx++] = v;
}

/** Draws a rectangle with the bottom left corner at x,y having the given width and height in pixels. The portion of the
* {@link Texture} given by srcX, srcY and srcWidth, srcHeight are used. These coordinates and sizes are given in texels.
*
* @param texture the Texture
* @param x the x-coordinate in screen space
* @param y the y-coordinate in screen space
* @param srcX the x-coordinate in texel space
* @param srcY the y-coordinate in texel space
* @param srcWidth the source with in texels
* @param srcHeight the source height in texels */
public void draw (Texture texture, float x, float y, int srcX, int srcY, int srcWidth, int srcHeight) {
if (!drawing) throw new IllegalStateException("SpriteBatch.begin must be called before draw.");

if (texture != lastTexture) {
switchTexture(texture);
} else if (idx == vertices.length) renderMesh();

final float u = srcX * invTexWidth;
final float v = (srcY + srcHeight) * invTexHeight;
final float u2 = (srcX + srcWidth) * invTexWidth;
final float v2 = srcY * invTexHeight;
final float fx2 = x + srcWidth;
final float fy2 = y + srcHeight;

vertices[idx++] = x;
vertices[idx++] = y;
vertices[idx++] = color;
vertices[idx++] = u;
vertices[idx++] = v;

vertices[idx++] = x;
vertices[idx++] = fy2;
vertices[idx++] = color;
vertices[idx++] = u;
vertices[idx++] = v2;

vertices[idx++] = fx2;
vertices[idx++] = fy2;
vertices[idx++] = color;
vertices[idx++] = u2;
vertices[idx++] = v2;

vertices[idx++] = fx2;
vertices[idx++] = y;
vertices[idx++] = color;
vertices[idx++] = u2;
vertices[idx++] = v;
}

/** Draws a rectangle with the bottom left corner at x,y having the given width and height in pixels. The portion of the
* {@link Texture} given by u, v and u2, v2 are used. These coordinates and sizes are given in texture size percentage. The
* rectangle will have the given tint {@link Color}.
*
* @param texture the Texture
* @param x the x-coordinate in screen space
* @param y the y-coordinate in screen space
* @param width the width in pixels
* @param height the height in pixels */
public void draw (Texture texture, float x, float y, float width, float height, float u, float v, float u2, float v2) {
if (!drawing) throw new IllegalStateException("SpriteBatch.begin must be called before draw.");

if (texture != lastTexture) {
switchTexture(texture);
} else if (idx == vertices.length) renderMesh();

final float fx2 = x + width;
final float fy2 = y + height;

vertices[idx++] = x;
vertices[idx++] = y;
vertices[idx++] = color;
vertices[idx++] = u;
vertices[idx++] = v;

vertices[idx++] = x;
vertices[idx++] = fy2;
vertices[idx++] = color;
vertices[idx++] = u;
vertices[idx++] = v2;

vertices[idx++] = fx2;
vertices[idx++] = fy2;
vertices[idx++] = color;
vertices[idx++] = u2;
vertices[idx++] = v2;

vertices[idx++] = fx2;
vertices[idx++] = y;
vertices[idx++] = color;
vertices[idx++] = u2;
vertices[idx++] = v;
}

/** Draws a rectangle with the bottom left corner at x,y having the width and height of the texture.
* @param texture the Texture
* @param x the x-coordinate in screen space
* @param y the y-coordinate in screen space */
public void draw (Texture texture, float x, float y) {
if (!drawing) throw new IllegalStateException("SpriteBatch.begin must be called before draw.");

if (texture != lastTexture) {
switchTexture(texture);
} else if (idx == vertices.length) renderMesh();

final float fx2 = x + texture.getWidth();
final float fy2 = y + texture.getHeight();

vertices[idx++] = x;
vertices[idx++] = y;
vertices[idx++] = color;
vertices[idx++] = 0;
vertices[idx++] = 1;

vertices[idx++] = x;
vertices[idx++] = fy2;
vertices[idx++] = color;
vertices[idx++] = 0;
vertices[idx++] = 0;

vertices[idx++] = fx2;
vertices[idx++] = fy2;
vertices[idx++] = color;
vertices[idx++] = 1;
vertices[idx++] = 0;

vertices[idx++] = fx2;
vertices[idx++] = y;
vertices[idx++] = color;
vertices[idx++] = 1;
vertices[idx++] = 1;
}

/** Draws a rectangle with the bottom left corner at x,y and stretching the region to cover the given width and height. */
public void draw (Texture texture, float x, float y, float width, float height) {
if (!drawing) throw new IllegalStateException("SpriteBatch.begin must be called before draw.");

if (texture != lastTexture) {
switchTexture(texture);
} else if (idx == vertices.length) //
renderMesh();

final float fx2 = x + width;
final float fy2 = y + height;
final float u = 0;
final float v = 1;
final float u2 = 1;
final float v2 = 0;

vertices[idx++] = x;
vertices[idx++] = y;
vertices[idx++] = color;
vertices[idx++] = u;
vertices[idx++] = v;

vertices[idx++] = x;
vertices[idx++] = fy2;
vertices[idx++] = color;
vertices[idx++] = u;
vertices[idx++] = v2;

vertices[idx++] = fx2;
vertices[idx++] = fy2;
vertices[idx++] = color;
vertices[idx++] = u2;
vertices[idx++] = v2;

vertices[idx++] = fx2;
vertices[idx++] = y;
vertices[idx++] = color;
vertices[idx++] = u2;
vertices[idx++] = v;
}

/** Draws a rectangle using the given vertices. There must be 4 vertices, each made up of 5 elements in this order: x, y, color,
* u, v. */
public void draw (Texture texture, float[] spriteVertices, int offset, int length) {
if (!drawing) throw new IllegalStateException("SpriteBatch.begin must be called before draw.");

if (texture != lastTexture) {
switchTexture(texture);
}

int remainingVertices = vertices.length - idx;
if (remainingVertices == 0) {
renderMesh();
remainingVertices = vertices.length;
}
int vertexCount = Math.min(remainingVertices, length - offset);
System.arraycopy(spriteVertices, offset, vertices, idx, vertexCount);
offset += vertexCount;
idx += vertexCount;

while (offset < length) {
renderMesh();
vertexCount = Math.min(vertices.length, length - offset);
System.arraycopy(spriteVertices, offset, vertices, 0, vertexCount);
offset += vertexCount;
idx += vertexCount;
}
}

/** Draws a rectangle with the bottom left corner at x,y having the width and height of the region. */
public void draw (TextureRegion region, float x, float y) {
draw(region, x, y, Math.abs(region.getRegionWidth()), Math.abs(region.getRegionHeight()));
}

/** Draws a rectangle with the bottom left corner at x,y and stretching the region to cover the given width and height. */
public void draw (TextureRegion region, float x, float y, float width, float height) {
if (!drawing) throw new IllegalStateException("SpriteBatch.begin must be called before draw.");

Texture texture = region.texture;
if (texture != lastTexture) {
switchTexture(texture);
} else if (idx == vertices.length) //
renderMesh();

final float fx2 = x + width;
final float fy2 = y + height;
final float u = region.u;
final float v = region.v2;
final float u2 = region.u2;
final float v2 = region.v;

vertices[idx++] = x;
vertices[idx++] = y;
vertices[idx++] = color;
vertices[idx++] = u;
vertices[idx++] = v;

vertices[idx++] = x;
vertices[idx++] = fy2;
vertices[idx++] = color;
vertices[idx++] = u;
vertices[idx++] = v2;

vertices[idx++] = fx2;
vertices[idx++] = fy2;
vertices[idx++] = color;
vertices[idx++] = u2;
vertices[idx++] = v2;

vertices[idx++] = fx2;
vertices[idx++] = y;
vertices[idx++] = color;
vertices[idx++] = u2;
vertices[idx++] = v;
}

/** Draws a rectangle with the bottom left corner at x,y and stretching the region to cover the given width and height. The
* rectangle is offset by originX, originY relative to the origin. Scale specifies the scaling factor by which the rectangle
* should be scaled around originX, originY. Rotation specifies the angle of counter clockwise rotation of the rectangle around
* originX, originY. */
public void draw (TextureRegion region, float x, float y, float originX, float originY, float width, float height,
float scaleX, float scaleY, float rotation) {
if (!drawing) throw new IllegalStateException("SpriteBatch.begin must be called before draw.");

Texture texture = region.texture;
if (texture != lastTexture) {
switchTexture(texture);
} else if (idx == vertices.length) //
renderMesh();

// bottom left and top right corner points relative to origin
final float worldOriginX = x + originX;
final float worldOriginY = y + originY;
float fx = -originX;
float fy = -originY;
float fx2 = width - originX;
float fy2 = height - originY;

// scale
if (scaleX != 1 || scaleY != 1) {
fx *= scaleX;
fy *= scaleY;
fx2 *= scaleX;
fy2 *= scaleY;
}

// construct corner points, start from top left and go counter clockwise
final float p1x = fx;
final float p1y = fy;
final float p2x = fx;
final float p2y = fy2;
final float p3x = fx2;
final float p3y = fy2;
final float p4x = fx2;
final float p4y = fy;

float x1;
float y1;
float x2;
float y2;
float x3;
float y3;
float x4;
float y4;

// rotate
if (rotation != 0) {
final float cos = MathUtils.cosDeg(rotation);
final float sin = MathUtils.sinDeg(rotation);

x1 = cos * p1x - sin * p1y;
y1 = sin * p1x + cos * p1y;

x2 = cos * p2x - sin * p2y;
y2 = sin * p2x + cos * p2y;

x3 = cos * p3x - sin * p3y;
y3 = sin * p3x + cos * p3y;

x4 = x1 + (x3 - x2);
y4 = y3 - (y2 - y1);
} else {
x1 = p1x;
y1 = p1y;

x2 = p2x;
y2 = p2y;

x3 = p3x;
y3 = p3y;

x4 = p4x;
y4 = p4y;
}

x1 += worldOriginX;
y1 += worldOriginY;
x2 += worldOriginX;
y2 += worldOriginY;
x3 += worldOriginX;
y3 += worldOriginY;
x4 += worldOriginX;
y4 += worldOriginY;

final float u = region.u;
final float v = region.v2;
final float u2 = region.u2;
final float v2 = region.v;

vertices[idx++] = x1;
vertices[idx++] = y1;
vertices[idx++] = color;
vertices[idx++] = u;
vertices[idx++] = v;

vertices[idx++] = x2;
vertices[idx++] = y2;
vertices[idx++] = color;
vertices[idx++] = u;
vertices[idx++] = v2;

vertices[idx++] = x3;
vertices[idx++] = y3;
vertices[idx++] = color;
vertices[idx++] = u2;
vertices[idx++] = v2;

vertices[idx++] = x4;
vertices[idx++] = y4;
vertices[idx++] = color;
vertices[idx++] = u2;
vertices[idx++] = v;
}

/** Draws a rectangle with the bottom left corner at x,y and stretching the region to cover the given width and height. The
* rectangle is offset by originX, originY relative to the origin. Scale specifies the scaling factor by which the rectangle
* should be scaled around originX, originY. Rotation specifies the angle of counter clockwise rotation of the rectangle around
* originX, originY. */
public void draw (TextureRegion region, float x, float y, float originX, float originY, float width, float height,
float scaleX, float scaleY, float rotation, boolean clockwise) {
if (!drawing) throw new IllegalStateException("SpriteBatch.begin must be called before draw.");

Texture texture = region.texture;
if (texture != lastTexture) {
switchTexture(texture);
} else if (idx == vertices.length) //
renderMesh();

// bottom left and top right corner points relative to origin
final float worldOriginX = x + originX;
final float worldOriginY = y + originY;
float fx = -originX;
float fy = -originY;
float fx2 = width - originX;
float fy2 = height - originY;

// scale
if (scaleX != 1 || scaleY != 1) {
fx *= scaleX;
fy *= scaleY;
fx2 *= scaleX;
fy2 *= scaleY;
}

// construct corner points, start from top left and go counter clockwise
final float p1x = fx;
final float p1y = fy;
final float p2x = fx;
final float p2y = fy2;
final float p3x = fx2;
final float p3y = fy2;
final float p4x = fx2;
final float p4y = fy;

float x1;
float y1;
float x2;
float y2;
float x3;
float y3;
float x4;
float y4;

// rotate
if (rotation != 0) {
final float cos = MathUtils.cosDeg(rotation);
final float sin = MathUtils.sinDeg(rotation);

x1 = cos * p1x - sin * p1y;
y1 = sin * p1x + cos * p1y;

x2 = cos * p2x - sin * p2y;
y2 = sin * p2x + cos * p2y;

x3 = cos * p3x - sin * p3y;
y3 = sin * p3x + cos * p3y;

x4 = x1 + (x3 - x2);
y4 = y3 - (y2 - y1);
} else {
x1 = p1x;
y1 = p1y;

x2 = p2x;
y2 = p2y;

x3 = p3x;
y3 = p3y;

x4 = p4x;
y4 = p4y;
}

x1 += worldOriginX;
y1 += worldOriginY;
x2 += worldOriginX;
y2 += worldOriginY;
x3 += worldOriginX;
y3 += worldOriginY;
x4 += worldOriginX;
y4 += worldOriginY;

float u1, v1, u2, v2, u3, v3, u4, v4;
if (clockwise) {
u1 = region.u2;
v1 = region.v2;
u2 = region.u;
v2 = region.v2;
u3 = region.u;
v3 = region.v;
u4 = region.u2;
v4 = region.v;
} else {
u1 = region.u;
v1 = region.v;
u2 = region.u2;
v2 = region.v;
u3 = region.u2;
v3 = region.v2;
u4 = region.u;
v4 = region.v2;
}

vertices[idx++] = x1;
vertices[idx++] = y1;
vertices[idx++] = color;
vertices[idx++] = u1;
vertices[idx++] = v1;

vertices[idx++] = x2;
vertices[idx++] = y2;
vertices[idx++] = color;
vertices[idx++] = u2;
vertices[idx++] = v2;

vertices[idx++] = x3;
vertices[idx++] = y3;
vertices[idx++] = color;
vertices[idx++] = u3;
vertices[idx++] = v3;

vertices[idx++] = x4;
vertices[idx++] = y4;
vertices[idx++] = color;
vertices[idx++] = u4;
vertices[idx++] = v4;
}

/** Causes any pending sprites to be rendered, without ending the SpriteBatch. */
public void flush () {
renderMesh();
}

private void renderMesh () {
if (idx == 0) return;

renderCalls++;
totalRenderCalls++;
int spritesInBatch = idx / 20;
if (spritesInBatch > maxSpritesInBatch) maxSpritesInBatch = spritesInBatch;

lastTexture.bind();
mesh.setVertices(vertices, 0, idx);
mesh.getIndicesBuffer().position(0);
mesh.getIndicesBuffer().limit(spritesInBatch * 6);

if (blendingDisabled) {
Gdx.gl.glDisable(GL20.GL_BLEND);
} else {
Gdx.gl.glEnable(GL20.GL_BLEND);
Gdx.gl.glBlendFunc(blendSrcFunc, blendDstFunc);
}

if (Gdx.graphics.isGL20Available()) {
if (customShader != null)
mesh.render(customShader, GL10.GL_TRIANGLES, 0, spritesInBatch * 6);
else
mesh.render(shader, GL10.GL_TRIANGLES, 0, spritesInBatch * 6);
} else {
mesh.render(GL10.GL_TRIANGLES, 0, spritesInBatch * 6);
}

idx = 0;
currBufferIdx++;
if (currBufferIdx == buffers.length) currBufferIdx = 0;
mesh = buffers[currBufferIdx];
}

/** Disables blending for drawing sprites. Does not disable blending for text rendering */
public void disableBlending () {
renderMesh();
blendingDisabled = true;
}

/** Enables blending for sprites */
public void enableBlending () {
renderMesh();
blendingDisabled = false;
}

/** Sets the blending function to be used when rendering sprites.
*
* @param srcFunc the source function, e.g. GL11.GL_SRC_ALPHA
* @param dstFunc the destination function, e.g. GL11.GL_ONE_MINUS_SRC_ALPHA */
public void setBlendFunction (int srcFunc, int dstFunc) {
renderMesh();
blendSrcFunc = srcFunc;
blendDstFunc = dstFunc;
}

/** Disposes all resources associated with this SpriteBatch */
public void dispose () {
for (int i = 0; i < buffers.length; i++)
buffers[i].dispose();
if (ownsShader && shader != null) shader.dispose();
}

/** Returns the current projection matrix. Changing this will result in undefined behaviour.
*
* @return the currently set projection matrix */
public Matrix4 getProjectionMatrix () {
return projectionMatrix;
}

/** Returns the current transform matrix. Changing this will result in undefined behaviour.
*
* @return the currently set transform matrix */
public Matrix4 getTransformMatrix () {
return transformMatrix;
}

/** Sets the projection matrix to be used by this SpriteBatch. If this is called inside a {@link #begin()}/{@link #end()} block.
* the current batch is flushed to the gpu.
*
* @param projection the projection matrix */
public void setProjectionMatrix (Matrix4 projection) {
if (drawing) flush();
projectionMatrix.set(projection);
if (drawing) setupMatrices();
}

/** Sets the transform matrix to be used by this SpriteBatch. If this is called inside a {@link #begin()}/{@link #end()} block.
* the current batch is flushed to the gpu.
*
* @param transform the transform matrix */
public void setTransformMatrix (Matrix4 transform) {
if (drawing) flush();
transformMatrix.set(transform);
if (drawing) setupMatrices();
}

private void setupMatrices () {
if (!Gdx.graphics.isGL20Available()) {
GL10 gl = Gdx.gl10;
gl.glMatrixMode(GL10.GL_PROJECTION);
gl.glLoadMatrixf(projectionMatrix.val, 0);
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadMatrixf(transformMatrix.val, 0);
} else {
combinedMatrix.set(projectionMatrix).mul(transformMatrix);
if (customShader != null) {
customShader.setUniformMatrix("u_proj", projectionMatrix);
customShader.setUniformMatrix("u_trans", transformMatrix);
customShader.setUniformMatrix("u_projTrans", combinedMatrix);
customShader.setUniformi("u_texture", 0);
} else {
shader.setUniformMatrix("u_projectionViewMatrix", combinedMatrix);
shader.setUniformi("u_texture", 0);
}
}
}

private void switchTexture(Texture texture) {
if(Gdx.graphics.isGL20Available()) {
renderMesh();
lastTexture = texture;
invTexWidth = 1.0f / texture.getWidth();
invTexHeight = 1.0f / texture.getHeight();
}
else {
renderMesh();
lastTexture = texture;
invTexWidth = 1.0f / texture.getWidth();
invTexHeight = 1.0f / texture.getHeight();
}
}

/** Sets the shader to be used in a GLES 2.0 environment. Vertex position attribute is called "a_position", the texture
* coordinates attribute is called called "a_texCoords0", the color attribute is called "a_color". See
* {@link ShaderProgram#POSITION_ATTRIBUTE}, {@link ShaderProgram#COLOR_ATTRIBUTE} and {@link ShaderProgram#TEXCOORD_ATTRIBUTE}
* which gets "0" appened to indicate the use of the first texture unit. The projection matrix is uploaded via a mat4 uniform
* called "u_proj", the transform matrix is uploaded via a uniform called "u_trans", the combined transform and projection
* matrx is is uploaded via a mat4 uniform called "u_projTrans". The texture sampler is passed via a uniform called
* "u_texture".</p>
*
* Call this method with a null argument to use the default shader.</p>
*
* This method will flush the batch before setting the new shader, you can call it in between
* {@link #begin()} and {@link #end()}.
*
* @param shader the {@link ShaderProgram} or null to use the default shader. */
public void setShader (ShaderProgram shader) {
if (drawing) {
flush();
if (customShader != null)
customShader.end();
else
this.shader.end();
}
customShader = shader;
if (drawing) {
if (customShader != null)
customShader.begin();
else
this.shader.begin();
setupMatrices();
}
}

/** @return whether blending for sprites is enabled */
public boolean isBlendingEnabled () {
return !blendingDisabled;
}

static public final int X1 = 0;
static public final int Y1 = 1;
static public final int C1 = 2;
static public final int U1 = 3;
static public final int V1 = 4;
static public final int X2 = 5;
static public final int Y2 = 6;
static public final int C2 = 7;
static public final int U2 = 8;
static public final int V2 = 9;
static public final int X3 = 10;
static public final int Y3 = 11;
static public final int C3 = 12;
static public final int U3 = 13;
static public final int V3 = 14;
static public final int X4 = 15;
static public final int Y4 = 16;
static public final int C4 = 17;
static public final int U4 = 18;
static public final int V4 = 19;
}

Change log

r3939 by badlogicgames on May 19, 2012   Diff
[fixed]  issue 851 , setting custom shader
on SpritBatch in between begin/end didn't
bind the new shader.
Go to: 
Project members, sign in to write a code review

Older revisions

r3865 by badlogicgames on Apr 18, 2012   Diff
[fixed] SpriteBatch#setShader didn't
flush. WTF
r3816 by badlogicgames on Apr 15, 2012   Diff
[updated] docs for SpriteBatch,
explaining the differences between
shaders passed via the constructor and
shaders set via setShader().
r3514 by badlogicgames on Mar 12, 2012   Diff
[fixed] optimization in spritebatch.
stupid me.
All revisions of this file

File info

Size: 39606 bytes, 1225 lines
Powered by Google Project Hosting