I have been working on this for 2 days and still cant figure out what is going on. I am working on a Game Framework and i have migrated from Slick2D to LibGDX and would like to render once to a texture as to save CPU/GPU cycles and improve performance. I have created a class called DrawSurface and its main goal is to allow me to draw to a offscreen texture and then just draw that using LibGDXs Built in Image class.
public final class DrawSurface {
public static interface DrawCall{
void draw(SpriteBatch b, int width, int height);
}
public static Texture offscreenDraw(DrawCall c, int canvasWidth, int canvasHeight){
FrameBuffer fbo = new FrameBuffer(Pixmap.Format.RGBA8888,(canvasWidth),(canvasHeight),true);
SpriteBatch batch = new SpriteBatch();
batch.setProjectionMatrix(new Matrix4().setToOrtho2D(0,0,fbo.getWidth(),fbo.getHeight()));
fbo.begin();
batch.begin();
c.draw(batch,fbo.getWidth(),fbo.getHeight());
batch.end();
Pixmap map = ScreenUtils.getFrameBufferPixmap(0,0,fbo.getWidth(),fbo.getHeight());
fbo.end();
Texture t = new Texture(map);
map.dispose();
return t;
}
}
Which Gives me This Result When Drawing Images:
The "DrawCall" that is used to get this image is:
Texture t = DrawSurface.offscreenDraw(new DrawSurface.DrawCall() {
#Override
public void draw(SpriteBatch b, int w, int h) {
Image img = new Image(0x0000FF,w,h);
img.setLocation(0,0);
img.draw(b);
for(int i = 0; i < w; i += 64){
for(int j = 0; j < h; j += 64){
Blocks.GRASS_BLOCK.setLocation(i,j);
Blocks.GRASS_BLOCK.draw(b);
}
}
}
},512,512);
The Image should Render as a blue Square 512x512 pixels in size, with small "Grass" block images that should also be square. Sized 16x16. Unfortunately i get a warped result, and i do not understand why. As the Large White thing (a software JoyStick i created) is not stretched while the "DrawsSurface" is. If you would like a look at my camera code:
// Constructor Above.
this.gameWidth = gameWidth;
this.gameHeight = gameHeight;
this.camera = new OrthographicCamera();
FlatPixelGame.gameCamera = this.camera;
this.viewport = new StretchViewport(gameWidth,gameHeight,camera);
this.viewport.apply();
LogBot.log("Game Instance Created Size [%s,%s]",gameWidth,gameHeight);
InputManager.getInstance().addListener(this);
this.camera.position.set(camera.viewportWidth / 2, camera.viewportHeight / 2, 0);
Any Help Would Be Appreciated, i have scoured Forums, StackOverflow and LibGDX Documentation and still cannot seem to fix this issue.
Again, Thank you in Advance :)
After a little work under the hood with LibGDX source code from BadLogic, as well as reading bug threads it seems there is an issue when converting a PixMap to a texture. Rendering a image derived from a file works fine, but generated images that come from a Pixmap can show warping due to the off-screen view-port not being the same as the current rendering view-port.
A quick way around this is using TextureRegions and Sprites.
public static Image offscreenDraw(DrawCall c, int canvasWidth, int canvasHeight){
FrameBuffer fbo = new FrameBuffer(Pixmap.Format.RGBA8888,canvasWidth,canvasHeight,true);
SpriteBatch batch = new SpriteBatch();
Matrix4 matrix = new Matrix4();
matrix.setToOrtho2D(0, 0, canvasWidth,canvasHeight);
batch.setProjectionMatrix(matrix);
fbo.begin();
batch.begin();
c.draw(batch,canvasWidth,canvasHeight);
batch.end();
Texture t = fbo.getColorBufferTexture();
t.setFilter(Texture.TextureFilter.Nearest, Texture.TextureFilter.Nearest);
if(!t.getTextureData().isPrepared()){
t.getTextureData().prepare();
}
fbo.end();
TextureRegion r = new TextureRegion(t,0,0,canvasWidth,canvasHeight);
return new Image(new com.badlogic.gdx.scenes.scene2d.ui.Image(new Sprite(r)));
}
Related
I would like to build a kind of image morphing tool in Processing. Similar to what you can see in this link:
https://giphy.com/gifs/painting-morph-oil-c8ygOpL64UDuw
My first step to achieve this was to build a two-dimensional grid of pixels. The pixels are filled with colour. The fill colour is created by reading colour from an image (PImage img1;) with the get(); function. This is how I recreated an image with my pixels. In the second step, I thought I would use the lerp(); function to give the respective pixels the colour of a second image (PImage img2;) - I thought this would create the desired morph effect. But I was wrong! The whole thing works - but the effect is only that a fade-in takes place between the two images. And no morphing. What exactly happens to pixels while this morph effect? How could I recreate it in Processing?
float pixel;
float pixelsize;
PImage img1;
PImage img2;
float counter;
void setup() {
size(1080, 1080);
pixel = 100;
pixelsize = width/pixel;
noStroke();
img1 = loadImage("0.jpg");
img2 = loadImage("1.jpg");
counter = 0;
}
void draw() {
background(255);
for (int y = 0; y < pixel; y++) {
for (int x = 0; x < pixel; x++) {
color c1 = img1.get(int(pixelsize*x), int(pixelsize*y));
color c2 = img2.get(int(pixelsize*x), int(pixelsize*y));
color from = c1;
color to = c2;
color interA = lerpColor(from, to, counter);
pushMatrix();
translate(pixelsize*x, pixelsize*y);
fill(interA);
rect(0, 0, pixelsize, pixelsize);
popMatrix();
}
}
counter= counter + 0.01;
}
Indeed it is not a straight forward task.
You're approach is not a bad start: it would result in a nice crossfade between the two images.
Bare in mind get() can be costly on the CPU.
You can however use the pixels[]:
PImage img1;
PImage img2;
// transition image
PImage imgT;
void setup() {
size(1080, 1080);
img1 = loadImage("0.jpg");
img2 = loadImage("1.jpg");
// copy the 1st image (copies width/height as well)
imgT = img1.get();
}
void draw() {
background(255);
// map transition amount to mouse X position
float t = map(mouseX, 0, width, 0.0, 1.0);
// make all pixels readable
imgT.loadPixels();
// lerp each pixel
for(int i = 0 ; i < imgT.pixels.length; i++){
imgT.pixels[i] = lerpColor(img1.pixels[i], img2.pixels[i], t);
}
// update all pixels in one go
imgT.updatePixels();
// display result
image(imgT, 0, 0);
}
Implementing a full morph image is non-trivial.
I can recomend two options to make use of existing algorithms, however these options are also not beginner friendly:
ImageMagick implements shepards distortion and there is a java library that interfaces with imagemagick: im4java. Note that you'd need to download the precompiled java library and drop the .jar file on top of your sketch and processing the output might take time: probably not feasible for realtime (however it should be possible to save individual frames to disk and assemble them as a gif/movie/etc.)
Using OpenCV: there's an OpenCV Face Morph tutorial with source code in c++ or Python and there is a Processing OpenCV library. It would be a matter of porting the c++/Python OpenCV calls to the Java OpenCV API.
I have been playing around with LibGDX for a good bit now and haven't found a problem I couldn't solve until now. When I run my project sometimes everything works perfectly fine but about 1 out of 10 times the camera will just refuse to update it seems unless the game window is either over another programs existing window or I physically grab the window with my mouse and move it around while moving my sprite around so that I can see it updating. anyone have any ideas what could be causing this?
#Override
public void create () {
int tileSize = 16;
int w = 30 * tileSize;
int h = 17 * tileSize;
viewport.setScreenSize(w, h);
camera = new OrthographicCamera();
camera.setToOrtho(false,viewport.getScreenWidth(),viewport.getScreenHeight());
camera.update();
mapGenerator = new MapGenerator();
tiledMapRenderer = new OrthogonalTiledMapRenderer(mapGenerator.getTiledMap());
spawner = new EntitySpawner(mapGenerator.getMapObjects());
spawner.spawn();//there is a to do in the entity spawner class to move this else where
sb = new SpriteBatch();
hud = new Hud(camera);
Gdx.input.setInputProcessor(this);
Gdx.graphics.setResizable(false);//makes it so you can't just drag the game window to any size since its easy to distort it
Gdx.graphics.setWindowedMode(1280,720);//resolution size windowed
}
#Override
public void render () {
Gdx.gl.glClearColor(1, 0, 0, 1);//background color
Gdx.gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
//update the camera
camera.position.x = spawner.player.sprite.getX();
camera.position.y = spawner.player.sprite.getY();
camera.update();
tiledMapRenderer.setView(camera);
tiledMapRenderer.render();
timeElapsed += Gdx.graphics.getDeltaTime();//simply time that has passed based off delta time
sb.setProjectionMatrix(camera.combined);
sb.begin(); //start the sprite batch
spawner.draw(sb);//draw any entities with the spawner
hud.draw(sb);//draw the HUD
sb.end();//end the sprite batch
}
I have trouble with my app. On iPhone (tested on 5c, 5s, 6) i have two black stripes on both sides (on android all looks well).
How i can dispose of them?
This is my code for drawing
#Override
public void create () {
mWidth = Gdx.graphics.getWidth();
mHeight = Gdx.graphics.getHeight();
mScale = Math.max(mWidth, mHeight) / 20f;
backgroundTexture = new Texture(Gdx.files.internal("backBlue.png"));
ShaderProgram.pedantic = false;
backgroundShader = new ShaderProgram(VERT, FRAG);
if (!backgroundShader.isCompiled()) {
System.err.println(backgroundShader.getLog());
System.exit(0);
}
if (backgroundShader.getLog().length()!=0)
System.out.println(backgroundShader.getLog());
backgroundBatch = new SpriteBatch(5, backgroundShader);
}
#Override
public void render () {
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
backgroundBatch.begin();
backgroundBatch.draw(backgroundTexture, 0, 0, mWidth, mHeight);
backgroundBatch.end();
...
}
#Override
public void resize (int width, int height) {
mWidth = width;
mHeight = height;
mScale = Math.max(width, height) / 20f;
backgroundShader.begin();
backgroundShader.setUniformf("resolution", width, height);
backgroundShader.end();
}
This is probably happening because the viewport you are using, which is usually set in the constructor or the create method.
What is a viewport?
Basically, a viewport is an object that is created to define the policy of how the game will be drawn on different screens. E.g., say your camera is set to be 480 x 800 (pixels). What happens if your game is running on a screen that has a different ratio, for instance, the iPhone 5 that has a screen of 1,136 × 640 pixels? Should the game be stretched? Should the image be cropped? Or should libgdx add black bars on either side to fit the camera size you are using? This decision is made by the viewport your camera is using.
This is it in a nutshell. It is highly recommended to read more on this on the wiki.
Additionally, a good tutorial on this subject can be found here.
I am trying to create a method which returns a texture modified by an overlay using libgdx and PixMap.
Assuming I have 2 images:
A Base Image in FileHandle textureInput
And an overlay image in FileHandle overLay
It should produce this texture:
So it should use the RGB values from the textureInput and the alpha values from the overLay and create the final image. I believe I can do this using the Pixmap class but I just can't seem to find exactly how.
Here is what I gather should be the structure of the method:
public Texture getOverlayTexture(FileHandle overLay, FileHandle textureInput){
Pixmap inputPix = new Pixmap(textureInput);
Pixmap overlayPix = new Pixmap(overLay);
Pixmap outputPix = new Pixmap(inputPix.getWidth(), inputPix.getHeight(), Format.RGBA8888);
// go over the inputPix and add each byte to the outputPix
// but only where the same byte is not alpha in the overlayPix
Texture outputTexture = new Texture(outputPix, Format.RGBA8888, false);
inputPix.dispose();
outputPix.dispose();
overlayPix.dispose();
return outputTexture;
}
I am just looking for a bit of direction as to where to go from here. Any help is really appreciated. I apologize if this question is too vague or if my approach is entirely off.
Thanks!
I finally found the way to do this.
How my game is setup is that each item draws itself. They are handed a spritebatch and can do stuff with it. I did it that way various reasons. There is an item manager containing a list of items. Each item has various attributes. Each item has it's own render method along with other independent methods. Here is what finally worked:
A normal item's render method which does not use any alpha masking:
public void render(SpriteBatch batch, int renderLayer) {
if(renderLayer == Integer.parseInt(render_layer)){ // be in the correct render layer
batch.draw(item.region,
item.position.x, // position.x
item.position.y, // position.y
0, //origin x
0, //origin y
item.region.getRegionWidth() , //w
item.region.getRegionHeight(), //h
item.t_scale, //scale x
item.t_scale, //scale y
item.manager.radiansToDegrees(item.rotation)); //angle
}
}
So it is handed a spritebatch that it draws to with the correct image, location, scale, and rotation, and that is that.
After playing around with what I found here: https://gist.github.com/mattdesl/6076846 for a while, this finally worked for an item who needs to use alpha masking:
public void render(SpriteBatch batch, int renderLayer) {
if(renderLayer == Integer.parseInt(render_layer)){
batch.enableBlending();
//draw the alpha mask
drawAlphaMask(batch, item.position.x, item.position.y, item.region.getRegionWidth(), item.region.getRegionHeight());
//draw our foreground elements
drawForeground(batch, item.position.x, item.position.y, item.region.getRegionWidth(), item.region.getRegionHeight());
batch.disableBlending();
}
}
There is a TextureRegion named alphaMask which contains a black shape.
It can be any image, but let's say in this instance its this shape / image:
Here is the function called above that uses that image:
private void drawAlphaMask(SpriteBatch batch, float x, float y, float width, float height) {
//disable RGB color, only enable ALPHA to the frame buffer
Gdx.gl.glColorMask(false, false, false, true);
// Get these values so I can be sure I set them back to how it was
dst = batch.getBlendDstFunc();
src = batch.getBlendSrcFunc();
//change the blending function for our alpha map
batch.setBlendFunction(GL10.GL_SRC_ALPHA, GL10.GL_ZERO);
//draw alpha mask sprite
batch.draw(alphaRegion,
x, // position.x
y, // position.y
0, // origin x
0, // origin y
alphaRegion.getRegionWidth(), // w
alphaRegion.getRegionHeight(), // h
item.t_scale, // scale x
item.t_scale, // scale y
item.manager.radiansToDegrees(item.rotation)); // angle
//flush the batch to the GPU
batch.flush();
}
There are a variety of "materials" to apply to any shape. In any instance one of them is assigned to the spriteRegion variable. Let's say right now it is this:
So the drawForeground method called above uses that image like this:
private void drawForeground(SpriteBatch batch, float clipX, float clipY, float clipWidth, float clipHeight) {
//now that the buffer has our alpha, we simply draw the sprite with the mask applied
Gdx.gl.glColorMask(true, true, true, true);
batch.setBlendFunction(GL10.GL_DST_ALPHA, GL10.GL_ONE_MINUS_DST_ALPHA);
batch.draw(spriteRegion,
clipX, // corrected center position.x
clipY, // corrected center position.y
0, //origin x
0, //origin y
spriteRegion.getRegionWidth() , //w
spriteRegion.getRegionHeight(), //h
item.t_scale, //scale x
item.t_scale, //scale y
item.manager.radiansToDegrees(item.rotation)); //angle
//remember to flush before changing GL states again
batch.flush();
// set it back to however it was before
batch.setBlendFunction(src, dst);
}
That all worked right away in the desktop build, and can produce "Brick Beams" (or whatever) in the game nicely:
However in Android and GWT builds (because after all, I am using libgdx) it did not incorporate the alpha mask, and instead rendered the full brick square.
After a lot of looking around I found this: https://github.com/libgdx/libgdx/wiki/Integrating-libgdx-and-the-device-camera
And so to fix this in Android I modified the MainActivity.java onCreate method like this:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
AndroidApplicationConfiguration cfg = new AndroidApplicationConfiguration();
cfg.useGL20 = false;
cfg.r = 8;
cfg.g = 8;
cfg.b = 8;
cfg.a = 8;
initialize(new SuperContraption("android"), cfg);
if (graphics.getView() instanceof SurfaceView) {
SurfaceView glView = (SurfaceView) graphics.getView();
// force alpha channel - I'm not sure we need this as the GL surface
// is already using alpha channel
glView.getHolder().setFormat(PixelFormat.TRANSLUCENT);
}
}
And that fixes it for Android.
I still cannot figure out how to make it work properly in gwt, as I cannot figure out how to tell libgdx to tell GWT to tell webGl to go ahead and pay attention to the alpha channel. I'm interested in how to do something like this in an easier or less expensive way (though this seems to work fine).
If anyone knows how to make this work with GWT, please post as another answer.
Here is the non-working GWT build if you want to see the texture issue:
https://supercontraption.com/assets/play/index.html
I'm struggling to draw a rotating bitmap around its center and do the rotating without resizing the bitmap. I'm drawing all my sprites to the screen via a game thread, so I'm looking for a solution that incorporates the original bitmap and not the canvas.
Thanks in advance.
This is my code so far, it turns a bitmap around its center, yet resizes it.
i = i + 2;
transform.postRotate(i, Assets.scoresScreen_LevelStar.getWidth()/2, Assets.scoresScreen_LevelStar.getHeight()/2);
Bitmap resizedBitmap = Bitmap.createBitmap(Assets.scoresScreen_LevelStar, 0, 0, Assets.scoresScreen_LevelStar.getWidth(), Assets.scoresScreen_LevelStar.getHeight(), transform, true);
game.getGraphics().getCanvasGameScreen().drawBitmap(resizedBitmap, null, this.levelStar.getHolderPolygons().get(0), null);
Update:
I've noticed this isn't as easy as it sounds. My rotating code is not the problem. The bitmap rotates, yet the dst rect will also have to increase/decrease depending on the angle of rotation, or else the bimap will appear smaller, since it's drawn into a fixed dst rect.
So I'm guessing I'll have to develop some method that will return a dst rect.
So the methods needed to rotate a bitmap without appeared resizing:
public static Bitmap rotateBitmap(Bitmap bitmap, int rotation) // I've got this method working
and
public static Rect rotateRect(Rect currentDst, int rotation) // Don't got this
I understand this will require some math (trig), anyone up for the challenge? :P
You should draw the bitmap using the Matrix class. Below is a very basic idea assuming that you want to rotate an image inside of a "Ship" class. You update the current position Matrix inside the update method. In the onDraw() you draw the bitmap using the newly updated position Matrix. This will draw the rotated bitmap without resizing it.
public class Ship extends View {
private float x, y;
private int rotation;
private Matrix position;
private Bitmap bitmap;
...
#Override
public void onDraw(Canvas canvas) {
// Draw the Bitmap using the current position
canvas.drawBitmap(bitmap, position, null);
}
public void update() {
// Generate a new matrix based off of the current rotation and x and y coordinates.
Matrix m = new Matrix();
m.postRotate(rotation, bitmap.getWidth()/2, bitmap.getHeight()/2);
m.postTranslate(x, y);
// Set the current position to the updated rotation
position.set(m);
rotation += 2;
}
....
}
Hope that helps!
Also keep in mind that generating a new Bitmap object inside you game loop will be resource intensive.
This worked for me!
I created a method that returns a matrix. The matrix can be used in the following drawing method:
public void drawBitmap (Bitmap bitmap, Matrix matrix, Paint paint)
Here you go! (The parameter shape can be replaced with ease, if you would like that, just leave a comment):
public static Matrix rotateMatrix(Bitmap bitmap, Shape shape, int rotation) {
float scaleWidth = ((float) shape.getWidth()) / bitmap.getWidth();
float scaleHeight = ((float) shape.getHeight()) / bitmap.getHeight();
Matrix rotateMatrix = new Matrix();
rotateMatrix.postScale(scaleWidth, scaleHeight);
rotateMatrix.postRotate(rotation, shape.getWidth()/2, shape.getHeight()/2);
rotateMatrix.postTranslate(shape.getX(), shape.getY());
return rotateMatrix;
}
NB: If you want an animated rotation, the rotation parameter will have to be updated with new values every frame eg. 1 then 2 then 3 ...