From ead3d6c8e4d7ffd6b83b44d6eaa2c80eccc1b5c1 Mon Sep 17 00:00:00 2001 From: wietse Date: Sat, 1 Aug 2026 17:03:38 +0200 Subject: [PATCH 1/3] Fix optional texture map lookup warnings Treat derived normal, specular, and displacement maps as optional assets and make their lookup cache safe for concurrent loading. Fixes #414. --- .../toniarts/openkeeper/utils/AssetUtils.java | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/src/main/java/toniarts/openkeeper/utils/AssetUtils.java b/src/main/java/toniarts/openkeeper/utils/AssetUtils.java index 83176862c..a00557f38 100644 --- a/src/main/java/toniarts/openkeeper/utils/AssetUtils.java +++ b/src/main/java/toniarts/openkeeper/utils/AssetUtils.java @@ -19,6 +19,7 @@ import com.jme3.asset.AssetInfo; import com.jme3.asset.AssetKey; import com.jme3.asset.AssetManager; +import com.jme3.asset.AssetNotFoundException; import com.jme3.asset.MaterialKey; import com.jme3.asset.ModelKey; import com.jme3.asset.TextureKey; @@ -48,9 +49,9 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import java.util.function.Supplier; import javax.imageio.ImageIO; import toniarts.openkeeper.Main; @@ -75,7 +76,7 @@ public final class AssetUtils { private final static Object ASSET_LOCK = new Object(); private final static AssetCache ASSET_CACHE = new SimpleAssetCache(); private final static AssetCache WEAK_ASSET_CACHE = new WeakRefAssetCache(); - private final static Map TEXTURE_MAP_CACHE = new HashMap<>(); + private final static ConcurrentMap TEXTURE_MAP_CACHE = new ConcurrentHashMap<>(); // Custom model data keys public final static String USER_DATA_KEY_REMOVABLE = "Removable"; @@ -234,16 +235,10 @@ public static void assignMapsToMaterial(AssetManager assetManager, Material mate private static void assignMapToMaterial(AssetManager assetManager, Material material, String paramName, String textureName) { - // Try to locate the texture - Boolean found = TEXTURE_MAP_CACHE.get(textureName); - if (found == null) { - TextureKey textureKey = new TextureKey(textureName, false); - - // See if it exists - AssetInfo assetInfo = assetManager.locateAsset(textureKey); - found = (assetInfo != null); - TEXTURE_MAP_CACHE.put(textureName, found); - } + // Companion maps are optional. Loading a missing asset throws without + // logging the warning produced by AssetManager.locateAsset(). + boolean found = TEXTURE_MAP_CACHE.computeIfAbsent(textureName, + name -> isOptionalTextureAvailable(assetManager, name)); // Set it if (found) { @@ -254,6 +249,15 @@ private static void assignMapToMaterial(AssetManager assetManager, Material mate } } + private static boolean isOptionalTextureAvailable(AssetManager assetManager, String textureName) { + try { + assetManager.loadTexture(new TextureKey(textureName, false)); + return true; + } catch (AssetNotFoundException e) { + return false; + } + } + private static String getNormalMapName(String texture) { return getCustomTextureMapName(texture, "n"); } From a3fdcf381729a722cb5dd0465d989a4aa6aebe6c Mon Sep 17 00:00:00 2001 From: wietse Date: Sun, 2 Aug 2026 19:12:12 +0200 Subject: [PATCH 2/3] Avoid duplicate load for optional texture maps --- .../toniarts/openkeeper/utils/AssetUtils.java | 35 +++++++++++-------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/src/main/java/toniarts/openkeeper/utils/AssetUtils.java b/src/main/java/toniarts/openkeeper/utils/AssetUtils.java index a00557f38..fa63557a8 100644 --- a/src/main/java/toniarts/openkeeper/utils/AssetUtils.java +++ b/src/main/java/toniarts/openkeeper/utils/AssetUtils.java @@ -235,29 +235,34 @@ public static void assignMapsToMaterial(AssetManager assetManager, Material mate private static void assignMapToMaterial(AssetManager assetManager, Material material, String paramName, String textureName) { - // Companion maps are optional. Loading a missing asset throws without - // logging the warning produced by AssetManager.locateAsset(). - boolean found = TEXTURE_MAP_CACHE.computeIfAbsent(textureName, - name -> isOptionalTextureAvailable(assetManager, name)); + Texture texture = null; + Boolean found = TEXTURE_MAP_CACHE.get(textureName); + if (found == null) { + // Companion maps are optional. Loading a missing asset throws without + // logging the warning produced by AssetManager.locateAsset(). + try { + texture = assetManager.loadTexture(new TextureKey(textureName, false)); + found = true; + } catch (AssetNotFoundException e) { + found = false; + } + Boolean cached = TEXTURE_MAP_CACHE.putIfAbsent(textureName, found); + if (cached != null) { + found = cached; + } + } // Set it if (found) { - TextureKey textureKey = new TextureKey(textureName, false); - material.setTexture(paramName, assetManager.loadTexture(textureKey)); + if (texture == null) { + texture = assetManager.loadTexture(new TextureKey(textureName, false)); + } + material.setTexture(paramName, texture); } else { material.clearParam(paramName); } } - private static boolean isOptionalTextureAvailable(AssetManager assetManager, String textureName) { - try { - assetManager.loadTexture(new TextureKey(textureName, false)); - return true; - } catch (AssetNotFoundException e) { - return false; - } - } - private static String getNormalMapName(String texture) { return getCustomTextureMapName(texture, "n"); } From 8dff1b846106a0ed1999572b7784e0c9d2a43644 Mon Sep 17 00:00:00 2001 From: wietse Date: Sun, 9 Aug 2026 13:40:15 +0200 Subject: [PATCH 3/3] Suppress instead of try/catch --- src/main/java/toniarts/openkeeper/Main.java | 8 ++++++++ .../toniarts/openkeeper/utils/AssetUtils.java | 17 +++-------------- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/src/main/java/toniarts/openkeeper/Main.java b/src/main/java/toniarts/openkeeper/Main.java index 11c7f4ca1..0c31be3b7 100644 --- a/src/main/java/toniarts/openkeeper/Main.java +++ b/src/main/java/toniarts/openkeeper/Main.java @@ -125,6 +125,7 @@ public static void main(String[] args) throws InvocationTargetException, Interru // set a better logging format System.setProperty("java.util.logging.SimpleFormatter.format", "%1$tF %1$tT %4$s %2$s - %5$s%6$s%n"); + suppressMissingAssetWarnings(); // Finally start it if everything went ok if (checkSetup(app)) { @@ -134,6 +135,13 @@ public static void main(String[] args) throws InvocationTargetException, Interru } } + private static void suppressMissingAssetWarnings() { + java.util.logging.Logger assetManagerLogger = java.util.logging.Logger.getLogger(AssetManager.class.getName()); + java.util.logging.Filter existingFilter = assetManagerLogger.getFilter(); + assetManagerLogger.setFilter(record -> !"Cannot locate resource: {0}".equals(record.getMessage()) + && (existingFilter == null || existingFilter.isLoggable(record))); + } + /** * Parse application parameters * diff --git a/src/main/java/toniarts/openkeeper/utils/AssetUtils.java b/src/main/java/toniarts/openkeeper/utils/AssetUtils.java index fa63557a8..357ddc86e 100644 --- a/src/main/java/toniarts/openkeeper/utils/AssetUtils.java +++ b/src/main/java/toniarts/openkeeper/utils/AssetUtils.java @@ -19,7 +19,6 @@ import com.jme3.asset.AssetInfo; import com.jme3.asset.AssetKey; import com.jme3.asset.AssetManager; -import com.jme3.asset.AssetNotFoundException; import com.jme3.asset.MaterialKey; import com.jme3.asset.ModelKey; import com.jme3.asset.TextureKey; @@ -235,17 +234,10 @@ public static void assignMapsToMaterial(AssetManager assetManager, Material mate private static void assignMapToMaterial(AssetManager assetManager, Material material, String paramName, String textureName) { - Texture texture = null; Boolean found = TEXTURE_MAP_CACHE.get(textureName); if (found == null) { - // Companion maps are optional. Loading a missing asset throws without - // logging the warning produced by AssetManager.locateAsset(). - try { - texture = assetManager.loadTexture(new TextureKey(textureName, false)); - found = true; - } catch (AssetNotFoundException e) { - found = false; - } + AssetInfo assetInfo = assetManager.locateAsset(new TextureKey(textureName, false)); + found = (assetInfo != null); Boolean cached = TEXTURE_MAP_CACHE.putIfAbsent(textureName, found); if (cached != null) { found = cached; @@ -254,10 +246,7 @@ private static void assignMapToMaterial(AssetManager assetManager, Material mate // Set it if (found) { - if (texture == null) { - texture = assetManager.loadTexture(new TextureKey(textureName, false)); - } - material.setTexture(paramName, texture); + material.setTexture(paramName, assetManager.loadTexture(new TextureKey(textureName, false))); } else { material.clearParam(paramName); }