I have been looking at using JOGL to create some things and have been looking through what documentation I can find.
Brief tutorials they all mention how using the JOGL version of canvas can have performance issues and instead you should use NEWT. However each and every tutorial / FAQ then goes on to use the canvas! Or simply specify a few tiny snippets of methods to create a window using NEWT but which (on my machine at least) I cannot get to correctly run.
Does anyone have a good source of examples of how to correctly implement creating and rendering to a window in JOGL using the NEWT method? I'm not even sure how it functions compared to the Canvas so an explanation of the differences between the two and a typical layout of methods to create / manage / render to a window would be ideal.
Just a little lost and cannot find anything useful. Hope someone's come across something before!
This tutorial helped me a lot, see chapter 3.9 - Yet Another Tutorial on JOGL.
Also documentation is useful. Take a look at the attached example, please.
JOGL2NewtDemo.java
import javax.media.opengl.GLCapabilities;
import javax.media.opengl.GLProfile;
import com.jogamp.newt.event.WindowAdapter;
import com.jogamp.newt.event.WindowEvent;
import com.jogamp.newt.opengl.GLWindow;
import com.jogamp.opengl.util.FPSAnimator;
/**
* A program that draws with JOGL in a NEWT GLWindow.
*
*/
public class JOGL2NewtDemo {
private static String TITLE = "JOGL 2 with NEWT"; // window's title
private static final int WINDOW_WIDTH = 640; // width of the drawable
private static final int WINDOW_HEIGHT = 480; // height of the drawable
private static final int FPS = 60; // animator's target frames per second
static {
GLProfile.initSingleton(); // The method allows JOGL to prepare some Linux-specific locking optimizations
}
/**
* The entry main() method.
*/
public static void main(String[] args) {
// Get the default OpenGL profile, reflecting the best for your running platform
GLProfile glp = GLProfile.getDefault();
// Specifies a set of OpenGL capabilities, based on your profile.
GLCapabilities caps = new GLCapabilities(glp);
// Create the OpenGL rendering canvas
GLWindow window = GLWindow.create(caps);
// Create a animator that drives canvas' display() at the specified FPS.
final FPSAnimator animator = new FPSAnimator(window, FPS, true);
window.addWindowListener(new WindowAdapter() {
#Override
public void windowDestroyNotify(WindowEvent arg0) {
// Use a dedicate thread to run the stop() to ensure that the
// animator stops before program exits.
new Thread() {
#Override
public void run() {
if (animator.isStarted())
animator.stop(); // stop the animator loop
System.exit(0);
}
}.start();
}
});
window.addGLEventListener(new JOGL2Renderer());
window.setSize(WINDOW_WIDTH, WINDOW_HEIGHT);
window.setTitle(TITLE);
window.setVisible(true);
animator.start(); // start the animator loop
}
}
JOGL2Renderer.java
import javax.media.opengl.GL;
import javax.media.opengl.GL2;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLEventListener;
/**
* Class handles the OpenGL events to render graphics.
*
*/
public class JOGL2Renderer implements GLEventListener {
private double theta = 0.0f; // rotational angle
/**
* Called back by the drawable to render OpenGL graphics
*/
#Override
public void display(GLAutoDrawable drawable) {
GL2 gl = drawable.getGL().getGL2(); // get the OpenGL graphics context
gl.glClear(GL.GL_COLOR_BUFFER_BIT); // clear background
gl.glLoadIdentity(); // reset the model-view matrix
// Rendering code - draw a triangle
float sine = (float)Math.sin(theta);
float cosine = (float)Math.cos(theta);
gl.glBegin(GL.GL_TRIANGLES);
gl.glColor3f(1, 0, 0);
gl.glVertex2d(-cosine, -cosine);
gl.glColor3f(0, 1, 0);
gl.glVertex2d(0, cosine);
gl.glColor3f(0, 0, 1);
gl.glVertex2d(sine, -sine);
gl.glEnd();
update();
}
/**
* Update the rotation angle after each frame refresh
*/
private void update() {
theta += 0.01;
}
/*... Other methods leave blank ...*/
}
take a look at JOGL's junit tests, they cover large parts of the NEWT API.
Related
I am trying to make a 2D game with Java and Swing, and the window refreshes too slow. But if I move the mouse or press keys, the window refreshes as fast as it should!
Here is a GIF showing how the window refreshes quickly only when I move the mouse.
Why does the window refresh slowly like that? Why does the mouse and keyboard affect its refresh rate? How, if possible, do I make it refresh quickly all the time?
Background Info
I use a javax.swing.Timer to update the game state every 1/25 seconds, after which it calls repaint() on the game panel to redraw the scene.
I understand that a Timer might not always delay for exactly 1/25 of a second.
I also understand that calling repaint() just requests the window to be repainted ASAP and does not repaint the window immediately.
My graphics card does not support OpenGL 2+ or hardware accelerated 3D graphics, which is why I am not using libgdx or JME for game development.
System Info
Operating system: Linux Mint 19 Tara
JDK version: OpenJDK 11.0.4
Graphics card: Intel Corporation 82945G/GZ
Research
This Stack Overflow user describes the same problem I have, but the author reportedly solved the issue by calling repaint() repeatedly on a separate timer. I tried this, and it does make the window refresh somewhat faster, but even then it is a slower than I want. In this case, wiggling the mouse on the window still improves the refresh rate. Therefore, it seems like that post did not truly solve the issue.
Another Stack Overflow user also encountered the issue, but they use a continuous while-loop instead of a Timer for their game loop. Apparently, this user solved the problem by using Thread.sleep() in their while loop. However, my code accomplishes the delay using a Timer, so I do not know how Thread.sleep() could solve my problem, or even where I would put it.
I've read through Painting with AWT and Swing to figure out whether I just misunderstood the concept of repainting, but nothing in that document elucidates the issue for me. I call repaint() whenever the game updates, and the window only refreshes quickly when mouse or keyboard input is happening.
I have searched the web for several days now trying to find an answer, but nothing seems to help!
Code
import java.awt.Graphics;
import java.awt.Dimension;
import java.awt.Color;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
class Game {
public static final int screenWidth = 160;
public static final int screenHeight = 140;
/**
* Create and show the GUI.
*/
private static void createAndShowGUI() {
/* Create the GUI. */
JFrame frame = new JFrame("Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.getContentPane().add(new GamePanel());
frame.pack();
/* Show the GUI. */
frame.setVisible(true);
}
/**
* Run the game.
*
* #param args the list of command-line arguments
*/
public static void main(String[] args) {
/* Schedule the GUI to be created on the EDT. */
SwingUtilities.invokeLater(() -> createAndShowGUI());
}
}
/**
* A GamePanel widget updates and shows the game scene.
*/
class GamePanel extends JPanel {
private Square square;
/**
* Create a game panel and start its update-and-draw cycle
*/
public GamePanel() {
super();
/* Set the size of the game screen. */
setPreferredSize(
new Dimension(
Game.screenWidth,
Game.screenHeight));
/* Create the square in the game world. */
square = new Square(0, 0, 32, 32, Square.Direction.LEFT);
/* Update the scene every 40 milliseconds. */
Timer timer = new Timer(40, (e) -> updateScene());
timer.start();
}
/**
* Paint the game scene using a graphics context.
*
* #param g the graphics context
*/
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
/* Clear the screen. */
g.setColor(Color.WHITE);
g.fillRect(0, 0, Game.screenWidth, Game.screenHeight);
/* Draw all objects in the scene. */
square.draw(g);
}
/**
* Update the game state.
*/
private void updateScene() {
/* Update all objects in the scene. */
square.update();
/* Request the scene to be repainted. */
repaint();
}
}
/**
* A Square is a game object which looks like a square.
*/
class Square {
public static enum Direction { LEFT, RIGHT };
private int x;
private int y;
private int width;
private int height;
private Direction direction;
/**
* Create a square game object.
*
* #param x the square's x position
* #param y the square's y position
* #param width the square's width (in pixels)
* #param height the square's height (in pixels)
* #param direction the square's direction of movement
*/
public Square(int x,
int y,
int width,
int height,
Direction direction) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.direction = direction;
}
/**
* Draw the square using a graphics context.
*
* #param g the graphics context
*/
public void draw(Graphics g) {
g.setColor(Color.RED);
g.fillRect(x, y, width, height);
g.setColor(Color.BLACK);
g.drawRect(x, y, width, height);
}
/**
* Update the square's state.
*
* The square slides horizontally
* until it reaches the edge of the screen,
* at which point it begins sliding in the
* opposite direction.
*
* This should be called once per frame.
*/
public void update() {
if (direction == Direction.LEFT) {
x--;
if (x <= 0) {
direction = Direction.RIGHT;
}
} else if (direction == Direction.RIGHT) {
x++;
if (x + width >= Game.screenWidth) {
direction = Direction.LEFT;
}
}
}
}
I guess you probably cannot solve your issue by enabling OpenGL, since your gpu does not support it, a possible silly workaround could be to fire a kind of event manually in each timer's iteration.
/* Update the scene every 40 milliseconds. */
final Robot robot = new Robot();
Timer timer = new Timer(40, (e) -> {
robot.mouseRelease(0); //some event
updateScene();
});
timer.start();
(And the only place you can Thread.sleep() in a Swing Application is inside a SwingWorker's doInBackground method. If you call it in EDT the whole GUI will freeze since events cannot take place.)
I ran into the same issue. The solution is quite simple. Call revalidate() immediately after you call repaint():
private void updateScene() {
/* Update all objects in the scene. */
square.update();
/* Request the scene to be repainted. */
repaint();
revalidate(); // <-- this will now repaint as fast as you wanted it to
}
I have a class that creates a JFrame on which a simple game of Tetris will be played, I also have a class DrawSquare, which does exactly what you think it does, however when I initialise a new instance of the DrawSquare class and then try to draw that one and all the others to my JFrame things start to go wrong, the code is intended for one square to be drawn in the top left hand corner and then drop down a line at a time until it reaches the bottom of the frame (it does this), then a new square should be drawn in the second column at the top of the frame, as well as our first square in the bottom left hand corner, however once it starts dropping down the second column I get a series of squares drawn in a diagonal towards the top right hand corner. At the moment all I plan for the code to do is have a square drop from the top row of each column and stop when it reaches the bottom of the frame, am I storing the instance of the class at the wrong point in the code? Edit: In fact I'm pretty sure it's that, I'd want to store that instance when it reaches the bottom. Does every instance of the class need its own timer?
public class Tetris extends JFrame {
public static final int height = 20; //height of a square
public static final int width = 20; //width of a square
public int xPos = 0; //column number of the square
public int yPos = 0; //row number of the square
public static void main(String[] args){
Tetris tet = new Tetris();
}
public Tetris() {
DrawSquare square = new DrawSquare(xPos, yPos, width, height, false);
add(square);
DrawSquare.squares.add(square);
setSize(220,440);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
}
}
public class DrawSquare extends JPanel {
public static List<DrawSquare> squares = new ArrayList<>();
protected int xPos;
protected int yPos;
protected int width;
protected int height;
protected Timer timer = new Timer(200, new TimerListener());
protected boolean endFall = false;
public DrawSquare(int xPos, int yPos, int width, int height, boolean endFall) {
this.xPos = xPos;
this.yPos = yPos;
this.width = width;
this.height = height;
this.endFall = endFall;
this.timer.start();
}
class TimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
yPos++;
if (yPos > 19) {
yPos = 19;
endFall = true;
}
if (endFall == true) {
timer.stop();
if (xPos > 8) {
xPos = 8;
}
xPos++;
endFall = false;
yPos = 0;
DrawSquare newSqr = new DrawSquare(xPos, yPos, width, height, true);
squares.add(newSqr);
add(newSqr);
}
timer.start();
repaint();
}
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Iterator<DrawSquare> it = squares.iterator();
while (it.hasNext()) {
DrawSquare square = it.next();
g.fillRect(square.xPos * square.width, square.yPos * square.height, square.width, square.height);
}
}
}
You are giving a great example of the fundamental misunderstanding beginners have of how the swing (and many other graphics toolkits) render stuff to the screen. I will give an overview of that, as it pertains to you, then answer your immediate questions and explain how to fix your code.
It took me a (very long) while to figure out how this stuff works my self, so please bear with me. I hope that reading through this answer will help you in a much more general way than answering this one question.
Asynchronous Drawing
Swing draws windows in a totally different sequence (the event dispatching thread) than the ones that modifies the state of your program (the main thread, as well as timer and other threads). You can modify the coordinates of things you want to draw as many times as you like in the main thread, but the changes will not show up until you request them to by calling JComponent.repaint() on one of your components. This will generally trigger a nearly-immediate repaint of the component, displaying your latest state.
If you change the coordinates of a widget like a JPanel in your main thread, it will likely show up immediately. This is because the methods you use to set the position will trigger repaint requests internally.
A repaint request gets queued and eventually processed by the event dispatching thread. This is where the paintComponent method gets called. The paintComponent method should therefore only draw. It should not do any other logic. If it needs to know how to draw some specialized stuff, the information for that should be stashed somewhere accessible by one of the other threads.
In short, you make calculations and update state as you need in the main thread or the timer. Then you access that state in the event dispatching thread via the paintComponent method.
Timers
There are a bunch of ways you can use timers to run your GUI, but you only really need one for the current application. In your case, the timer only needs to do two things:
Check if a block has fallen all the way down and doesn't need to move any more.
Trigger a repaint of your panel.
You do not need to compute the updated position of the blocks in the timer if the block's position is a simple equation with respect to time. If you know the time at which a block appears on the screen and the current time, you know how far the block has moved, so you can paint it in the correct spot based purely on the elapsed time.
If you had a more complicated system with paths that you could not predict purely on the time, I would recommend sticking the movement logic into the timer events as well. In that case, you might consider having multiple timers, or switching to java.util.timer. But again, this does not apply to your current case (even with multiple blocks).
Model and View
The model of your program is the thing that holds the abstract state. In this case, the positions and other meta-data about all your blocks. The view is the part that does the rendering. It is usually a good idea to separate these two things. There is often a third component to GUIs, called the controller, which connects the model and view to the user. We will ignore it here since you are not asking about controlling the blocks yet.
In your current code, you have attempted to represent your blocks with an extension to JPanel and a static list of existing blocks. While a JPanel may be a convenient way to display rectangular blocks with some custom graphics in them (like icons), I would recommend that you start by drawing the blocks directly using the Graphics object passed to paintComponent. At least initially, it will help you to think of the drawing code and the game logic as separate entities.
Final Rant Before Code Dump
I have made rewrites to your code to encapsulate all the ranting I did before into code. Here are some additional minor points about what I did that may help explain my reasoning:
When you call JFrame.add(...) to add a component to a JFrame, you are really calling JFrame.getContentPane().add(...). The content pane is where 90% of normal swing components go in a window. Therefore, we can either set the JPanel that will do the rendering as your content pane or we can add it to the current content pane. I have chosen to do the latter so that you can add other widgets, like a score board, at a later time.
Class names should generally be nouns, while methods are often verbs. This is not an absolute rule (nothing really is), but naming things this way will often help you visualize the interactions between objects in a more meaningful way. I have renamed DrawSquare to GamePiece for this reason.
There is no longer any reason for GamePiece to be a JPanel. It just needs to know its own width, height, and time of appearance.
The other problem with trying to have DrawSquare draw itself is that a component can only really draw within its own bounding box. So you really want to override the paintComponent of whatever holds the rectangles.
The rendering class maintains a reference to two lists of GamePieces. One is for the moving objects and one is for the ones that have fallen. The logic for moving them between the lists is in the timer. This is better than say adding a flag to GamePiece because it facilitates incremental repaint. I will only partially illustrate this here, but there is a version of repaint that only requests a small region to be painted. This would be useful to speed up the movement.
Code
public class Tetris extends JFrame
{
public static final int height = 20; //height of a square
public static final int width = 20; //width of a square
public static final int x = 0;
private GamePanel gamePanel;
public static void main(String[] args)
{
Tetris tet = new Tetris();
// Normally you would tie this to a button or some other user-triggered action.
tet.gamePanel.start();
tet.gamePanel.addPiece(new GamePiece(width, height, x));
}
public Tetris()
{
getContentPane().setLayout(new BorderLayout());
gamePanel = GamePanel();
add(gamePanel, BorderLayout.CENTER);
setSize(220,440);
setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setVisible(true);
}
}
public class GamePanel extends JPanel
{
private List<GamePiece> moving;
private List<GamePiece> still;
private Timer timer;
public GamePanel()
{
moving = new ArrayList<>();
still = new ArrayList<>();
timer = new Timer(100, new TimerListener());
}
public addPiece(int width, int height, int x)
{
moving.add(new GamePiece(width, height, x));
}
public void start()
{
timer.start();
}
#Override
public void paintComponent(Graphics g)
{
Rectangle clip = g.getClipBounds(null);
Rectangle rectToDraw = new Rectangle();
// I prefer this, but you can make the call every
// time you call `GamePiece.getY()`
long time = System.currentTimeMillis();
for(GamePiece piece : this.moving) {
rectToDraw.setSize(piece.width, piece.height)
rectToDraw.setLocation(piece.x, piece.getY(time))
if(rectangleToDraw.intersects(clip))
((Graphics2D)g).fill(rectToDraw)
}
for(GamePiece piece : this.still) {
rectToDraw.setSize(piece.width, piece.height)
rectToDraw.setLocation(piece.x, piece.getY(time))
if(rectangleToDraw.intersects(clip))
((Graphics2D)g).fill(rectToDraw)
}
}
private class TimerListener implements ActionListener
{
#Override
public void actionPerformed(ActionEvent e)
{
long time = System.currentTimeMillis();
// Using non-iterator loop to move the pieces that
// stopped safely. Iterator would crash on in-loop move.
for(int i = 0; i < moving.size(); i++) {
piece = moving.get(i);
if(piece.getY(time) > 440 - piece.height) {
moving.remove(i);
still.add(piece);
i--;
}
}
repaint();
}
}
}
public class GamePiece
{
public final int width;
public final int height;
public final long startTime;
public int x;
public GamePiece(int width, int height, int x)
{
this.width = width;
this.height = height;
this.startTime = System.currentTimeMillis();
this.x = x;
}
public int getY(long time)
{
// This hard-codes a velocity of 10px/sec. You could
// implement a more complex relationship with time here.
return (int)((time - this.startTime) / 100.0);
}
}
Your main problem in a nutshell: you need to separate the JPanel component class from the square logical class. Right now, they are one and the same, and every time you create a new DrawSqaure, you're creating a new JPanel, starting a new Swing Timer, and thus calling code that doesn't need to be called. This is also forcing you to make the List static else you'd have a stack overflow error. Solution: separate the two out, make your List non-static, and use only one Swing Timer.
Am trying to reduce number of calls being made everytime a Screen is called on my game in a bid to make my game faster and I noticed I do alot the same calculation on every screen..how can i avoid this?
I do this in practically every screen
public class ****Screen implements Screen {
#Override
public void show() {
float screenWidth = Gdx.graphics.getWidth();
float screenHeight = Gdx.graphics.getHeight();
float gameWidth = 360;
float gameHeight = screenHeight / (screenWidth / gameWidth);
midPointY = (int) (gameHeight / 2);
cam = new OrthographicCamera();
cam.setToOrtho(true, gameWidth, gameHeight);
viewport = new FitViewport(gameWidth, gameHeight, cam);
viewport.apply();
stage = new Stage(viewport);
Gdx.input.setInputProcessor(stage);
yet I have a GameClass..How can I implement the above in my gameclass(below) and only have to call it once?...
public class Start extends Game {
#Override
public void create() {
float screenWidth = Gdx.graphics.getWidth();
float screenHeight = Gdx.graphics.getHeight();
float gameWidth = 360;
float gameHeight = screenHeight / (screenWidth / gameWidth);
assets = new AssetLoader();
assetManager = new AssetManager();
camera = new OrthographicCamera();
camera.setToOrtho(true, gameWidth, gameHeight);
//initialize screens here
mainMenu = new MenuScreen(this);
loadingScreen = new LoadingScreen(this);
gameScreen = new GameScreen(this);
.......
//call assets
AssetLoader.load();
//start mainmenu...
this.setScreen(mainMenu);
}
First i have to tell you actualy this wont speed up your game.
If you change screen every 20 seconds then that means game does calculations 1 frame per 1200 frames.
However i am same like you and really looking for most optimize ways while doing game.
I found a solution for this case.
You can pass objects that you use in all screens, from game class to screen class.
Screen class
public class MainMenuScreen implements Screen {
public MainMenuScreen(OrthographicCamera camera) {
this.camera=camera;
}
//...Rest of class omitted for succinctness.
}
Game class
public class Starts extends Game {
OrthographicCamera camera;
public void create() {
camera=new OrthographicCamera();
camera.setToOrtho(false, 800, 480);
this.setScreen(new MainMenuScreen(camera));// change screen and pass camera to new screen.
}
}
Or you can even pass the whole game class like this.
public class MainMenuScreen implements Screen {
final Starts game;
public MainMenuScreen(final Starts game) {
this.game = game;
}
#Override
public void render(float delta) {
Gdx.gl.glClearColor(0, 0, 0.2f, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
game.camera.update();
game.batch.setProjectionMatrix(game.camera.combined);
game.batch.begin();
game.font.draw(...);
game.font.draw(...);
game.batch.end();
if (Gdx.input.isTouched()) {
game.setScreen(new AnotherScreen(game));
dispose();
}
}
}
just need to call like this in game class. So you can use stage batches fonts etc. of game in all screens.
this.setScreen(new MainMenuScreen(this));
I assume you mean you want to shorten the little hiccup you get when switching screens. (Your game's frame rate is not affected by this.)
Easy way: move that code from show to the constructor of each screen class.
However, it is wasteful to be creating a new Stage for each Screen without passing it a SpriteBatch, because you are essentially instantiating three separate SpriteBatches, which are heavy objects (they have a big array for the mesh data, a big mesh on the GPU, and have to compile a shader).
You can instantiate shared objects in your Game class and pass them into the constructors of your Screens like this:
public class Start extends Game {
SpriteBatch spriteBatch;
#Override
public void create() {
//...
//initialize screens here
spriteBatch = new SpriteBatch(); //must be disposed in dispose()
mainMenu = new MenuScreen(this, spriteBatch);
loadingScreen = new LoadingScreen(this, spriteBatch);
gameScreen = new GameScreen(this, spriteBatch);
//...
}
}
Update your Screens' constructors accordingly and pass the sprite batch into the stage constructors. Viewports and cameras are lightweight, so I wouldn't bother with moving those up to the Game class unless it helps make your code more maintainable.
By the way, you're kind of abusing FitViewport by pre-calculating the aspect ratio. The point of Viewports is that you don't need to calculate anything when setting up. Use new ExtendViewport(360, 1, cam) to get the same thing you're doing without the calculations. And make sure you're updating it in resize().
Im working on my 3D Game with LibGdx.
After looking some other Threads and posts with some realy good tutorials I get the first shader working. My Problem now is to get a Cel/Outline/Toon Shader working.
Therefore I found also a tutorial and a project but they havnt worked.
After reading some posts how to solve this shading Problem (with rendering the Object twice,...) I tried this method but got some sideeffects.
Actually I got as result a darfred rendering Scene.
My Question is now, If my Models just need some other Material or why I get these results.
I wrote a cel shader based on the KBAL tutorial that results in renderings like the one above. I've been meaning to write up something on it since the library has changed a lot since then. It seems like you got stuck on the depth shader, which is one of the parts from the original tutorial that needed the most updating.
Besides compatibility updates, I removed one render pass by modifying the uber shader that comes with LibGDX to perform the discretization in the KBAL tutorial's toonify() function during the initial rendering of geometry rather than in a post pass. Aside from that it follows the same pattern.
The code below is a bare bones implementation of my cel shader code. The class is derived extends AbstractScreen which implements some base functionality for LibGDX's Screen interface. Read more about Screen's here and see the CelTutorialScreen source within a full project context here.
package com.hh.ghoststory.screen;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.assets.AssetManager;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.PerspectiveCamera;
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.graphics.g3d.Environment;
import com.badlogic.gdx.graphics.g3d.Model;
import com.badlogic.gdx.graphics.g3d.ModelBatch;
import com.badlogic.gdx.graphics.g3d.ModelInstance;
import com.badlogic.gdx.graphics.g3d.environment.DirectionalLight;
import com.badlogic.gdx.graphics.glutils.FrameBuffer;
import com.badlogic.gdx.graphics.glutils.ShaderProgram;
import com.badlogic.gdx.math.Matrix4;
import com.badlogic.gdx.utils.Array;
import com.hh.ghoststory.GhostStory;
import com.hh.ghoststory.render.shaders.CelDepthShaderProvider;
import com.hh.ghoststory.render.shaders.CelLineShaderProgram;
public class CelTutorialScreen extends AbstractScreen {
private PerspectiveCamera camera = new PerspectiveCamera(67, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
private AssetManager assetManager = new AssetManager();
private Array<ModelInstance> instances = new Array<ModelInstance>();
private FrameBuffer fbo;
private TextureRegion textureRegion;
private ShaderProgram lineShader = new CelLineShaderProgram();
private SpriteBatch spriteBatch = new SpriteBatch();
private ModelBatch modelBatch = new ModelBatch(Gdx.files.classpath("com/badlogic/gdx/graphics/g3d/shaders/default.vertex.glsl").readString(), Gdx.files.internal("shaders/cel.main.fragment.glsl").readString());
private ModelBatch depthBatch = new ModelBatch(new CelDepthShaderProvider());
private Environment environment = new Environment();
public CelTutorialScreen(GhostStory game) {
super(game);
Gdx.gl.glClearColor(1.0f, 0.0f, 1.0f, 0.0f);
Gdx.gl.glClear(GL20.GL_DEPTH_BUFFER_BIT | GL20.GL_COLOR_BUFFER_BIT);
// setup camera
camera.position.set(5, 5, 5);
camera.lookAt(0, 0, 0);
camera.near = 1;
camera.far = 1000;
camera.update();
// add a light
environment.add(new DirectionalLight().set(0.8f, 0.8f, 1.8f, -1f, -0.8f, 0.2f));
// load our model
assetManager.load("models/spider.g3dj", Model.class);
loading = true;
}
#Override
public void render(float delta) {
if (loading && assetManager.update())
doneLoading();
camera.update();
Gdx.gl.glClearColor(1.0f, 0.0f, 0.0f, 0.0f);
Gdx.gl.glClear(GL20.GL_DEPTH_BUFFER_BIT | GL20.GL_COLOR_BUFFER_BIT);
// render depth map to fbo
captureDepth();
// draw the scene
renderScene();
// put fbo texture in a TextureRegion and flip it
prepTextureRegion();
// draw the cel outlines
drawOutlines();
}
/*
* Draws the cel outlines using the CelLineShaderProgram
*/
protected void drawOutlines() {
spriteBatch.setShader(lineShader);
lineShader.setUniformf("u_size", Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
spriteBatch.begin();
spriteBatch.draw(textureRegion, 0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
spriteBatch.end();
spriteBatch.setShader(null);
}
/*
* Stores fbo texture in a TextureRegion and flips it vertically.
*/
protected void prepTextureRegion() {
textureRegion = new TextureRegion(fbo.getColorBufferTexture());
textureRegion.flip(false, true);
}
/*
* Draws the depth pass to an fbo, using a ModelBatch created with CelDepthShaderProvider()
*/
protected void captureDepth() {
fbo.begin();
Gdx.gl.glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
Gdx.gl.glClear(GL20.GL_DEPTH_BUFFER_BIT | GL20.GL_COLOR_BUFFER_BIT);
depthBatch.begin(camera);
depthBatch.render(instances);
depthBatch.end();
fbo.end();
}
/*
* Renders the scene.
*/
protected void renderScene() {
Gdx.gl.glClear(GL20.GL_DEPTH_BUFFER_BIT | GL20.GL_COLOR_BUFFER_BIT);
modelBatch.begin(camera);
modelBatch.render(instances, environment);
modelBatch.end();
}
#Override
protected void doneLoading() {
loading = false;
instances.add(new ModelInstance(assetManager.get("models/spider.g3dj", Model.class)));
}
/*
* Set camera width and height, SpriteBatch projection matrix, and reinit the FBOs
*/
#Override
public void resize(int width, int height) {
camera.position.set(camera.position);
camera.viewportWidth = width;
camera.viewportHeight = height;
camera.update();
if (fbo != null) fbo.dispose();
fbo = new FrameBuffer(Pixmap.Format.RGBA8888, Gdx.graphics.getWidth(), Gdx.graphics.getHeight(), true);
spriteBatch.setProjectionMatrix(new Matrix4().setToOrtho2D(0, 0, width, height));
}
#Override
public void dispose() {
assetManager.dispose();
modelBatch.dispose();
depthBatch.dispose();
spriteBatch.dispose();
fbo.dispose();
lineShader.dispose();
}
}
The render performs 3 passes to create the end product.
The first is contained in the captureDepth() function.
protected void captureDepth() {
fbo.begin();
Gdx.gl.glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
Gdx.gl.glClear(GL20.GL_DEPTH_BUFFER_BIT | GL20.GL_COLOR_BUFFER_BIT);
depthBatch.begin(camera);
depthBatch.render(instances);
depthBatch.end();
fbo.end();
}
A framebuffer is started, glClear is called and then the depthBatch ModelBatch() renders the model instances (only one in this case) before the framebuffer is ended.
The depthBatch is a ModelBatch that uses a CelDepthShaderProvider, which provides a CelDepthShader. CellDepthShaderProvider is a small class that extends BaseShaderProvider and overrides createShader to return an instance of CelDepthShader, which registers and sets u_near and u_far uniforms as well as sets up the use of the cel depth vertex and fragment GLSL shaders.
I'm guessing the GLSL files are where you're running into issues. The vertex shader I linked to is the same as the KBAL vertex shader with the exception of line 125, which removed some artifacts on the cel edges:
v_depth = (pos.z + u_near) / (u_far - u_near);
The fragment shader is very similar to the one in the KBAL tutorial, but is actually copied from LibGDX's built in depth fragment shader. It's quite possible that the current LigGDX DepthShader could be used instead of my CelDepthShader, but I haven't had time to look into this.
After the first pass, the packed depth map has been captured by the FBO. The second pass is ready to be run and will draw the scene with LibGDXs' default vertex shader and a slightly modified version of its fragment shader.
The changes from the default fragment shader are in lines 140-150 where the specular value is discretized before being added to gl_FragColor:
if (specIntensity > 0.6)
specFactor = 1.0;
else if (specIntensity > 0.3)
specFactor = 0.5;
else
specFactor = 0.1;
specular *= specFactor;
And 173-182 where the overall gl_FragColor is discretized:
float intensity = max(gl_FragColor.r, max(gl_FragColor.g, gl_FragColor.b));
float factor;
if (intensity > 0.8)
factor = 1.0;
else if (intensity > 0.5)
factor = 0.8;
else if (intensity > 0.25)
factor = 0.3;
else
factor = 0.1;
And that's it for the main cel pass.
Next in render() the prepTextureRegion() function is called. This just puts the depth texture captured to our fbo into a texture region and flips it vertically before using it to draw the cel outlines in the final pass.
The final pass is performed in drawOutlines() and makes use of a SpriteBatch since we're drawing a 2d texture instead of geometry. The call to spriteBatch.setShader(lineshader) sets the SpriteBatch to use an instance of CelLineShaderProgram, another class that extends ShaderProgram. It sets a u_size uniform and uses cel.line.vertex.glsl and cel.line.fragment.glsl.
This shader program runs the Laplace filter. The vertex shader is copied from the KBAL edge shader and updated to work with newer versions of LibGDX, it passes the sampled coordinate of the depth map as well as its top, bottom, left and right neighboring texels to the fragment shader as varyings.
The fragment shader uses an updated method of unpacking the depth values based on code from the getShadowness() function here as recommended by Xoppa.
There are some improvements to this process that could be made. For one, I haven't implemented the super sampling in the original tutorial.
Also, it's not really noticeable in this still image, but once you have a controllable camera in the scene, or geometry moving around, you'll notice the per pixel lighting looks a little weird, especially with limited polygons in your geometry. There is a per-pixel lighting fragment shader in the LibGDX shadow system tests that could be used as a base to implement this with cel shading. The shadow systems might even be a good base to create a multi-pass rendering system for cel shading. And there is undoubtedly code that could be removed from the modified base LibGDX shaders I've used, as well as other optimizations and cleanup.
Hope this helps you or anyone else looking for info on multipass cel shading.
I am making a Java game using JMonkeyEngine and I have followed the tutorial on their site to shoot balls (bullets) at a wall. I get how everything works, but when I increase the velocity of the bullet, it passes straight through the wall.
Now I know the reason why this is happening (because the bullet moves too fast per frame for collision to register). I also know how to solve for this and I have found the following method on their website (JMonkeyEngine).
setCcdMotionThreshold(0f)
But can anyone tell me how this is implemented or where I can find a sample of someone using this?
Introduction
Continuous collision detection (ccd) deals with the bullet vs paper problem. Where a fast moving object is one side of a thin object at one timestep and the other side at the next time step leading to the physics engine believing that no collision has occurred at all. Continuous collision detection on the other hand doesn't use time steps at all. It creates a swept volume over the entire period of the timestep and finds if that swept volume collides with anything. This is both expensive and inaccurate (because a simple circular collision shape is used for this rather than the full collision shape).
Usage
Continuous collision detection is set on a per object basic, you can have objects that use ccd and objects that don't in the same scene at the same time, ccd is set on a physics object as shown below
RigidBodyControl physicsObject = new RigidBodyControl(mass);
physicsObject.setCcdMotionThreshold(expectedWidthOfThinObject);
physicsObject.setCcdSweptSphereRadius(radiusOfSphereThatWillFullyContainObject);
You want to set expectedWidthOfThinObject as high you can get away
with; remember ccd is expensive and inaccurate. Setting it as zero will turn ccd off
You want to set radiusOfSphereThatWillFullyContainObject as small as possible while fully containing the object
Full example
The following source code will show the difference between using Continuous Collision Detection and using the standard collision detection. It fires two sets of balls, one fast and one slow and turns ccd on and off at 5 second intervals. The slow balls always collide with the paper, the fast ones only when ccd is turned on.
import com.jme3.app.SimpleApplication;
import com.jme3.bullet.BulletAppState;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.font.BitmapText;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Box;
import com.jme3.scene.shape.Sphere;
public class BulletTest extends SimpleApplication {
public static void main(String args[]) {
BulletTest app = new BulletTest();
app.start();
}
/** Prepare the Physics Application State (jBullet) */
private BulletAppState bulletAppState;
/** Prepare Materials */
Material wall_mat;
Material slow_mat;
Material fast_mat;
Material floor_mat;
private RigidBodyControl brick_phy;
private static final Box box;
private static final Sphere sphere;
private RigidBodyControl floor_phy;
private static final Box floor;
/** dimensions used for wall */
private static final float brickLength = 2f;
private static final float brickWidth = 0.015f;
private static final float brickHeight = 1f;
static {
/** Initialize the cannon ball geometry */
sphere = new Sphere(32, 32, 0.1f, true, false);
/** Initialize the brick geometry */
box = new Box(brickWidth, brickHeight, brickLength);
/** Initialize the floor geometry */
floor = new Box(10f, 0.1f, 5f);
}
#Override
public void simpleInitApp() {
/** Set up Physics Game */
bulletAppState = new BulletAppState();
stateManager.attach(bulletAppState);
//bulletAppState.getPhysicsSpace().enableDebug(assetManager);
/** Configure cam to look at scene */
cam.setLocation(new Vector3f(0, 4f, 6f));
cam.lookAt(new Vector3f(2, 2, 0), Vector3f.UNIT_Y);
/** Initialize the scene, materials, and physics space */
initMaterials();
initWall();
initFloor();
setUpHUDText();
}
public static final float FIREPERIOD=0.5f;
double fireTimer=0;
public static final float SWEEPPERIOD=5f;
double sweepTimer=0;
boolean continuouslySweeping=true;
#Override
public void simpleUpdate(float tpf) {
fireTimer+=tpf;
sweepTimer+=tpf;
if (sweepTimer>SWEEPPERIOD){
sweepTimer=0;
continuouslySweeping=!continuouslySweeping;
}
hudText.setText("ContinouslySweeping=" + continuouslySweeping + "(" + (int)(SWEEPPERIOD-sweepTimer) + ")" );
if (fireTimer>FIREPERIOD){
fireTimer=0;
makeCannonBall(new Vector3f(-4,3,0),new Vector3f(6,4,0),slow_mat,continuouslySweeping); //slow arcing ball
makeCannonBall(new Vector3f(-4,3,-0.5f),new Vector3f(10,1,0),fast_mat,continuouslySweeping); //fast straight ball
}
}
public BitmapText hudText;
private void setUpHUDText(){
hudText = new BitmapText(guiFont, false);
hudText.setSize(guiFont.getCharSet().getRenderedSize()); // font size
hudText.setColor(ColorRGBA.White); // font color
hudText.setText("ContinouslySweeping=true"); // the text
hudText.setLocalTranslation(300, hudText.getLineHeight(), 0); // position
guiNode.attachChild(hudText);
}
/** Initialize the materials used in this scene. */
public void initMaterials() {
wall_mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
wall_mat.setColor("Color", ColorRGBA.Blue);
fast_mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
fast_mat.setColor("Color", ColorRGBA.Red);
slow_mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
slow_mat.setColor("Color", ColorRGBA.Green);
floor_mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
floor_mat.setColor("Color", ColorRGBA.Gray);
}
/** Make a solid floor and add it to the scene. */
public void initFloor() {
Geometry floor_geo = new Geometry("Floor", floor);
floor_geo.setMaterial(floor_mat);
floor_geo.setLocalTranslation(0, -0.1f, 0);
this.rootNode.attachChild(floor_geo);
/* Make the floor physical with mass 0.0f! */
floor_phy = new RigidBodyControl(0.0f);
floor_geo.addControl(floor_phy);
bulletAppState.getPhysicsSpace().add(floor_phy);
}
/** This loop builds a wall out of individual bricks. */
public void initWall() {
Vector3f location=new Vector3f(2,2,0);
Geometry brick_geo = new Geometry("brick", box);
brick_geo.setMaterial(wall_mat);
rootNode.attachChild(brick_geo);
/** Position the brick geometry */
brick_geo.setLocalTranslation(location);
//paper thin objects will fall down, mass 0 clamps it in position
brick_phy = new RigidBodyControl(0);
/** Add physical brick to physics space. */
brick_geo.addControl(brick_phy);
bulletAppState.getPhysicsSpace().add(brick_phy);
}
public void makeCannonBall(Vector3f startPoint, Vector3f initialVelocity, Material material, boolean continuouslySwept) {
/** Create a cannon ball geometry and attach to scene graph. */
Geometry ball_geo = new Geometry("cannon ball", sphere);
ball_geo.setMaterial(material);
rootNode.attachChild(ball_geo);
/** Position the cannon ball */
ball_geo.setLocalTranslation(startPoint);
/** Make the ball physcial with a mass > 0.0f */
RigidBodyControl ball_phy = new RigidBodyControl(1f);
/** Add physical ball to physics space. */
ball_geo.addControl(ball_phy);
bulletAppState.getPhysicsSpace().add(ball_phy);
/** Accelerate the physcial ball to shoot it. */
ball_phy.setLinearVelocity(initialVelocity);
if (continuouslySwept){
ball_phy.setCcdMotionThreshold(0.015f);
ball_phy.setCcdSweptSphereRadius(0.01f);
}
}
}
With continuous detection on both sets of balls bounce of as expected (balls entering from the top left):
With continuous detection off the fast set of balls (red) passes through the paper as if it wasn't there (and very occasionally a slow one (green) does too):
NB: this code is loosely based on the hello physics code with added functionality from advanced physics