I'm having problems understanding how to apply a texture to a non-rectangular object. The following code creates textures such as this:
from the debug renderer I think I've got the physical shape of the "earth" correct. However, I don't know how to apply a texture to it. I have a 50x50 pixel image (in the environment constructor as "dirt.png"), that I want to apply to the hills. I have a vague idea that this seems to involve the mesh class and possibly a ShapeRenderer, but the little i'm finding online is just confusing me.
Bellow is code from the class that makes and regulates the terrain and the code in a separate file that is supposed to render it (but crashes on the mesh.render() call). Any pointers would be appreciated.
public class Environment extends Actor{
Pixmap sky;
public Texture groundTexture;
Texture skyTexture;
double tankypos; //TODO delete, temp
public Tank etank; //TODO delete, temp
int destructionRes; // how wide is a static pixel
private final float viewWidth;
private final float viewHeight;
private ChainShape terrain;
public Texture dirtTexture;
private World world;
public Mesh terrainMesh;
private static final String LOG = Environment.class.getSimpleName();
// Constructor
public Environment(Tank tank, FileHandle sfileHandle, float w, float h, int destructionRes) {
world = new World(new Vector2(0, -10), true);
this.destructionRes = destructionRes;
sky = new Pixmap(sfileHandle);
viewWidth = w;
viewHeight = h;
skyTexture = new Texture(sky);
terrain = new ChainShape();
genTerrain((int)w, (int)h, 6);
Texture tankSprite = new Texture(Gdx.files.internal("TankSpriteBase.png"));
Texture turretSprite = new Texture(Gdx.files.internal("TankSpriteTurret.png"));
tank = new Tank(0, true, tankSprite, turretSprite);
Rectangle tankrect = new Rectangle(300, (int)tankypos, 44, 45);
tank.setRect(tankrect);
BodyDef terrainDef = new BodyDef();
terrainDef.type = BodyType.StaticBody;
terrainDef.position.set(0, 0);
Body terrainBody = world.createBody(terrainDef);
FixtureDef fixtureDef = new FixtureDef();
fixtureDef.shape = terrain;
terrainBody.createFixture(fixtureDef);
BodyDef tankDef = new BodyDef();
Rectangle rect = tank.getRect();
tankDef.type = BodyType.DynamicBody;
tankDef.position.set(0,0);
tankDef.position.x = rect.x;
tankDef.position.y = rect.y;
Body tankBody = world.createBody(tankDef);
FixtureDef tankFixture = new FixtureDef();
PolygonShape shape = new PolygonShape();
shape.setAsBox(rect.width*WORLD_TO_BOX, rect.height*WORLD_TO_BOX);
fixtureDef.shape = shape;
dirtTexture = new Texture(Gdx.files.internal("dirt.png"));
etank = tank;
}
private void genTerrain(int w, int h, int hillnessFactor){
int width = w;
int height = h;
Random rand = new Random();
//min and max bracket the freq's of the sin/cos series
//The higher the max the hillier the environment
int min = 1;
//allocating horizon for screen width
Vector2[] horizon = new Vector2[width+2];
horizon[0] = new Vector2(0,0);
double[] skyline = new double[width]; //TODO skyline necessary as an array?
//ratio of amplitude of screen height to landscape variation
double r = (int) 2.0/5.0;
//number of terms to be used in sine/cosine series
int n = 4;
int[] f = new int[n*2];
//calculating omegas for sine series
for(int i = 0; i < n*2 ; i ++){
f[i] = rand.nextInt(hillnessFactor - min + 1) + min;
}
//amp is the amplitude of the series
int amp = (int) (r*height);
double lastPoint = 0.0;
for(int i = 0 ; i < width; i ++){
skyline[i] = 0;
for(int j = 0; j < n; j++){
skyline[i] += ( Math.sin( (f[j]*Math.PI*i/height) ) + Math.cos(f[j+n]*Math.PI*i/height) );
}
skyline[i] *= amp/(n*2);
skyline[i] += (height/2);
skyline[i] = (int)skyline[i]; //TODO Possible un-necessary float to int to float conversions
tankypos = skyline[i];
horizon[i+1] = new Vector2((float)i, (float)skyline[i]);
if(i == width) lastPoint = skyline[i];
}
horizon[width+1] = new Vector2(800, (float)lastPoint);
terrain.createChain(horizon);
terrain.createLoop(horizon);
//I have no idea if the following does anything useful :(
terrainMesh = new Mesh(true, (width+2)*2, (width+2)*2,
new VertexAttribute(Usage.Position, (width+2)*2, "a_position"));
float[] vertices = new float[(width+2)*2];
short[] indices = new short[(width+2)*2];
for(int i=0; i < (width+2); i+=2){
vertices[i] = horizon[i].x;
vertices[i+1] = horizon[i].y;
indices[i] = (short)i;
indices[i+1] = (short)(i+1);
}
terrainMesh.setVertices(vertices);
terrainMesh.setIndices(indices);
}
Here is the code that is (supposed to) render the terrain.
@Override
public void render(float delta) {
Gdx.gl.glClearColor(1, 1, 1, 1);
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
// tell the camera to update its matrices.
camera.update();
// tell the SpriteBatch to render in the
// coordinate system specified by the camera.
backgroundStage.draw();
backgroundStage.act(delta);
uistage.draw();
uistage.act(delta);
batch.begin();
debugRenderer.render(this.ground.getWorld(), camera.combined);
batch.end();
//Gdx.graphics.getGL10().glEnable(GL10.GL_TEXTURE_2D);
ground.dirtTexture.bind();
ground.terrainMesh.render(GL10.GL_TRIANGLE_FAN); //I'm particularly lost on this
ground.step();
}