331 lines
12 KiB
Java
331 lines
12 KiB
Java
package electrosphere.renderer.model;
|
|
|
|
import electrosphere.engine.Globals;
|
|
import electrosphere.logger.LoggerInterface;
|
|
import electrosphere.renderer.OpenGLState;
|
|
import electrosphere.renderer.texture.Texture;
|
|
import electrosphere.util.FileUtils;
|
|
|
|
import org.lwjgl.PointerBuffer;
|
|
import org.lwjgl.assimp.AIMaterial;
|
|
import org.lwjgl.assimp.AIMaterialProperty;
|
|
import org.lwjgl.assimp.AIScene;
|
|
import org.lwjgl.assimp.AIString;
|
|
import org.lwjgl.assimp.AITexture;
|
|
import org.lwjgl.assimp.Assimp;
|
|
import org.lwjgl.opengl.GL45;
|
|
|
|
import java.io.File;
|
|
import java.nio.IntBuffer;
|
|
import java.nio.file.Path;
|
|
|
|
/**
|
|
* A material
|
|
*/
|
|
public class Material {
|
|
|
|
|
|
/**
|
|
* The path of the diffuse texture when using texture lookups
|
|
*/
|
|
private String diffuse;
|
|
|
|
/**
|
|
* The path of the specular texture when using texture lookups
|
|
*/
|
|
private String specular;
|
|
|
|
/**
|
|
* Tracks whether this material has transparency or not
|
|
*/
|
|
public boolean hasTransparency = false;
|
|
|
|
/**
|
|
* Sets whether this material should get its texture pointers from the assetManager by looking up diffuse and specular paths
|
|
* or whether it should have a manually set texturePointer and not look up while binding
|
|
*/
|
|
private boolean usesFetch = true;
|
|
|
|
/**
|
|
* texture pointer for the specular
|
|
*/
|
|
private int texturePointer;
|
|
|
|
/**
|
|
* texture pointer for the normal
|
|
*/
|
|
private int normalPointer;
|
|
|
|
/**
|
|
* A material that contains textures
|
|
*/
|
|
public Material(){
|
|
}
|
|
|
|
/**
|
|
* Creates a material with a diffuse texture
|
|
* @param diffuse The path to the diffuse texture
|
|
*/
|
|
public Material(String diffuse){
|
|
this.diffuse = diffuse;
|
|
}
|
|
|
|
/**
|
|
* Loads materials from the ai material object
|
|
* @param path The path to the file we're loading materials from
|
|
* @param input The input scene
|
|
* @param input The input ai material
|
|
* @return The resulting engine material
|
|
*/
|
|
public static Material loadMaterialFromAIMaterial(String path, AIScene scene, AIMaterial input){
|
|
Material rVal = new Material();
|
|
|
|
AIString aiPathString = AIString.calloc();
|
|
|
|
//read props
|
|
|
|
//read textures
|
|
PointerBuffer texturePtrBuff = scene.mTextures();
|
|
String[] texPaths = new String[scene.mNumTextures()];
|
|
for(int i = 0; i < scene.mNumTextures(); i++){
|
|
AITexture tex = AITexture.create(texturePtrBuff.get());
|
|
texPaths[i] = tex.mFilename().dataString();
|
|
}
|
|
//discover diffuse
|
|
boolean foundDiffuse = false;
|
|
int textureCount = Assimp.aiGetMaterialTextureCount(input, Assimp.aiTextureType_DIFFUSE);
|
|
if(textureCount > 0){
|
|
//for the time being, only load the first diffuse
|
|
int textureIndex = 0;
|
|
int retCode = Assimp.aiGetMaterialTexture(input, Assimp.aiTextureType_DIFFUSE, textureIndex, aiPathString, (IntBuffer)null, null, null, null, null, null);
|
|
if(retCode != Assimp.aiReturn_SUCCESS){
|
|
throw new Error("Failed to read diffuse! " + textureCount + " " + Assimp.aiGetErrorString());
|
|
}
|
|
String texturePath = aiPathString.dataString();
|
|
if(texturePath == null || texturePath.length() <= 0){
|
|
throw new Error("Texture path is empty " + texturePath);
|
|
}
|
|
if(texturePath.length() == 2 && texturePath.startsWith("*")){
|
|
//older versions of Assimp require you to read the INDEX of the texture from the material, then look up that texture in the scene itself
|
|
//format looks like "*<index>" ie "*0"
|
|
int indexInLoadedTexturePaths = Integer.parseInt(texturePath.substring(1));
|
|
if(indexInLoadedTexturePaths >= texPaths.length){
|
|
throw new Error("Index discovered is outside the array's length " + indexInLoadedTexturePaths + " " + texPaths.length);
|
|
}
|
|
String resolved = Material.resolveTexturePath(path, texPaths[indexInLoadedTexturePaths]);
|
|
if(resolved != null && resolved.length() > 0){
|
|
rVal.usesFetch = true;
|
|
rVal.diffuse = resolved;
|
|
Globals.assetManager.addTexturePathtoQueue(rVal.diffuse);
|
|
foundDiffuse = true;
|
|
}
|
|
} else {
|
|
String resolved = Material.resolveTexturePath(path, texturePath);
|
|
if(resolved != null && resolved.length() > 0){
|
|
rVal.usesFetch = true;
|
|
rVal.diffuse = resolved;
|
|
Globals.assetManager.addTexturePathtoQueue(rVal.diffuse);
|
|
foundDiffuse = true;
|
|
}
|
|
}
|
|
}
|
|
if(!foundDiffuse){
|
|
textureCount = Assimp.aiGetMaterialTextureCount(input, Assimp.aiTextureType_BASE_COLOR);
|
|
if(textureCount > 0){
|
|
//for the time being, only load the first diffuse
|
|
int textureIndex = 0;
|
|
int retCode = Assimp.aiGetMaterialTexture(input, Assimp.aiTextureType_BASE_COLOR, textureIndex, aiPathString, (IntBuffer)null, null, null, null, null, null);
|
|
if(retCode != Assimp.aiReturn_SUCCESS){
|
|
throw new Error("Failed to read diffuse! " + textureCount + " " + Assimp.aiGetErrorString());
|
|
}
|
|
String texturePath = aiPathString.dataString();
|
|
if(texturePath == null || texturePath.length() <= 0){
|
|
throw new Error("Texture path is empty " + texturePath);
|
|
}
|
|
if(texturePath.length() == 2 && texturePath.startsWith("*")){
|
|
//older versions of Assimp require you to read the INDEX of the texture from the material, then look up that texture in the scene itself
|
|
//format looks like "*<index>" ie "*0"
|
|
int indexInLoadedTexturePaths = Integer.parseInt(texturePath.substring(1));
|
|
if(indexInLoadedTexturePaths >= texPaths.length){
|
|
throw new Error("Index discovered is outside the array's length " + indexInLoadedTexturePaths + " " + texPaths.length);
|
|
}
|
|
String resolved = Material.resolveTexturePath(path, texPaths[indexInLoadedTexturePaths]);
|
|
if(resolved != null && resolved.length() > 0){
|
|
rVal.usesFetch = true;
|
|
rVal.diffuse = resolved;
|
|
Globals.assetManager.addTexturePathtoQueue(rVal.diffuse);
|
|
foundDiffuse = true;
|
|
}
|
|
} else {
|
|
String resolved = Material.resolveTexturePath(path, texturePath);
|
|
if(resolved != null && resolved.length() > 0){
|
|
rVal.usesFetch = true;
|
|
rVal.diffuse = resolved;
|
|
Globals.assetManager.addTexturePathtoQueue(rVal.diffuse);
|
|
foundDiffuse = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//free mem
|
|
aiPathString.free();
|
|
|
|
return rVal;
|
|
}
|
|
|
|
/**
|
|
* Describes the properties of a material
|
|
* @param material The material
|
|
*/
|
|
public static void listMaterialProps(AIMaterial material){
|
|
LoggerInterface.loggerRenderer.WARNING("Describing material");
|
|
for(int i = 0; i < material.mNumProperties(); i++){
|
|
AIMaterialProperty prop = AIMaterialProperty.create(material.mProperties().get(i));
|
|
String key = prop.mKey().dataString();
|
|
int propType = prop.mSemantic();
|
|
if(propType == Assimp.aiTextureType_NONE){
|
|
//non-texture prop
|
|
LoggerInterface.loggerRenderer.WARNING("Prop \"" + key + "\" is not a texture");
|
|
} else {
|
|
LoggerInterface.loggerRenderer.WARNING("Prop \"" + key + "\" is a texture");
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Resolves the filepath of the texture
|
|
* @param path The path of the ai scene itself
|
|
* @param filename The name of the file
|
|
* @return The full path to load
|
|
*/
|
|
private static String resolveTexturePath(String path, String filename){
|
|
File fileObj = FileUtils.getAssetFile(path);
|
|
File parentDir = fileObj.getParentFile();
|
|
File[] contents = parentDir.listFiles();
|
|
File discovered = null;
|
|
for(File child : contents){
|
|
String name = child.getName();
|
|
String nameNoExt = child.getName().replaceFirst("[.][^.]+$", "");
|
|
if(name.equals(filename) || nameNoExt.equals(filename)){
|
|
discovered = child;
|
|
}
|
|
}
|
|
|
|
if(discovered == null){
|
|
LoggerInterface.loggerRenderer.WARNING("Failed to find texture \"" + filename + "\" for model " + path);
|
|
return null;
|
|
} else {
|
|
Path relative = new File("./assets").toPath().relativize(discovered.toPath());
|
|
return relative.toString().replace("\\","/");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gets the path for the diffuse of the material
|
|
* @return The path for the diffuse texture
|
|
*/
|
|
public String getDiffuse(){
|
|
return diffuse;
|
|
}
|
|
|
|
/**
|
|
* Gets the path for the specular of the material
|
|
* @return The path for the specular texture
|
|
*/
|
|
public String getSpecular(){
|
|
return specular;
|
|
}
|
|
|
|
/**
|
|
* Sets the diffuse texture
|
|
* @param t The texture path
|
|
*/
|
|
public void setDiffuse(String t){
|
|
diffuse = t;
|
|
}
|
|
|
|
/**
|
|
* Sets the specular texture path
|
|
* @param t The specular texture path
|
|
*/
|
|
public void setSpecular(String t){
|
|
specular = t;
|
|
}
|
|
|
|
/**
|
|
* Sets the texture pointer
|
|
* @param pointer The texture pointer
|
|
*/
|
|
public void setTexturePointer(int pointer){
|
|
texturePointer = pointer;
|
|
usesFetch = false;
|
|
}
|
|
|
|
/**
|
|
* Sets the in-material pointer for the normal
|
|
* @param pointer the normal texture
|
|
*/
|
|
public void setNormalTexturePointer(int pointer){
|
|
normalPointer = pointer;
|
|
usesFetch = false;
|
|
}
|
|
|
|
/**
|
|
* Applies the material
|
|
*/
|
|
public void applyMaterial(OpenGLState openGLState){
|
|
//Controls whether the texturePointer should be resolved by looking up the diffuse in asset manager or using the texture pointer already set in this material
|
|
if(usesFetch){
|
|
if(diffuse != null){
|
|
Texture diffuseTexture = Globals.assetManager.fetchTexture(diffuse);
|
|
if(diffuseTexture != null){
|
|
diffuseTexture.bind(openGLState,0);
|
|
Globals.renderingEngine.checkError();
|
|
openGLState.getActiveShader().setUniform(openGLState, "material.diffuse", 0);
|
|
Globals.renderingEngine.checkError();
|
|
}
|
|
}
|
|
if(specular != null){
|
|
Texture specularTexture = Globals.assetManager.fetchTexture(specular);
|
|
if(specularTexture != null){
|
|
specularTexture.bind(openGLState,1);
|
|
Globals.renderingEngine.checkError();
|
|
openGLState.getActiveShader().setUniform(openGLState, "material.specular", 1);
|
|
Globals.renderingEngine.checkError();
|
|
}
|
|
}
|
|
} else {
|
|
openGLState.glBindTextureUnit(GL45.GL_TEXTURE0, texturePointer, GL45.GL_TEXTURE_2D);
|
|
Globals.renderingEngine.checkError();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks if this material has transparency
|
|
* @return true if there is transparency, false otherwise
|
|
*/
|
|
public boolean isTransparent(){
|
|
boolean rVal = false;
|
|
Texture diffuseTexture = Globals.assetManager.fetchTexture(diffuse);
|
|
if(diffuseTexture != null && diffuseTexture.isTransparent()){
|
|
rVal = true;
|
|
}
|
|
Texture specularTexture = Globals.assetManager.fetchTexture(specular);
|
|
if(specularTexture != null && specularTexture.isTransparent()){
|
|
rVal = true;
|
|
}
|
|
return rVal;
|
|
}
|
|
|
|
/**
|
|
* Frees the material
|
|
*/
|
|
public void free(){
|
|
GL45.glDeleteTextures(new int[]{
|
|
this.texturePointer,
|
|
this.normalPointer,
|
|
});
|
|
}
|
|
}
|