Home | History | Annotate | Download | only in ogre
      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