Basic 3D using LibGDX

This tutorial guides you in using the basics of the 3d api LibGDX offers.

The full source and a runnable tests of this tutorial can be found on this github repository.

I assume you are already familiar with LibGDX, so let’s setup a new project and call it Basic3DTest. We will have to implement ApplicationListener, so let’s start that from scratch:

public class Basic3DTest implements ApplicationListener {
	@Override
	public void create () {
	}

	@Override
	public void render () {
	}
	
	@Override
	public void dispose () {
	}
	
	@Override
	public void resume () {
	}

	@Override
	public void resize (int width, int height) {
	}

	@Override
	public void pause () {
	}
}

Now let’s give that some body.
First we’ll need to add a camera, which allows us to view the 3D scene we’re creating from a certain perspective.

public class Basic3DTest implements ApplicationListener {
	public PerspectiveCamera cam;

	@Override
	public void create () {
		cam = new PerspectiveCamera(67, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
		cam.position.set(10f, 10f, 10f);
		cam.lookAt(0,0,0);
		cam.near = 1f;
		cam.far = 300f;
		cam.update();
	}
...
}

Here we create a new PerectiveCamera with a field of view of 67 degrees (which is common to use) and we set the aspect ratio to the current width and height. Then we set the camera 10 units to the right, 10 units up and 10 units back. The Z axis is pointing towards the viewer, so for the viewer a positive Z value of the camera is moving the viewer back. We set the camera to look at (0,0,0) because that is where we are placing our 3D object to view. We set the near and far values to make sure we would always see our object. And finally we update the camera so all changes we made are reflected by the camera.

Now let’s add something we can actually render. We could ofcourse launch some modeling application and create something to render (we will get to that soon), but for now we’ll just create a simple box by code.

public class Basic3DTest implements ApplicationListener {
	public PerspectiveCamera cam;
	public Model model;
	public ModelInstance instance;

	@Override
	public void create () {
		cam = new PerspectiveCamera(67, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
		...
		cam.update();

		ModelBuilder modelBuilder = new ModelBuilder();
		model = modelBuilder.createBox(5f, 5f, 5f, 
			new Material(ColorAttribute.createDiffuse(Color.GREEN)),
			Usage.Position | Usage.Normal);
		instance = new ModelInstance(model);
	}

	@Override
	public void dispose () {
		model.dispose();
	}
...
}

Here we instantiate a ModelBuilder, which can be used to create models on code. Then we create a simple model box with a size of 5x5x5. We also add a material with a green diffuse color to it and add position and normal components to the model. When creating a model at least Usage.Position is required. Usage.Normal adds normals to the box, so e.g. lighting works correctly. Usage is a subclass of VertexAttributes.

A model contains everything on what to render and it keeps track of the resources. It doesn’t contain information like where to render the model. Therefor we need to create a ModelInstance. A ModelInstance contains the location, rotation and scale the model should be rendered at. By default this is at (0,0,0), so we just create a ModelInstance which should be rendered at (0,0,0).

The model needs to be disposed, so we added a line to our Dispose() method.

Now let’s render the model instance.

public class Basic3DTest implements ApplicationListener {
	public PerspectiveCamera cam;
	public ModelBatch modelBatch;
	public Model model;
	public ModelInstance instance;
	
	@Override
	public void create () {
		modelBatch = new ModelBatch();
		
		cam = new PerspectiveCamera(67, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
		...
		cam.update();

		ModelBuilder modelBuilder = new ModelBuilder();
		model = modelBuilder.createBox(5f, 5f, 5f, 
				new Material(ColorAttribute.createDiffuse(Color.GREEN)),
				Usage.Position | Usage.Normal);
		instance = new ModelInstance(model);
	}

	@Override
	public void render () {
		Gdx.gl.glViewport(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
		Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);

		modelBatch.begin(cam);
		modelBatch.render(instance);
		modelBatch.end();
	}
	
	@Override
	public void dispose () {
		modelBatch.dispose();
		model.dispose();
	}
...
}

View full source code on github

Here we add the ModelBatch, which is responsable for rendering and we initialize it in the create method. In the render method we clear the screen, call modelBatch.begin(cam), render our ModelInstance and then call modelBatch.end() to finish rendering. Finally we need to dispose the modelBatch to make sure all resources (like the shaders it uses) are properly disposed.
basic3dtest1
That looks OK, but some lighting might help a bit, so let’s add lighting:

public class Basic3DTest implements ApplicationListener {
	public Environment environment;
	...
	
	@Override
	public void create () {
		environment = new Environment();
		environment.set(new ColorAttribute(ColorAttribute.AmbientLight, 0.4f, 0.4f, 0.4f, 1f));
		environment.add(new DirectionalLight().set(0.8f, 0.8f, 0.8f, -1f, -0.8f, -0.2f));
		...
	}

	@Override
	public void render () {
		Gdx.gl.glViewport(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
		Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);

		modelBatch.begin(cam);
		modelBatch.render(instance, environment);
		modelBatch.end();
	}
	...
}

View full source code on github

Here we add an Environment instance. We construct it and set the ambient light (0.4, 0.4, 0.4), note that the alpha value is ignored. Then we add a DirectionalLight with the color of (0.8, 0.8, 0.8) and the direction of (-1.0, -0.8f, 0.2). I assume that you’re familiar with lights in general. Finally we pass the environment to the modelbatch when rendering the instance.
basic3dtest2
That looks alot better. Now let’s control the camera so we can look at the model from other perspectives.

public class Basic3DTest implements ApplicationListener {
	...
	public CameraInputController camController;
	...
	
	@Override
	public void create () {
		...
		camController = new CameraInputController(cam);
		Gdx.input.setInputProcessor(camController);
	}

	@Override
	public void render () {
		camController.update();
		...
	}
...
}

View full source code on github

Here we add a CameraInputController which we create with our cam as argument. We also set Gdx.input.setInputProcessor to this camController and make sure to update it in the render call. That’s all there is to add the basic camera controller. You can now drag to make the camera rotate.

The complete Basic3DTest code is also included with the LibGDX tests.

Next: loading models using LibGDX

-= All Xoppa libGDX 3D Tutorials =-
  1. 1. Basic 3D using libGDX
  2. 2. Loading models
  3. 3. Loading a scene
  4. 4. Behind the scenes part 1
  5. 5. Behind the scenes part 2
  6. 6. Creating a shader
  7. 7. Using materials
  8. 8. Frustum culling
  9. 9. Ray picking
  10. 10. Collision shapes
  11. 11. 3D collision detection
  12. 12. 3D physics simulation

53 thoughts on “Basic 3D using LibGDX

  1. This one is really helpful. I’m actually kind of new to graphical programming and all this new stuff get’s me a little bit confused. How to I change the Model’s position now? I was testing with Meshes before and there I could simply go and do this before rendering:
    Gdx.gl10.glPushMatrix();
    Gdx.gl10.glTranslatef(x,y,z);
    Gdx.gl10.glRotatef(angle,xaxis,yaxis,zaxis);
    mesh.render();
    Gdx.gl10.glPopMatrix();
    And the mesh would be placed at x,y,z and rotated to my likings. How do I do this now? Translating seems to be possible by accessing the ModelInstance.transform.setToTranslation. But how do I rotate it now? And how can I scale it? Questions upon questions…

  2. Great! Very appreciated. The only strange thing is that i lost the color of the box when I put lights on modelBatch.render(instance, lights)

    My laptop specs:
    OS: Ubuntu 13.04 64 bits
    Processor: Intel® Core™ i7-3610QM CPU @ 2.30GHz × 8,
    Graphics: Intel® Ivybridge Mobile (Intel HD 4000)

    Thanks!

  3. Great tutorial! Have been waiting for some docs or tutorials on the new 3d API. Keep up the good work!

  4. Awesome blog. I enjoyed reading your articles. This is truly a great read for me. I have bookmarked it and I am looking forward to reading new articles. Keep up the good work.

  5. Awesome Tutorial, it really helped me getting started with the 3d api.

    However I have to import some of the classes manually (eg. Lights or ModelBuilder). Did I do something wrong in my Gdx-Setup? Because all other classes are automatically imported (so by pressing O).
    It all works perfectly fine I just don’t understand why some classes are imported without my help while others aren’t…

    Thanks for the tutorial man!

  6. Hi,
    really nice tutorials, but even with your tutorials, i’m stick on one problem :
    How do you texture only one face of the cube ?
    Thanks.

  7. Thanks for the tutorial but just a quick question: Are you using a different base/package of LibGDX? Some classes like Model, etc cannot be found in the default LibGDX download. Did you extend gdx.Graphic or is there some package to downoad?

  8. Hi.!
    I have problem(Nooob..?)
    I cant find ModelInstance, when i try to compile show this error:
    ModelInstance cannot be resolved to type.

  9. hey noob question here….i followed everything in this tutorial and everything compiles perfectly……but why am i always shown a black screen instead of the model from the modelbuilder as well as the model loader ??

  10. please reply to my problem….its frustrating me because i really want to start using the 3D API so badly.

    I ran the code exactly like from the tutorial…i even copy pasted into eclipse to make sure i did not miss anything crucial…but there seems to be no errors or warnings…..just an empty black screen…halllppp !!

  11. great tuts! appreciate your work with libgdx

    it’d be nice to see the full project in a zip or atleast include on here the import section, which seems to be missing from your tutorials.

    thanks again!

  12. first i would like to thank you for the great tutorial
    2nd i would like to ask if you have any information about Jbullet library with LibGdx

  13. A great series of tutorials…
    I don’t know how to thank people like badlogic or xoppa for the ease they have offered to the noobs like me.
    It would be really great if you people make a detailed series of tutorials on g3d part too(like animation-building in blender/3ds max etc, and loading them in libgdx…)

    • wow…
      It was easy too…
      Just built an animation in 3ds, exported to .fbx and used fbx-conv (as mentioned in “behind the scenes, part-1″)…
      thanks again

  14. Hey! Thx for your manuals, Xoppa, u’r doing a great job. How can i realize spot light effect? Can u give me a small example?

  15. I’m trying to use the ModelBuilder class but Eclipse is saying it cannot be resolved. I believe I’m using libgdx v0.9.8.

    Do I need a newer version?

  16. Is there any way to set a specific color/texture for each side of the cube? I’m trying to render a two-sided playing card using a thin cube but I can’t seem to figure out how to texture each face of the cube separately. I also can’t find anything related to texture coordinates either. :/

  17. Hey. I tried using the nightly build like you suggested but I’m still getting errors saying Model isn’t there.

    I tried updating an already existing project with the gdx-setup-ui.jar.

    What step am I missing?

  18. Hi Xoppa!
    I can’t seem to find a good tutorial on how to install the LibGDX in Eclipse. When I use your code I get errors, I guess I don’t know how to import the LibGDX.

    Do you know any tutorial that goes through the installation process? Most of them seem to be outdated.

    Thanks,
    Adrian

  19. I got mine to be controlled by the compass!

    First I added a field
    static boolean gotCompass = Gdx.input.isPeripheralAvailable(Peripheral.Compass);

    then I set it to only assign camController if (!gotCompass)

    finally, in render, I replaced camController.update(); with the following:

    if (gotCompass)
    {
    float distance = 10f;

    float pitch = 360-Gdx.input.getPitch();
    float azimuth = 360-Gdx.input.getAzimuth();
    float roll = Gdx.input.getRoll();

    Quaternion q = new Quaternion().setEulerAngles(pitch, azimuth, roll);
    cam.position.set(q.transform(new Vector3(distance,0,0)));
    cam.direction.set(q.transform(new Vector3(-1,0,0)));
    cam.up.set(q.transform(new Vector3(0,1,0)));

    cam.update();
    }
    else
    {
    camController.update();
    }

    It seems to behave *mostly* right. The east and west faces of the cube seem to behave a little erratically when the phone is looking at them oriented vertically. I think it might have something to do with the whole zero vs 360 thing …. any idea of how to fix that? Thanks for this great tutorial. I learned alot! :D

  20. i made it so i can rotate the camera around the x-axis i just wanna now how i can know which face is facing the camera :/

    any idea on how i can do that?!?

  21. I am sorry, not being stupid or silly, but somehow I cannot make this work at all.
    I tried downloading the master zip file from the URL mentioned above.
    I have working eclipse and libgdx-0.9.8, also have downloaded nightly build.
    When I import the “tutorials” into eclipse it keeps complaining “Project ‘tutorials’ is missing required Java project: ‘gdx-backend-lwjgl'” and another gdx.jar as well..
    I tried importing it into the “tutorials” project as “external jars” but it doesnt like it as in the build path it shows these 2 files ‘gdx-backend-lwjgl’ & gdx.jar as PROJECTS?
    How do I make this work without any error?
    Please be gnetle as I am quite new to this, and a step by step explanation would really be helpful.
    thanks for this

  22. looks like Gdx.graphics.getWidth/Height got changed to return int instead of floats

    the line for the camera constructor needs to convert to floats now

    cam = new PerspectiveCamera(67, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());

  23. good tutorial,but why it do not work for me?I use your code, and I Use libgdx0.99,it not work,Than i down the
    Nightly,also not work,error:

    Exception in thread “LWJGL Application” java.lang.IllegalStateException: Function is not supported
    at org.lwjgl.BufferChecks.checkFunctionAddress(BufferChecks.java:58)
    at org.lwjgl.opengl.GL15.glGenBuffers(GL15.java:106)
    at com.badlogic.gdx.backends.lwjgl.LwjglGL11.glGenBuffers(LwjglGL11.java:134)
    at com.badlogic.gdx.graphics.glutils.VertexBufferObject.createBufferObject(VertexBufferObject.java:97)
    at com.badlogic.gdx.graphics.glutils.VertexBufferObject.(VertexBufferObject.java:89)
    at com.badlogic.gdx.graphics.Mesh.(Mesh.java:116)
    at com.badlogic.gdx.graphics.g3d.utils.MeshBuilder.end(MeshBuilder.java:206)
    at com.badlogic.gdx.graphics.g3d.utils.ModelBuilder.end(ModelBuilder.java:79)
    at com.badlogic.gdx.graphics.g3d.utils.ModelBuilder.createBox(ModelBuilder.java:201)
    at com.badlogic.gdx.graphics.g3d.utils.ModelBuilder.createBox(ModelBuilder.java:190)
    at com.me.mygdxgame.MyGdxGame.create(MyGdxGame.java:40)
    at com.badlogic.gdx.backends.lwjgl.LwjglApplication.mainLoop(LwjglApplication.java:136)
    at com.badlogic.gdx.backends.lwjgl.LwjglApplication$1.run(LwjglApplication.java:114)

  24. Hi,

    Thanks for the tutorial.
    I have a question here about coordinate system.
    When we do something like:
    cam.position.set(10f, 10f, 10f);
    cam.lookAt(0,0,0);
    So we say, set camera at given coordinates or make camera look at given
    coordinates.But we haven’t defined any origin etc, so how are these positions
    determined without any origin and axes.
    Please help.

    Manish

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>