Below is the single source file to the basic demonstration in Java of how to build a 3D mesh out of multiple objects and render it in a single call.
Optimising render calls like this reduces overhead and speeds things up a hell of a lot. Which is essential when you need every CPU clock cycle to process a shed load more information.
You can find the actual demo in action in HTML5 HERE (Demo3D2)…
This is using libGdx and it runs on desktop, android and in a web browser.
And it’s as simple as it gets.
package com.wlgfx.demo3d2; import com.badlogic.gdx.ApplicationAdapter; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.graphics.GL20; import com.badlogic.gdx.graphics.PerspectiveCamera; import com.badlogic.gdx.graphics.Texture; import com.badlogic.gdx.graphics.VertexAttributes.Usage; import com.badlogic.gdx.graphics.g2d.SpriteBatch; import com.badlogic.gdx.graphics.g3d.Environment; import com.badlogic.gdx.graphics.g3d.Material; 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.attributes.ColorAttribute; import com.badlogic.gdx.graphics.g3d.attributes.FloatAttribute; import com.badlogic.gdx.graphics.g3d.attributes.TextureAttribute; import com.badlogic.gdx.graphics.g3d.environment.DirectionalLight; import com.badlogic.gdx.graphics.g3d.environment.PointLight; import com.badlogic.gdx.graphics.g3d.utils.MeshPartBuilder; import com.badlogic.gdx.graphics.g3d.utils.ModelBuilder; public class Demo3d2 extends ApplicationAdapter { ModelBatch modelBatch; SpriteBatch batch; Texture img; PerspectiveCamera cam; Environment environment; Model model; Texture texture; ModelInstance gridInstance; Material mat; PointLight light; float camx = 0, camy = 0, camz = 0; float camxs = 10, camys = 15, camzs = 20; float camxr = 65, camyr = 75, camzr = 55; @Override public void create () { batch = new SpriteBatch(); img = new Texture("badlogic.jpg"); modelBatch = new ModelBatch(); texture = new Texture("metal.jpg"); 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)); light = new PointLight().set(1, 1, 1, 0, 0, 0, 50); environment.add(light); cam = new PerspectiveCamera(67, Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); cam.position.set(10f, 10f, 10f); cam.lookAt(0,0,0); cam.near = 0f; cam.far = 1000f; cam.update(); mat = new Material(); mat.set(TextureAttribute.createDiffuse(texture)); mat.set(TextureAttribute.createBump(texture)); mat.set(ColorAttribute.createSpecular(1, 1, 1, 1)); mat.set(FloatAttribute.createShininess(8f)); model = buildGrid(); gridInstance = new ModelInstance(model); Gdx.gl20.glEnable(GL20.GL_DEPTH_TEST); Gdx.gl20.glDepthFunc(GL20.GL_GREATER); Gdx.gl20.glDepthMask(true); Gdx.gl20.glDepthRangef(0.0f, 100.0f); Gdx.gl20.glClearDepthf(1.0f); } private Model buildGrid() { mat = new Material(); mat.set(TextureAttribute.createDiffuse(texture)); mat.set(TextureAttribute.createBump(texture)); mat.set(ColorAttribute.createSpecular(1, 1, 1, 1)); mat.set(FloatAttribute.createShininess(8f)); ModelBuilder builder = new ModelBuilder(); builder.begin(); MeshPartBuilder partBuilder = builder.part("meshX", GL20.GL_TRIANGLES, Usage.Position | Usage.Normal | Usage.TextureCoordinates | Usage.BiNormal, mat); int step = 20; for (int a = -100; a <= 100; a += step) { for (int b = -100; b <= 100; b += step) { for (int c = -100; c <= 100; c += step * 4) { partBuilder.box(a, b, c, 1, 1, step * 4); partBuilder.box(a, c, b, 1, step * 4, 1); partBuilder.box(c, a, b, step * 4, 1, 1); } } } model = builder.end(); return model; } private void updateCamera(float delta) { camx = (camx + camxs * delta) % 360.0f; camy = (camy + camys * delta) % 360.0f; camz = (camz + camzs * delta) % 360.0f; cam.position.x = (float)Math.sin(Math.toRadians(camx)) * camxr; cam.position.y = (float)Math.cos(Math.toRadians(camy)) * camyr; cam.position.z = (float)Math.sin(Math.toRadians(camz)) * camzr; cam.lookAt(0, 0, 0); light.setPosition(cam.position.x, cam.position.y, cam.position.z); cam.update(); } @Override public void render () { Gdx.gl.glClearColor(1, 0, 0, 1); Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT); batch.begin(); batch.draw(img, 0, 0); batch.end(); float delta = Gdx.graphics.getDeltaTime(); updateCamera(delta); modelBatch.begin(cam); modelBatch.render(gridInstance, environment); modelBatch.end(); } }