1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package com.irurueta.geometry.io;
17
18 import java.io.File;
19 import java.io.IOException;
20 import java.util.HashSet;
21 import java.util.Set;
22 import java.util.StringTokenizer;
23
24
25
26
27
28 public class MaterialLoaderOBJ extends MaterialLoader {
29
30
31
32
33 public static final short MAX_COLOR_VALUE = 255;
34
35
36
37
38 private final Set<Material> materials = new HashSet<>();
39
40
41
42
43 private MaterialOBJ currentMaterial = null;
44
45
46
47
48 private int textureCounter;
49
50
51
52
53 public MaterialLoaderOBJ() {
54 super();
55 }
56
57
58
59
60
61
62
63
64 public MaterialLoaderOBJ(final File f) throws IOException {
65 super(f);
66 }
67
68
69
70
71
72
73
74 public MaterialLoaderOBJ(final MaterialLoaderListener listener) {
75 super(listener);
76 }
77
78
79
80
81
82
83
84
85
86
87 public MaterialLoaderOBJ(final File f, final MaterialLoaderListener listener) throws IOException {
88 super(f, listener);
89 }
90
91
92
93
94
95
96
97 @Override
98 public boolean isReady() {
99 return hasFile();
100 }
101
102
103
104
105
106
107
108
109
110
111
112
113 @Override
114 public Set<Material> load() throws LockedException, NotReadyException, IOException, LoaderException {
115 if (!isReady()) {
116 throw new NotReadyException();
117 }
118 if (isLocked()) {
119 throw new LockedException();
120 }
121
122 setLocked(true);
123
124 materials.clear();
125 textureCounter = 0;
126
127 if (listener != null) {
128 listener.onLoadStart(this);
129 }
130
131 String line;
132 do {
133 line = reader.readLine();
134 if (line != null) {
135 parseLine(line);
136 }
137 } while (line != null);
138
139 currentMaterial.setId(materials.size());
140 materials.add(currentMaterial);
141
142 if (listener != null) {
143 listener.onLoadEnd(this);
144 }
145
146 setLocked(false);
147
148 return materials;
149 }
150
151
152
153
154
155
156
157 @SuppressWarnings({"DuplicateExpressions", "DuplicatedCode"})
158 private void parseLine(final String line) throws LoaderException {
159 if (line == null || line.isEmpty()) {
160 return;
161 }
162
163 try {
164 final var tokenizer = new StringTokenizer(line);
165 if (!tokenizer.hasMoreTokens()) {
166
167
168 return;
169 }
170
171 final var command = tokenizer.nextToken();
172
173
174 final var commandPos = line.indexOf(command);
175
176 if ("newmtl".equalsIgnoreCase(command)) {
177 if (currentMaterial != null) {
178 currentMaterial.setId(materials.size());
179 materials.add(currentMaterial);
180 }
181 final var name = line.substring(commandPos + command.length()).trim();
182 currentMaterial = new MaterialOBJ(name);
183
184 } else if ("ka".equalsIgnoreCase(command)) {
185 if (currentMaterial == null) {
186 throw new LoaderException();
187 }
188 currentMaterial.setAmbientColor(
189 (short) (Float.parseFloat(tokenizer.nextToken()) * MAX_COLOR_VALUE),
190 (short) (Float.parseFloat(tokenizer.nextToken()) * MAX_COLOR_VALUE),
191 (short) (Float.parseFloat(tokenizer.nextToken()) * MAX_COLOR_VALUE));
192
193 } else if ("kd".equalsIgnoreCase(command)) {
194 if (currentMaterial == null) {
195 throw new LoaderException();
196 }
197 currentMaterial.setDiffuseColor(
198 (short) (Float.parseFloat(tokenizer.nextToken()) * MAX_COLOR_VALUE),
199 (short) (Float.parseFloat(tokenizer.nextToken()) * MAX_COLOR_VALUE),
200 (short) (Float.parseFloat(tokenizer.nextToken()) * MAX_COLOR_VALUE));
201
202 } else if ("Ks".equalsIgnoreCase(command)) {
203 if (currentMaterial == null) {
204 throw new LoaderException();
205 }
206 currentMaterial.setSpecularColor(
207 (short) (Float.parseFloat(tokenizer.nextToken()) * MAX_COLOR_VALUE),
208 (short) (Float.parseFloat(tokenizer.nextToken()) * MAX_COLOR_VALUE),
209 (short) (Float.parseFloat(tokenizer.nextToken()) * MAX_COLOR_VALUE));
210
211 } else if ("Ns".equalsIgnoreCase(command) || "Ni".equalsIgnoreCase(command)) {
212 if (currentMaterial == null) {
213 throw new LoaderException();
214 }
215 currentMaterial.setSpecularCoefficient(Float.parseFloat(tokenizer.nextToken()));
216
217 } else if ("d".equalsIgnoreCase(command) || "Tr".equalsIgnoreCase(command)) {
218 if (currentMaterial == null) {
219 throw new LoaderException();
220 }
221 currentMaterial.setTransparency((short) (Float.parseFloat(tokenizer.nextToken()) * MAX_COLOR_VALUE));
222
223 } else if ("illum".equalsIgnoreCase(command)) {
224 if (currentMaterial == null) {
225 throw new LoaderException();
226 }
227 currentMaterial.setIllumination(Illumination.forValue(Integer.parseInt(tokenizer.nextToken())));
228
229 } else if ("map_Kd".equalsIgnoreCase(command)) {
230 final var textureName = line.substring(commandPos + command.length()).trim();
231 final var tex = new Texture(textureName, textureCounter);
232 textureCounter++;
233
234 var valid = true;
235 if (textureValidationEnabled && listener != null) {
236 valid = listener.onValidateTexture(this, tex);
237 }
238 if (!valid) {
239 throw new InvalidTextureException();
240 }
241
242 currentMaterial.setDiffuseTextureMap(tex);
243
244 } else if ("map_Ka".equalsIgnoreCase(command)) {
245 final var textureName = line.substring(commandPos + command.length()).trim();
246 final var tex = new Texture(textureName, textureCounter);
247 textureCounter++;
248
249 var valid = true;
250 if (textureValidationEnabled && listener != null) {
251 valid = listener.onValidateTexture(this, tex);
252 }
253 if (!valid) throw new InvalidTextureException();
254
255 currentMaterial.setAmbientTextureMap(tex);
256
257 } else if ("map_Ks".equalsIgnoreCase(command)) {
258 final var textureName = line.substring(commandPos + command.length()).trim();
259 final var tex = new Texture(textureName, textureCounter);
260 textureCounter++;
261
262 var valid = true;
263 if (textureValidationEnabled && listener != null) {
264 valid = listener.onValidateTexture(this, tex);
265 }
266 if (!valid) {
267 throw new InvalidTextureException();
268 }
269
270 currentMaterial.setSpecularTextureMap(tex);
271
272 } else if ("map_d".equalsIgnoreCase(command)) {
273 final var textureName = line.substring(commandPos + command.length()).trim();
274 final var tex = new Texture(textureName, textureCounter);
275 textureCounter++;
276
277 var valid = true;
278 if (textureValidationEnabled && listener != null) {
279 valid = listener.onValidateTexture(this, tex);
280 }
281 if (!valid) {
282 throw new InvalidTextureException();
283 }
284
285 currentMaterial.setAlphaTextureMap(tex);
286
287 } else if ("map_bump".equalsIgnoreCase(command) || "bump".equalsIgnoreCase(command)) {
288 final var textureName = line.substring(commandPos + command.length()).trim();
289 final var tex = new Texture(textureName, textureCounter);
290 textureCounter++;
291
292 var valid = true;
293 if (textureValidationEnabled && listener != null) {
294 valid = listener.onValidateTexture(this, tex);
295 }
296 if (!valid) {
297 throw new InvalidTextureException();
298 }
299
300 currentMaterial.setBumpTextureMap(tex);
301 }
302 } catch (final Exception t) {
303 throw new LoaderException(t);
304 }
305 }
306
307
308
309
310
311
312 public boolean areMaterialsAvailable() {
313 return !materials.isEmpty();
314 }
315
316
317
318
319
320
321 public Set<Material> getMaterials() {
322 return materials;
323 }
324
325
326
327
328
329
330
331 public MaterialOBJ getMaterialByName(final String name) {
332 if (name == null) {
333 return null;
334 }
335
336 for (final var m : this.materials) {
337 if (m instanceof MaterialOBJ m2) {
338
339 if (m2.getMaterialName() == null) {
340 continue;
341 }
342
343 if (m2.getMaterialName().equals(name)) {
344 return m2;
345 }
346 }
347 }
348 return null;
349 }
350
351
352
353
354
355
356
357 public boolean containsMaterial(final String name) {
358 return getMaterialByName(name) != null;
359 }
360
361
362
363
364
365
366
367 public Material getMaterialByTextureMapName(final String name) {
368 if (name == null) {
369 return null;
370 }
371
372 for (final Material m : this.materials) {
373 if (m.getAlphaTextureMap() != null && m.getAlphaTextureMap().getFileName() != null
374 && m.getAlphaTextureMap().getFileName().equals(name)) {
375 return m;
376 }
377
378 if (m.getAmbientTextureMap() != null && m.getAmbientTextureMap().getFileName() != null
379 && m.getAmbientTextureMap().getFileName().equals(name)) {
380 return m;
381 }
382
383 if (m.getDiffuseTextureMap() != null && m.getDiffuseTextureMap().getFileName() != null
384 && m.getDiffuseTextureMap().getFileName().equals(name)) {
385 return m;
386 }
387
388 if (m.getSpecularTextureMap() != null && m.getSpecularTextureMap().getFileName() != null
389 && m.getSpecularTextureMap().getFileName().equals(name)) {
390 return m;
391 }
392 }
393 return null;
394 }
395 }