package engine.model;
import static engine.GfxEngine.loadShader;
import static engine.GfxEngine.loadTexture;
import static engine.GfxEngine.loadMaterial;
import static engine.GfxEngine.loadCubemap;
import engine.Vec3f;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.lwjgl.opengl.ARBMultitexture;
import org.lwjgl.opengl.GL13;
import org.lwjgl.opengl.GL20;
import static org.lwjgl.opengl.GL11.*;
/**
* This class is used to store references to textures (that are stored in GfxEngine) and color data.
* Entity passes Material assigned to it to its Model when Entity.exist() is called. Models use the provided data to make surfaces look nice.
*
* @author simo <simppak@gmail.com>
*
*/
public class Material implements Cloneable{
public CTexture texture0;
public CTexture texture1;
public CTextureCube cube0;
public Vec3f color;
public boolean tex_blend;
public boolean cull_face;
public boolean shader_enabled;
private String frag_path;
private String vert_path;
public Shader shader;
private Vector<Float> texture_alpha;
private String filepath;
private float shininess;
private Vec3f specular;
public Material create(String path){
this.color = new Vec3f(1.0f,1.0f,1.0f);
this.filepath = path;
this.texture_alpha = new Vector<>();
this.texture_alpha.add(1.0f);
this.texture_alpha.add(1.0f);
this.shininess = 25f;
this.specular = new Vec3f(1f,1f,1f);
this.shader_enabled = false;
this.tex_blend = false;
this.cull_face = true;
if(this.load(path) == true){
System.out.println("Loaded " + path);
}
return this;
}
/**
* Returns a clone of this material
*
* @return the clone
*/
@Override
public Object clone(){
Material another = null;
try{
another = (Material) super.clone();
another.texture_alpha = (Vector<Float>) this.texture_alpha.clone();
return another;
}
catch( CloneNotSupportedException e ){
return null;
}
}
/**
* Parses the given bulk String and sets the material's attributes
*
* @param file the String to parse
* @return success
*/
private boolean processString(Vector<String> file){
String[] temp;
String line;
for(int i=0; i<file.size();i++){
line = file.get(i).trim();
if(line.length() > 0){
if(line.startsWith("//")){
continue;
}
else if(line.startsWith("texture0 ")){
temp = line.split("\\s+");
//texture0 = new CTexture();
//this.setTexture(this.texture0, temp[1]);
this.texture0 = loadTexture(temp[1]);
}
else if(line.startsWith("texture1 ")){
temp = line.split("\\s+");
//texture0 = new CTexture();
//this.setTexture(this.texture0, temp[1]);
this.texture1 = loadTexture(temp[1]);
}
else if(line.startsWith("cube0 ")){
temp = line.split("\\s+");
this.cube0 = loadCubemap(temp[1]);
}
else if(line.startsWith("blend ")){
this.tex_blend = true;
}
else if(line.startsWith("shininess ")){
temp = line.split("\\s+");
this.shininess = Float.parseFloat(temp[1]);
}
else if(line.startsWith("shader ")){
temp = line.split("\\s+");
if(temp.length > 2){
this.vert_path = temp[1];
this.frag_path = temp[2];
}
}
else if(line.startsWith("cullface ")){
temp = line.split("\\s+");
if(temp.length > 1){
try {
int cull = Integer.parseInt(temp[1]);
this.cull_face = (cull == 0)?false:true;
} catch(NumberFormatException e) {}
}
}
else if(line.startsWith("color ")){
temp = line.split("\\s+");
if(temp.length > 1){
temp = temp[1].split(",");
if(temp.length > 0){
try {
this.color.x = Float.parseFloat(temp[0]);
} catch(NumberFormatException e) {}
}
if(temp.length > 1){
try {
this.color.y = Float.parseFloat(temp[1]);
} catch(NumberFormatException e) {}
}
if(temp.length > 2){
try {
this.color.z = Float.parseFloat(temp[2]);
} catch(NumberFormatException e) {}
}
}
}
else if(line.startsWith("specular ")){
temp = line.split("\\s+");
if(temp.length > 1){
temp = temp[1].split(",");
if(temp.length > 0){
try {
this.specular.x = Float.parseFloat(temp[0]);
} catch(NumberFormatException e) {}
}
if(temp.length > 1){
try {
this.specular.y = Float.parseFloat(temp[1]);
} catch(NumberFormatException e) {}
}
if(temp.length > 2){
try {
this.specular.z = Float.parseFloat(temp[2]);
} catch(NumberFormatException e) {}
}
}
}
}
}
return true;
}
public void setTexture(int id, CTexture texture){
this.texture0 = texture;
}
/**
* Sets the specified texture unit's alpha
*
* @param textureunit the texture unit
*/
public void setTextureAlpha(int textureunit, float alpha){
this.texture_alpha.setElementAt(alpha, textureunit);
}
/**
* gets the specified texture unit's alpha
* @param textureunit
* @return float alpha value
*/
public float getTextureAlpha(int textureunit) {
return this.texture_alpha.elementAt(textureunit);
}
/**
* Sends the texture unit's alpha value to shaders
*
* @param textureunit the texture unit
*/
public void updateTextureAlpha(int textureunit){
GL20.glUniform1f(this.shader.alpha_loc.get(textureunit), this.texture_alpha.get(textureunit));
}
public void updateShininessUniform(){
GL20.glUniform1f(this.shader.getShininessLoc(), this.shininess);
}
public void updateSpecularUniform(){
GL20.glUniform4f(this.shader.getSpecularLoc(), this.specular.x, this.specular.y, this.specular.z,1f);
}
/**
* Attaches the shader specified in .mat to this Material
*/
private void attachShader(){
if(this.vert_path == null || this.frag_path == null || this.vert_path.isEmpty() || this.frag_path.isEmpty()) {
return;
}
File v = new File(this.vert_path);
File f = new File(this.frag_path);
if(v.exists() == false){
System.out.println("Failed to load "+vert_path);
return;
}
if(f.exists() == false){
System.out.println("Failed to load "+frag_path);
return;
}
this.shader = loadShader(vert_path, frag_path);
this.shader_enabled = (this.shader == null || this.shader.program == 0)?false:true;
}
/**
* not-so complicated .mat fileloader
*
* @param path where to load the .mat-file from.
* @return success
*/
public boolean load(String path) {
Vector<String> file = new Vector<>();
if(path != null){
file = this.readFile(path);
}
if(path == null || file.isEmpty()){
InputStream resource = this.getClass().getResourceAsStream("mat/defmat.mat");
InputStreamReader readr = new InputStreamReader(resource);
String wat = new String();
int one;
try {
while((one = readr.read()) != -1){
wat = wat+""+(char)one;
}
} catch (IOException ex) {
Logger.getLogger(Material.class.getName()).log(Level.SEVERE, null, ex);
}
file = new Vector<>();
file.add(wat);
try {
resource.close();
readr.close();
} catch (IOException ex) {
Logger.getLogger(Material.class.getName()).log(Level.SEVERE, null, ex);
}
}
/*
else{
System.out.println("Failed to load " + path);
return false;
}
*/
this.processString(file);
this.attachShader();
return true;
}
public static String convertStreamToString(java.io.InputStream is) {
java.util.Scanner s = new java.util.Scanner(is).useDelimiter("\\A");
return s.hasNext() ? s.next() : "";
}
/**
* Reads the given file from disk
*
* @param path where to load the file from
* @return success
*/
private Vector<String> readFile(String path) {
Vector<String> file = new Vector<String>();
try {
BufferedReader reader = new BufferedReader(new FileReader(path));
//temporary string to store a line from file
String s;
while((s = reader.readLine()) != null) {
file.addElement(s);
}
reader.close();
} catch(IOException e) {
//TODO error handling
}
return file;
}
public String getMatFilePath(){
return this.filepath;
}
public boolean hasCubemap(){
return(this.cube0 != null)?true:false;
}
public void bindCubemap(){
ARBMultitexture.glActiveTextureARB(ARBMultitexture.GL_TEXTURE2_ARB);
glBindTexture(GL13.GL_TEXTURE_CUBE_MAP, this.cube0.getTextureID());
}
public void unbindCubemap(){
ARBMultitexture.glActiveTextureARB(ARBMultitexture.GL_TEXTURE2_ARB);
glBindTexture(GL13.GL_TEXTURE_CUBE_MAP, 0);
}
}