1/*******************************************************************************
2 * Copyright 2011 See AUTHORS file.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *   http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 ******************************************************************************/
16
17package com.badlogic.gdx.graphics.glutils;
18
19import com.badlogic.gdx.Gdx;
20import com.badlogic.gdx.graphics.Color;
21import com.badlogic.gdx.graphics.Mesh;
22import com.badlogic.gdx.graphics.VertexAttribute;
23import com.badlogic.gdx.graphics.VertexAttributes.Usage;
24import com.badlogic.gdx.math.Matrix4;
25import com.badlogic.gdx.utils.Array;
26
27/** Immediate mode rendering class for GLES 2.0. The renderer will allow you to specify vertices on the fly and provides a default
28 * shader for (unlit) rendering.</p> *
29 *
30 * @author mzechner */
31public class ImmediateModeRenderer20 implements ImmediateModeRenderer {
32	private int primitiveType;
33	private int vertexIdx;
34	private int numSetTexCoords;
35	private final int maxVertices;
36	private int numVertices;
37
38	private final Mesh mesh;
39	private ShaderProgram shader;
40	private boolean ownsShader;
41	private final int numTexCoords;
42	private final int vertexSize;
43	private final int normalOffset;
44	private final int colorOffset;
45	private final int texCoordOffset;
46	private final Matrix4 projModelView = new Matrix4();
47	private final float[] vertices;
48	private final String[] shaderUniformNames;
49
50	public ImmediateModeRenderer20 (boolean hasNormals, boolean hasColors, int numTexCoords) {
51		this(5000, hasNormals, hasColors, numTexCoords, createDefaultShader(hasNormals, hasColors, numTexCoords));
52		ownsShader = true;
53	}
54
55	public ImmediateModeRenderer20 (int maxVertices, boolean hasNormals, boolean hasColors, int numTexCoords) {
56		this(maxVertices, hasNormals, hasColors, numTexCoords, createDefaultShader(hasNormals, hasColors, numTexCoords));
57		ownsShader = true;
58	}
59
60	public ImmediateModeRenderer20 (int maxVertices, boolean hasNormals, boolean hasColors, int numTexCoords, ShaderProgram shader) {
61		this.maxVertices = maxVertices;
62		this.numTexCoords = numTexCoords;
63		this.shader = shader;
64
65		VertexAttribute[] attribs = buildVertexAttributes(hasNormals, hasColors, numTexCoords);
66		mesh = new Mesh(false, maxVertices, 0, attribs);
67
68		vertices = new float[maxVertices * (mesh.getVertexAttributes().vertexSize / 4)];
69		vertexSize = mesh.getVertexAttributes().vertexSize / 4;
70		normalOffset = mesh.getVertexAttribute(Usage.Normal) != null ? mesh.getVertexAttribute(Usage.Normal).offset / 4 : 0;
71		colorOffset = mesh.getVertexAttribute(Usage.ColorPacked) != null ? mesh.getVertexAttribute(Usage.ColorPacked).offset / 4
72			: 0;
73		texCoordOffset = mesh.getVertexAttribute(Usage.TextureCoordinates) != null ? mesh
74			.getVertexAttribute(Usage.TextureCoordinates).offset / 4 : 0;
75
76		shaderUniformNames = new String[numTexCoords];
77		for (int i = 0; i < numTexCoords; i++) {
78			shaderUniformNames[i] = "u_sampler" + i;
79		}
80	}
81
82	private VertexAttribute[] buildVertexAttributes (boolean hasNormals, boolean hasColor, int numTexCoords) {
83		Array<VertexAttribute> attribs = new Array<VertexAttribute>();
84		attribs.add(new VertexAttribute(Usage.Position, 3, ShaderProgram.POSITION_ATTRIBUTE));
85		if (hasNormals) attribs.add(new VertexAttribute(Usage.Normal, 3, ShaderProgram.NORMAL_ATTRIBUTE));
86		if (hasColor) attribs.add(new VertexAttribute(Usage.ColorPacked, 4, ShaderProgram.COLOR_ATTRIBUTE));
87		for (int i = 0; i < numTexCoords; i++) {
88			attribs.add(new VertexAttribute(Usage.TextureCoordinates, 2, ShaderProgram.TEXCOORD_ATTRIBUTE + i));
89		}
90		VertexAttribute[] array = new VertexAttribute[attribs.size];
91		for (int i = 0; i < attribs.size; i++)
92			array[i] = attribs.get(i);
93		return array;
94	}
95
96	public void setShader (ShaderProgram shader) {
97		if (ownsShader) this.shader.dispose();
98		this.shader = shader;
99		ownsShader = false;
100	}
101
102	public void begin (Matrix4 projModelView, int primitiveType) {
103		this.projModelView.set(projModelView);
104		this.primitiveType = primitiveType;
105	}
106
107	public void color (Color color) {
108		vertices[vertexIdx + colorOffset] = color.toFloatBits();
109	}
110
111	public void color (float r, float g, float b, float a) {
112		vertices[vertexIdx + colorOffset] = Color.toFloatBits(r, g, b, a);
113	}
114
115	public void color (float colorBits) {
116		vertices[vertexIdx + colorOffset] = colorBits;
117	}
118
119	public void texCoord (float u, float v) {
120		final int idx = vertexIdx + texCoordOffset;
121		vertices[idx + numSetTexCoords] = u;
122		vertices[idx + numSetTexCoords + 1] = v;
123		numSetTexCoords += 2;
124	}
125
126	public void normal (float x, float y, float z) {
127		final int idx = vertexIdx + normalOffset;
128		vertices[idx] = x;
129		vertices[idx + 1] = y;
130		vertices[idx + 2] = z;
131	}
132
133	public void vertex (float x, float y, float z) {
134		final int idx = vertexIdx;
135		vertices[idx] = x;
136		vertices[idx + 1] = y;
137		vertices[idx + 2] = z;
138
139		numSetTexCoords = 0;
140		vertexIdx += vertexSize;
141		numVertices++;
142	}
143
144	public void flush () {
145		if (numVertices == 0) return;
146		shader.begin();
147		shader.setUniformMatrix("u_projModelView", projModelView);
148		for (int i = 0; i < numTexCoords; i++)
149			shader.setUniformi(shaderUniformNames[i], i);
150		mesh.setVertices(vertices, 0, vertexIdx);
151		mesh.render(shader, primitiveType);
152		shader.end();
153
154		numSetTexCoords = 0;
155		vertexIdx = 0;
156		numVertices = 0;
157	}
158
159	public void end () {
160		flush();
161	}
162
163	public int getNumVertices () {
164		return numVertices;
165	}
166
167	@Override
168	public int getMaxVertices () {
169		return maxVertices;
170	}
171
172	public void dispose () {
173		if (ownsShader && shader != null) shader.dispose();
174		mesh.dispose();
175	}
176
177	static private String createVertexShader (boolean hasNormals, boolean hasColors, int numTexCoords) {
178		String shader = "attribute vec4 " + ShaderProgram.POSITION_ATTRIBUTE + ";\n"
179			+ (hasNormals ? "attribute vec3 " + ShaderProgram.NORMAL_ATTRIBUTE + ";\n" : "")
180			+ (hasColors ? "attribute vec4 " + ShaderProgram.COLOR_ATTRIBUTE + ";\n" : "");
181
182		for (int i = 0; i < numTexCoords; i++) {
183			shader += "attribute vec2 " + ShaderProgram.TEXCOORD_ATTRIBUTE + i + ";\n";
184		}
185
186		shader += "uniform mat4 u_projModelView;\n";
187		shader += (hasColors ? "varying vec4 v_col;\n" : "");
188
189		for (int i = 0; i < numTexCoords; i++) {
190			shader += "varying vec2 v_tex" + i + ";\n";
191		}
192
193		shader += "void main() {\n" + "   gl_Position = u_projModelView * " + ShaderProgram.POSITION_ATTRIBUTE + ";\n"
194			+ (hasColors ? "   v_col = " + ShaderProgram.COLOR_ATTRIBUTE + ";\n" : "");
195
196		for (int i = 0; i < numTexCoords; i++) {
197			shader += "   v_tex" + i + " = " + ShaderProgram.TEXCOORD_ATTRIBUTE + i + ";\n";
198		}
199		shader += "   gl_PointSize = 1.0;\n";
200		shader += "}\n";
201		return shader;
202	}
203
204	static private String createFragmentShader (boolean hasNormals, boolean hasColors, int numTexCoords) {
205		String shader = "#ifdef GL_ES\n" + "precision mediump float;\n" + "#endif\n";
206
207		if (hasColors) shader += "varying vec4 v_col;\n";
208		for (int i = 0; i < numTexCoords; i++) {
209			shader += "varying vec2 v_tex" + i + ";\n";
210			shader += "uniform sampler2D u_sampler" + i + ";\n";
211		}
212
213		shader += "void main() {\n" + "   gl_FragColor = " + (hasColors ? "v_col" : "vec4(1, 1, 1, 1)");
214
215		if (numTexCoords > 0) shader += " * ";
216
217		for (int i = 0; i < numTexCoords; i++) {
218			if (i == numTexCoords - 1) {
219				shader += " texture2D(u_sampler" + i + ",  v_tex" + i + ")";
220			} else {
221				shader += " texture2D(u_sampler" + i + ",  v_tex" + i + ") *";
222			}
223		}
224
225		shader += ";\n}";
226		return shader;
227	}
228
229	/** Returns a new instance of the default shader used by SpriteBatch for GL2 when no shader is specified. */
230	static public ShaderProgram createDefaultShader (boolean hasNormals, boolean hasColors, int numTexCoords) {
231		String vertexShader = createVertexShader(hasNormals, hasColors, numTexCoords);
232		String fragmentShader = createFragmentShader(hasNormals, hasColors, numTexCoords);
233		ShaderProgram program = new ShaderProgram(vertexShader, fragmentShader);
234		return program;
235	}
236}
237