Instancing Discussion

Awesome thanks!

It is somewhat setup to not be changed however, which isn’t really what I need (projectiles). The good thing is that my idea of having it dynamically rebuilt every frame works as well:

import java.nio.FloatBuffer;
import java.util.ArrayList;

import com.jme3.app.SimpleApplication;
import com.jme3.input.MouseInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.MouseButtonTrigger;
import com.jme3.material.Material;
import com.jme3.math.Matrix4f;
import com.jme3.math.Quaternion;
import com.jme3.math.Transform;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.Mesh;
import com.jme3.scene.Spatial.CullHint;
import com.jme3.scene.VertexBuffer;
import com.jme3.scene.VertexBuffer.Usage;
import com.jme3.scene.shape.Box;
import com.jme3.util.BufferUtils;

public class MCVE extends SimpleApplication {
	
	private static boolean shoot = false;
	
	private static Mesh boxes;
	private static Geometry model;
	private static ArrayList<Shot> objects = new ArrayList<Shot>();
	
	public static void main(String[] args) {
		MCVE app = new MCVE();
		app.start();
	}

	@Override
	public void simpleInitApp(){		
		flyCam.setMoveSpeed(10);
		cam.setFrustumPerspective(70f, (float) cam.getWidth() / cam.getHeight(), 1f, 1000f);
		cam.setLocation(Vector3f.UNIT_Z.mult(10f));
		
		boolean instance = true;		
		
		boxes = new Box(1f,1f,1f);
		
        Material stone_mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
        stone_mat.setTexture("ColorMap", assetManager.loadTexture("Textures/Terrain/Rock/Rock.PNG"));
        stone_mat.setBoolean("UseInstancing", instance);
        
		model = new Geometry("sph", boxes);
		model.setCullHint(CullHint.Never);
		model.setLocalTranslation(0, 0, 0);
		model.setMaterial(stone_mat);
		rootNode.attachChild(model);
		
		rootNode.setCullHint(CullHint.Never);
		
        inputManager.addListener(new ActionListener() {
            public void onAction(String name, boolean isPressed, float tpf) {
            	if(name.equals("shoot"))
            		shoot = isPressed;
            }
        }, "shoot");
        inputManager.addMapping("shoot", new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
	}
	
	@Override
    public void simpleUpdate(float tpf){
		if(shoot){
			Vector3f side = cam.getRotation().getRotationColumn(0).mult(3);
			
			objects.add(new Shot(cam.getLocation().add(side),cam.getRotation()));
			objects.add(new Shot(cam.getLocation().add(side.negate()),cam.getRotation()));
		}
		
		for (int i = 0; i < objects.size(); i++) {
			Shot b = objects.get(i);
			b.ttl-=tpf;
			
			if(b.ttl > 0)
				b.update(tpf);
			else
			{
				objects.remove(b);
				i--;
			}
		}
		
		recalculate();
	}

	private void recalculate() {
		if(objects.size() == 0){
			return;
		}
		
		VertexBuffer dataVb = new VertexBuffer(VertexBuffer.Type.InstanceData);
		dataVb.setInstanced(true);

		FloatBuffer data = BufferUtils.createFloatBuffer(16*objects.size());
		
		for (int i = 0; i < objects.size(); i++) {
			Matrix4f mat4 = objects.get(i).transform.toTransformMatrix();
			mat4.fillFloatBuffer(data, true);
		}
		
		data.flip();
		dataVb.setupData(Usage.Static, 16, VertexBuffer.Format.Float, data);
		
		boxes.clearBuffer(VertexBuffer.Type.InstanceData);
		boxes.setBuffer(dataVb);
		boxes.updateCounts();
		boxes.updateBound();
	}
}

class Shot{
	
	float ttl = 10;
	Transform transform;
	
	public Shot(Vector3f loc, Quaternion dir){
		transform = new Transform();
		transform.setTranslation(loc);
		transform.setRotation(dir);
	}
	
	public void update(float tpf){
		Vector3f dir = transform.getRotation().getRotationColumn(2).mult(60*tpf);
		transform.setTranslation(transform.getTranslation().add(dir));
	}
	
}

Funny thing, the previous test I posted was made with static (very highly subdivided) spheres ran at like 30 fps when not instanced and around 5 fps otherwise.

So I figured I should test with something low poly like boxes. Yet here it seems to perform in a splendid fashion.

I think the idea that instancing is for heavy meshes is somewhat off, with the main difference being the possibility of easier geometry moving here. (and not having to murder yourself with buffer editing when batching)

It’s still a bit annoying that the InstanceData buffer needs to be cleared every frame, but I don’t see an alternative from all the errors I get trying to edit the existing one.

Having to set everything to not cull itself is a bit of a hard sell as well.

The main problem is probably that this requires one hell of an infrastructure - one that has to be fully disabled and replaced with something else when not running on opengl 3.1+