diff --git a/assets/Data/entity/foliage/flowers.json b/assets/Data/entity/foliage/flowers.json index d95d42a4..a8b1c840 100644 --- a/assets/Data/entity/foliage/flowers.json +++ b/assets/Data/entity/foliage/flowers.json @@ -21,6 +21,28 @@ "path" : "Models/foliage/flowers/Flower_3_Group.gltf" } } + }, + { + "id" : "mushroom1", + "tokens" : [ + "FLAMMABLE" + ], + "hitboxes" : [ + { + "type": "hurt", + "offset": [0, 0.6, 0], + "radius": 0.35 + } + ], + "healthSystem" : { + "maxHealth" : 5, + "onDamageIFrames" : 0 + }, + "graphicsTemplate": { + "model": { + "path" : "Models/foliage/mushroom1.glb" + } + } } ], "files" : [ diff --git a/assets/Models/foliage/Mushrooms.png b/assets/Models/foliage/Mushrooms.png new file mode 100644 index 00000000..c5d11a30 Binary files /dev/null and b/assets/Models/foliage/Mushrooms.png differ diff --git a/assets/Models/foliage/mushroom1.glb b/assets/Models/foliage/mushroom1.glb new file mode 100644 index 00000000..47fe2a3e Binary files /dev/null and b/assets/Models/foliage/mushroom1.glb differ diff --git a/docs/src/progress/renderertodo.md b/docs/src/progress/renderertodo.md index 54a8f43a..c9dc489e 100644 --- a/docs/src/progress/renderertodo.md +++ b/docs/src/progress/renderertodo.md @@ -1599,6 +1599,7 @@ Refactor ProceduralChunkGenerator NoiseVoxelGen work to make elevation values align with voxel values that are generated Non-procedural voxel generation fix Flower foliage item +Texture loading from model files (ie can load texture path from model file) @@ -1725,9 +1726,6 @@ Light Manager - Eventually support spot lights? - Point shadows ??? -gltf Support - - Texture loading from gltf file - Security - FileUtils sanitation function check if requested file is in game root dir diff --git a/src/main/java/electrosphere/engine/assetmanager/AssetManager.java b/src/main/java/electrosphere/engine/assetmanager/AssetManager.java index bc181dd4..cd0df053 100644 --- a/src/main/java/electrosphere/engine/assetmanager/AssetManager.java +++ b/src/main/java/electrosphere/engine/assetmanager/AssetManager.java @@ -125,7 +125,8 @@ public class AssetManager { Globals.profiler.beginCpuSample("AssetManager.loadAssetsInQueue - Load textures"); for(String currentPath : texturesInQueue){ texturesInQueue.remove(currentPath); - texturesLoadedIntoMemory.put(currentPath, new Texture(Globals.renderingEngine.getOpenGLState(), currentPath)); + Texture tex = new Texture(Globals.renderingEngine.getOpenGLState(), currentPath); + texturesLoadedIntoMemory.put(currentPath, tex); } Globals.profiler.endCpuSample(); //audio from disk diff --git a/src/main/java/electrosphere/renderer/model/Material.java b/src/main/java/electrosphere/renderer/model/Material.java index b20ae519..ca0897e6 100644 --- a/src/main/java/electrosphere/renderer/model/Material.java +++ b/src/main/java/electrosphere/renderer/model/Material.java @@ -1,14 +1,26 @@ 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.AIScene; +import org.lwjgl.assimp.AIString; +import org.lwjgl.assimp.AITexture; +import org.lwjgl.assimp.Assimp; import org.lwjgl.opengl.GL40; import static org.lwjgl.opengl.GL11.GL_TEXTURE_2D; import static org.lwjgl.opengl.GL13.GL_TEXTURE0; +import java.io.File; +import java.nio.IntBuffer; +import java.nio.file.Path; + /** * A material */ @@ -31,7 +43,6 @@ public class Material { * A material that contains textures */ public Material(){ - } /** @@ -42,21 +53,104 @@ public class Material { this.diffuse = diffuse; } - //basically useless because blender doesn't support exporting mats with fbx - public static Material load_material_from_aimaterial(AIMaterial input){ + /** + * 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 load_material_from_aimaterial(String path, AIScene scene, AIMaterial input){ Material rVal = new Material(); -// if(input.mNumProperties() > 0){ -// PointerBuffer property_buffer = input.mProperties(); -// while(property_buffer.hasRemaining()){ -// AIMaterialProperty new_property = AIMaterialProperty.create(property_buffer.get()); -// // System.out.println("Property: " + new_property.mKey().dataString()); -// } -// } + + 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 + 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 "*" 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); + } + } else { + throw new Error("Probably got an actual texture path, haven't written code to handle this.. " + texturePath); + } + } + + //free mem + aiPathString.free(); + return rVal; } + + /** + * 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 get_diffuse(){ return diffuse; } + + /** + * Gets the path for the specular of the material + * @return The path for the specular texture + */ public String get_specular(){ return specular; } diff --git a/src/main/java/electrosphere/renderer/model/Model.java b/src/main/java/electrosphere/renderer/model/Model.java index 7ac0e56b..f1a9b50e 100644 --- a/src/main/java/electrosphere/renderer/model/Model.java +++ b/src/main/java/electrosphere/renderer/model/Model.java @@ -101,6 +101,19 @@ public class Model { if(modelMetadata != null){ globalTransform = modelMetadata.getGlobalTransform(); } + + + // + //Load materials + // + if(scene.mNumMaterials() > 0){ + rVal.materials = new ArrayList(); + PointerBuffer material_buffer = scene.mMaterials(); + while(material_buffer.hasRemaining()){ + rVal.materials.add(Material.load_material_from_aimaterial(path,scene,AIMaterial.create(material_buffer.get()))); + } + } + // //load meshes @@ -120,6 +133,16 @@ public class Model { if(currentMesh.getBoundingSphere().r > rVal.boundingSphere.r){ rVal.boundingSphere.r = currentMesh.getBoundingSphere().r; } + + //conditionally add material + int materialIndex = aiMesh.mMaterialIndex(); + if(materialIndex < rVal.materials.size()){ + Material mat = rVal.materials.get(materialIndex); + //only assign if the diffuse is actually set (ie we've actually loaded it properly) + if(mat.get_diffuse() != null){ + currentMesh.setMaterial(rVal.materials.get(materialIndex)); + } + } } // @@ -196,17 +219,6 @@ public class Model { rVal.animMap.put(newAnim.name,newAnim); } - // - //Load materials - // - if(scene.mNumMaterials() > 0){ - rVal.materials = new ArrayList(); - PointerBuffer material_buffer = scene.mMaterials(); - while(material_buffer.hasRemaining()){ - rVal.materials.add(Material.load_material_from_aimaterial(AIMaterial.create(material_buffer.get()))); - } - } - return rVal; } diff --git a/src/main/java/electrosphere/renderer/texture/Texture.java b/src/main/java/electrosphere/renderer/texture/Texture.java index 0840a72a..b970f3a5 100644 --- a/src/main/java/electrosphere/renderer/texture/Texture.java +++ b/src/main/java/electrosphere/renderer/texture/Texture.java @@ -232,6 +232,12 @@ public class Texture { */ public Texture(OpenGLState openGlState, String path){ LoggerInterface.loggerRenderer.DEBUG("Create texture " + path); + if(path == null){ + throw new Error("Path is null"); + } + if(path.length() == 0){ + throw new Error("Path is empty"); + } this.path = path; if(!Globals.HEADLESS){ LoggerInterface.loggerRenderer.DEBUG("Setup texture object");