• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2009-2010 jMonkeyEngine
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  * * Redistributions of source code must retain the above copyright
10  *   notice, this list of conditions and the following disclaimer.
11  *
12  * * Redistributions in binary form must reproduce the above copyright
13  *   notice, this list of conditions and the following disclaimer in the
14  *   documentation and/or other materials provided with the distribution.
15  *
16  * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
17  *   may be used to endorse or promote products derived from this software
18  *   without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 package com.jme3.scene.plugins;
34 
35 import com.jme3.asset.*;
36 import com.jme3.material.Material;
37 import com.jme3.material.MaterialList;
38 import com.jme3.material.RenderState.BlendMode;
39 import com.jme3.math.ColorRGBA;
40 import com.jme3.texture.Texture;
41 import com.jme3.texture.Texture.WrapMode;
42 import com.jme3.texture.Texture2D;
43 import com.jme3.util.PlaceholderAssets;
44 import java.io.File;
45 import java.io.IOException;
46 import java.io.InputStream;
47 import java.util.Locale;
48 import java.util.NoSuchElementException;
49 import java.util.Scanner;
50 import java.util.logging.Level;
51 import java.util.logging.Logger;
52 
53 public class MTLLoader implements AssetLoader {
54 
55     private static final Logger logger = Logger.getLogger(MTLLoader.class.getName());
56 
57     protected Scanner scan;
58     protected MaterialList matList;
59     //protected Material material;
60     protected AssetManager assetManager;
61     protected String folderName;
62     protected AssetKey key;
63 
64     protected Texture diffuseMap, normalMap, specularMap, alphaMap;
65     protected ColorRGBA ambient = new ColorRGBA();
66     protected ColorRGBA diffuse = new ColorRGBA();
67     protected ColorRGBA specular = new ColorRGBA();
68     protected float shininess = 16;
69     protected boolean shadeless;
70     protected String matName;
71     protected float alpha = 1;
72     protected boolean transparent = false;
73     protected boolean disallowTransparency = false;
74     protected boolean disallowAmbient = false;
75     protected boolean disallowSpecular = false;
76 
reset()77     public void reset(){
78         scan = null;
79         matList = null;
80 //        material = null;
81 
82         resetMaterial();
83     }
84 
readColor()85     protected ColorRGBA readColor(){
86         ColorRGBA v = new ColorRGBA();
87         v.set(scan.nextFloat(), scan.nextFloat(), scan.nextFloat(), 1.0f);
88         return v;
89     }
90 
nextStatement()91     protected String nextStatement(){
92         scan.useDelimiter("\n");
93         String result = scan.next();
94         scan.useDelimiter("\\p{javaWhitespace}+");
95         return result;
96     }
97 
skipLine()98     protected boolean skipLine(){
99         try {
100             scan.skip(".*\r{0,1}\n");
101             return true;
102         } catch (NoSuchElementException ex){
103             // EOF
104             return false;
105         }
106     }
107 
resetMaterial()108     protected void resetMaterial(){
109         ambient.set(ColorRGBA.DarkGray);
110         diffuse.set(ColorRGBA.LightGray);
111         specular.set(ColorRGBA.Black);
112         shininess = 16;
113         disallowTransparency = false;
114         disallowAmbient = false;
115         disallowSpecular = false;
116         shadeless = false;
117         transparent = false;
118         matName = null;
119         diffuseMap = null;
120         specularMap = null;
121         normalMap = null;
122         alphaMap = null;
123         alpha = 1;
124     }
125 
createMaterial()126     protected void createMaterial(){
127         Material material;
128 
129         if (alpha < 1f && transparent && !disallowTransparency){
130             diffuse.a = alpha;
131         }
132 
133         if (shadeless){
134             material = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
135             material.setColor("Color", diffuse.clone());
136             material.setTexture("ColorMap", diffuseMap);
137             // TODO: Add handling for alpha map?
138         }else{
139             material = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
140             material.setBoolean("UseMaterialColors", true);
141             material.setColor("Ambient",  ambient.clone());
142             material.setColor("Diffuse",  diffuse.clone());
143             material.setColor("Specular", specular.clone());
144             material.setFloat("Shininess", shininess); // prevents "premature culling" bug
145 
146             if (diffuseMap != null)  material.setTexture("DiffuseMap", diffuseMap);
147             if (specularMap != null) material.setTexture("SpecularMap", specularMap);
148             if (normalMap != null)   material.setTexture("NormalMap", normalMap);
149             if (alphaMap != null)    material.setTexture("AlphaMap", alphaMap);
150         }
151 
152         if (transparent && !disallowTransparency){
153             material.setTransparent(true);
154             material.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);
155             material.getAdditionalRenderState().setAlphaTest(true);
156             material.getAdditionalRenderState().setAlphaFallOff(0.01f);
157         }
158 
159         matList.put(matName, material);
160     }
161 
startMaterial(String name)162     protected void startMaterial(String name){
163         if (matName != null){
164             // material is already in cache, generate it
165             createMaterial();
166         }
167 
168         // now, reset the params and set the name to start a new material
169         resetMaterial();
170         matName = name;
171     }
172 
loadTexture(String path)173     protected Texture loadTexture(String path){
174         String[] split = path.trim().split("\\p{javaWhitespace}+");
175 
176         // will crash if path is an empty string
177         path = split[split.length-1];
178 
179         String name = new File(path).getName();
180         TextureKey texKey = new TextureKey(folderName + name);
181         texKey.setGenerateMips(true);
182         Texture texture;
183         try {
184             texture = assetManager.loadTexture(texKey);
185             texture.setWrap(WrapMode.Repeat);
186         } catch (AssetNotFoundException ex){
187             logger.log(Level.WARNING, "Cannot locate {0} for material {1}", new Object[]{texKey, key});
188             texture = new Texture2D(PlaceholderAssets.getPlaceholderImage());
189         }
190         return texture;
191     }
192 
readLine()193     protected boolean readLine(){
194         if (!scan.hasNext()){
195             return false;
196         }
197 
198         String cmd = scan.next().toLowerCase();
199         if (cmd.startsWith("#")){
200             // skip entire comment until next line
201             return skipLine();
202         }else if (cmd.equals("newmtl")){
203             String name = scan.next();
204             startMaterial(name);
205         }else if (cmd.equals("ka")){
206             ambient.set(readColor());
207         }else if (cmd.equals("kd")){
208             diffuse.set(readColor());
209         }else if (cmd.equals("ks")){
210             specular.set(readColor());
211         }else if (cmd.equals("ns")){
212             float shiny = scan.nextFloat();
213             if (shiny >= 1){
214                 shininess = shiny; /* (128f / 1000f)*/
215                 if (specular.equals(ColorRGBA.Black)){
216                     specular.set(ColorRGBA.White);
217                 }
218             }else{
219                 // For some reason blender likes to export Ns 0 statements
220                 // Ignore Ns 0 instead of setting it
221             }
222 
223         }else if (cmd.equals("d") || cmd.equals("tr")){
224             alpha = scan.nextFloat();
225             transparent = true;
226         }else if (cmd.equals("map_ka")){
227             // ignore it for now
228             return skipLine();
229         }else if (cmd.equals("map_kd")){
230             String path = nextStatement();
231             diffuseMap = loadTexture(path);
232         }else if (cmd.equals("map_bump") || cmd.equals("bump")){
233             if (normalMap == null){
234                 String path = nextStatement();
235                 normalMap = loadTexture(path);
236             }
237         }else if (cmd.equals("map_ks")){
238             String path = nextStatement();
239             specularMap = loadTexture(path);
240             if (specularMap != null){
241                 // NOTE: since specular color is modulated with specmap
242                 // make sure we have it set
243                 if (specular.equals(ColorRGBA.Black)){
244                     specular.set(ColorRGBA.White);
245                 }
246             }
247         }else if (cmd.equals("map_d")){
248             String path = scan.next();
249             alphaMap = loadTexture(path);
250             transparent = true;
251         }else if (cmd.equals("illum")){
252             int mode = scan.nextInt();
253 
254             switch (mode){
255                 case 0:
256                     // no lighting
257                     shadeless = true;
258                     disallowTransparency = true;
259                     break;
260                 case 1:
261                     disallowSpecular = true;
262                     disallowTransparency = true;
263                     break;
264                 case 2:
265                 case 3:
266                 case 5:
267                 case 8:
268                     disallowTransparency = true;
269                     break;
270                 case 4:
271                 case 6:
272                 case 7:
273                 case 9:
274                     // Enable transparency
275                     // Works best if diffuse map has an alpha channel
276                     transparent = true;
277                     break;
278             }
279         }else if (cmd.equals("ke") || cmd.equals("ni")){
280             // Ni: index of refraction - unsupported in jME
281             // Ke: emission color
282             return skipLine();
283         }else{
284             logger.log(Level.WARNING, "Unknown statement in MTL! {0}", cmd);
285             return skipLine();
286         }
287 
288         return true;
289     }
290 
291     @SuppressWarnings("empty-statement")
load(AssetInfo info)292     public Object load(AssetInfo info) throws IOException{
293         reset();
294 
295         this.key = info.getKey();
296         this.assetManager = info.getManager();
297         folderName = info.getKey().getFolder();
298         matList = new MaterialList();
299 
300         InputStream in = null;
301         try {
302             in = info.openStream();
303             scan = new Scanner(in);
304             scan.useLocale(Locale.US);
305 
306             while (readLine());
307         } finally {
308             if (in != null){
309                 in.close();
310             }
311         }
312 
313         if (matName != null){
314             // still have a material in the vars
315             createMaterial();
316             resetMaterial();
317         }
318 
319         MaterialList list = matList;
320 
321 
322 
323         return list;
324     }
325 }
326