[SOLVED] Project a PNG on a mesh

So, either it’s bounding shape is wrong or you are near-plane clipping… but then you’d have been clipping the big model, too.

Probably you need to call updateBound() on the mesh.


You are so right. That did the trick. Thank you very much.

@pspeed @oxplay2


I will refactor my code and then post it.


nice work! :slight_smile: so issue was about near-plane clipping, but caused by bounding issue.

also i did not take in mind you would want to “overlay” multiple stickers on each other, then your current solution will work well :slight_smile:

@pspeed he said model is about to be just a solid color, so i thought ealier it would be better to reuse model buffer, but now i see copy buffer as new mesh is better in this case.

Near plane clipping is different.

This was because the model’s bounds were at 0,0,0 and probably 0 in size. So as soon as 0,0,0 was not in the camera frustum then the object would be “clipped”.

Near plane clipping is when pixels aren’t drawn because they are too close to the camera and so outside the range of the z-buffer.


Here’s my final solution:

	public static Geometry createTargetMarker(Mesh mesh, Vector3f markerPosition, AssetManager assetManager) {
		Mesh markerMesh = new Mesh();
		float maxDistance = 20;
		List<Vector3f> position = new ArrayList<>();
		List<Vector3f> normals = new ArrayList<>();
		List<Integer> indices = new ArrayList<>();

		for (int i = 0; i < mesh.getTriangleCount(); i++) {
			Triangle tri = new Triangle();
			mesh.getTriangle(i, tri);
			boolean isClose = tri.get1().distance(markerPosition) < maxDistance
					|| tri.get2().distance(markerPosition) < maxDistance
					|| tri.get3().distance(markerPosition) < maxDistance;
			if (isClose) {
				for (int j = 0; j < 3; j++) {
					Vector3f v = tri.get(j);
					int index = 0;
					if (position.contains(v)) {
						index = position.indexOf(v);
					} else {
						index = position.size();

		Vector3f normal = normals.stream().reduce(new Vector3f(), (total, next) -> total.addLocal(next)).normalize();
		// Find a vector 90 degrees from both the normal and the world up vector
		Vector3f left = normal.cross(Vector3f.UNIT_Y);
		// Find a vector 90 degrees from both the normal and the left vector
		Vector3f up = normal.cross(left);
		float uMin = Float.MAX_VALUE, uMax = -Float.MAX_VALUE, vMin = Float.MAX_VALUE, vMax = -Float.MAX_VALUE;
		List<Vector2f> uvCoords = new ArrayList<>();
		for (Vector3f p : position) {
			float u = left.dot(p);
			float v = up.dot(p);
			uvCoords.add(new Vector2f(u, v));
			uMin = Math.min(uMin, u);
			uMax = Math.max(uMax, u);
			vMin = Math.min(vMin, v);
			vMax = Math.max(vMax, v);
		// Normalize UV coords
		for (Vector2f textCoord : uvCoords) {
			float u = textCoord.x, v = textCoord.y;
			textCoord.x = (u - uMin) / (uMax - uMin);
			textCoord.y = (v - vMin) / (vMax - vMin);

		markerMesh.setBuffer(Type.Position, 3, createFloatBuffer(position.toArray(new Vector3f[] {})));
		markerMesh.setBuffer(Type.Normal, 3, createFloatBuffer(normals.toArray(new Vector3f[] {})));
		markerMesh.setBuffer(Type.Index, 3, createIntBuffer(indices.stream().mapToInt(Integer::intValue).toArray()));
		markerMesh.setBuffer(Type.TexCoord, 2, createFloatBuffer(uvCoords.toArray(new Vector2f[] {})));

		Geometry geo = new Geometry("Marker", markerMesh);
		Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
		Texture2D tex = (Texture2D) assetManager.loadTexture("marker-target-sticker.png");
		mat.setTexture("ColorMap", tex);
		mat.getAdditionalRenderState().setPolyOffset(-1f, -1f);
		return geo;