From c9f55a3d9e0c5e9628996bdce51392a1f0dce99f Mon Sep 17 00:00:00 2001 From: Wyatt Gillette Date: Wed, 18 Jun 2025 14:10:08 +0200 Subject: [PATCH] Update EmitterMeshFaceShape.java --- .../effect/shapes/EmitterMeshFaceShape.java | 131 ++++++++++++------ 1 file changed, 92 insertions(+), 39 deletions(-) diff --git a/jme3-core/src/main/java/com/jme3/effect/shapes/EmitterMeshFaceShape.java b/jme3-core/src/main/java/com/jme3/effect/shapes/EmitterMeshFaceShape.java index cda0c911eb..cdaefa40de 100644 --- a/jme3-core/src/main/java/com/jme3/effect/shapes/EmitterMeshFaceShape.java +++ b/jme3-core/src/main/java/com/jme3/effect/shapes/EmitterMeshFaceShape.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2021 jMonkeyEngine + * Copyright (c) 2009-2025 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -36,6 +36,7 @@ import com.jme3.scene.Mesh; import com.jme3.scene.VertexBuffer.Type; import com.jme3.util.BufferUtils; + import java.util.ArrayList; import java.util.List; @@ -52,79 +53,131 @@ public EmitterMeshFaceShape() { } /** - * Constructor. It stores a copy of vertex list of all meshes. - * @param meshes - * a list of meshes that will form the emitter's shape + * Constructor. Initializes the emitter shape with a list of meshes. + * The vertices and normals for all triangles of these meshes are + * extracted and stored internally. + * + * @param meshes a list of {@link Mesh} objects that will define the + * shape from which particles are emitted. */ public EmitterMeshFaceShape(List meshes) { super(meshes); } + /** + * Sets the meshes for this emitter shape. This method extracts all + * triangle vertices and computes their normals, storing them internally + * for subsequent particle emission. + * + * @param meshes a list of {@link Mesh} objects to set as the emitter's shape. + */ @Override public void setMeshes(List meshes) { this.vertices = new ArrayList>(meshes.size()); this.normals = new ArrayList>(meshes.size()); + for (Mesh mesh : meshes) { Vector3f[] vertexTable = BufferUtils.getVector3Array(mesh.getFloatBuffer(Type.Position)); int[] indices = new int[3]; - List vertices = new ArrayList<>(mesh.getTriangleCount() * 3); - List normals = new ArrayList<>(mesh.getTriangleCount()); + List meshVertices = new ArrayList<>(mesh.getTriangleCount() * 3); + List meshNormals = new ArrayList<>(mesh.getTriangleCount()); + for (int i = 0; i < mesh.getTriangleCount(); ++i) { mesh.getTriangle(i, indices); - vertices.add(vertexTable[indices[0]]); - vertices.add(vertexTable[indices[1]]); - vertices.add(vertexTable[indices[2]]); - normals.add(FastMath.computeNormal(vertexTable[indices[0]], vertexTable[indices[1]], vertexTable[indices[2]])); + + Vector3f v1 = vertexTable[indices[0]]; + Vector3f v2 = vertexTable[indices[1]]; + Vector3f v3 = vertexTable[indices[2]]; + + // Add all three vertices of the triangle + meshVertices.add(v1); + meshVertices.add(v2); + meshVertices.add(v3); + + // Compute and add the normal for the current triangle face + meshNormals.add(FastMath.computeNormal(v1, v2, v3)); } - this.vertices.add(vertices); - this.normals.add(normals); + this.vertices.add(meshVertices); + this.normals.add(meshNormals); } } /** - * Randomly selects a point on a random face. + * Randomly selects a point on a random face of one of the stored meshes. + * The point is generated using barycentric coordinates to ensure uniform + * distribution within the selected triangle. * - * @param store - * storage for the coordinates of the selected point + * @param store a {@link Vector3f} object where the coordinates of the + * selected point will be stored. */ @Override public void getRandomPoint(Vector3f store) { int meshIndex = FastMath.nextRandomInt(0, vertices.size() - 1); + List currVertices = vertices.get(meshIndex); + int numVertices = currVertices.size(); + // the index of the first vertex of a face (must be dividable by 3) - int vertIndex = FastMath.nextRandomInt(0, vertices.get(meshIndex).size() / 3 - 1) * 3; - // put the point somewhere between the first and the second vertex of a face - float moveFactor = FastMath.nextRandomFloat(); - store.set(Vector3f.ZERO); - store.addLocal(vertices.get(meshIndex).get(vertIndex)); - store.addLocal((vertices.get(meshIndex).get(vertIndex + 1).x - vertices.get(meshIndex).get(vertIndex).x) * moveFactor, (vertices.get(meshIndex).get(vertIndex + 1).y - vertices.get(meshIndex).get(vertIndex).y) * moveFactor, (vertices.get(meshIndex).get(vertIndex + 1).z - vertices.get(meshIndex).get(vertIndex).z) * moveFactor); - // move the result towards the last face vertex - moveFactor = FastMath.nextRandomFloat(); - store.addLocal((vertices.get(meshIndex).get(vertIndex + 2).x - store.x) * moveFactor, (vertices.get(meshIndex).get(vertIndex + 2).y - store.y) * moveFactor, (vertices.get(meshIndex).get(vertIndex + 2).z - store.z) * moveFactor); + int faceIndex = FastMath.nextRandomInt(0, numVertices / 3 - 1); + int vertIndex = faceIndex * 3; + + // Generate the random point on the triangle + generateRandomPointOnTriangle(currVertices, vertIndex, store); } /** - * Randomly selects a point on a random face. - * The {@code normal} argument is set to the normal of the selected face. + * Randomly selects a point on a random face of one of the stored meshes, + * and also sets the normal of that selected face. + * The point is generated using barycentric coordinates for uniform distribution. * - * @param store - * storage for the coordinates of the selected point - * @param normal - * storage for the normal of the selected face + * @param store a {@link Vector3f} object where the coordinates of the + * selected point will be stored. + * @param normal a {@link Vector3f} object where the normal of the + * selected face will be stored. */ @Override public void getRandomPointAndNormal(Vector3f store, Vector3f normal) { int meshIndex = FastMath.nextRandomInt(0, vertices.size() - 1); + List currVertices = vertices.get(meshIndex); + int numVertices = currVertices.size(); + // the index of the first vertex of a face (must be dividable by 3) - int faceIndex = FastMath.nextRandomInt(0, vertices.get(meshIndex).size() / 3 - 1); + int faceIndex = FastMath.nextRandomInt(0, numVertices / 3 - 1); int vertIndex = faceIndex * 3; - // put the point somewhere between the first and the second vertex of a face - float moveFactor = FastMath.nextRandomFloat(); - store.set(Vector3f.ZERO); - store.addLocal(vertices.get(meshIndex).get(vertIndex)); - store.addLocal((vertices.get(meshIndex).get(vertIndex + 1).x - vertices.get(meshIndex).get(vertIndex).x) * moveFactor, (vertices.get(meshIndex).get(vertIndex + 1).y - vertices.get(meshIndex).get(vertIndex).y) * moveFactor, (vertices.get(meshIndex).get(vertIndex + 1).z - vertices.get(meshIndex).get(vertIndex).z) * moveFactor); - // move the result towards the last face vertex - moveFactor = FastMath.nextRandomFloat(); - store.addLocal((vertices.get(meshIndex).get(vertIndex + 2).x - store.x) * moveFactor, (vertices.get(meshIndex).get(vertIndex + 2).y - store.y) * moveFactor, (vertices.get(meshIndex).get(vertIndex + 2).z - store.z) * moveFactor); + + // Generate the random point on the triangle + generateRandomPointOnTriangle(currVertices, vertIndex, store); + // Set the normal from the pre-computed normals list for the selected face normal.set(normals.get(meshIndex).get(faceIndex)); } + + /** + * Internal method to generate a random point within a specific triangle + * using barycentric coordinates. + * + * @param currVertices The list of vertices for the current mesh. + * @param vertIndex The starting index of the triangle's first vertex + * within the {@code currVertices} list. + * @param store A {@link Vector3f} object where the calculated point will be stored. + */ + private void generateRandomPointOnTriangle(List currVertices, int vertIndex, Vector3f store) { + + Vector3f v1 = currVertices.get(vertIndex); + Vector3f v2 = currVertices.get(vertIndex + 1); + Vector3f v3 = currVertices.get(vertIndex + 2); + + // Generate random barycentric coordinates + float u = FastMath.nextRandomFloat(); + float v = FastMath.nextRandomFloat(); + + if ((u + v) > 1) { + u = 1 - u; + v = 1 - v; + } + + // P = v1 + u * (v2 - v1) + v * (v3 - v1) + store.x = v1.x + u * (v2.x - v1.x) + v * (v3.x - v1.x); + store.y = v1.y + u * (v2.y - v1.y) + v * (v3.y - v1.y); + store.z = v1.z + u * (v2.z - v1.z) + v * (v3.z - v1.z); + } + }