I feel like the mesh class could be more convenient to work with if a simple utility method was added to it. I’ve created a class below that extends mesh just to show what I mean, but it makes life a lot simpler when dealing with mesh creation; arrays vs lists, Buffers, and all the little niggling things that are annoying - like creating a List<Integer> for triangles because you can’t use primitives in a collection, having to convert it to a int[] array, etc… All the little annoyances that can be taken care of with boilerplate code.
JmeMesh mesh = new JmeMesh();
// set the buffer for any type using collections or arrays, primitives or wrapped primitives.
List<Vector3f> vertsList....
Vector3f[] normArray....
List<Integer> triangles...
mesh.set(Type.Position, vertsList);
mesh.set(Type.Normal, normals);
mesh.set(Type.Index, triangles);
// and so on...
package com.jayfella.pixels.grid;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.math.Vector4f;
import com.jme3.scene.Mesh;
import com.jme3.scene.VertexBuffer;
import com.jme3.util.BufferUtils;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.Collection;
public class JmeMesh extends Mesh {
public JmeMesh() {
super();
}
public void set(VertexBuffer.Type type, Collection<?> inputValues) {
set(type, inputValues.toArray());
}
public void set(VertexBuffer.Type type, Object[] inputValues) {
set(type, null, inputValues);
}
public void set(VertexBuffer.Type type, Integer components, Object[] inputValues) {
if (inputValues == null || inputValues.length == 0) {
clearBuffer(type);
return;
}
if (inputValues.getClass().getComponentType() == float.class || inputValues.getClass().getComponentType() == Float.class) {
FloatBuffer fb = BufferUtils.createFloatBuffer(inputValues.length);
for (Object inputValue : inputValues) {
fb.put( (float) inputValue );
}
int c = (components == null ? 1 : components);
setBuffer(type, c, fb);
}
if (inputValues.getClass().getComponentType() == int.class || inputValues.getClass().getComponentType() == Integer.class) {
IntBuffer ib = BufferUtils.createIntBuffer(inputValues.length);
for (Object inputValue : inputValues) {
ib.put((int) inputValue);
}
int c = type == VertexBuffer.Type.Index
? 3
: (components == null ? 3 : components);
setBuffer(type, c, ib);
}
else if (inputValues.getClass().getComponentType() == Vector2f.class) {
FloatBuffer fb = BufferUtils.createFloatBuffer(inputValues.length * 2);
for (Object inputValue : inputValues) {
fb.put(((Vector2f)inputValue).x);
fb.put(((Vector2f)inputValue).y);
}
setBuffer(type, 2, fb);
}
else if (inputValues.getClass().getComponentType() == Vector3f.class) {
FloatBuffer fb = BufferUtils.createFloatBuffer(inputValues.length * 3);
for (Object inputValue : inputValues) {
fb.put(((Vector3f)inputValue).x);
fb.put(((Vector3f)inputValue).y);
fb.put(((Vector3f)inputValue).z);
}
setBuffer(type, 3, fb);
}
else if (inputValues.getClass().getComponentType() == Vector4f.class) {
FloatBuffer fb = BufferUtils.createFloatBuffer(inputValues.length * 4);
for (Object inputValue : inputValues) {
fb.put(((Vector4f)inputValue).x);
fb.put(((Vector4f)inputValue).y);
fb.put(((Vector4f)inputValue).z);
fb.put(((Vector4f)inputValue).w);
}
setBuffer(type, 4, fb);
}
// if we update the positions of the vertices, we also need to update the bounds of the mesh.
if (type == VertexBuffer.Type.Position) {
updateBound();
}
}
}
In what use-cases do you not already know the types of the things that you are setting?
I didn’t really understand a lot of what you mentioned in your justification.
I don’t understand why triangles means a list of integers… or even an array of integers.
I get the impression that some people make meshes in strange ways… like half way between object oriented and raw mesh and somehow making 9 copies of their data structures along the way.
I meant indices, or to be clear, the VertexBuffer.Type.Index buffer. For example if I’m creating a mesh from noise, I won’t know the amount of indices I’ll need. You can’t explicitly convert a list of Integer to an int array, so you have to create an int array and fill it with the values of the list. Which is tedious (not exactly a valid argument) but boilerplate. Repeatable.
I’m trying to understand how you can know what your indexes will be for your triangles without knowing how many triangles you will have.
An index buffer is only useful when triangles share vertexes… which already implies some kind of foreknowledge of the data structure.
Because otherwise, my point would be to build a list of triangle objects and from that go straight to buffers. By pass creating the other lists, creating the other extra arrays, etc… It’s all unnecessary RAM.
My thinking:
If you are using indexes at all then triangles are sharing vertexes. Which means there is already some predictability to how many vertexes you will have. Else how will you know when triangle A shares vertexes with triangle B in the next ‘row’.
Either, you have a predetermined arrangement of vertexes or you are really dealing with pairs of triangles that only share the inner edge (like for a quad).
Unless you are using an index buffer when you don’t even need one.
I get what you’re saying now. Yeah. So it doesn’t make sense for indices. I was scratching my head wondering how I could know how many vertices I need, but you were taking about indices specifically.
And if you are not sharing vertexes, ie: no index buffer and you don’t know how many vertexes you have then it implies that you are skipping a data structure.
Instead of a list of triangles and going straight to buffers you are somehow managing your individual vertexes as sets of three in an array list.
…which you would then convert to an array of Vector3f… to convert to a buffer of floats. When you could have gone straight from triangle → buffers.
A college programmer would collapse because of the triple copy of the source code, but sometimes it is practical and more pleasant to simply write down the thoughts one after the other.
public abstract class JaReMeshCalculator {
public static final float EPSILON = 0.001f;
protected ArrayList<Float> filledList(final float[] array) {
final int size = array.length;
final ArrayList<Float> arrayList = new ArrayList<>(size + 9);
for (int i = 0; i < size; i++) {
arrayList.add(array[i]);
}
return arrayList;
}
protected short[] toShortArray(final ArrayList<Short> list) {
final int size = list.size();
final short[] ret = new short[size];
for (int i = 0; i < size; i++) {
ret[i] = list.get(i);
}
return ret;
}
protected float[] toFloatArray(final ArrayList<Float> list) {
final int size = list.size();
final float[] ret = new float[size];
for (int i = 0; i < size; i++) {
ret[i] = list.get(i);
}
return ret;
}
}
with TEX_COORD_COMP = 3;
Note: The Vertrex and TexCord are still duplicated in both meshes! Only the indices are divided.