I wanted to create a simple 30x30 isometric tiled map and add a listener to be able to click on the tiles. I looked up a lot of the articles and posts here but none of them helped me.
My issue is, i have a viewport, 30x17, a camera, a stage and a tiled map with tileWidth = 32 and tileHeight = 16 pixels.
Now when i render the tiled map it looks fine.
When i click on stage and try to get the world coordinates i see some really weird coordinates.
This is the code:
private static final float TILE_WIDTH = 32;
private static final float TILE_HEIGHT = 16;
private OrthographicCamera camera;
private Viewport viewport;
private Stage stage;
private IsometricTiledMapRenderer isometricTiledMapRenderer;
private Matrix4 isoTransform;
private Matrix4 invIsotransform;
public void load(AssetManagerLoaderV2 assetManagerLoader) {
assetManagerLoader.load();
init();
camera = new OrthographicCamera();
viewport = new FitViewport(30, 17, camera);
stage = new Stage(viewport);
TiledMap tiledMap = new TiledMapGenerator(assetManagerLoader).generate(30, 30);
isometricTiledMapRenderer = new IsometricTiledMapRenderer(tiledMap, 1/32f);
stage.addListener(new InputListener() {
#Override
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
System.out.println(screenToCell(x, y));
return true;
}
});
}
#Override
public void show() {
Gdx.input.setInputProcessor(stage);
}
#Override
public void render(float delta) {
DrawUtils.clearScreen();
viewport.apply();
isometricTiledMapRenderer.setView(camera);
isometricTiledMapRenderer.render();
}
#Override
public void resize(int width, int height) {
stage.getViewport().update(width, height, true);
}
#Override
public void pause() {
}
#Override
public void resume() {
}
#Override
public void hide() {
}
#Override
public void dispose() {
isometricTiledMapRenderer.dispose();
stage.dispose();
}
public void init () {
//create the isometric transform
isoTransform = new Matrix4();
isoTransform.idt();
isoTransform.translate(0.0f, 0.25f, 0.0f);
isoTransform.scale((float)(Math.sqrt(2.0) / 2.0), (float)(Math.sqrt(2.0) / 4.0), 1.0f);
isoTransform.rotate(0.0f, 0.0f, 1.0f, -45.0f);
//... and the inverse matrix
invIsotransform = new Matrix4(isoTransform);
invIsotransform.inv();
}
public Vector2 worldToCell(float x, float y) {
float halfTileWidth = TILE_WIDTH * 0.5f;
float halfTileHeight = TILE_HEIGHT * 0.5f;
float row = (1.0f/2) * (x/halfTileWidth + y/halfTileHeight);
float col = (1.0f/2) * (x/halfTileWidth - y/halfTileHeight);
return new Vector2((int)col,(int)row);
}
public Vector2 screenToWorld(float x, float y){
Vector3 touch = new Vector3(x,y,0);
camera.unproject(touch);
touch.mul(invIsotransform);
touch.mul(isoTransform);
return new Vector2(touch.x,touch.y);
}
public Vector2 screenToCell(float x, float y) {
Vector2 world = screenToWorld(x,y);
world.y -= TILE_HEIGHT *0.5f;
return worldToCell(world.x,world.y);
}
Does anyone have an idea how to write worldToCell to get the proper coordinates?
I found the issue, i was missing one step.
This is what touchDown should look like:
#Override
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
Vector2 newCoords = stage.stageToScreenCoordinates(new Vector2(x, y));
System.out.println(screenToCell(newCoords.x, newCoords.y));
return true;
}
I need to convert stage coordinates to screen coordinates, now this code is working.
I am developing a top-down 2D game with a TiledMap.
Currently i want to create a minimap of my TiledMap, but i´m really confused how to do this.
I´ve read something that i should create another camera, zoom out and render the map again, but how should i draw the zoomed out map in the upper right corner?
Am i overthinking this whole thing?
My Map:
public class LiquidMap {
private TiledMap map;
private OrthogonalTiledMapRenderer renderer;
private OrthographicCamera camera = new OrthographicCamera();
private MiniMap miniMap;
public LiquidMap(String filePath) {
map = new TmxMapLoader().load(filePath);
renderer = new OrthogonalTiledMapRenderer(map, 1 / 32f);
camera.setToOrtho(false, 30, 20);
miniMap = new MiniMap(map);
}
public void update(float x, float y){
camera.position.x = x;
camera.position.y = y;
camera.update();
//renderer.setView(camera.combined, x - 10, y - 10, 20, 20);
renderer.setView(camera);
miniMap.update(x, y);
}
public void update(HostPlayer player){
this.update(player.position.x + (player.skin.getWidth()/2f)/32f, player.position.y + (player.skin.getHeight()/2f)/32f);
}
public void render(HostPlayer player){
renderer.render();
renderer.getBatch().begin();
renderer.getBatch().draw(player.skin, player.position.x, player.position.y, 1/32f * player.skin.getWidth(), 1/32f * player.skin.getHeight());
renderer.getBatch().end();
miniMap.render();
}
And my MiniMap:
public class MiniMap {
private OrthogonalTiledMapRenderer renderer;
private OrthographicCamera camera = new OrthographicCamera();
public MiniMap(TiledMap map) {
renderer = new OrthogonalTiledMapRenderer(map, 1 / 32f);
camera.setToOrtho(false, 30, 20);
camera.zoom = 10;
}
public void update(){
}
public void update(float x, float y){
//Pixventure.instance.gameScreen.getCamera()
camera.position.x = x;
camera.position.y = y;
camera.update();
renderer.setView(camera.combined, x-15, y-15, 30, 30);
//renderer.setView(camera);
}
public void render(){
renderer.render();
}
}
The whole situation is looking like this:
Minimap's camera position is updated by player position. Place your minimap at the corner and don't change his position by your update() method.
After several days of struggling I came here. I'm trying to pass a custom per-vertex vec3 attribute to a custom shader based on this tutorial. The tutorial describes how to pass a custom uniform which actually works fine. However when I'm trying to modify the code to pass my custom per-vertex attribute it seems that nothing is transferred to vertex shader and I can't figure out how to make it to work.
So far I've done the following:
I've created several boxes with modelBuilder.createBox() (so I know for sure every model has 24 vertexes)
Then I'v generated a FloatBuffer containing actual attribute data like this:
int[] data = new int[]{x1, y1, z1, x1, y1, z1, ...}
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(data.length * 4);
byteBuffer.order(ByteOrder.nativeOrder());
mAttributeBuffer = byteBuffer.asFloatBuffer();
mAttributeBuffer.put(data);
mAttributeBuffer.position(0);
Then I'm initializing the corresponding attribute location variable (successfully, a_coord >= 0):
a_coord = program.getAttributeLocation("a_coord");
After that on libgdx side in custom shader's render(Renderable) method I'm passing the buffer to OpenGL like this:
program.setVertexAttribute(a_coord, 3, Gdx.gl20.GL_FLOAT, false, 0, mAttributeBuffer);
My custom vertex shader is as the following:
attribute vec3 a_position;
attribute vec3 a_normal;
attribute vec2 a_texCoord0;
uniform mat4 u_worldTrans;
uniform mat4 u_projTrans;
varying vec2 v_texCoord0;
//my custom attribute
attribute vec2 a_coord;
void main() {
v_texCoord0 = a_texCoord0;
float posY = a_position.y + a_coord.y;
gl_Position = u_projTrans * u_worldTrans * vec4(a_position.x, posY, a_position.z, 1.0);
}
The problem
At the moment a_coord is 0 for every vertex. What am I missing and how to correctly pass custom attribute to vertex shader?
I'm guessing the problem is somewhere in VBO field and the way libGDX passes attribute data to vertexes but I still can't figure out how to make it work.
I'll be glad if anyone can point me in the right direction on this question.
Complete code:
Main AplicationListener class:
public class ProtoGame implements ApplicationListener {
public ProtoGame()
{
super();
}
public PerspectiveCamera cam;
public CameraInputController camController;
public Model model;
public Array<ModelInstance> instances = new Array<ModelInstance>();
public ModelBatch modelBatch;
#Override
public void create () {
cam = new PerspectiveCamera(67, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
cam.position.set(0f, 8f, 8f);
cam.lookAt(0,0,0);
cam.near = 1f;
cam.far = 300f;
cam.update();
camController = new CameraInputController(cam);
Gdx.input.setInputProcessor(camController);
ModelBuilder modelBuilder = new ModelBuilder();
model = modelBuilder.createBox(1f, 1f, 1f,
new Material(),
VertexAttributes.Usage.Position | VertexAttributes.Usage.Normal | VertexAttributes.Usage.TextureCoordinates);
Color colorU = new Color(), colorV = new Color();
for (int x = -5; x <= 5; x+=2) {
for (int z = -5; z<=5; z+=2) {
ModelInstance instance = new ModelInstance(model, x, 0, z);
//this is where I'll put per-vertex attribute data for every instance
//but for now it's hardcoded in the Shader class so the data is the same across instances
TestShader.DoubleColorAttribute attr = new TestShader.DoubleColorAttribute(TestShader.DoubleColorAttribute.DiffuseUV,
colorU.set((x+5f)/10f, 1f - (z+5f)/10f, 0, 1),
colorV.set(1f - (x+5f)/10f, 0, (z+5f)/10f, 1));
instance.materials.get(0).set(attr);
instances.add(instance);
}
}
modelBatch = new ModelBatch(new BaseShaderProvider() {
#Override
protected Shader createShader(Renderable renderable) {
return new TestShader();
}
});
}
#Override
public void render () {
camController.update();
Gdx.gl.glViewport(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
Gdx.gl.glClearColor(1, 1, 1, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);
modelBatch.begin(cam);
for (ModelInstance instance : instances)
modelBatch.render(instance);
modelBatch.end();
}
#Override
public void dispose () {
model.dispose();
modelBatch.dispose();
}
}
Custom libgdx shader class:
public class TestShader implements Shader {
private FloatBuffer mAttributeBuffer;
ShaderProgram program;
Camera camera;
RenderContext context;
int u_projTrans;
int u_worldTrans;
int u_colorU;
int u_colorV;
int a_coord;
private static String getCustomVertexShader() {
return Gdx.files.internal("shader/test.vertex.glsl").readString();
}
private static String getCustomFragmentShader() {
return Gdx.files.internal("shader/test.fragment.glsl").readString();
}
#Override
public void init() {
program = new ShaderProgram(getCustomVertexShader(), getCustomFragmentShader());
if (!program.isCompiled())
throw new GdxRuntimeException(program.getLog());
//tutorial's logic to init custom uniform locations
u_projTrans = program.getUniformLocation("u_projTrans");
u_worldTrans = program.getUniformLocation("u_worldTrans");
u_colorU = program.getUniformLocation("u_colorU");
u_colorV = program.getUniformLocation("u_colorV");
//initing custom attribute location
a_coord = program.getAttributeLocation("a_coord");
//generating data and passing it to nio Buffer
float data[] = generateData();
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(data.length * 4);
byteBuffer.order(ByteOrder.nativeOrder());
mAttributeBuffer = byteBuffer.asFloatBuffer();
mAttributeBuffer.put(data);
mAttributeBuffer.position(0);
}
private float[] generateData() {
Vector3[] dataArray = new Vector3[1];
dataArray[0] = new Vector3(2, 2, 2);
int components = 3;
int vertexPerModel = 24;
float[] data = new float[dataArray.length * components * vertexPerModel];
for(int i = 0; i < dataArray.length; ++i){
int i3 = i * components;
for(int j = 0; j < vertexPerModel; ++j) {
int j3 = j * components;
data[i3 + 0 + j3] = dataArray[i].x;
data[i3 + 1 + j3] = dataArray[i].y;
data[i3 + 2 + j3] = dataArray[i].z;
}
}
return data;
}
#Override
public void dispose() {
program.dispose();
}
#Override
public void begin(Camera camera, RenderContext context) {
this.camera = camera;
this.context = context;
program.begin();
program.setUniformMatrix(u_projTrans, camera.combined);
context.setDepthTest(GL20.GL_LEQUAL);
context.setCullFace(GL20.GL_BACK);
}
#Override
public void render(Renderable renderable) {
program.setUniformMatrix(u_worldTrans, renderable.worldTransform);
//tutorial's logic to pass uniform
DoubleColorAttribute attribute = ((DoubleColorAttribute) renderable.material.get(DoubleColorAttribute.DiffuseUV));
program.setUniformf(u_colorU, attribute.color1.r, attribute.color1.g, attribute.color1.b);
program.setUniformf(u_colorV, attribute.color2.r, attribute.color2.g, attribute.color2.b);
//passing my custom attributes to the vertex shader
program.setVertexAttribute(a_coord, 3, Gdx.gl20.GL_FLOAT, false, 0, mAttributeBuffer);
renderable.mesh.render(program, renderable.primitiveType,
renderable.meshPartOffset, renderable.meshPartSize);
}
#Override
public void end() {
program.end();
}
#Override
public int compareTo(Shader other) {
return 0;
}
#Override
public boolean canRender(Renderable renderable) {
return renderable.material.has(DoubleColorAttribute.DiffuseUV);
}
}
Finally I was able to pass a custom attribute to vertex shader! Thanks a lot to #Xoppa for pointing me in the right direction.
This is the working solution I've got so far (I'm open for any further advices on how to implement it in a more elegant way):
First of all, as Xoppa stated in the comment it's required to create a model providing custom vertex structure while building it. So model creation may look like this:
VertexAttribute posAttr = new VertexAttribute(VertexAttributes.Usage.Position, 3, ShaderProgram.POSITION_ATTRIBUTE);
...
VertexAttribute customVertexAttr = new VertexAttribute(512, 3, "a_custom");
VertexAttributes vertexAttributes = new VertexAttributes(
posAttr,
...
customVertexAttr);
ModelBuilder modelBuilder = new ModelBuilder();
modelBuilder.begin();
modelBuilder.
part("box", GL20.GL_TRIANGLES, vertexAttributes, new Material()).
box(1f, 1f, 1f);
model = modelBuilder.end();
Or the same with MeshBuilder:
MeshBuilder meshBuilder = new MeshBuilder();
VertexAttributes vertexAttributes = new VertexAttributes(...);
meshBuilder.begin(vertexAttributes);
meshBuilder.part("box", GL20.GL_TRIANGLES);
meshBuilder.setColor(color);
meshBuilder.box(1f, 1f, 1f);
Mesh mesh = meshBuilder.end();
This code will create model with vertices containing additional data according to the provided attributes. It's time to fill the corresponding vertex array. You need a mesh for this - it stores vertices array - a flat array of packed attributes one after another vertex by vertex. So what you need is a number of attributes per vertex as well as an offset for attribute which needs to be modified. Mesh stores all that data:
Mesh mesh = model.meshes.get(0);
int numVertices = mesh.getNumVertices();
// vertex size and offset are in byte so we need to divide it by 4
int vertexSize = mesh.getVertexAttributes().vertexSize / 4;
//it's possible to use usage int here passed previously to VertexAttribute constructor.
VertexAttribute customAttribute = mesh.getVertexAttribute(512)
int offset = customAttribute.offset / 4;
float[] vertices = new float[numVertices * vertexSize];
mesh.getVertices(vertices);
We are ready to pass the data:
List<Vector3> customData ...
for(int i = 0; i < numVertices; ++i){
int index = i * vertexSize + offset;
vertices[index + 0] = customData.get(i).x;
vertices[index + 1] = customData.get(i).y;
vertices[index + 2] = customData.get(i).z;
}
And don't forget to pass the updated vertices array back to the mesh:
mesh.updateVertices(0, vertices);
That's it.
Here's also an implementation of a helper method to create a mix of default attributes using Usage flags alongside with custom attributes:
private VertexAttributes createMixedVertexAttribute(int defaultAtributes, List<VertexAttribute> customAttributes){
VertexAttributes defaultAttributes = MeshBuilder.createAttributes(defaultAtributes);
List<VertexAttribute> attributeList = new ArrayList<VertexAttribute>();
for(VertexAttribute attribute: defaultAttributes){
attributeList.add(attribute);
}
attributeList.addAll(customAttributes);
VertexAttribute[] typeArray = new VertexAttribute[0];
VertexAttributes mixedVertexAttributes = new VertexAttributes(attributeList.toArray(typeArray));
return mixedVertexAttributes;
}
The full source:
public class ProtoGame implements ApplicationListener {
private static final int CUSTOM_ATTRIBUTE_USAGE = 512;
public ProtoGame()
{
super();
}
public PerspectiveCamera cam;
public CameraInputController camController;
public Model model;
public Array<ModelInstance> instances = new Array<ModelInstance>();
public ModelBatch modelBatch;
#Override
public void create () {
cam = new PerspectiveCamera(67, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
cam.position.set(0f, 8f, 8f);
cam.lookAt(0, 0, 0);
cam.near = 1f;
cam.far = 300f;
cam.update();
camController = new CameraInputController(cam);
Gdx.input.setInputProcessor(camController);
Model model = createModelWithCustomAttributes();
Mesh mesh = model.meshes.get(0);
setCustomAttributeData(mesh);
Color colorU = new Color(), colorV = new Color();
for (int x = -5; x <= 5; x+=2) {
for (int z = -5; z<=5; z+=2) {
ModelInstance instance = new ModelInstance(model, x, 0, z);
TestShader.DoubleColorAttribute attr = new TestShader.DoubleColorAttribute(TestShader.DoubleColorAttribute.DiffuseUV,
colorU.set((x+5f)/10f, 1f - (z+5f)/10f, 0, 1),
colorV.set(1f - (x+5f)/10f, 0, (z+5f)/10f, 1));
instance.materials.get(0).set(attr);
instances.add(instance);
}
}
modelBatch = new ModelBatch(new BaseShaderProvider() {
#Override
protected Shader createShader(Renderable renderable) {
return new TestShader();
}
});
}
#Override
public void render () {
camController.update();
Gdx.gl.glViewport(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
Gdx.gl.glClearColor(1, 1, 1, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);
modelBatch.begin(cam);
for (ModelInstance instance : instances)
modelBatch.render(instance);
modelBatch.end();
}
private Model createModelWithCustomAttributes() {
int defaultAttributes = VertexAttributes.Usage.Position | VertexAttributes.Usage.Normal | VertexAttributes.Usage.TextureCoordinates;
VertexAttribute customVertexAttr = new VertexAttribute(CUSTOM_ATTRIBUTE_USAGE, 3, "a_custom");
List<VertexAttribute> customAttributeList = new ArrayList<VertexAttribute>();
customAttributeList.add(customVertexAttr);
VertexAttributes vertexAttributes = createMixedVertexAttribute(defaultAttributes, customAttributeList);
ModelBuilder modelBuilder = new ModelBuilder();
modelBuilder.begin();
modelBuilder.
part("box", GL20.GL_TRIANGLES, vertexAttributes, new Material()).
box(1f, 1f, 1f);
return modelBuilder.end();
}
private void setCustomAttributeData(Mesh mesh) {
int numVertices = mesh.getNumVertices();
int vertexSize = mesh.getVertexAttributes().vertexSize / 4;
int offset = mesh.getVertexAttribute(CUSTOM_ATTRIBUTE_USAGE).offset / 4;
float[] vertices = new float[numVertices * vertexSize];
mesh.getVertices(vertices);
for(int i = 0; i < numVertices; ++i){
int index = i * vertexSize + offset;
vertices[index + 0] = i;
vertices[index + 1] = i;
vertices[index + 2] = i;
}
mesh.updateVertices(0, vertices);
}
#Override
public void dispose () {
model.dispose();
modelBatch.dispose();
}
private VertexAttributes createMixedVertexAttribute(int defaultAtributes, List<VertexAttribute> customAttributes){
VertexAttributes defaultAttributes = MeshBuilder.createAttributes(defaultAtributes);
List<VertexAttribute> attributeList = new ArrayList<VertexAttribute>();
for(VertexAttribute attribute: defaultAttributes){
attributeList.add(attribute);
}
attributeList.addAll(customAttributes);
VertexAttribute[] typeArray = new VertexAttribute[0];
VertexAttributes mixedVertexAttributes = new VertexAttributes(attributeList.toArray(typeArray));
return mixedVertexAttributes;
}
#Override
public void resize(int width, int height) {
}
#Override
public void pause() {
}
#Override
public void resume() {
}
}
Vertex shader:
attribute vec3 a_position;
attribute vec3 a_normal;
attribute vec2 a_texCoord0;
uniform mat4 u_worldTrans;
uniform mat4 u_projTrans;
varying vec2 v_texCoord0;
attribute vec3 a_custom;
void main() {
v_texCoord0 = a_texCoord0;
float posX = a_position.x + a_custom.x;
float posY = a_position.y + a_custom.y;
float posZ = a_position.z + a_custom.z;
gl_Position = u_projTrans * u_worldTrans * vec4(posX, posY, posZ, 1.0);
}
Fragment shader
#ifdef GL_ES
precision mediump float;
#endif
uniform vec3 u_colorU;
uniform vec3 u_colorV;
varying vec2 v_texCoord0;
void main() {
gl_FragColor = vec4(v_texCoord0.x * u_colorU + v_texCoord0.y * u_colorV, 1.0);
}
Based on Xoppa's comment, I managed to get custom attributes working. It's actually so simple! Here is an ES 3.0 example:
#version 300 es
//v30.glsl
in vec3 vertexPosition;
in vec3 vertexColor;
out vec3 outColor;
void main()
{
outColor = vertexColor;
gl_Position = vec4(vertexPosition, 1.0);
}
fragment shader:
#version 300 es
//f30.glsl
precision mediump float;
in vec3 outColor;
out vec4 fragColor;
void main()
{
fragColor = vec4(outColor, 1.0);
}
Gl30Mesh.java
package com.example.jgles;
import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL30;
import com.badlogic.gdx.graphics.Mesh;
import com.badlogic.gdx.graphics.VertexAttribute;
import com.badlogic.gdx.graphics.VertexAttributes;
import com.badlogic.gdx.graphics.glutils.ShaderProgram;
public class Gl30Mesh extends ApplicationAdapter
{
private Mesh triangleMesh;
private ShaderProgram passShader;
#Override
public void create()
{
final float [] combinedData = {
-0.8f, -0.8f, 0.0f, 1.0f, 0.0f, 0.0f,
0.8f, -0.8f, 0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.8f, 0.0f, 0.0f, 0.0f, 1.0f
};
VertexAttribute vertexChannel = new VertexAttribute(VertexAttributes.Usage.Generic, 3, "vertexPosition");
VertexAttribute colorChannel = new VertexAttribute(VertexAttributes.Usage.Generic, 3, "vertexColor");
String vertexShader = Gdx.files.internal("v30.glsl").readString();
String fragmentShader = Gdx.files.internal("f30.glsl").readString();
passShader = new ShaderProgram(vertexShader, fragmentShader);
if (!passShader.isCompiled()){
throw new IllegalStateException(passShader.getLog());
}
passShader.bind();
triangleMesh = new Mesh(true, 3, 0, vertexChannel, colorChannel);
triangleMesh.setVertices(combinedData);
}
#Override
public void render()
{
Gdx.gl.glViewport(0, 0, 640, 480);
Gdx.gl.glClearColor(0, 0, 1.0f, 0);
Gdx.gl.glClear(GL30.GL_COLOR_BUFFER_BIT);
triangleMesh.render(passShader, GL30.GL_TRIANGLES);
}
}
It seems to me when you are already using custom shaders, you use whatever is named in your shaders instead of POSITION_ATTRIBUTE, NORMAL_ATTRIBUTE etc. And as Xoppa said, ShaderProgram.setVertexAttribute simply won't work.
Actually it is possible to call setVertexAttributes directly and have it work without needing a mesh. Just need to extend ShaderProgram like so
public class ShaderProgramExtended extends ShaderProgram {
public ShaderProgramExtended(String v, String f){
super(v,f);
}
/*
This is VERY NAUGHTY. Mario and Nathan probably made it private for a reason
*/
public int getProgram(){
int result;
try{
Field field = ShaderProgram.class.getDeclaredField("program");
field.setAccessible(true);
Object value = field.get(this);
field.setAccessible(false);
if (value == null) {
result = 0;
}else{
result = (Integer) value;
}
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
return result;
}
public void begin(int program){
Gdx.gl20.glUseProgram(program);
}
public void draw(int mode, int first, int count){
Gdx.gl20.glDrawArrays(mode,first,count);
}
}
And then call it as you would normally with the "minor" change of sending the shader object integer to the new begin method
public class TestlibGDXv2 extends ApplicationAdapter {
private final String tag = (this).getClass().getSimpleName();
String message = "";
private static final int FLOAT_BYTES = 4;
FloatBuffer vertexData;
FloatBuffer colorData;
ShaderProgramExtended shader;
private static final String COLOR_ATTRIBUTE = ShaderProgram.COLOR_ATTRIBUTE;
private int aColourLocation;
private static final String POSITION_ATTRIBUTE = ShaderProgram.POSITION_ATTRIBUTE;
private int aPositionLocation;
/*
Anti-clockwise winding order. Note, we could share two of the vertices. Haven't for clarity.
*/
float[] vertices = {
-0.5f, -0.5f,
0.5f, -0.5f,
-0.5f, 0.5f,
-0.5f, 0.5f,
0.5f, -0.5f,
0.5f, 0.5f
};
/*
Need to colour each vertex, so need 6.
*/
float[] colors = {1.0f, 0.0f, 0.0f, 1.0f,
0.0f,1.0f,0.0f,1.0f,
0.0f,0.0f,1.0f,1.0f,
0.0f,0.0f,1.0f,1.0f,
0.0f,1.0f,0.0f,1.0f,
1.0f,0.0f,0.0f,1.0f
};
#Override
public void create() {
/*
Convert from Dalvik VM to OpenGL native
*/
vertexData = ByteBuffer.allocateDirect(vertices.length * FLOAT_BYTES)
.order(ByteOrder.nativeOrder())
.asFloatBuffer();
colorData = ByteBuffer.allocateDirect(colors.length * FLOAT_BYTES)
.order(ByteOrder.nativeOrder())
.asFloatBuffer();
vertexData.put(vertices).position(0);
colorData.put(colors).position(0);
initialiseShaders();
}
#Override
public void render() {
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
shader.begin(shader.getProgram());
shader.setVertexAttribute(aPositionLocation, 2, GL20.GL_FLOAT, false, 0, vertexData);
shader.enableVertexAttribute(aPositionLocation);
shader.setVertexAttribute(aColourLocation, 4, GL20.GL_FLOAT, false, 0, colorData);
shader.enableVertexAttribute(aColourLocation);
shader.draw(GL20.GL_TRIANGLES, 0, 6);
shader.end();
}
private void initialiseShaders() {
String vertexShaderSource =
"#version 130\n" +
"attribute vec4 " + POSITION_ATTRIBUTE + ";\n" + // x,y,z and w
"attribute vec4 " + COLOR_ATTRIBUTE + ";\n" + // r,g,b and a
"varying vec4 v_color;\n" + // pass to fragment shader
"void main(){\n" +
" v_color = "+ COLOR_ATTRIBUTE + ";\n" +
" gl_Position = " + POSITION_ATTRIBUTE + ";\n" +
"}";
String fragmentShaderSource =
"#version 130\n" +
"#ifdef GL_ES\n" +
" precision mediump float;\n" + // medium a good balance between speed and quality
"#endif\n" +
"varying vec4 v_color;\n" + // incoming from vertex shader
"void main(){\n" +
" gl_FragColor = v_color;\n" +
"}";
shader = new ShaderProgramExtended(vertexShaderSource,fragmentShaderSource);
aPositionLocation = shader.getAttributeLocation(POSITION_ATTRIBUTE);
aColourLocation = shader.getAttributeLocation(COLOR_ATTRIBUTE);
}
}
The attribute "a_color" (from ShaderProgram.COLOR_ATTRIBUTE) could just as easily have been "a_custom".
I used this method when I was trying to learn OpenGL ES from WebGL books - there are a lot more of them than libGDX OpenGL books. However, only use the above for learning, the shader is now no longer being managed by libGDX and so will not work nicely on Android when context is lost. OK for desktop, though.
Cheers
John
What i want:
I want allow the user to drag and drop the actors he puts on the screen
1) An actor (32x32 pixel) can be put in a certain area defined by me (a centered rectangle 5x5, 32 pixel each cell)
2) The actors are snapped to an imaginary grid of the screen when added to the stage.
3) This snap to grid is needed also during the drag and drop
4) An actor can't be moved on another one
5) An actor must be rotated by 90 degrees when the user clicks on it
6) The actor can't be moved outside the bounds
What actually i get:
1) ok
2) ok
3) ok (with bugs)
4) not implemented yet
5) ok (with bugs)
6) not implemented yet
The bugs are:
1) The rotations is applied in any case. I don't want this behavior. I don't want to rotate an actor if the user is just doing drag and drop. (i know that this happens because i put this part of the code in the touchDown method)
2) This is hard to describe with my english, i'll try.
I put an actor and i can move it (when i say "move" i mean drag and drop).
I put a second actor and i can move it.
Now, if i try to move the first actor i put, the second one moves over the first (overlapping to it) and starts moving along with it!
Now the code:
Events in the GameStage
#Override
public boolean touchDown(int screenX, int screenY, int pointer, int button) {
translateScreenToWorldCoordinates(screenX, screenY);
// check if the user clicked or not in the center area
if(!centerArea.contains(touchPoint.x, touchPoint.y)) {
return true;
}
// rotation
Iterator<Square> squareIterator = squares.iterator();
while(squareIterator.hasNext()) {
Square nextSquare = squareIterator.next();
if(nextSquare.getBounds().contains(touchPoint.x, touchPoint.y)) {
nextSquare.rotate();
return true;
}
}
// add an Actor
touchPoint.set(touchPoint.x - Constants.SQUARE_SIZE / 2, touchPoint.y - Constants.SQUARE_SIZE / 2, 0f);
float snapX = Math.round((touchPoint.x / Constants.SQUARE_SIZE)) * Constants.SQUARE_SIZE;
float snapY = Math.round((touchPoint.y / Constants.SQUARE_SIZE)) * Constants.SQUARE_SIZE;
Square square = new Square(snapX, snapY);
squares.add(square);
addActor(square);
return true;
}
public boolean touchDragged(int screenX, int screenY, int pointer) {
Vector3 newTouchPoint = new Vector3(screenX, screenY, 0f);
getCamera().unproject(newTouchPoint.set(screenX, screenY, 0f));
Square next = null;
Iterator<Square> squareIterator = squares.listIterator();
while(squareIterator.hasNext()) {
next = squareIterator.next();
if(next.getBounds().contains(newTouchPoint.x, newTouchPoint.y)) {
next.setX(newTouchPoint.x - Constants.SQUARE_SIZE / 2);
next.setY(newTouchPoint.y - Constants.SQUARE_SIZE / 2);
continue;
}
}
newTouchPoint.set(newTouchPoint.x - Constants.SQUARE_SIZE / 2, newTouchPoint.y - Constants.SQUARE_SIZE / 2, 0f);
float snapX = Math.round((newTouchPoint.x / Constants.SQUARE_SIZE)) * Constants.SQUARE_SIZE;
float snapY = Math.round((newTouchPoint.y / Constants.SQUARE_SIZE)) * Constants.SQUARE_SIZE;
next.setX(snapX);
next.setY(snapY);
return true;
}
private void translateScreenToWorldCoordinates(int x, int y) {
getCamera().unproject(touchPoint.set(x, y, 0f));
}
Actor
public class Square extends GameActor {
private float rotation;
private ShapeRenderer shapeRenderer;
public Square(float x, float y) {
super(x, y);
shapeRenderer = new ShapeRenderer();
}
#Override
protected void initializeSprite() {
texture = new Texture(Gdx.files.internal(Constants.SQUARE_LOCATION));
}
#Override
public void act(float delta) {
super.act(delta);
}
#Override
protected void updateBounds() {
bounds.setPosition(x, y);
}
public void rotate() {
rotation -= 90;
if(rotation <= -360) {
rotation = 0;
}
setRotation(rotation);
}
public void setX(float x) {
this.x = x;
}
public void setY(float y) {
this.y = y;
}
#Override
public void draw(Batch batch, float parentAlpha) {
super.draw(batch, parentAlpha);
/*shapeRenderer.begin(ShapeRenderer.ShapeType.Line);
shapeRenderer.setColor(Color.RED);
shapeRenderer.rect(bounds.getX(), bounds.getY(), Constants.SQUARE_SIZE, Constants.SQUARE_SIZE);
shapeRenderer.end();*/
//batch.begin();
batch.draw(texture, x, y, texture.getWidth() / 2, texture.getHeight() / 2,
texture.getWidth(), texture.getHeight(), 1, 1, rotation, 0, 0, Constants.SQUARE_SIZE, Constants.SQUARE_SIZE, false, false);
}
}
I hope you can help me, i'm new to Libgdx, thank you.
UPDATE
I fixed the rotation bug moving its logic inside the touchUp method.