Skip to content

Instantly share code, notes, and snippets.

@FyiurAmron
Created May 29, 2015 18:41
Show Gist options
  • Save FyiurAmron/89523fa43cc3b624038d to your computer and use it in GitHub Desktop.
Save FyiurAmron/89523fa43cc3b624038d to your computer and use it in GitHub Desktop.
package vax.libgdx;
import java.io.*;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.g3d.model.data.ModelMaterial;
import com.badlogic.gdx.graphics.g3d.model.data.ModelTexture;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.ObjectMap;
import com.badlogic.gdx.utils.ObjectSet;
import vax.sys.MainLogger;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* A dedicated, stand-alone Wavefront MTL (.mtl) parser/loader, intended for use with ObjLoader.
* <p>
* Currently supports all of most common MTL parameters. Note that you have to have a shader capable of processing the
* various Texture map/color attributes to be actually able to benefit from it.
*
* @author vaxquis
*/
public class MtlLoader {
public static interface MtlProvider {
ObjectMap<String, ModelMaterial> get( FileHandle fileHandle );
}
/**
* An MTL provider allowing to ignore explicit MTL naming in mtllib statements, providing all materials from a
* single file.
*/
public static class SingleFileMtlProvider implements MtlProvider {
private final ObjectMap<String, ModelMaterial> singleMtl;
public SingleFileMtlProvider( String filename ) {
singleMtl = MtlLoader.load( filename );
}
public SingleFileMtlProvider( FileHandle fileHandle ) {
singleMtl = MtlLoader.load( fileHandle );
}
@Override
public ObjectMap<String, ModelMaterial> get( FileHandle fileHandle ) {
return singleMtl;
}
}
/**
* Default MTL provider; simply loads an MTL file from a requested handle. Suppresses all exceptions, but emits
* Gdx.app.error() warnings on them if logWarnings is set.
*/
public static MtlProvider DEFAULT_MTL_PROVIDER = new MtlProvider() {
@Override
public ObjectMap<String, ModelMaterial> get( FileHandle fileHandle ) {
return MtlLoader.load( fileHandle );
}
};
private static ObjectMap<String, Integer> usageMap;
private static ObjectSet<String> unsupportedSet;
private static ModelMaterial DEFAULT_MATERIAL;
static final String DEFAULT_MATERIAL_NAME = "default";
public static boolean logWarnings = true;
private static void initStaticData() {
if ( usageMap != null ) {
return;
}
usageMap = new ObjectMap<String, Integer>( 10 );
usageMap.put( "map_ka", ModelTexture.USAGE_AMBIENT );
usageMap.put( "map_kd", ModelTexture.USAGE_DIFFUSE );
usageMap.put( "map_ks", ModelTexture.USAGE_SPECULAR );
usageMap.put( "map_ke", ModelTexture.USAGE_EMISSIVE ); // popular extension
usageMap.put( "map_ns", ModelTexture.USAGE_SHININESS );
usageMap.put( "map_d", ModelTexture.USAGE_TRANSPARENCY );
usageMap.put( "map_bump", ModelTexture.USAGE_BUMP ); // popular extension
usageMap.put( "bump", ModelTexture.USAGE_BUMP );
usageMap.put( "refl", ModelTexture.USAGE_REFLECTION );
usageMap.put( "disp", ModelTexture.USAGE_NORMAL );
unsupportedSet = new ObjectSet<String>( 6 );
unsupportedSet.add( "illum" ); // illumination model - unsupported
unsupportedSet.add( "tf" ); // transmission filter - currently unsupported
unsupportedSet.add( "ni" ); // optical density - currently unsupported
unsupportedSet.add( "sharpness" ); // reflection sharpness - currently unsupported
unsupportedSet.add( "map_aat" ); // per-texture antialiasing - currently unsupported
unsupportedSet.add( "decal" ); // decal scalar texture - currently unsupported
}
private MtlLoader() {
throw new UnsupportedOperationException();
}
private static Color parseColor( String[] tokens ) {
float r = Float.parseFloat( tokens[1] );
float g = Float.parseFloat( tokens[2] );
float b = Float.parseFloat( tokens[3] );
float a = ( tokens.length <= 4 )
? 1
: Float.parseFloat( tokens[4] );
return new Color( r, g, b, a );
}
private static ModelMaterial createMaterial( String name ) {
ModelMaterial mat = new ModelMaterial();
mat.diffuse = new Color( Color.WHITE );
mat.specular = new Color( Color.WHITE );
mat.id = name;
return mat;
}
static ModelMaterial getDefaultMaterial() {
if ( DEFAULT_MATERIAL == null ) {
DEFAULT_MATERIAL = createMaterial( DEFAULT_MATERIAL_NAME );
}
return DEFAULT_MATERIAL;
}
/**
* Loads a Wavefront MTL material file from an internal file with a given name.
*
* @param filename
* @return
*/
public static ObjectMap<String, ModelMaterial> load( String filename ) {
return load( Gdx.files.internal( filename ) );
}
/**
* Loads a Wavefront MTL material file for a file handle.
*
* @param file
* @return
*/
@SuppressWarnings( "AssignmentToForLoopParameter" )
public static ObjectMap<String, ModelMaterial> load( FileHandle file ) {
if ( !file.exists() ) {
throw new IllegalArgumentException( "file '" + file + "' doesn't exist" );
}
initStaticData();
ObjectMap<String, ModelMaterial> materials = new ObjectMap<String, ModelMaterial>();
ModelMaterial mat = getDefaultMaterial(); // to allow parsing slightly malformed MTL files (no newmtl statement)
BufferedReader reader = null;
try {
reader = new BufferedReader( new InputStreamReader( file.read() ), 4096 );
for( String line = reader.readLine(); line != null; line = reader.readLine() ) {
line = line.trim();
String[] tokens = line.split( "\\s+" );
if ( tokens[0].length() == 0 || tokens[0].charAt( 0 ) == '#' ) { // line comment or empty line
continue;
}
final String key = tokens[0].toLowerCase();
if ( key.equals( "newmtl" ) ) { // new material
mat = ( tokens.length <= 1 )
? getDefaultMaterial()
: createMaterial( tokens[1].replace( '.', '_' ) );
materials.put( mat.id, mat );
} else if ( key.equals( "ka" ) ) { // ambient color
mat.ambient = parseColor( tokens );
} else if ( key.equals( "kd" ) ) { // diffuse color
mat.diffuse = parseColor( tokens );
} else if ( key.equals( "ks" ) ) { // specular color
mat.specular = parseColor( tokens );
} else if ( key.equals( "ke" ) ) { // emissive color
mat.emissive = parseColor( tokens );
} else if ( key.equals( "tr" ) || key.equals( "d" ) ) { // transmission/dissolve (alpha)
mat.opacity = Float.parseFloat( tokens[1] );
} else if ( key.equals( "ns" ) ) { // shininess exponent
mat.shininess = Float.parseFloat( tokens[1] );
} else if ( unsupportedSet.contains( key ) ) {
// Gdx.app.error( "MtlLoader", "unsupported MTL statement '" + tokens[0] + "'" );
} else {
Integer usage = usageMap.get( key );
if ( usage != null ) {
ModelTexture tex = new ModelTexture();
tex.usage = usage;
MainLogger.info( "pre-normalize:", file.parent().child( tokens[1] ) ); // debug
//tex.fileName = VaxApp.getPathProvider().normalize( file.parent().child( tokens[1] ).path() );
try {
tex.fileName = new URI( file.parent().child( tokens[1] ).path() ).normalize().getPath();
} catch ( Exception ex ) {
throw new RuntimeException(ex);
}
MainLogger.info( "post-normalize:", tex.fileName ); // debug
if ( mat.textures == null ) {
mat.textures = new Array<ModelTexture>( 1 );
}
mat.textures.add( tex );
} else if ( logWarnings ) {
Gdx.app.error( "MtlLoader.load()", "unknown MTL statement '" + tokens[0] + "'" );
}
}
}
} catch ( IOException ex ) {
if ( logWarnings ) {
Gdx.app.error( "MtlLoader.load()", ex.toString() );
}
return null;
} finally {
if ( reader != null ) {
try {
reader.close();
} catch ( IOException ex ) {
Gdx.app.error( "MtlLoader.load()", ex.toString() );
}
}
}
return materials;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment