OO Design - which is better?

I’m creating a buffer object helper class which is usable by a user with little or no knowledge of OpenGL. I have a helper class for creating and handling buffers called NIOBuffer. No solution is going to be pretty but I’m wondering which solution would be best. The first problem is how to deal with inheritance as you cannot pass Buffer to e.g glBufferData - I could either:

    1. Eliminate inheritance and create five different classes for each data type.
    1. Cast to the correct buffer type.

I’d assume the latter is preferred for ease of use purposes but for design purposes they’re both ugly. Now lets consider the actual design of that. Here I have one of three choices:

    1. Hold the buffer data as the data type Buffer, like so:

public class BufferObject extends NIOBuffer {

	private int id;
	private int target;
	private int usage;

	public BufferObject(int target, Buffer buffer, int usage) {
		super(buffer);
		this.id = glGenBuffers();
		this.target = target;
		this.usage = usage;
	}

	//...
}
public class NIOBuffer {

	public Buffer data;

	public NIOBuffer(Buffer buffer) {
		data = buffer;
	}

	//...
}

Pros:
Easy to read
Cons:
Type-unsafe (doesn’t accept LongBuffer)

[li]2. This is pretty much thrown in as an opportunity to use generics which some would say is better OOD, but I could make the buffer type generic which would still require casting:


public class BufferObject<T extends Buffer> extends NIOBuffer<T> {

	private int id;
	private int target;
	private int usage;

	public BufferObject(int target, T buffer, int usage) {
		super(buffer);
		this.id = glGenBuffers();
		this.target = target;
		this.usage = usage;
	}

	//...
}
public class NIOBuffer<T extends Buffer> {

	public T data;

	public NIOBuffer(T buffer) {
		data = buffer;
	}

	//...
}

Pros:
Easy to read
Cons:
Not fully type-safe (again LongBuffer)

    1. Use multiple static nested classes and make it fully type-safe.

public class BufferObject<T extends NIOBuffer<?>> {

	private int id;
	private int target;
	private int usage;
	private T buffer

	public BufferObject(int target, T buffer, int usage) {
		this.id = glGenBuffers();
		this.target = target;
		this.usage = usage;
		this.buffer = buffer;
	}

	//...
}
public class NIOBuffer<T extends Buffer> {

	public T data;

	public NIOBuffer(T buffer) {
		data = buffer;
	}

		public static class NIOByteBuffer extends NIOBuffer<ByteBuffer> {

			public NIOByteBuffer(ByteBuffer buffer) {
				super(buffer);
			}

		}

		// other classes...

	}
}

Pros:
Type-safe
Cons:
Hard to read?

And then here is how I would have to cast everything (prepare to throw up):

Fields:


private Class<ByteBuffer> byteType = null;
private Class<ShortBuffer> shortType = null;
private Class<IntBuffer> intType = null;
private Class<FloatBuffer> floatType = null;
private Class<DoubleBuffer> doubleType = null;

In my constructor I’d have to add:


if(buffer.data instanceof ByteBuffer) {
	byteType = (Class<ByteBuffer>) buffer.data.getClass();
} else if(buffer.data instanceof ShortBuffer) {
	shortType = (Class<ShortBuffer>) buffer.data.getClass();
} else if(buffer.data instanceof IntBuffer) {
	intType = (Class<IntBuffer>) buffer.data.getClass();
} else if(buffer.data instanceof FloatBuffer) {
	floatType = (Class<FloatBuffer>) buffer.data.getClass();
} else if(buffer.data instanceof DoubleBuffer) {
	doubleType = (Class<DoubleBuffer>) buffer.data.getClass();
} else {
	throw new IllegalStateException(buffer.data.getClass().toString());
}

For a single OpenGL function…


if(byteType != null) {
	glBufferData(target, (byteType.cast(buffer.data)), usage);
} else if(shortType != null) {
	glBufferData(target, (shortType.cast(buffer.data)), usage);
} else if(intType != null) {
	glBufferData(target, (intType.cast(buffer.data)), usage);
 else if(floatType != null) {
	glBufferData(target, (floatType.cast(buffer.data)), usage);
} else if(doubleType != null) {
	glBufferData(target, (doubleType.cast(buffer.data)), usage);
}

So… have fun answering. Please give some other design alternatives too. Oh and if anyone has any better methods of casting stuff please post them below. This is just what I could think from off the top of my head.

Only use ByteBuffer in your API.
A user can always convert a ByteBuffer to any other data-type-buffer.

Alway remember the KISS principle :slight_smile:

Hard to comment without knowing what your helper stuff shall actually do which is pretty unclear from your snippets and description.

Ah I never even realised that you could do that with ByteBuffer. However that would give me the exact same problem. I’d have to cast it if the buffer type was changed.

The helper class just holds the buffer data and provides helper methods which allows easy creation of a buffer from a set of floats, e.g:

	public static NIOShortBuffer createShortBuffer(short... contents) {
		return new NIOShortBuffer((ShortBuffer) ShortBuffer
				.allocate(contents.length).put(contents).flip());
	}

.

And what does a NIOBuffer do, what a BufferObject ?

no need for any casting, let me give you an example:


float[] data = ...;
ByteBuffer buffer = BufferUtil.createByteBuffer(4 * data.length);
FloatBuffer floats = buffer.asFloatBuffer();
floats.put(data);

new BufferObject(TARGET, buffer, USAGE);

just force your user to provide a ByteBuffer and everybody is happy.

I agree with Danny02 – KISS.

If your library is bloated and over-engineered, and doesn’t even provide any high-level features, then it sounds pretty useless. I would steer away from trying to wrap GL concepts exactly 1:1, and instead aim to provide a useful library. If a user needs to provide some obscure and specific parameters to accomplish a particular optimization, they can just call GL directly. See LibGDX’s glutils for reference.

How would that work? Java is pass by value.

That’s the exact opposite of what I’m trying to do. I’m trying to create a library which is usable by someone without any past experience of OpenGL. I’m not trying to make them have to pass obscure and specific parameters but to simply pass a buffer of any kind.

NIOBuffer holds a java.nio.[datatype]Buffer. A BufferObject is an object which holds data on the GPU. LWJGL has several overloaded methods which pass either a ByteBuffer, ShortBuffer, IntBuffer, FloatBuffer or DoubleBuffer to the GPU. What I’m trying to do is allow the user to use one of the helper methods in NIOBuffer which returns a sub-class of NIOBuffer that holds the buffer data and then the user can use that as the generic type parameter of BufferObject and pass that subclass to the constructor which uses that to obtain the buffer data.

Since LWJGL cannot accept java.nio.Buffer as a parameter (since LongBuffer is not supported) I have to cast that Buffer to the correct buffer type. That however requires the horrible bloated code which I posted above. What I want is to either find a more efficient and easier way of casting the Buffer to the correct buffer type which I don’t think is possible or an alternative design for my library which is still usable by a user with no knowledge of how to use OpenGL.

what has this to do with pass by value in your mind?
Take a look at the docs:

Take a step back, re-read by yourself, and think whether this concept is suitable for OpenGL newbies and simplifies its usage.

@Danny02: I don’t quite understand. How does the ByteBuffer share the modified data of the FloatBuffer? I wrote this simple code;


ByteBuffer bb = (ByteBuffer) BufferUtils.createByteBuffer(2 << 2);
FloatBuffer fb = bb.asFloatBuffer();
fb.put(3f).put(4f).rewind();
System.out.println(fb.get() +", "+ bb.get() +", "+ fb.position() +", "+ bb.position());

Result:
[icode]3.0, 0, 1, 1[/icode]

According to the docs the result should be 3.0, 3.0, 1, 1 should it not?

[quote]changes to this buffer’s content will be visible in the new buffer, and vice versa
[/quote]
@65K: Do you have any other alternative designs which allow the user to create a buffer object with no knowledge of using OpenGL? It seems simple enough to me. Anyone with a good knowledge of the Java language should grasp the idea quite easily if it’s well documented.

What kind of knowledge do we not need when we use this buffer stuff ?
Give us a line of code what you would users like to type to use it.

If you wanted to create a simple vbo and fill it with data,

With LWJGL:


int vboId = glGenBuffers();
float[] data = {...};
glBindBuffer(GL_ARRAY_BUFFER, vboId);
glBufferData(GL_ARRAY_BUFFER, (FloatBuffer) BufferUtils.createFloatBuffer(data.length).put(data).flip(), GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);

With my library:


float[] data = {...};
BufferObject<NIOFloatBuffer> vbo = new BufferObject<NIOFloatBuffer>(BufferObject.Target.ARRAY_BUFFER, NIOBuffer.createFloatBuffer(data), BufferObject.Usage.STATIC_DRAW);

Everything would be fully documented and I’d include method chaining. Overall my main purpose is to create a library that someone familiar with Java but not familiar with OpenGL could use.

I never used VBO’s, and thus don’t know how to use them correctly, and the pure LWJGL-Code seems much simpler than your code.
You are making the interfacing with OpenGL even harder than before!

You should better make a class that lets you create a ‘Mesh’, to which you can then add vertices in any way you wan’t.
The class itself then handles the VBO stuff in the background, so the user will only have to call simple ‘addVertex’ ‘addTriangle’ ‘addQuad’ ‘renderMesh’ methods.

That would be much more user friendly than your current approach.

Have a nice day.

  • Longor1996

@Longor1996 Buffer objects aren’t just for vertices. That’s just one usage of them. They have many uses; their purpose is to store data on the GPU. They can be used for other things like texture coordinates. I do however like your idea of adding helper methods for creating simple shapes. I’ll find a way to implement that in the future.

All of this to save a couple lines of code. When the reality is that nobody will use your BufferObject class if you give them a VertexBuffer and IndexBuffer utility, like LibGDX.

IMHO this is an example of over-engineering. You’re adding unnecessary bloat and complexity to a library instead of focusing on what really matters.

That was my point as well.
And the approach does not free you from OpenGL knowledge at all.

pls read the docs^^ and then try getFloat on the ByteBuffer or you will get only the first byte of the float which is 0 as it seems for 3f.

btw as a general thought, in my framework I can do something like this. I will probably change my code in the near future quite drasticly, but I think as a first try it was quite a nice api.


VectorType FLOAT3 = new GenericVector(FLOAT, 3);
Element POSITION = new Element(FLOAT3, "position");
Element COLOR = new Element(FLOAT3, "color");

DataLayout layout = new DataLayout(INTERLEAVE32, POSITION, COLOR);

VertexBuffer buffer = new VertexBuffer(layout, 3);

for(Vertex v: buffer)
{
  v.set(POSITION, 2, 3.4f, 1);
  v.set(COLOR, 0.3f, 0.8f, 0.9f);
}

@Troubleshoots: you should drop the idea of writing a library, and reconsider it after at least a year, when you have more experience. You can only build tools for others when you have a solid understanding of the underlying mechanics. My impression is that you’re just starting out and still discover new things and concepts every day. My advice to you is to use libraries, not make them. By the time you understand how these libraries work, and more importantly, why they were designed that way, you will have enough information to roll your own - if you still feel like it’s the way to go.

I hate the state based structure of OpenGL which is the main reason why I’m writing this library. It’s mainly for personal use.

Writing my own library will help me learn. Design can be changed. I don’t see the point of using a library if my goal is to create a library. Using a library is a short-cut. I feel that I’ll learn more by going the longer route; after all, I have the time.

Oh thanks I’ve got it to work. I’ll change my design.