Skip to content

Commit 4db274c

Browse files
authored
Merge pull request #2503 from capdevon/capdevon-EmitterMeshFaceShape
Feat: EmitterMeshFaceShape - enhance particle emission uniformity
2 parents 3613bfd + c9f55a3 commit 4db274c

File tree

1 file changed

+92
-39
lines changed

1 file changed

+92
-39
lines changed
Lines changed: 92 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2009-2021 jMonkeyEngine
2+
* Copyright (c) 2009-2025 jMonkeyEngine
33
* All rights reserved.
44
*
55
* Redistribution and use in source and binary forms, with or without
@@ -36,6 +36,7 @@
3636
import com.jme3.scene.Mesh;
3737
import com.jme3.scene.VertexBuffer.Type;
3838
import com.jme3.util.BufferUtils;
39+
3940
import java.util.ArrayList;
4041
import java.util.List;
4142

@@ -52,79 +53,131 @@ public EmitterMeshFaceShape() {
5253
}
5354

5455
/**
55-
* Constructor. It stores a copy of vertex list of all meshes.
56-
* @param meshes
57-
* a list of meshes that will form the emitter's shape
56+
* Constructor. Initializes the emitter shape with a list of meshes.
57+
* The vertices and normals for all triangles of these meshes are
58+
* extracted and stored internally.
59+
*
60+
* @param meshes a list of {@link Mesh} objects that will define the
61+
* shape from which particles are emitted.
5862
*/
5963
public EmitterMeshFaceShape(List<Mesh> meshes) {
6064
super(meshes);
6165
}
6266

67+
/**
68+
* Sets the meshes for this emitter shape. This method extracts all
69+
* triangle vertices and computes their normals, storing them internally
70+
* for subsequent particle emission.
71+
*
72+
* @param meshes a list of {@link Mesh} objects to set as the emitter's shape.
73+
*/
6374
@Override
6475
public void setMeshes(List<Mesh> meshes) {
6576
this.vertices = new ArrayList<List<Vector3f>>(meshes.size());
6677
this.normals = new ArrayList<List<Vector3f>>(meshes.size());
78+
6779
for (Mesh mesh : meshes) {
6880
Vector3f[] vertexTable = BufferUtils.getVector3Array(mesh.getFloatBuffer(Type.Position));
6981
int[] indices = new int[3];
70-
List<Vector3f> vertices = new ArrayList<>(mesh.getTriangleCount() * 3);
71-
List<Vector3f> normals = new ArrayList<>(mesh.getTriangleCount());
82+
List<Vector3f> meshVertices = new ArrayList<>(mesh.getTriangleCount() * 3);
83+
List<Vector3f> meshNormals = new ArrayList<>(mesh.getTriangleCount());
84+
7285
for (int i = 0; i < mesh.getTriangleCount(); ++i) {
7386
mesh.getTriangle(i, indices);
74-
vertices.add(vertexTable[indices[0]]);
75-
vertices.add(vertexTable[indices[1]]);
76-
vertices.add(vertexTable[indices[2]]);
77-
normals.add(FastMath.computeNormal(vertexTable[indices[0]], vertexTable[indices[1]], vertexTable[indices[2]]));
87+
88+
Vector3f v1 = vertexTable[indices[0]];
89+
Vector3f v2 = vertexTable[indices[1]];
90+
Vector3f v3 = vertexTable[indices[2]];
91+
92+
// Add all three vertices of the triangle
93+
meshVertices.add(v1);
94+
meshVertices.add(v2);
95+
meshVertices.add(v3);
96+
97+
// Compute and add the normal for the current triangle face
98+
meshNormals.add(FastMath.computeNormal(v1, v2, v3));
7899
}
79-
this.vertices.add(vertices);
80-
this.normals.add(normals);
100+
this.vertices.add(meshVertices);
101+
this.normals.add(meshNormals);
81102
}
82103
}
83104

84105
/**
85-
* Randomly selects a point on a random face.
106+
* Randomly selects a point on a random face of one of the stored meshes.
107+
* The point is generated using barycentric coordinates to ensure uniform
108+
* distribution within the selected triangle.
86109
*
87-
* @param store
88-
* storage for the coordinates of the selected point
110+
* @param store a {@link Vector3f} object where the coordinates of the
111+
* selected point will be stored.
89112
*/
90113
@Override
91114
public void getRandomPoint(Vector3f store) {
92115
int meshIndex = FastMath.nextRandomInt(0, vertices.size() - 1);
116+
List<Vector3f> currVertices = vertices.get(meshIndex);
117+
int numVertices = currVertices.size();
118+
93119
// the index of the first vertex of a face (must be dividable by 3)
94-
int vertIndex = FastMath.nextRandomInt(0, vertices.get(meshIndex).size() / 3 - 1) * 3;
95-
// put the point somewhere between the first and the second vertex of a face
96-
float moveFactor = FastMath.nextRandomFloat();
97-
store.set(Vector3f.ZERO);
98-
store.addLocal(vertices.get(meshIndex).get(vertIndex));
99-
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);
100-
// move the result towards the last face vertex
101-
moveFactor = FastMath.nextRandomFloat();
102-
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);
120+
int faceIndex = FastMath.nextRandomInt(0, numVertices / 3 - 1);
121+
int vertIndex = faceIndex * 3;
122+
123+
// Generate the random point on the triangle
124+
generateRandomPointOnTriangle(currVertices, vertIndex, store);
103125
}
104126

105127
/**
106-
* Randomly selects a point on a random face.
107-
* The {@code normal} argument is set to the normal of the selected face.
128+
* Randomly selects a point on a random face of one of the stored meshes,
129+
* and also sets the normal of that selected face.
130+
* The point is generated using barycentric coordinates for uniform distribution.
108131
*
109-
* @param store
110-
* storage for the coordinates of the selected point
111-
* @param normal
112-
* storage for the normal of the selected face
132+
* @param store a {@link Vector3f} object where the coordinates of the
133+
* selected point will be stored.
134+
* @param normal a {@link Vector3f} object where the normal of the
135+
* selected face will be stored.
113136
*/
114137
@Override
115138
public void getRandomPointAndNormal(Vector3f store, Vector3f normal) {
116139
int meshIndex = FastMath.nextRandomInt(0, vertices.size() - 1);
140+
List<Vector3f> currVertices = vertices.get(meshIndex);
141+
int numVertices = currVertices.size();
142+
117143
// the index of the first vertex of a face (must be dividable by 3)
118-
int faceIndex = FastMath.nextRandomInt(0, vertices.get(meshIndex).size() / 3 - 1);
144+
int faceIndex = FastMath.nextRandomInt(0, numVertices / 3 - 1);
119145
int vertIndex = faceIndex * 3;
120-
// put the point somewhere between the first and the second vertex of a face
121-
float moveFactor = FastMath.nextRandomFloat();
122-
store.set(Vector3f.ZERO);
123-
store.addLocal(vertices.get(meshIndex).get(vertIndex));
124-
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);
125-
// move the result towards the last face vertex
126-
moveFactor = FastMath.nextRandomFloat();
127-
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);
146+
147+
// Generate the random point on the triangle
148+
generateRandomPointOnTriangle(currVertices, vertIndex, store);
149+
// Set the normal from the pre-computed normals list for the selected face
128150
normal.set(normals.get(meshIndex).get(faceIndex));
129151
}
152+
153+
/**
154+
* Internal method to generate a random point within a specific triangle
155+
* using barycentric coordinates.
156+
*
157+
* @param currVertices The list of vertices for the current mesh.
158+
* @param vertIndex The starting index of the triangle's first vertex
159+
* within the {@code currVertices} list.
160+
* @param store A {@link Vector3f} object where the calculated point will be stored.
161+
*/
162+
private void generateRandomPointOnTriangle(List<Vector3f> currVertices, int vertIndex, Vector3f store) {
163+
164+
Vector3f v1 = currVertices.get(vertIndex);
165+
Vector3f v2 = currVertices.get(vertIndex + 1);
166+
Vector3f v3 = currVertices.get(vertIndex + 2);
167+
168+
// Generate random barycentric coordinates
169+
float u = FastMath.nextRandomFloat();
170+
float v = FastMath.nextRandomFloat();
171+
172+
if ((u + v) > 1) {
173+
u = 1 - u;
174+
v = 1 - v;
175+
}
176+
177+
// P = v1 + u * (v2 - v1) + v * (v3 - v1)
178+
store.x = v1.x + u * (v2.x - v1.x) + v * (v3.x - v1.x);
179+
store.y = v1.y + u * (v2.y - v1.y) + v * (v3.y - v1.y);
180+
store.z = v1.z + u * (v2.z - v1.z) + v * (v3.z - v1.z);
181+
}
182+
130183
}

0 commit comments

Comments
 (0)