-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathMesh.java
More file actions
283 lines (248 loc) · 10.6 KB
/
Mesh.java
File metadata and controls
283 lines (248 loc) · 10.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
import java.util.ArrayList;
import java.util.Scanner;
import java.util.StringTokenizer;
import java.io.FileNotFoundException;
import java.io.File;
import java.io.IOException;
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.awt.image.Raster;
import javax.imageio.ImageIO;
//a class for storing groups of triangles in a mesh.
public class Mesh
{
//a collection of all the triangles in the mesh.
private ArrayList<Triangle> triangles;
private ArrayList<Vector3> vertices;
//the color of all the triangles of the mesh.
private Color baseColor;
//should the mesh be effected by lighting?
private boolean shading;
//a sum of all translations
private Vector3 totalMovement;
//the lighting object which was used last to recalculate lighting
private Lighting lighting;
//the texture applied to the mesh
private BufferedImage texture;
private Raster textureRaster;
//should the back face of the mesh be rendered? (keeping enabled greatly increases preformance, roughly 2x faster)*
//*however, due to a poor implementation of backFaceCulling, for some cases, it is recommended to dissable this,
//which will may sacrafice preformance (especially for larger models), however it will mitigate the
//strange visual effects that it may cause for certain models.
private boolean backFaceCull = true;
//overloaded constructor takes in the name of the model, transform offsets, color and the boolean values
public Mesh(String modelFileName, String textureFileName, Vector3 modelOffsetAmount, EulerAngle modelOffsetRotation, double scale, boolean shaded, boolean shouldBackFaceCull)
{
long start = System.nanoTime();
texture = null;
try
{
if (textureFileName != null)
texture = ImageIO.read(new File(FlightSimulator.RESOURCES_FOLDER, textureFileName));
}
catch (IOException e)
{
System.err.println("ERROR at: Mesh/constructor:\n\tError while loading texture: " + textureFileName);
}
if (texture != null)
textureRaster = texture.getData();
vertices = new ArrayList<Vector3>();
triangles = new ArrayList<Triangle>();
shading = shaded;
backFaceCull = shouldBackFaceCull;
baseColor = Color.MAGENTA;
totalMovement = new Vector3();
if (modelFileName.endsWith(".obj"))
{
createTriangles(modelFileName, modelOffsetAmount, modelOffsetRotation, scale);
}
else
{
System.err.println("ERROR at: Mesh/constructor:\n\tUnsupported 3d model file type. Please use .obj files");
}
System.out.println("mesh created: " + modelFileName + " in " + (System.nanoTime() - start)/1000000 + "ms\n\t- " + triangles.size() + " triangles");
}
//a second constructor for models without a texture file
public Mesh(String modelFileName, Color color, Vector3 modelOffsetAmount, EulerAngle modelOffsetRotation, double scale, boolean shaded, boolean shouldBackFaceCull)
{
long start = System.nanoTime();
texture = null;
textureRaster = null;
vertices = new ArrayList<Vector3>();
triangles = new ArrayList<Triangle>();
shading = shaded;
backFaceCull = shouldBackFaceCull;
baseColor = (color == null)? Color.MAGENTA : color;
totalMovement = new Vector3();
if (modelFileName.endsWith(".obj"))
{
createTriangles(modelFileName, modelOffsetAmount, modelOffsetRotation, scale);
}
else
{
System.err.println("ERROR at: Mesh/constructor:\n\tUnsupported 3d model file type. Please use .obj files");
}
System.out.println("mesh created: " + modelFileName + " in " + (System.nanoTime() - start)/1000000 + "ms\n\t- " + triangles.size() + " triangles");
}
//mesh constructor for children
protected Mesh(boolean shadedIn, boolean shouldBackFaceCull)
{
shading = shadedIn;
backFaceCull = shouldBackFaceCull;
triangles = new ArrayList<Triangle>();
}
//rotates each triangle in the mesh according to a rotation matrix, and around the center of rotation.
public void rotate(Matrix3x3 rotationMatrix, Vector3 centerOfRotation)
{
for (int i = 0; i < vertices.size(); i ++)
{
vertices.get(i).set(Vector3.add(Vector3.applyMatrix(rotationMatrix, Vector3.subtract(vertices.get(i), centerOfRotation)), centerOfRotation));
}
}
//translates each triangle in the mesh by "amount"
public void translate(Vector3 amount)
{
for (int i = 0; i < vertices.size(); i ++)
{
vertices.get(i).set(Vector3.add(vertices.get(i), amount));
}
totalMovement = Vector3.add(totalMovement, amount);
}
//#region getter methods
public boolean isShaded()
{
return shading;
}
public boolean backFaceCulling()
{
return backFaceCull;
}
public Raster getTextureRaster()
{
return textureRaster;
}
public ArrayList<Triangle> getTriangles()
{
return triangles;
}
public void resetPosition()
{
translate(Vector3.negate(totalMovement));
}
public ArrayList<Vector3> getVertices()
{
return vertices;
}
//#endregion
//calculates the lighting of each triangle in the mesh based off the given
//lighting object
public void calculateLighting(Lighting lightingIn)
{
if (shading)
{
for (int i = 0; i < triangles.size(); i++)
{
triangles.get(i).calculateLightingColor(lightingIn);
}
}
lighting = lightingIn;
}
//refreshes the lighting based on the stored lighting object.
public void refreshLighting()
{
if (shading)
{
for (int i = 0; i < triangles.size(); i++)
{
triangles.get(i).calculateLightingColor(lighting);
}
}
}
//reads a .obj file (a text file) and stores triangles inside the triangle list.
private void createTriangles(String fileName, Vector3 offsetPosition, EulerAngle offsetOrientation, double scale)
{
Matrix3x3 offsetRotationMatrix = Matrix3x3.eulerRotation(offsetOrientation);
//vertices are temporarily stored before they are conbined into triangles and added into the main
//triangle list.
ArrayList<Vector2> textureCoords = new ArrayList<Vector2>();
Scanner scanner;
String line = "";
//innitialize the scanner
try
{
scanner = new Scanner(new File(FlightSimulator.RESOURCES_FOLDER, fileName));
}
catch (FileNotFoundException e)
{
System.err.println("ERROR at: Mesh/readObjFile() method:\n\tfile " + fileName + " not found in " + FlightSimulator.RESOURCES_FOLDER.getAbsolutePath());
return;
}
//scanner goes through the file
while(scanner.hasNextLine())
{
line = scanner.nextLine();
if (!line.equals(""))
{
//v means Vector3 in .obj files
if (line.startsWith("v "))
{
StringTokenizer lineTokens = new StringTokenizer(line);
lineTokens.nextToken();
//create the Vector3 object
Vector3 vertexCoordinate = new Vector3(Double.parseDouble(lineTokens.nextToken()), Double.parseDouble(lineTokens.nextToken()), Double.parseDouble(lineTokens.nextToken()));
//apply transformations to the Vector3 based on offset params
vertexCoordinate = Vector3.applyMatrix(offsetRotationMatrix, vertexCoordinate);
vertexCoordinate = Vector3.add(offsetPosition, vertexCoordinate);
vertexCoordinate = Vector3.multiply(vertexCoordinate, scale);
//adds the Vector3 to the array of vertices
vertices.add(vertexCoordinate);
}
//vt means Vector3 texture coordinates.
if (texture!= null && line.startsWith("vt "))
{
StringTokenizer tokens = new StringTokenizer(line);
tokens.nextToken();
textureCoords.add(new Vector2(Double.parseDouble(tokens.nextToken()), Double.parseDouble(tokens.nextToken())));
}
//f means face in .obj files
if (line.startsWith("f "))
{
StringTokenizer lineTokens = new StringTokenizer(line);
lineTokens.nextToken();
int tokenLength = lineTokens.countTokens();
int[] coordinateIndexes = new int[tokenLength];
int[] textureIndexes = new int[tokenLength];
String[] tempArr;
Color color = baseColor;
for (int i = 0; i < tokenLength; i ++)
{
tempArr = lineTokens.nextToken().split("/");
coordinateIndexes[i] = Integer.parseInt(tempArr[0])-1;
if (texture != null)
textureIndexes[i] = Integer.parseInt(tempArr[1])-1;
}
//create triangles based on the indicated verticies. However often verticies are not in sets of 3, so create multiple triangles if necessary.
for (int i = 0; i < coordinateIndexes.length - 2; i ++)
{
if (texture == null)
triangles.add(new Triangle(this, vertices.get(coordinateIndexes[0]), vertices.get(coordinateIndexes[i+1]), vertices.get(coordinateIndexes[i+2]), color));
else
triangles.add
(
new Triangle
(
this,
vertices.get(coordinateIndexes[0]),
vertices.get(coordinateIndexes[i+1]),
vertices.get(coordinateIndexes[i+2]),
textureCoords.get(textureIndexes[0]),
textureCoords.get(textureIndexes[i+1]),
textureCoords.get(textureIndexes[i+2])
)
);
}
}
}
}
}
}