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.ogre; 34 35 import com.jme3.asset.*; 36 import com.jme3.material.Material; 37 import com.jme3.material.MaterialList; 38 import com.jme3.material.RenderState; 39 import com.jme3.math.ColorRGBA; 40 import com.jme3.scene.plugins.ogre.matext.MaterialExtensionLoader; 41 import com.jme3.scene.plugins.ogre.matext.MaterialExtensionSet; 42 import com.jme3.scene.plugins.ogre.matext.OgreMaterialKey; 43 import com.jme3.texture.Texture; 44 import com.jme3.texture.Texture.WrapMode; 45 import com.jme3.texture.Texture2D; 46 import com.jme3.util.PlaceholderAssets; 47 import com.jme3.util.blockparser.BlockLanguageParser; 48 import com.jme3.util.blockparser.Statement; 49 import java.io.IOException; 50 import java.io.InputStream; 51 import java.util.Arrays; 52 import java.util.List; 53 import java.util.Scanner; 54 import java.util.logging.Level; 55 import java.util.logging.Logger; 56 57 public class MaterialLoader implements AssetLoader { 58 59 private static final Logger logger = Logger.getLogger(MaterialLoader.class.getName()); 60 61 private String folderName; 62 private AssetManager assetManager; 63 private ColorRGBA ambient, diffuse, specular, emissive; 64 private Texture[] textures = new Texture[4]; 65 private String texName; 66 private String matName; 67 private float shinines; 68 private boolean vcolor = false; 69 private boolean blend = false; 70 private boolean twoSide = false; 71 private boolean noLight = false; 72 private boolean separateTexCoord = false; 73 private int texUnit = 0; 74 75 private ColorRGBA readColor(String content){ 76 String[] split = content.split("\\s"); 77 78 ColorRGBA color = new ColorRGBA(); 79 color.r = Float.parseFloat(split[0]); 80 color.g = Float.parseFloat(split[1]); 81 color.b = Float.parseFloat(split[2]); 82 if (split.length >= 4){ 83 color.a = Float.parseFloat(split[3]); 84 } 85 return color; 86 } 87 88 private void readTextureImage(String content){ 89 // texture image def 90 String path = null; 91 92 // find extension 93 int extStart = content.lastIndexOf("."); 94 for (int i = extStart; i < content.length(); i++){ 95 char c = content.charAt(i); 96 if (Character.isWhitespace(c)){ 97 // extension ends here 98 path = content.substring(0, i).trim(); 99 content = content.substring(i+1).trim(); 100 break; 101 } 102 } 103 if (path == null){ 104 path = content.trim(); 105 content = ""; 106 } 107 108 Scanner lnScan = new Scanner(content); 109 String mips = null; 110 String type = null; 111 if (lnScan.hasNext()){ 112 // more params 113 type = lnScan.next(); 114 // if (!lnScan.hasNext("\n") && lnScan.hasNext()){ 115 // mips = lnScan.next(); 116 // if (lnScan.hasNext()){ 117 // even more params.. 118 // will have to ignore 119 // } 120 // } 121 } 122 123 boolean genMips = true; 124 boolean cubic = false; 125 if (type != null && type.equals("0")) 126 genMips = false; 127 128 if (type != null && type.equals("cubic")){ 129 cubic = true; 130 } 131 132 TextureKey texKey = new TextureKey(folderName + path, false); 133 texKey.setGenerateMips(genMips); 134 texKey.setAsCube(cubic); 135 136 try { 137 Texture loadedTexture = assetManager.loadTexture(texKey); 138 139 textures[texUnit].setImage(loadedTexture.getImage()); 140 textures[texUnit].setMinFilter(loadedTexture.getMinFilter()); 141 textures[texUnit].setKey(loadedTexture.getKey()); 142 143 // XXX: Is this really neccessary? 144 textures[texUnit].setWrap(WrapMode.Repeat); 145 if (texName != null){ 146 textures[texUnit].setName(texName); 147 texName = null; 148 }else{ 149 textures[texUnit].setName(texKey.getName()); 150 } 151 } catch (AssetNotFoundException ex){ 152 logger.log(Level.WARNING, "Cannot locate {0} for material {1}", new Object[]{texKey, matName}); 153 textures[texUnit].setImage(PlaceholderAssets.getPlaceholderImage()); 154 } 155 } 156 157 private void readTextureUnitStatement(Statement statement){ 158 String[] split = statement.getLine().split(" ", 2); 159 String keyword = split[0]; 160 if (keyword.equals("texture")){ 161 readTextureImage(split[1]); 162 }else if (keyword.equals("tex_address_mode")){ 163 String mode = split[1]; 164 if (mode.equals("wrap")){ 165 textures[texUnit].setWrap(WrapMode.Repeat); 166 }else if (mode.equals("clamp")){ 167 textures[texUnit].setWrap(WrapMode.Clamp); 168 }else if (mode.equals("mirror")){ 169 textures[texUnit].setWrap(WrapMode.MirroredRepeat); 170 }else if (mode.equals("border")){ 171 textures[texUnit].setWrap(WrapMode.BorderClamp); 172 } 173 }else if (keyword.equals("filtering")){ 174 // ignored.. only anisotropy is considered 175 }else if (keyword.equals("tex_coord_set")){ 176 int texCoord = Integer.parseInt(split[1]); 177 if (texCoord == 1){ 178 separateTexCoord = true; 179 } 180 }else if (keyword.equals("max_anisotropy")){ 181 int amount = Integer.parseInt(split[1]); 182 textures[texUnit].setAnisotropicFilter(amount); 183 }else{ 184 logger.log(Level.WARNING, "Unsupported texture_unit directive: {0}", keyword); 185 } 186 } 187 188 private void readTextureUnit(Statement statement){ 189 String[] split = statement.getLine().split(" ", 2); 190 // name is optional 191 if (split.length == 2){ 192 texName = split[1]; 193 }else{ 194 texName = null; 195 } 196 197 textures[texUnit] = new Texture2D(); 198 for (Statement texUnitStat : statement.getContents()){ 199 readTextureUnitStatement(texUnitStat); 200 } 201 if (textures[texUnit].getImage() != null){ 202 texUnit++; 203 }else{ 204 // no image was loaded, ignore 205 textures[texUnit] = null; 206 } 207 } 208 209 private void readPassStatement(Statement statement){ 210 // read until newline 211 String[] split = statement.getLine().split(" ", 2); 212 String keyword = split[0]; 213 if (keyword.equals("diffuse")){ 214 if (split[1].equals("vertexcolour")){ 215 // use vertex colors 216 diffuse = ColorRGBA.White; 217 vcolor = true; 218 }else{ 219 diffuse = readColor(split[1]); 220 } 221 }else if(keyword.equals("ambient")) { 222 if (split[1].equals("vertexcolour")){ 223 // use vertex colors 224 ambient = ColorRGBA.White; 225 }else{ 226 ambient = readColor(split[1]); 227 } 228 }else if (keyword.equals("emissive")){ 229 emissive = readColor(split[1]); 230 }else if (keyword.equals("specular")){ 231 String[] subsplit = split[1].split("\\s"); 232 specular = new ColorRGBA(); 233 specular.r = Float.parseFloat(subsplit[0]); 234 specular.g = Float.parseFloat(subsplit[1]); 235 specular.b = Float.parseFloat(subsplit[2]); 236 float unknown = Float.parseFloat(subsplit[3]); 237 if (subsplit.length >= 5){ 238 // using 5 float values 239 specular.a = unknown; 240 shinines = Float.parseFloat(subsplit[4]); 241 }else{ 242 // using 4 float values 243 specular.a = 1f; 244 shinines = unknown; 245 } 246 }else if (keyword.equals("texture_unit")){ 247 readTextureUnit(statement); 248 }else if (keyword.equals("scene_blend")){ 249 String mode = split[1]; 250 if (mode.equals("alpha_blend")){ 251 blend = true; 252 } 253 }else if (keyword.equals("cull_hardware")){ 254 String mode = split[1]; 255 if (mode.equals("none")){ 256 twoSide = true; 257 } 258 }else if (keyword.equals("cull_software")){ 259 // ignore 260 }else if (keyword.equals("lighting")){ 261 String isOn = split[1]; 262 if (isOn.equals("on")){ 263 noLight = false; 264 }else if (isOn.equals("off")){ 265 noLight = true; 266 } 267 }else{ 268 logger.log(Level.WARNING, "Unsupported pass directive: {0}", keyword); 269 } 270 } 271 272 private void readPass(Statement statement){ 273 String name; 274 String[] split = statement.getLine().split(" ", 2); 275 if (split.length == 1){ 276 // no name 277 name = null; 278 }else{ 279 name = split[1]; 280 } 281 282 for (Statement passStat : statement.getContents()){ 283 readPassStatement(passStat); 284 } 285 286 texUnit = 0; 287 } 288 289 private void readTechnique(Statement statement){ 290 String[] split = statement.getLine().split(" ", 2); 291 String name; 292 if (split.length == 1){ 293 // no name 294 name = null; 295 }else{ 296 name = split[1]; 297 } 298 for (Statement techStat : statement.getContents()){ 299 readPass(techStat); 300 } 301 } 302 303 private void readMaterialStatement(Statement statement){ 304 if (statement.getLine().startsWith("technique")){ 305 readTechnique(statement); 306 }else if (statement.getLine().startsWith("receive_shadows")){ 307 String isOn = statement.getLine().split("\\s")[1]; 308 if (isOn != null && isOn.equals("true")){ 309 } 310 } 311 } 312 313 @SuppressWarnings("empty-statement") 314 private void readMaterial(Statement statement){ 315 for (Statement materialStat : statement.getContents()){ 316 readMaterialStatement(materialStat); 317 } 318 } 319 320 private Material compileMaterial(){ 321 Material mat; 322 if (noLight){ 323 mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); 324 }else{ 325 mat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md"); 326 } 327 if (blend){ 328 RenderState rs = mat.getAdditionalRenderState(); 329 rs.setAlphaTest(true); 330 rs.setAlphaFallOff(0.01f); 331 rs.setBlendMode(RenderState.BlendMode.Alpha); 332 333 if (twoSide){ 334 rs.setFaceCullMode(RenderState.FaceCullMode.Off); 335 } 336 337 // rs.setDepthWrite(false); 338 mat.setTransparent(true); 339 if (!noLight){ 340 mat.setBoolean("UseAlpha", true); 341 } 342 }else{ 343 if (twoSide){ 344 RenderState rs = mat.getAdditionalRenderState(); 345 rs.setFaceCullMode(RenderState.FaceCullMode.Off); 346 } 347 } 348 349 if (!noLight){ 350 if (shinines > 0f) { 351 mat.setFloat("Shininess", shinines); 352 } else { 353 mat.setFloat("Shininess", 16f); // set shininess to some value anyway.. 354 } 355 356 if (vcolor) 357 mat.setBoolean("UseVertexColor", true); 358 359 if (textures[0] != null) 360 mat.setTexture("DiffuseMap", textures[0]); 361 362 mat.setBoolean("UseMaterialColors", true); 363 if(diffuse != null){ 364 mat.setColor("Diffuse", diffuse); 365 }else{ 366 mat.setColor("Diffuse", ColorRGBA.White); 367 } 368 369 if(ambient != null){ 370 mat.setColor("Ambient", ambient); 371 }else{ 372 mat.setColor("Ambient", ColorRGBA.DarkGray); 373 } 374 375 if(specular != null){ 376 mat.setColor("Specular", specular); 377 }else{ 378 mat.setColor("Specular", ColorRGBA.Black); 379 } 380 381 if (emissive != null){ 382 mat.setColor("GlowColor", emissive); 383 } 384 }else{ 385 if (vcolor) { 386 mat.setBoolean("VertexColor", true); 387 } 388 389 if (textures[0] != null && textures[1] == null){ 390 if (separateTexCoord){ 391 mat.setTexture("LightMap", textures[0]); 392 mat.setBoolean("SeparateTexCoord", true); 393 }else{ 394 mat.setTexture("ColorMap", textures[0]); 395 } 396 }else if (textures[1] != null){ 397 mat.setTexture("ColorMap", textures[0]); 398 mat.setTexture("LightMap", textures[1]); 399 if (separateTexCoord){ 400 mat.setBoolean("SeparateTexCoord", true); 401 } 402 } 403 404 if(diffuse != null){ 405 mat.setColor("Color", diffuse); 406 } 407 408 if (emissive != null){ 409 mat.setColor("GlowColor", emissive); 410 } 411 } 412 413 noLight = false; 414 Arrays.fill(textures, null); 415 diffuse = null; 416 specular = null; 417 shinines = 0f; 418 vcolor = false; 419 blend = false; 420 texUnit = 0; 421 separateTexCoord = false; 422 return mat; 423 } 424 425 private MaterialList load(AssetManager assetManager, AssetKey key, InputStream in) throws IOException{ 426 folderName = key.getFolder(); 427 this.assetManager = assetManager; 428 429 MaterialList list = null; 430 List<Statement> statements = BlockLanguageParser.parse(in); 431 432 for (Statement statement : statements){ 433 if (statement.getLine().startsWith("import")){ 434 MaterialExtensionSet matExts = null; 435 if (key instanceof OgreMaterialKey){ 436 matExts = ((OgreMaterialKey)key).getMaterialExtensionSet(); 437 } 438 439 if (matExts == null){ 440 throw new IOException("Must specify MaterialExtensionSet when loading\n"+ 441 "Ogre3D materials with extended materials"); 442 } 443 444 list = new MaterialExtensionLoader().load(assetManager, key, matExts, statements); 445 break; 446 }else if (statement.getLine().startsWith("material")){ 447 if (list == null){ 448 list = new MaterialList(); 449 } 450 String[] split = statement.getLine().split(" ", 2); 451 matName = split[1].trim(); 452 readMaterial(statement); 453 Material mat = compileMaterial(); 454 list.put(matName, mat); 455 } 456 } 457 458 return list; 459 } 460 461 public Object load(AssetInfo info) throws IOException { 462 InputStream in = null; 463 try { 464 in = info.openStream(); 465 return load(info.getManager(), info.getKey(), in); 466 } finally { 467 if (in != null){ 468 in.close(); 469 } 470 } 471 } 472 473 } 474