Hi all,
I am building a Path mesh creator tool into my new scene editor which gives the user of the tool the ability to construct a path of any sorts, such as roads/rivers/etc.
I got the Path mesh to work kindof except when I slapped a texture onto it.
Now it looks like every second quad with a set of 2 triangles gets it’s UV’s flipped.
Like this screenshot:
Here is the code with a test, if there is anyone willing to help me, please?
Path class:
public class Path extends Mesh {
private float width;
private List<Spatial> controlPoints;
private int[] vertexIndices;
private Vector3f[] vertexPositions;
private Vector3f[] normalPositions;
private Vector2f[] vertexTexCoordinates;
/**
* Serialization only. Do not use.
*/
public Path() {
}
/**
* Create a path from a list of spatial points. The spatial's position,
* rotation and scale is then used to create the path strip.
*
* @param points
*/
public Path(List<Spatial> points) {
this.width = 1;
this.controlPoints = points;
this.generateVertices();
this.generateIndices();
this.buildMesh();
}
public void rebuild() {
this.generateVertices();
this.generateIndices();
buildMesh();
}
private void buildMesh() {
setMode(Mode.Triangles);
setBuffer(VertexBuffer.Type.Index, 3, BufferUtils.createIntBuffer(vertexIndices));
setBuffer(VertexBuffer.Type.Position, 3, BufferUtils.createFloatBuffer(vertexPositions));
setBuffer(VertexBuffer.Type.TexCoord, 2, BufferUtils.createFloatBuffer(vertexTexCoordinates));
setBuffer(VertexBuffer.Type.Normal, 3, BufferUtils.createFloatBuffer(normalPositions));
updateCounts();
updateBound();
setStatic();
}
private void generateIndices() {
// Set the index buffer for the mesh (each pair of vertices defines a quad)
vertexIndices = new int[(controlPoints.size() - 1) * 6];
for (int i = 0; i < controlPoints.size() - 1; i++) {
if (i % 2 == 0) {
vertexIndices[i * 6 + 0] = i * 2 + 0;
vertexIndices[i * 6 + 1] = i * 2 + 1;
vertexIndices[i * 6 + 2] = i * 2 + 2;
vertexIndices[i * 6 + 3] = i * 2 + 1;
vertexIndices[i * 6 + 4] = i * 2 + 3;
vertexIndices[i * 6 + 5] = i * 2 + 2;
} else {
//Need to flip it
//NB: TODO: STRUGGLING WITH THIS PIECE OF CODE.
//CHANGING THE INDICES DOES NOT SEEM TO WORK.
vertexIndices[i * 6 + 0] = i * 2 + 0;
vertexIndices[i * 6 + 1] = i * 2 + 1;
vertexIndices[i * 6 + 2] = i * 2 + 2;
vertexIndices[i * 6 + 3] = i * 2 + 1;
vertexIndices[i * 6 + 4] = i * 2 + 3;
vertexIndices[i * 6 + 5] = i * 2 + 2;
}
}
}
private void generateVertices() {
this.vertexPositions = new Vector3f[controlPoints.size() * 2];
this.normalPositions = new Vector3f[controlPoints.size() * 2];
this.vertexTexCoordinates = new Vector2f[controlPoints.size() * 2];
Spatial current = null;
Vector3f left = null;
Vector3f right = null;
for (int i = 0; i < controlPoints.size(); i++) {
// Calculate the position of the point along the road
current = controlPoints.get(i);
width = current.getLocalScale().x;
left = new Vector3f(-width * 0.5f, 0, 0);
left = current.getLocalRotation().mult(left);
left = current.getLocalTranslation().add(left);
right = new Vector3f(width * 0.5f, 0, 0);
right = current.getLocalRotation().mult(right);
right = current.getLocalTranslation().add(right);
vertexPositions[i * 2] = new Vector3f(left.x, left.y, left.z);
vertexPositions[i * 2 + 1] = new Vector3f(right.x, right.y, right.z);
// Calculate the UV coordinates for the left and right vertices
if (i % 2 == 0) {
vertexTexCoordinates[i * 2] = new Vector2f(0, 0);
vertexTexCoordinates[i * 2 + 1] = new Vector2f(1, 0);
} else {
vertexTexCoordinates[i * 2] = new Vector2f(0, 1);
vertexTexCoordinates[i * 2 + 1] = new Vector2f(1, 1);
}
normalPositions[i * 2] = new Vector3f(0, 1, 0);
normalPositions[i * 2 + 1] = new Vector3f(0, 1, 0);
}
}
public List<Spatial> getControlPoints() {
return controlPoints;
}
public void setControlPoints(List<Spatial> controlPoints) {
this.controlPoints = controlPoints;
}
@Override
public void read(JmeImporter importer) throws IOException {
super.read(importer);
InputCapsule capsule = importer.getCapsule(this);
controlPoints = capsule.readSavableArrayList("controlPoints", null);
this.generateVertices();
this.generateIndices();
this.buildMesh();
}
@Override
public void write(JmeExporter e) throws IOException {
super.write(e);
e.getCapsule(this).writeSavableArrayList(new ArrayList(controlPoints), "controlPoints", null);
}
}
And a test SimpleApplication. Just add your own texture.
public class TestRoad extends SimpleApplication {
private Path path;
private Geometry pathGeom;
public static void main(String[] args) {
TestRoad app = new TestRoad();
AppSettings settings = new AppSettings(true);
settings.setWidth(1280);
settings.setHeight(720);
app.setSettings(settings);
app.start(JmeContext.Type.Display);
}
@Override
public void simpleInitApp() {
viewPort.setBackgroundColor(ColorRGBA.DarkGray);
loadScene();
loadCamera();
}
private void loadScene() {
List<Spatial> points = new ArrayList<>();
for (int i = 0; i < 5; i++) {
Node point = new Node("point");
point.setLocalTranslation(0, 0, -i);
points.add(point);
}
path = new Path(points);
pathGeom = new Geometry("path", path);
addMaterial(pathGeom, ColorRGBA.White, "Textures/proto.png");
rootNode.attachChild(pathGeom);
}
private void loadCamera() {
cam.setLocation(new Vector3f(-3, 3, 2));
cam.lookAt(new Vector3f(0, 0, 0), Vector3f.UNIT_Y);
flyCam.setMoveSpeed(10);
}
private Material addMaterial(Spatial spatial, ColorRGBA colorRGBA, String textureStr) {
Material material = null;
material = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
material.setColor("Color", colorRGBA);
Texture texture = assetManager.loadTexture(textureStr);
texture.setWrap(Texture.WrapMode.Repeat);
material.setTexture("ColorMap", texture);
spatial.setMaterial(material);
return material;
}
}